diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 0d8fb3a2fb..d58dfaf6ea 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -11,6 +11,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added - Added a new variant of `UnityTransport.GetDefaultPipelineConfigurations` that takes a reference to the created `NetworkDriver`. This will register all pipeline stages that `UnityTransport` requires, removing the need to manually register them in your own custom driver constructor. (#3980) +- Added support for Unity's Fast Enter Play Mode with domain reload disabled. (#3956) ### Changed diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index 7d82872890..3fb8166408 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -706,7 +706,6 @@ protected virtual bool OnIsServerAuthoritative() private int[] m_TransitionHash; private int[] m_AnimationHash; private float[] m_LayerWeights; - private static byte[] s_EmptyArray = new byte[] { }; private List m_ParametersToUpdate; private RpcParams m_RpcParams; private IGroupRpcTarget m_TargetGroup; diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 3ca9093b34..7254cac5cc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -24,6 +24,18 @@ public class NetworkTransform : NetworkBehaviour [HideInInspector] [SerializeField] internal bool NetworkTransformExpanded; + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + CurrentTick = 0; + TrackStateUpdateId = false; + AssignDefaultInterpolationType = false; + DefaultInterpolationType = default; + s_NetworkTickRegistration = new Dictionary(); + InterpolationBufferTickOffset = 0; + s_TickSynchPosition = 0; + } #endif internal enum Axis { X, Y, Z } @@ -1361,7 +1373,7 @@ public enum AuthorityModes /// /// When set each state update will contain a state identifier /// - internal static bool TrackStateUpdateId = false; + internal static bool TrackStateUpdateId; /// /// Enabled by default. @@ -2062,19 +2074,6 @@ private void TryCommitTransform(bool synchronize = false, bool settingState = fa childNetworkTransform.OnNetworkTick(true); } } - - // Synchronize any parented children with the parent's motion - foreach (var child in m_ParentedChildren) - { - // Synchronize any nested NetworkTransforms of the child with the parent's - foreach (var childNetworkTransform in child.NetworkTransforms) - { - if (childNetworkTransform.CanCommitToTransform) - { - childNetworkTransform.OnNetworkTick(true); - } - } - } } } } @@ -2221,13 +2220,11 @@ private bool CheckForStateChange(ref NetworkTransformState networkState, bool is // values are applied. var hasParentNetworkObject = false; - var parentNetworkObject = (NetworkObject)null; - // If the NetworkObject belonging to this NetworkTransform instance has a parent // (i.e. this handles nested NetworkTransforms under a parent at some layer above) if (NetworkObject.transform.parent != null) { - parentNetworkObject = NetworkObject.transform.parent.GetComponent(); + var parentNetworkObject = NetworkObject.transform.parent.GetComponent(); // In-scene placed NetworkObjects parented under a GameObject with no // NetworkObject preserve their lossyScale when synchronizing. @@ -3363,19 +3360,6 @@ private void OnNetworkStateChanged(NetworkTransformState oldState, NetworkTransf childNetworkTransform.OnNetworkTick(true); } } - - // Synchronize any parented children with the parent's motion - foreach (var child in m_ParentedChildren) - { - // Synchronize any nested NetworkTransforms of the child with the parent's - foreach (var childNetworkTransform in child.NetworkTransforms) - { - if (childNetworkTransform.CanCommitToTransform) - { - childNetworkTransform.OnNetworkTick(true); - } - } - } } // Provide notifications when the state has been updated @@ -3634,8 +3618,6 @@ internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManag /// public override void OnNetworkSpawn() { - m_ParentedChildren.Clear(); - Initialize(); if (CanCommitToTransform && !SwitchTransformSpaceWhenParented) @@ -3647,7 +3629,6 @@ public override void OnNetworkSpawn() private void CleanUpOnDestroyOrDespawn() { - m_ParentedChildren.Clear(); #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D var forUpdate = !m_UseRigidbodyForMotion; #else @@ -3861,7 +3842,6 @@ protected override void OnOwnershipChanged(ulong previous, ulong current) } internal bool IsNested; - private List m_ParentedChildren = new List(); private bool m_IsFirstNetworkTransform; @@ -4694,7 +4674,7 @@ internal static void UpdateNetworkTick(NetworkManager networkManager) /// The default value is 1 tick (plus the tick latency). When running on a local network, reducing this to 0 is recommended.
/// /// - public static int InterpolationBufferTickOffset = 0; + public static int InterpolationBufferTickOffset; internal static float GetTickLatency(NetworkManager networkManager) { if (networkManager.IsListening) @@ -4799,6 +4779,7 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) } } } + private static int s_TickSynchPosition; private int m_NextTickSync; diff --git a/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs b/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs index f4d9b25dd2..91941c812e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs @@ -42,9 +42,6 @@ public static class QuaternionCompressor private const ushort k_True = 1; private const ushort k_False = 0; - // Used to store the absolute value of the 4 quaternion elements - private static Quaternion s_QuatAbsValues = Quaternion.identity; - /// /// Compresses a Quaternion into an unsigned integer /// @@ -54,38 +51,31 @@ public static class QuaternionCompressor public static uint CompressQuaternion(ref Quaternion quaternion) { // Store off the absolute value for each Quaternion element - s_QuatAbsValues[0] = Mathf.Abs(quaternion[0]); - s_QuatAbsValues[1] = Mathf.Abs(quaternion[1]); - s_QuatAbsValues[2] = Mathf.Abs(quaternion[2]); - s_QuatAbsValues[3] = Mathf.Abs(quaternion[3]); + var quatAbsValue0 = Mathf.Abs(quaternion[0]); + var quatAbsValue1 = Mathf.Abs(quaternion[1]); + var quatAbsValue2 = Mathf.Abs(quaternion[2]); + var quatAbsValue3 = Mathf.Abs(quaternion[3]); // Get the largest element value of the quaternion to know what the remaining "Smallest Three" values are - var quatMax = Mathf.Max(s_QuatAbsValues[0], s_QuatAbsValues[1], s_QuatAbsValues[2], s_QuatAbsValues[3]); + var quatMax = Mathf.Max(quatAbsValue0, quatAbsValue1, quatAbsValue2, quatAbsValue3); // Find the index of the largest element so we can skip that element while compressing and decompressing - var indexToSkip = (ushort)(s_QuatAbsValues[0] == quatMax ? 0 : s_QuatAbsValues[1] == quatMax ? 1 : s_QuatAbsValues[2] == quatMax ? 2 : 3); + var indexToSkip = (ushort)(quatAbsValue0 == quatMax ? 0 : quatAbsValue1 == quatMax ? 1 : quatAbsValue2 == quatMax ? 2 : 3); // Get the sign of the largest element which is all that is needed when calculating the sum of squares of a normalized quaternion. - var quatMaxSign = (quaternion[indexToSkip] < 0 ? k_True : k_False); // Start with the index to skip which will be shifted to the highest two bits var compressed = (uint)indexToSkip; - // Step 1: Start with the first element - var currentIndex = 0; - // Step 2: If we are on the index to skip preserve the current compressed value, otherwise proceed to step 3 and 4 // Step 3: Get the sign of the element we are processing. If it is the not the same as the largest value's sign bit then we set the bit // Step 4: Get the compressed and encoded value by multiplying the absolute value of the current element by k_CompressionEcodingMask and round that result up - compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed; - currentIndex++; + compressed = 0 != indexToSkip ? (compressed << 10) | (uint)((quaternion[0] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * quatAbsValue0) : compressed; // Repeat the last 3 steps for the remaining elements - compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed; - currentIndex++; - compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed; - currentIndex++; - compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed; + compressed = 1 != indexToSkip ? (compressed << 10) | (uint)((quaternion[1] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * quatAbsValue1) : compressed; + compressed = 2 != indexToSkip ? (compressed << 10) | (uint)((quaternion[2] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * quatAbsValue2) : compressed; + compressed = 3 != indexToSkip ? (compressed << 10) | (uint)((quaternion[3] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * quatAbsValue3) : compressed; // Return the compress quaternion return compressed; diff --git a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs index bf54c4d964..7239da4e7a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs @@ -20,7 +20,7 @@ public struct ContactEventHandlerInfo ///
public bool ProvideNonRigidBodyContactEvents; /// - /// When set to true, the will prioritize invoking

+ /// When set to true, the will prioritize invoking
/// if it is the 2nd colliding body in the contact pair being processed. With distributed authority, setting this value to true when a is owned by the local client
/// will assure is only invoked on the authoritative side. ///
@@ -109,7 +109,7 @@ private void OnEnable() { m_ResultsArray = new NativeArray(16, Allocator.Persistent); Physics.ContactEvent += Physics_ContactEvent; - if (Instance != null) + if (Instance != null && Instance != this) { NetworkLog.LogError($"[Invalid][Multiple Instances] Found more than one instance of {nameof(RigidbodyContactEventManager)}: {name} and {Instance.name}"); NetworkLog.LogError($"[Disable][Additional Instance] Disabling {name} instance!"); diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs index c6f9b4a222..f6266db2a5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs @@ -13,49 +13,31 @@ public class CommandLineOptions /// /// Command-line options singleton /// - public static CommandLineOptions Instance - { - get - { - if (s_Instance == null) - { - s_Instance = new CommandLineOptions(); - } - return s_Instance; - } - private set - { - s_Instance = value; - } - } - private static CommandLineOptions s_Instance; - - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void RuntimeInitializeOnLoad() => Instance = new CommandLineOptions(); + public static CommandLineOptions Instance { get; private set; } // Contains the current application instance domain's command line arguments - internal static List CommandLineArguments = new List(); + private static List s_CommandLineArguments = new List(Environment.GetCommandLineArgs()); - // Invoked upon application start, after scene load - [RuntimeInitializeOnLoadMethod] - private static void ParseCommandLineArguments() + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void InitializeOnLoad() { - // Get all the command line arguments to be parsed later and/or modified - // prior to being parsed (for testing purposes). - CommandLineArguments = new List(Environment.GetCommandLineArgs()); + Instance = new CommandLineOptions(); +#if UNITY_EDITOR + s_CommandLineArguments = new List(Environment.GetCommandLineArgs()); +#endif } /// - /// Returns the value of an argument or null if there the argument is not present + /// Returns the value of an argument or null if the argument is not present /// /// The name of the argument /// Value of the command line argument passed in. public string GetArg(string arg) { - var argIndex = CommandLineArguments.IndexOf(arg); - if (argIndex >= 0 && argIndex < CommandLineArguments.Count - 1) + var argIndex = s_CommandLineArguments.IndexOf(arg); + if (argIndex >= 0 && argIndex < s_CommandLineArguments.Count - 1) { - return CommandLineArguments[argIndex + 1]; + return s_CommandLineArguments[argIndex + 1]; } return null; } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs index 52945c4e46..6733429ee2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs @@ -14,6 +14,10 @@ internal static class ComponentFactory internal delegate object CreateObjectDelegate(NetworkManager networkManager); private static Dictionary s_Delegates = new Dictionary(); +#if UNITY_EDITOR + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => s_Delegates = new Dictionary(); +#endif /// /// Instantiates an instance of a given interface diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 59f9e637b6..28ec5033b8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -1299,8 +1299,6 @@ internal void NetworkVariableUpdate(ulong targetClientId, bool forceSend = false } } - internal static bool LogSentVariableUpdateMessage; - private bool CouldHaveDirtyNetworkVariables() { // TODO: There should be a better way by reading one dirty variable vs. 'n' diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 8fc97dd84e..b915e21d4b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -21,6 +21,20 @@ namespace Unity.Netcode [HelpURL(HelpUrls.NetworkManager)] public class NetworkManager : MonoBehaviour, INetworkUpdateSystem { +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + Singleton = null; + OnInstantiated = null; + OnDestroying = null; + OnSingletonReady = null; + OnNetworkManagerReset = null; + IsDistributedAuthority = false; + s_SerializedType = new List(); + DisableNotOptimizedSerializedType = false; + } +#endif /// /// Subscribe to this static event to get notifications when a instance has been instantiated. /// @@ -31,7 +45,6 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem /// public static event Action OnDestroying; - #if UNITY_EDITOR // Inspector view expand/collapse settings for this derived child class [HideInInspector] @@ -1797,6 +1810,10 @@ internal abstract class NetcodeAnalytics internal static ResetNetworkManagerDelegate OnNetworkManagerReset; + + /// + /// This is called by the Unity Editor reset button. See which is handled in "NetworkManagerHelper.cs". + /// private void Reset() { OnNetworkManagerReset?.Invoke(this); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index d2f5aa5869..ca7253caf1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1725,6 +1725,11 @@ internal void SetIsDestroying() IsDestroying = true; } + private void OnDisable() + { + SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged; + } + private void OnDestroy() { // Apply the is destroying flag @@ -2506,6 +2511,10 @@ private void OnTransformParentChanged() // If you couldn't find your parent, we put you into OrphanChildren set and every time we spawn another NetworkObject locally due to replication, // we call CheckOrphanChildren() method and quickly iterate over OrphanChildren set and see if we can reparent/adopt one. internal static HashSet OrphanChildren = new HashSet(); +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => OrphanChildren = new HashSet(); +#endif internal bool ApplyNetworkParenting(bool removeParent = false, bool ignoreNotSpawned = false, bool orphanedChildPass = false, bool enableNotification = true) { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs index c48dea0333..5ac60e2cd4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs @@ -70,19 +70,19 @@ public enum NetworkUpdateStage : byte /// public static class NetworkUpdateLoop { - private static Dictionary> s_UpdateSystem_Sets; - private static Dictionary s_UpdateSystem_Arrays; - private const int k_UpdateSystem_InitialArrayCapacity = 1024; + private static Dictionary> s_UpdateSystemSets; + private static Dictionary s_UpdateSystemArrays; + private const int k_UpdateSystemInitialArrayCapacity = 1024; static NetworkUpdateLoop() { - s_UpdateSystem_Sets = new Dictionary>(); - s_UpdateSystem_Arrays = new Dictionary(); + s_UpdateSystemSets = new Dictionary>(); + s_UpdateSystemArrays = 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]); + s_UpdateSystemSets.Add(updateStage, new HashSet()); + s_UpdateSystemArrays.Add(updateStage, new INetworkUpdateSystem[k_UpdateSystemInitialArrayCapacity]); } } @@ -105,19 +105,19 @@ public static void RegisterAllNetworkUpdates(this INetworkUpdateSystem updateSys /// The being registered for the implementation public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update) { - var sysSet = s_UpdateSystem_Sets[updateStage]; + var sysSet = s_UpdateSystemSets[updateStage]; if (!sysSet.Contains(updateSystem)) { sysSet.Add(updateSystem); int setLen = sysSet.Count; - var sysArr = s_UpdateSystem_Arrays[updateStage]; + var sysArr = s_UpdateSystemArrays[updateStage]; int arrLen = sysArr.Length; if (setLen > arrLen) { // double capacity - sysArr = s_UpdateSystem_Arrays[updateStage] = new INetworkUpdateSystem[arrLen *= 2]; + sysArr = s_UpdateSystemArrays[updateStage] = new INetworkUpdateSystem[arrLen *= 2]; } sysSet.CopyTo(sysArr); @@ -149,13 +149,13 @@ public static void UnregisterAllNetworkUpdates(this INetworkUpdateSystem 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]; + var sysSet = s_UpdateSystemSets[updateStage]; if (sysSet.Contains(updateSystem)) { sysSet.Remove(updateSystem); int setLen = sysSet.Count; - var sysArr = s_UpdateSystem_Arrays[updateStage]; + var sysArr = s_UpdateSystemArrays[updateStage]; int arrLen = sysArr.Length; sysSet.CopyTo(sysArr); @@ -177,7 +177,7 @@ internal static void RunNetworkUpdateStage(NetworkUpdateStage updateStage) { UpdateStage = updateStage; - var sysArr = s_UpdateSystem_Arrays[updateStage]; + var sysArr = s_UpdateSystemArrays[updateStage]; int arrLen = sysArr.Length; for (int curIdx = 0; curIdx < arrLen; curIdx++) { @@ -291,6 +291,17 @@ public static PlayerLoopSystem CreateLoopSystem() [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void Initialize() { +#if UNITY_EDITOR + // Reset statics + s_UpdateSystemSets = new Dictionary>(); + s_UpdateSystemArrays = new Dictionary(); + foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage))) + { + s_UpdateSystemSets.Add(updateStage, new HashSet()); + s_UpdateSystemArrays.Add(updateStage, new INetworkUpdateSystem[k_UpdateSystemInitialArrayCapacity]); + } + UpdateStage = default; +#endif UnregisterLoopSystems(); RegisterLoopSystems(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs index 29a092e77d..a52b174133 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs @@ -6,13 +6,21 @@ namespace Unity.Netcode { + /// + /// Log configuration containing : + /// - used in LogContextNetworkManager.cs + /// - used in SceneEventData.cs + /// - used in SceneEventData.cs + /// internal struct LogConfiguration { internal bool LogNetworkManagerRole; + internal bool LogSerializationOrder; + internal bool EnableSerializationLogs; } /// - /// Helper class for logging + /// Helper class for logging. /// public static class NetworkLog { @@ -58,7 +66,7 @@ internal static void ConfigureIntegrationTestLogging(NetworkManager networkManag internal static void LogWarning(Context context) => s_Log.Warning(context); /// - /// Locally logs a error log with Netcode prefixing. + /// Locally logs an error log with Netcode prefixing. /// /// The message to log [HideInCallstack] diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs index f05d000fec..4d0cd8fec8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs @@ -96,6 +96,10 @@ public virtual unsafe void CleanupStaleTriggers() /// Used for testing purposes /// internal static bool IncludeMessageType = true; +#if UNITY_EDITOR + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => IncludeMessageType = true; +#endif private string GetWarningMessage(IDeferredNetworkMessageManager.TriggerType triggerType, ulong key, TriggerInfo triggerInfo, float spawnTimeout) { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs index ec460cc821..26f33c1045 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -#if UNITY_EDITOR -using UnityEditor; -#endif namespace Unity.Netcode { @@ -53,6 +50,10 @@ internal struct ILPPMessageProvider : INetworkMessageProvider // Enable this for integration tests that need no message types defined internal static bool IntegrationTestNoMessages; +#if UNITY_EDITOR + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => IntegrationTestNoMessages = false; +#endif /// /// Returns a table of message type to NetworkMessageTypes enum value @@ -141,15 +142,15 @@ internal static Dictionary GetMessageTypesMap() } #if UNITY_EDITOR - [InitializeOnLoadMethod] + [UnityEditor.InitializeOnLoadMethod] public static void NotifyOnPlayStateChange() { - EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + UnityEditor.EditorApplication.playModeStateChanged += OnPlayModeStateChanged; } - public static void OnPlayModeStateChanged(PlayModeStateChange change) + public static void OnPlayModeStateChanged(UnityEditor.PlayModeStateChange change) { - if (change == PlayModeStateChange.ExitingPlayMode) + if (change == UnityEditor.PlayModeStateChange.ExitingPlayMode) { // Clear out the network message types, because ILPP-generated RuntimeInitializeOnLoad code will // run again and add more messages to it. diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs index fe481d25f5..7417bd6b85 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs @@ -1,4 +1,3 @@ - namespace Unity.Netcode { /// @@ -45,8 +44,10 @@ internal interface INetworkMessage public int Version { get; } } - - internal static class MessageDeliveryType where T : INetworkMessage +#if UNITY_6000_6_OR_NEWER + [Scripting.LifecycleManagement.AutoStaticsCleanup] +#endif + internal static partial class MessageDeliveryType where T : INetworkMessage { internal static NetworkDelivery DefaultDelivery { get; private set; } internal static void Initialize() diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs index e8cf2ca6db..cc54d9d102 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Unity.Netcode; -using UnityEditor; using UnityEngine; internal static class MessageDelivery @@ -15,17 +14,21 @@ internal static class MessageDelivery /// when sending the message via public API. /// - Skip the time sync messages since it has always used unreliable network delivery. /// - private static HashSet s_SkipMessageTypes = new HashSet(){ + private static readonly HashSet k_SkipMessageTypes = new HashSet(){ NetworkMessageTypes.NamedMessage, NetworkMessageTypes.Unnamed}; - [RuntimeInitializeOnLoadMethod] + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void OnApplicationStart() { +#if UNITY_EDITOR + s_MessageToDelivery = new Dictionary(); + s_MessageToMessageType = new Dictionary(); +#endif UpdateMessageTypes(); } /// - /// FIrst pass at providing an easier path to configuring the network + /// First pass at providing an easier path to configuring the network /// delivery type for the message type. /// TODO: Once coalesces all reliable messages /// and/or organizes by a more unified order of operation tracking built into the @@ -40,7 +43,7 @@ private static void UpdateMessageTypes() foreach (var messageTypeObject in networkMessageTypes) { var messageType = (NetworkMessageTypes)messageTypeObject; - if (s_SkipMessageTypes.Contains(messageType)) + if (k_SkipMessageTypes.Contains(messageType)) { continue; } @@ -56,9 +59,13 @@ private static void UpdateMessageTypes() MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); + MessageDeliveryType.Initialize(); + MessageDeliveryType.Initialize(); + MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); + MessageDeliveryType.Initialize(); // RpcMessage.cs { MessageDeliveryType.Initialize(); @@ -71,18 +78,10 @@ private static void UpdateMessageTypes() MessageDeliveryType.Initialize(); } -#if UNITY_EDITOR - [InitializeOnLoadMethod] - [InitializeOnEnterPlayMode] - private static void OnEnterPlayMode() - { - UpdateMessageTypes(); - } -#endif internal static NetworkDelivery GetDelivery(Type type) { // Return the default if not registered or null - if (type == null || s_SkipMessageTypes.Contains(s_MessageToMessageType[type])) + if (type == null || k_SkipMessageTypes.Contains(s_MessageToMessageType[type])) { return NetworkDelivery.ReliableFragmentedSequenced; } @@ -91,7 +90,7 @@ internal static NetworkDelivery GetDelivery(Type type) internal static NetworkDelivery GetDelivery(NetworkMessageTypes messageType) { - if (s_SkipMessageTypes.Contains(messageType)) + if (k_SkipMessageTypes.Contains(messageType)) { throw new Exception($"{messageType} is not registered in the message type to network delivery map!"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs index c66aa62273..53714455c6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs @@ -34,9 +34,9 @@ public InvalidMessageStructureException(string issue) : base(issue) internal class NetworkMessageManager : IDisposable { public bool StopProcessing = false; - private static Type s_ConnectionApprovedType = typeof(ConnectionApprovedMessage); - private static Type s_ConnectionRequestType = typeof(ConnectionRequestMessage); - private static Type s_DisconnectReasonType = typeof(DisconnectReasonMessage); + private static readonly Type k_ConnectionApprovedType = typeof(ConnectionApprovedMessage); + private static readonly Type k_ConnectionRequestType = typeof(ConnectionRequestMessage); + private static readonly Type k_DisconnectReasonType = typeof(DisconnectReasonMessage); private struct ReceiveQueueItem { @@ -149,8 +149,6 @@ public NetworkMessageManager(INetworkMessageSender sender, object owner, INetwor } } - internal static bool EnableMessageOrderConsoleLog = false; - public void Dispose() { if (m_Disposed) @@ -549,7 +547,7 @@ internal int GetMessageVersion(Type type, ulong clientId, bool forReceive = fals // Special cases because these are the messages that carry the version info - thus the version info isn't // populated yet when we get these. The first part of these messages always has to be the version data // and can't change. - if (messageType != s_ConnectionRequestType && messageType != s_ConnectionApprovedType && messageType != s_DisconnectReasonType && context.SenderId != manager.m_LocalClientId) + if (messageType != k_ConnectionRequestType && messageType != k_ConnectionApprovedType && messageType != k_DisconnectReasonType && context.SenderId != manager.m_LocalClientId) { messageVersion = manager.GetMessageVersion(messageType, context.SenderId, true); if (messageVersion < 0) @@ -603,7 +601,7 @@ internal int SendMessage(ref TMessageType messa var messageVersion = 0; // Special case because this is the message that carries the version info - thus the version info isn't populated yet when we get this. // The first part of this message always has to be the version data and can't change. - if (typeof(TMessageType) != s_ConnectionRequestType) + if (typeof(TMessageType) != k_ConnectionRequestType) { messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]); if (messageVersion < 0) @@ -657,7 +655,7 @@ internal unsafe int SendPreSerializedMessage(in FastBufferWriter t // Special case because this is the message that carries the version info - thus the version info isn't populated yet when we get this. // The first part of this message always has to be the version data and can't change. - if (typeof(TMessageType) != s_ConnectionRequestType) + if (typeof(TMessageType) != k_ConnectionRequestType) { var messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]); if (messageVersion < 0) @@ -741,7 +739,7 @@ internal unsafe int SendPreSerializedMessage(in FastBufferWriter t // Special case because this is the message that carries the version info - thus the version info isn't // populated yet when we get this. The first part of this message always has to be the version data // and can't change. - if (typeof(TMessageType) != s_ConnectionRequestType) + if (typeof(TMessageType) != k_ConnectionRequestType) { messageVersion = GetMessageVersion(typeof(TMessageType), clientId); if (messageVersion < 0) diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs index b171932103..09325be701 100644 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs @@ -11,26 +11,26 @@ namespace Unity.Netcode internal class NetworkMetrics : INetworkMetrics { private const ulong k_MaxMetricsPerFrame = 1000L; - private static Dictionary s_SceneEventTypeNames; - private static ProfilerMarker s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); + private static readonly Dictionary k_SceneEventTypeNames; + private static ProfilerMarker s_FrameDispatch; +#if UNITY_EDITOR + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); +#endif static NetworkMetrics() { - s_SceneEventTypeNames = new Dictionary(); + k_SceneEventTypeNames = new Dictionary(); foreach (SceneEventType type in Enum.GetValues(typeof(SceneEventType))) { - s_SceneEventTypeNames[(uint)type] = type.ToString(); + k_SceneEventTypeNames[(uint)type] = type.ToString(); } + s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); } private static string GetSceneEventTypeName(uint typeCode) { - if (!s_SceneEventTypeNames.TryGetValue(typeCode, out string name)) - { - name = "Unknown"; - } - - return name; + return k_SceneEventTypeNames.GetValueOrDefault(typeCode, "Unknown"); } private readonly Counter m_TransportBytesSent = new Counter(NetworkMetricTypes.TotalBytesSent.Id) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs index d9327f2441..7c1769d9d8 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs @@ -6,7 +6,7 @@ namespace Unity.Netcode { - internal static class CollectionSerializationUtility + internal static partial class CollectionSerializationUtility { public static void WriteNativeArrayDelta(FastBufferWriter writer, ref NativeArray value, ref NativeArray previousValue) where T : unmanaged { @@ -260,24 +260,24 @@ public static void ReadListDelta(FastBufferReader reader, ref List value) // so we're going to keep static lists that we can reuse in these methods. private static class ListCache { - private static List s_AddedList = new List(); - private static List s_RemovedList = new List(); - private static List s_ChangedList = new List(); + private static readonly List k_AddedList = new List(); + private static readonly List k_RemovedList = new List(); + private static readonly List k_ChangedList = new List(); public static List GetAddedList() { - s_AddedList.Clear(); - return s_AddedList; + k_AddedList.Clear(); + return k_AddedList; } public static List GetRemovedList() { - s_RemovedList.Clear(); - return s_RemovedList; + k_RemovedList.Clear(); + return k_RemovedList; } public static List GetChangedList() { - s_ChangedList.Clear(); - return s_ChangedList; + k_ChangedList.Clear(); + return k_ChangedList; } } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs index 1a00d2c86a..8b35317ceb 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs @@ -9,8 +9,11 @@ namespace Unity.Netcode /// based on which constraints are met by `T` using reflection, which is done at module load time. /// /// The type the associated NetworkVariable is templated on +#if UNITY_6000_6_OR_NEWER + [Scripting.LifecycleManagement.AutoStaticsCleanup] +#endif [Serializable] - public static class NetworkVariableSerialization + public static partial class NetworkVariableSerialization { internal static INetworkVariableSerializer Serializer = new FallbackSerializer(); diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs index 3f2a64585c..fe6a7acbba 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs @@ -7,7 +7,10 @@ namespace Unity.Netcode /// users to tell NetworkVariable about those extension methods (or simply pass in a lambda) /// /// The type of value being serialized - public class UserNetworkVariableSerialization +#if UNITY_6000_6_OR_NEWER + [Scripting.LifecycleManagement.AutoStaticsCleanup] +#endif + public partial class UserNetworkVariableSerialization { /// /// The write value delegate handler definition diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs index 5a11a01b4a..de9e4c059b 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs @@ -206,7 +206,7 @@ public void PopulateLoadedScenes(ref Dictionary scene /// Unloads any scenes that have not been assigned. /// /// - public void UnloadUnassignedScenes(NetworkManager networkManager = null) + public void UnloadUnassignedScenes(NetworkManager networkManager) { var sceneManager = networkManager.SceneManager; SceneManager.sceneUnloaded += SceneManager_SceneUnloaded; diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 2fbdf1fbe9..3d46ae52c3 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using Unity.Collections; using UnityEngine; using UnityEngine.SceneManagement; +using Debug = UnityEngine.Debug; namespace Unity.Netcode @@ -548,6 +550,15 @@ internal bool RemoveServerClientSceneHandle(NetworkSceneHandle serverHandle, Net /// not destroy temporary scene are moved into the active scene /// internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + DisableReSynchronization = false; + IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + SceneUnloadEventHandler.ResetInstances(); + } +#endif /// /// Client and Server: @@ -590,7 +601,7 @@ internal bool HasSceneAuthority() } /// - /// Handle NetworkSeneManager clean up + /// Handle NetworkSceneManager clean up /// public void Dispose() { @@ -1570,6 +1581,9 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc internal class SceneUnloadEventHandler { private static Dictionary> s_Instances = new Dictionary>(); +#if UNITY_EDITOR + internal static void ResetInstances() => s_Instances = new Dictionary>(); +#endif internal static void RegisterScene(NetworkSceneManager networkSceneManager, Scene scene, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation = null) { diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 0fff313574..90443abfde 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -133,7 +133,6 @@ internal class SceneEventData : IDisposable private List m_NetworkObjectsSync = new List(); private List m_DespawnedInSceneObjectsSync = new List(); - private Dictionary> m_DespawnedInSceneObjects = new Dictionary>(); /// /// Server Side Re-Synchronization: @@ -165,10 +164,10 @@ internal class SceneEventData : IDisposable /// we must distinguish which scene we are talking about when the server tells the client to unload a scene. /// The server will always communicate its local relative scene's handle and the client will determine its /// local relative handle from the table being built. - /// Look for usage to see where + /// Look for usage to see where /// entries are being added to or removed from the table /// - /// + /// /// internal void AddSceneToSynchronize(uint sceneHash, NetworkSceneHandle sceneHandle) { @@ -316,8 +315,6 @@ private void SortParentedNetworkObjects() } } - internal static bool LogSerializationOrder = false; - internal void AddSpawnedNetworkObjects() { m_NetworkObjectsSync.Clear(); @@ -353,7 +350,7 @@ private void SortObjectsToSync() // This is useful to know what NetworkObjects a client is going to be synchronized with // as well as the order in which they will be deserialized - if (LogSerializationOrder && m_NetworkManager.LogLevel == LogLevel.Developer) + if (NetworkLog.Config.LogSerializationOrder && m_NetworkManager.LogLevel == LogLevel.Developer) { var messageBuilder = new StringBuilder(0xFFFF); messageBuilder.AppendLine("[Server-Side Client-Synchronization] NetworkObject serialization order:"); @@ -446,8 +443,6 @@ private int SortNetworkObjects(NetworkObject first, NetworkObject second) return 0; } - internal bool EnableSerializationLogs = false; - private void LogArray(byte[] data, int start = 0, int stop = 0, StringBuilder builder = null) { var usingExternalBuilder = builder != null; @@ -529,7 +524,7 @@ internal void Serialize(FastBufferWriter writer) WriteSceneSynchronizationData(writer); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { LogArray(writer.ToArray(), 0, writer.Length); } @@ -579,7 +574,7 @@ private unsafe void CopyInternalBuffer(ref FastBufferWriter writer) internal void WriteSceneSynchronizationData(FastBufferWriter writer) { var builder = (StringBuilder)null; - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { builder = new StringBuilder(); builder.AppendLine($"[Write][Synchronize-Start][WPos: {writer.Position}] Begin:"); @@ -594,7 +589,7 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) { writer.WriteValueSafe(m_InternalBufferSize); CopyInternalBuffer(ref writer); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { LogArray(writer.ToArray(), positionStart); } @@ -609,7 +604,7 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) // Write the number of NetworkObjects we are serializing writer.WriteValueSafe(m_NetworkObjectsSync.Count); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { builder.AppendLine($"[Synchronize Objects][positionStart: {positionStart}][WPos: {writer.Position}][NO-Count: {m_NetworkObjectsSync.Count}] Begin:"); } @@ -626,7 +621,7 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) serializedObject.Serialize(writer); var noStop = writer.Position; totalBytes += noStop - noStart; - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { var offStart = noStart - (positionStart + sizeof(int)); var offStop = noStop - (positionStart + sizeof(int)); @@ -634,7 +629,7 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) LogArray(writer.ToArray(), noStart, noStop, builder); } } - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { UnityEngine.Debug.Log(builder.ToString()); } @@ -658,7 +653,7 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) // Write the total size written to the stream by NetworkObjects being serialized writer.WriteValueSafe(bytesWritten); writer.Seek(positionEnd); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { LogArray(writer.ToArray(), positionStart); } @@ -778,7 +773,7 @@ internal void Deserialize(FastBufferReader reader) case SceneEventType.Synchronize: { reader.ReadValueSafe(out ActiveSceneHash); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { LogArray(reader.ToArray(), 0, reader.Length); } @@ -848,7 +843,7 @@ internal void CopySceneSynchronizationData(FastBufferReader reader) m_HasInternalBuffer = true; // We use Allocator.Persistent since scene synchronization will most likely take longer than 4 frames InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.Persistent, sizeToCopy); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { LogArray(InternalBuffer.ToArray()); } @@ -1009,7 +1004,6 @@ internal void WriteClientSynchronizationResults(FastBufferWriter writer) private void DeserializeDespawnedInScenePlacedNetworkObjects() { // Process all de-spawned in-scene NetworkObjects for this network session - m_DespawnedInSceneObjects.Clear(); InternalBuffer.ReadValueSafe(out int despawnedObjectsCount); var sceneCache = new Dictionary>(); @@ -1026,8 +1020,6 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() var localSceneHandle = m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle[networkSceneHandle]; if (m_NetworkManager.SceneManager.ScenesLoaded.ContainsKey(localSceneHandle)) { - var objectRelativeScene = m_NetworkManager.SceneManager.ScenesLoaded[localSceneHandle]; - // Find all active and non-active in-scene placed NetworkObjects var inSceneNetworkObjects = FindObjects.ByType(true, true).Where((c) => c.GetSceneOriginHandle() == localSceneHandle && (c.IsSceneObject != false)).ToList(); @@ -1093,7 +1085,7 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) { var builder = (StringBuilder)null; - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { builder = new StringBuilder(); } @@ -1102,7 +1094,7 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) { // Process all spawned NetworkObjects for this network session InternalBuffer.ReadValueSafe(out int newObjectsCount); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { builder.AppendLine($"[Read][Synchronize Objects][WPos: {InternalBuffer.Position}][NO-Count: {newObjectsCount}] Begin:"); } @@ -1121,12 +1113,12 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) var spawnedNetworkObject = NetworkObject.Deserialize(serializedObject, InternalBuffer, networkManager); var noStop = InternalBuffer.Position; - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { builder.AppendLine($"[Head: {noStart}][Tail: {noStop}][Size: {noStop - noStart}][{spawnedNetworkObject.name}][NID-{spawnedNetworkObject.NetworkObjectId}][Children: {spawnedNetworkObject.ChildNetworkBehaviours.Count}]"); LogArray(InternalBuffer.ToArray(), noStart, noStop, builder); } - // If we failed to deserialize the NetowrkObject then don't add null to the list + // If we failed to deserialize the NetworkObject then don't add null to the list if (spawnedNetworkObject != null) { if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) @@ -1135,7 +1127,7 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) } } } - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { UnityEngine.Debug.Log(builder.ToString()); } @@ -1156,7 +1148,10 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) catch (Exception ex) { UnityEngine.Debug.LogException(ex); - UnityEngine.Debug.Log(builder.ToString()); + if (NetworkLog.Config.EnableSerializationLogs) + { + UnityEngine.Debug.Log(builder.ToString()); + } } finally { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index bea537965e..ca39ea8c62 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -31,7 +31,7 @@ internal struct WriterHandle internal unsafe WriterHandle* Handle; - private static byte[] s_ByteArrayCache = new byte[65535]; + private static readonly byte[] k_ByteArrayCache = new byte[65535]; /// /// The current write position @@ -379,17 +379,17 @@ public unsafe byte[] ToArray() internal unsafe ArraySegment ToTempByteArray() { var length = Length; - if (length > s_ByteArrayCache.Length) + if (length > k_ByteArrayCache.Length) { return new ArraySegment(ToArray(), 0, length); } - fixed (byte* b = s_ByteArrayCache) + fixed (byte* b = k_ByteArrayCache) { UnsafeUtility.MemCpy(b, Handle->BufferPointer, length); } - return new ArraySegment(s_ByteArrayCache, 0, length); + return new ArraySegment(k_ByteArrayCache, 0, length); } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs index 19cecefc48..0b2e406e4f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs @@ -11,8 +11,7 @@ public struct NetworkBehaviourReference : INetworkSerializable, IEquatable /// Creates a new instance of the struct. @@ -24,7 +23,7 @@ public NetworkBehaviourReference(NetworkBehaviour networkBehaviour) if (networkBehaviour == null) { m_NetworkObjectReference = new NetworkObjectReference((NetworkObject)null); - m_NetworkBehaviourId = s_NullId; + m_NetworkBehaviourId = k_NullId; return; } if (networkBehaviour.NetworkObject == null) @@ -64,7 +63,7 @@ public bool TryGet(out T networkBehaviour, NetworkManager networkManager = nu [MethodImpl(MethodImplOptions.AggressiveInlining)] private static NetworkBehaviour GetInternal(NetworkBehaviourReference networkBehaviourRef, NetworkManager networkManager = null) { - if (networkBehaviourRef.m_NetworkBehaviourId == s_NullId) + if (networkBehaviourRef.m_NetworkBehaviourId == k_NullId) { return null; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs index 32ab0ed162..db5ed69ced 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs @@ -10,7 +10,7 @@ namespace Unity.Netcode public struct NetworkObjectReference : INetworkSerializable, IEquatable { private ulong m_NetworkObjectId; - private static ulong s_NullId = ulong.MaxValue; + private const ulong k_NullId = ulong.MaxValue; /// /// The of the referenced . @@ -31,7 +31,7 @@ public NetworkObjectReference(NetworkObject networkObject) { if (networkObject == null) { - m_NetworkObjectId = s_NullId; + m_NetworkObjectId = k_NullId; return; } @@ -53,7 +53,7 @@ public NetworkObjectReference(GameObject gameObject) { if (gameObject == null) { - m_NetworkObjectId = s_NullId; + m_NetworkObjectId = k_NullId; return; } @@ -92,7 +92,7 @@ public bool TryGet(out NetworkObject networkObject, NetworkManager networkManage [MethodImpl(MethodImplOptions.AggressiveInlining)] private static NetworkObject Resolve(NetworkObjectReference networkObjectRef, NetworkManager networkManager = null) { - if (networkObjectRef.m_NetworkObjectId == s_NullId) + if (networkObjectRef.m_NetworkObjectId == k_NullId) { return null; } diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/SinglePlayer/SinglePlayerTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/SinglePlayer/SinglePlayerTransport.cs index 7b2d6b1719..e94955618c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/SinglePlayer/SinglePlayerTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/SinglePlayer/SinglePlayerTransport.cs @@ -20,7 +20,7 @@ public class SinglePlayerTransport : NetworkTransport /// public override ulong ServerClientId { get; } = 0; - internal static string NotStartingAsHostErrorMessage = $"When using {nameof(SinglePlayerTransport)}, you must start a hosted session so both client and server are available locally."; + internal static readonly string NotStartingAsHostErrorMessage = $"When using {nameof(SinglePlayerTransport)}, you must start a hosted session so both client and server are available locally."; private struct MessageData { @@ -31,6 +31,10 @@ private struct MessageData } private static Dictionary> s_MessageQueue = new Dictionary>(); +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => s_MessageQueue = new Dictionary>(); +#endif private ulong m_TransportId = 0; private NetworkManager m_NetworkManager; diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index e392af403c..6a3fbae94a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -68,7 +68,7 @@ public enum ProtocolType // frame at 60 FPS. This will be a large over-estimation in any realistic scenario. private const int k_MaxReliableThroughput = (NetworkParameterConstants.MTU * 64 * 60) / 1000; // bytes per millisecond - private static ConnectionAddressData s_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, WebSocketPath = "/", ServerListenAddress = string.Empty }; + private static readonly ConnectionAddressData k_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, WebSocketPath = "/", ServerListenAddress = string.Empty }; #pragma warning disable IDE1006 // Naming Styles /// @@ -308,7 +308,7 @@ public NetworkEndpoint ListenEndPoint /// This is where you can change IP Address, Port, or server's listen address. /// /// - public ConnectionAddressData ConnectionData = s_DefaultConnectionAddressData; + public ConnectionAddressData ConnectionData = k_DefaultConnectionAddressData; /// /// Parameters for the Network Simulator @@ -369,6 +369,19 @@ private struct PacketLossCache #endif internal static event Action TransportInitialized; internal static event Action TransportDisposed; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + s_DriverConstructor = null; +#if UNITY_6000_2_OR_NEWER + OnDriverInitialized = null; + OnDisposingDriver = null; +#endif + TransportInitialized = null; + TransportDisposed = null; + } +#endif /// /// Provides access to the for this instance. diff --git a/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs new file mode 100644 index 0000000000..64468ba2d3 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs @@ -0,0 +1,29 @@ +using System.Collections; +using NUnit.Framework; +using UnityEditor; +using UnityEngine.TestTools; + +namespace Unity.Netcode.EditorTests +{ + + internal class FastEnterPlayModeTests + { + [UnityTest] + public IEnumerator NetworkManagerSingletonResetsOnPlayModeEnter( + [Values(EnterPlayModeOptions.None, + EnterPlayModeOptions.DisableDomainReload | EnterPlayModeOptions.DisableSceneReload)] + EnterPlayModeOptions playmodeOption) + { + EditorSettings.enterPlayModeOptionsEnabled = true; + EditorSettings.enterPlayModeOptions = playmodeOption; + + // First play session — create a NetworkManager to set Singleton + yield return new EnterPlayMode(); + + yield return new ExitPlayMode(); + + // Restore default settings + EditorSettings.enterPlayModeOptionsEnabled = false; + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs.meta new file mode 100644 index 0000000000..1888e14b72 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d7659423d2b74f36806a2860809a086a +timeCreated: 1779380261 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index a6f4a83275..f03de92977 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -2406,8 +2406,6 @@ public NetcodeIntegrationTest(HostOrServer hostOrServer) private void InitializeTestConfiguration(NetworkTopologyTypes networkTopologyType, HostOrServer? hostOrServer) { - NetworkMessageManager.EnableMessageOrderConsoleLog = false; - // Set m_NetworkTopologyType first because m_DistributedAuthority is calculated from it. m_NetworkTopologyType = networkTopologyType; diff --git a/testproject/ProjectSettings/EditorSettings.asset b/testproject/ProjectSettings/EditorSettings.asset index f92054474a..3ddd8f044f 100644 --- a/testproject/ProjectSettings/EditorSettings.asset +++ b/testproject/ProjectSettings/EditorSettings.asset @@ -23,7 +23,7 @@ EditorSettings: m_EnableTextureStreamingInEditMode: 1 m_EnableTextureStreamingInPlayMode: 1 m_AsyncShaderCompilation: 1 - m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptionsEnabled: 1 m_EnterPlayModeOptions: 3 m_ShowLightmapResolutionOverlay: 1 m_UseLegacyProbeSampleCount: 0 diff --git a/testproject/ProjectSettings/ProjectSettings.asset b/testproject/ProjectSettings/ProjectSettings.asset index 3bb16ba357..176a6e2232 100644 --- a/testproject/ProjectSettings/ProjectSettings.asset +++ b/testproject/ProjectSettings/ProjectSettings.asset @@ -741,8 +741,7 @@ PlayerSettings: scriptingDefineSymbols: Standalone: UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT additionalCompilerArguments: - Standalone: - - -warnaserror + Standalone: [] platformArchitecture: {} scriptingBackend: {} il2cppCompilerConfiguration: {}