using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.LowLevel;
using UnityEngine.PlayerLoop;
namespace Unity.Netcode
{
///
/// Defines the required interface of a network update system being executed by the .
///
public interface INetworkUpdateSystem
{
///
/// The update method that is being executed in the context of related .
///
/// The that is being executed.
void NetworkUpdate(NetworkUpdateStage updateStage);
}
///
/// Defines network update stages being executed by the network update loop.
/// See for more details on update stages:
/// https://docs.unity3d.com/ScriptReference/PlayerLoop.Initialization.html
///
public enum NetworkUpdateStage : byte
{
///
/// Default value
///
Unset = 0,
///
/// Very first initialization update
///
Initialization = 1,
///
/// Invoked before Fixed update
///
EarlyUpdate = 2,
///
/// Fixed Update (i.e. state machine, physics, animations, etc)
///
FixedUpdate = 3,
///
/// Updated before the Monobehaviour.Update for all components is invoked
///
PreUpdate = 4,
///
/// Updated when the Monobehaviour.Update for all components is invoked
///
Update = 5,
///
/// Updated before the Monobehaviour.LateUpdate for all components is invoked
///
PreLateUpdate = 6,
///
/// Updated after the Monobehaviour.LateUpdate for all components is invoked
///
PostLateUpdate = 7
}
///
/// Represents the network update loop injected into low-level player loop in Unity.
///
public static class NetworkUpdateLoop
{
private static Dictionary> s_UpdateSystem_Sets;
private static Dictionary s_UpdateSystem_Arrays;
private const int k_UpdateSystem_InitialArrayCapacity = 1024;
static NetworkUpdateLoop()
{
s_UpdateSystem_Sets = new Dictionary>();
s_UpdateSystem_Arrays = new Dictionary();
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
{
s_UpdateSystem_Sets.Add(updateStage, new HashSet());
s_UpdateSystem_Arrays.Add(updateStage, new INetworkUpdateSystem[k_UpdateSystem_InitialArrayCapacity]);
}
}
///
/// Registers a network update system to be executed in all network update stages.
///
/// The implementation to register for all network updates
public static void RegisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
{
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
{
RegisterNetworkUpdate(updateSystem, updateStage);
}
}
///
/// Registers a network update system to be executed in a specific network update stage.
///
/// The implementation to register for all network updates
/// The being registered for the implementation
public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
{
var sysSet = s_UpdateSystem_Sets[updateStage];
if (!sysSet.Contains(updateSystem))
{
sysSet.Add(updateSystem);
int setLen = sysSet.Count;
var sysArr = s_UpdateSystem_Arrays[updateStage];
int arrLen = sysArr.Length;
if (setLen > arrLen)
{
// double capacity
sysArr = s_UpdateSystem_Arrays[updateStage] = new INetworkUpdateSystem[arrLen *= 2];
}
sysSet.CopyTo(sysArr);
if (setLen < arrLen)
{
// null terminator
sysArr[setLen] = null;
}
}
}
///
/// Unregisters a network update system from all network update stages.
///
/// The implementation to deregister from all network updates
public static void UnregisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
{
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
{
UnregisterNetworkUpdate(updateSystem, updateStage);
}
}
///
/// Unregisters a network update system from a specific network update stage.
///
/// The implementation to deregister from all network updates
/// The to be deregistered from the implementation
public static void UnregisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
{
var sysSet = s_UpdateSystem_Sets[updateStage];
if (sysSet.Contains(updateSystem))
{
sysSet.Remove(updateSystem);
int setLen = sysSet.Count;
var sysArr = s_UpdateSystem_Arrays[updateStage];
int arrLen = sysArr.Length;
sysSet.CopyTo(sysArr);
if (setLen < arrLen)
{
// null terminator
sysArr[setLen] = null;
}
}
}
///
/// The current network update stage being executed.
///
public static NetworkUpdateStage UpdateStage;
private static void RunNetworkUpdateStage(NetworkUpdateStage updateStage)
{
UpdateStage = updateStage;
var sysArr = s_UpdateSystem_Arrays[updateStage];
int arrLen = sysArr.Length;
for (int curIdx = 0; curIdx < arrLen; curIdx++)
{
var curSys = sysArr[curIdx];
if (curSys == null)
{
// null terminator
break;
}
curSys.NetworkUpdate(updateStage);
}
}
internal struct NetworkInitialization
{
public static PlayerLoopSystem CreateLoopSystem()
{
return new PlayerLoopSystem
{
type = typeof(NetworkInitialization),
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.Initialization)
};
}
}
internal struct NetworkEarlyUpdate
{
public static PlayerLoopSystem CreateLoopSystem()
{
return new PlayerLoopSystem
{
type = typeof(NetworkEarlyUpdate),
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.EarlyUpdate)
};
}
}
internal struct NetworkFixedUpdate
{
public static PlayerLoopSystem CreateLoopSystem()
{
return new PlayerLoopSystem
{
type = typeof(NetworkFixedUpdate),
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.FixedUpdate)
};
}
}
internal struct NetworkPreUpdate
{
public static PlayerLoopSystem CreateLoopSystem()
{
return new PlayerLoopSystem
{
type = typeof(NetworkPreUpdate),
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PreUpdate)
};
}
}
internal struct NetworkUpdate
{
public static PlayerLoopSystem CreateLoopSystem()
{
return new PlayerLoopSystem
{
type = typeof(NetworkUpdate),
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.Update)
};
}
}
internal struct NetworkPreLateUpdate
{
public static PlayerLoopSystem CreateLoopSystem()
{
return new PlayerLoopSystem
{
type = typeof(NetworkPreLateUpdate),
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PreLateUpdate)
};
}
}
internal struct NetworkPostLateUpdate
{
public static PlayerLoopSystem CreateLoopSystem()
{
return new PlayerLoopSystem
{
type = typeof(NetworkPostLateUpdate),
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PostLateUpdate)
};
}
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void Initialize()
{
UnregisterLoopSystems();
RegisterLoopSystems();
}
private enum LoopSystemPosition
{
After,
Before
}
private static bool TryAddLoopSystem(ref PlayerLoopSystem parentLoopSystem, PlayerLoopSystem childLoopSystem, Type anchorSystemType, LoopSystemPosition loopSystemPosition)
{
int systemPosition = -1;
if (anchorSystemType != null)
{
for (int i = 0; i < parentLoopSystem.subSystemList.Length; i++)
{
var subsystem = parentLoopSystem.subSystemList[i];
if (subsystem.type == anchorSystemType)
{
systemPosition = loopSystemPosition == LoopSystemPosition.After ? i + 1 : i;
break;
}
}
}
else
{
systemPosition = loopSystemPosition == LoopSystemPosition.After ? parentLoopSystem.subSystemList.Length : 0;
}
if (systemPosition == -1)
{
return false;
}
var newSubsystemList = new PlayerLoopSystem[parentLoopSystem.subSystemList.Length + 1];
// begin = systemsBefore + systemsAfter
// + systemsBefore
if (systemPosition > 0)
{
Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition);
}
// + childSystem
newSubsystemList[systemPosition] = childLoopSystem;
// + systemsAfter
if (systemPosition < parentLoopSystem.subSystemList.Length)
{
Array.Copy(parentLoopSystem.subSystemList, systemPosition, newSubsystemList, systemPosition + 1, parentLoopSystem.subSystemList.Length - systemPosition);
}
// end = systemsBefore + childSystem + systemsAfter
parentLoopSystem.subSystemList = newSubsystemList;
return true;
}
private static bool TryRemoveLoopSystem(ref PlayerLoopSystem parentLoopSystem, Type childSystemType)
{
int systemPosition = -1;
for (int i = 0; i < parentLoopSystem.subSystemList.Length; i++)
{
var subsystem = parentLoopSystem.subSystemList[i];
if (subsystem.type == childSystemType)
{
systemPosition = i;
break;
}
}
if (systemPosition == -1)
{
return false;
}
var newSubsystemList = new PlayerLoopSystem[parentLoopSystem.subSystemList.Length - 1];
// begin = systemsBefore + childSystem + systemsAfter
// + systemsBefore
if (systemPosition > 0)
{
Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition);
}
// + systemsAfter
if (systemPosition < parentLoopSystem.subSystemList.Length - 1)
{
Array.Copy(parentLoopSystem.subSystemList, systemPosition + 1, newSubsystemList, systemPosition, parentLoopSystem.subSystemList.Length - systemPosition - 1);
}
// end = systemsBefore + systemsAfter
parentLoopSystem.subSystemList = newSubsystemList;
return true;
}
internal static void RegisterLoopSystems()
{
var rootPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
for (int i = 0; i < rootPlayerLoop.subSystemList.Length; i++)
{
ref var currentSystem = ref rootPlayerLoop.subSystemList[i];
if (currentSystem.type == typeof(Initialization))
{
TryAddLoopSystem(ref currentSystem, NetworkInitialization.CreateLoopSystem(), null, LoopSystemPosition.After);
}
else if (currentSystem.type == typeof(EarlyUpdate))
{
TryAddLoopSystem(ref currentSystem, NetworkEarlyUpdate.CreateLoopSystem(), typeof(EarlyUpdate.ScriptRunDelayedStartupFrame), LoopSystemPosition.Before);
}
else if (currentSystem.type == typeof(FixedUpdate))
{
TryAddLoopSystem(ref currentSystem, NetworkFixedUpdate.CreateLoopSystem(), typeof(FixedUpdate.ScriptRunBehaviourFixedUpdate), LoopSystemPosition.Before);
}
else if (currentSystem.type == typeof(PreUpdate))
{
TryAddLoopSystem(ref currentSystem, NetworkPreUpdate.CreateLoopSystem(), typeof(PreUpdate.PhysicsUpdate), LoopSystemPosition.Before);
}
else if (currentSystem.type == typeof(Update))
{
TryAddLoopSystem(ref currentSystem, NetworkUpdate.CreateLoopSystem(), typeof(Update.ScriptRunBehaviourUpdate), LoopSystemPosition.Before);
}
else if (currentSystem.type == typeof(PreLateUpdate))
{
TryAddLoopSystem(ref currentSystem, NetworkPreLateUpdate.CreateLoopSystem(), typeof(PreLateUpdate.ScriptRunBehaviourLateUpdate), LoopSystemPosition.Before);
}
else if (currentSystem.type == typeof(PostLateUpdate))
{
TryAddLoopSystem(ref currentSystem, NetworkPostLateUpdate.CreateLoopSystem(), typeof(PostLateUpdate.PlayerSendFrameComplete), LoopSystemPosition.After);
}
}
PlayerLoop.SetPlayerLoop(rootPlayerLoop);
}
internal static void UnregisterLoopSystems()
{
var rootPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
for (int i = 0; i < rootPlayerLoop.subSystemList.Length; i++)
{
ref var currentSystem = ref rootPlayerLoop.subSystemList[i];
if (currentSystem.type == typeof(Initialization))
{
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkInitialization));
}
else if (currentSystem.type == typeof(EarlyUpdate))
{
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkEarlyUpdate));
}
else if (currentSystem.type == typeof(FixedUpdate))
{
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkFixedUpdate));
}
else if (currentSystem.type == typeof(PreUpdate))
{
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPreUpdate));
}
else if (currentSystem.type == typeof(Update))
{
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkUpdate));
}
else if (currentSystem.type == typeof(PreLateUpdate))
{
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPreLateUpdate));
}
else if (currentSystem.type == typeof(PostLateUpdate))
{
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPostLateUpdate));
}
}
PlayerLoop.SetPlayerLoop(rootPlayerLoop);
}
}
}