From c6f0d4b60e91e5e85ea12f4bbe23ffcada6f6a4a Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Fri, 24 Apr 2026 15:17:39 +0200 Subject: [PATCH 01/20] fix: reset static fields for Fast Enter Play Mode --- .../Runtime/Components/NetworkAnimator.cs | 1 - .../Runtime/Components/NetworkTransform.cs | 19 +++++++++++--- .../Components/QuaternionCompressor.cs | 4 +++ .../RigidbodyContactEventManager.cs | 6 ++++- .../Configuration/CommandLineOptions.cs | 24 +++++++++--------- .../Runtime/Core/ComponentFactory.cs | 5 ++++ .../Runtime/Core/NetworkBehaviour.cs | 2 -- .../Runtime/Core/NetworkManager.cs | 13 ++++++++++ .../Runtime/Core/NetworkObject.cs | 9 +++++++ .../Runtime/Core/NetworkUpdateLoop.cs | 10 ++++++++ .../Runtime/Logging/NetworkLog.cs | 4 +++ .../Messaging/DeferredMessageManager.cs | 5 ++++ .../Runtime/Messaging/ILPPMessageProvider.cs | 4 +++ .../Runtime/Messaging/INetworkMessage.cs | 2 ++ .../Runtime/Messaging/MessageDelivery.cs | 25 ++++++++----------- .../Messaging/NetworkMessageManager.cs | 16 ++++++------ .../Runtime/Metrics/NetworkMetrics.cs | 13 ++++++++++ .../NetworkVariable/NetworkVariableBase.cs | 4 +++ .../CollectionSerializationUtility.cs | 10 ++++++++ .../Serialization/TypedILPPInitializers.cs | 2 +- .../SceneManagement/NetworkSceneManager.cs | 13 ++++++++++ .../Runtime/SceneManagement/SceneEventData.cs | 11 +++++--- .../Runtime/Serialization/FastBufferWriter.cs | 8 +++--- .../NetworkBehaviourReference.cs | 7 +++--- .../Serialization/NetworkObjectReference.cs | 8 +++--- .../SinglePlayer/SinglePlayerTransport.cs | 6 ++++- .../Runtime/Transports/UTP/UnityTransport.cs | 15 +++++++++++ .../TestHelpers/NetcodeIntegrationTest.cs | 2 -- 28 files changed, 185 insertions(+), 63 deletions(-) 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..618e9723b6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -2221,13 +2221,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. @@ -4800,6 +4798,21 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) } } private static int s_TickSynchPosition; + +#if UNITY_EDITOR + [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 + private int m_NextTickSync; internal void RegisterForTickSynchronization() diff --git a/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs b/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs index f4d9b25dd2..3c6af11529 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs @@ -44,6 +44,10 @@ public static class QuaternionCompressor // Used to store the absolute value of the 4 quaternion elements private static Quaternion s_QuatAbsValues = Quaternion.identity; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => s_QuatAbsValues = Quaternion.identity; +#endif /// /// Compresses a Quaternion into an unsigned integer diff --git a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs index bf54c4d964..ac54c3b6e5 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. ///
@@ -76,6 +76,10 @@ public interface IContactEventHandlerWithInfo : IContactEventHandler public class RigidbodyContactEventManager : MonoBehaviour { public static RigidbodyContactEventManager Instance { get; private set; } +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => Instance = null; +#endif private struct JobResultStruct { diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs index c6f9b4a222..49bfe092ff 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs @@ -30,32 +30,32 @@ private set } private static CommandLineOptions s_Instance; - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void RuntimeInitializeOnLoad() => Instance = new CommandLineOptions(); - // 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() +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() { + Instance = new CommandLineOptions(); + s_Instance = new CommandLineOptions(); // 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()); + 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..8798d67858 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using UnityEngine; namespace Unity.Netcode { @@ -14,6 +15,10 @@ internal static class ComponentFactory internal delegate object CreateObjectDelegate(NetworkManager networkManager); private static Dictionary s_Delegates = new Dictionary(); +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(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 049f28070b..1e35126542 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -1297,8 +1297,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 d8bcfaffff..b40025e90f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1809,6 +1809,19 @@ internal abstract class NetcodeAnalytics internal delegate void ResetNetworkManagerDelegate(NetworkManager manager); internal static ResetNetworkManagerDelegate OnNetworkManagerReset; + //We already are in an #if UNITY_ENGINE def + [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; + } private void Reset() { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index c351f7d53b..0c844fba5e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1688,6 +1688,11 @@ public static void NetworkHide(List networkObjects, ulong clientI } } + private void OnDisable() + { + SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged; + } + private void OnDestroy() { var networkManager = NetworkManager; @@ -2449,6 +2454,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..eff98799c2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs @@ -291,6 +291,16 @@ public static PlayerLoopSystem CreateLoopSystem() [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void Initialize() { +#if UNITY_EDITOR + 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]); + } + 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 59557cc81a..07cc3fa499 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs @@ -58,6 +58,10 @@ public static class NetworkLog public static void LogErrorServer(string message) => LogServer(message, LogType.Error); internal static NetworkManager NetworkManagerOverride; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStatics() => NetworkManagerOverride = null; +#endif private static void LogServer(string message, LogType logType) { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs index f05d000fec..d570970292 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Unity.Collections; +using UnityEngine; namespace Unity.Netcode { @@ -96,6 +97,10 @@ public virtual unsafe void CleanupStaleTriggers() /// Used for testing purposes /// internal static bool IncludeMessageType = true; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStatics() => 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..fa3c125a7d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif @@ -54,6 +55,9 @@ internal struct ILPPMessageProvider : INetworkMessageProvider // Enable this for integration tests that need no message types defined internal static bool IntegrationTestNoMessages; + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStatics() => IntegrationTestNoMessages = false; + /// /// Returns a table of message type to NetworkMessageTypes enum value /// diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs index fe481d25f5..7940efa1ab 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs @@ -1,3 +1,4 @@ +using UnityEngine; namespace Unity.Netcode { @@ -49,6 +50,7 @@ internal interface INetworkMessage internal static class MessageDeliveryType where T : INetworkMessage { internal static NetworkDelivery DefaultDelivery { get; private set; } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] internal static void Initialize() { DefaultDelivery = MessageDelivery.GetDelivery(typeof(T)); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs index e8cf2ca6db..e70dfd17f1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs @@ -15,17 +15,20 @@ 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] - private static void OnApplicationStart() + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void OnSubsystemRegistration() { + s_MessageToDelivery = new Dictionary(); + s_MessageToMessageType = new Dictionary(); + 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; } @@ -71,18 +74,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 +86,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..727508b116 100644 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs @@ -1,6 +1,7 @@ #if MULTIPLAYER_TOOLS using System; using System.Collections.Generic; +using UnityEngine; using Unity.Multiplayer.Tools; using Unity.Multiplayer.Tools.MetricTypes; using Unity.Multiplayer.Tools.NetStats; @@ -13,6 +14,18 @@ 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"); +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + s_SceneEventTypeNames = new Dictionary(); + foreach (SceneEventType type in Enum.GetValues(typeof(SceneEventType))) + { + s_SceneEventTypeNames[(uint)type] = type.ToString(); + } + s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); + } +#endif static NetworkMetrics() { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs index 25abeb20e9..52b3359867 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -307,6 +307,10 @@ internal void UpdateLastSentTime() } internal static bool IgnoreInitializeWarning; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => IgnoreInitializeWarning = false; +#endif /// /// Marks the associated NetworkBehaviour as dirty, indicating it needs synchronization diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs index d9327f2441..101a635be8 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs @@ -3,6 +3,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; +using UnityEngine; namespace Unity.Netcode { @@ -263,6 +264,15 @@ 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(); +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + s_AddedList = new List(); + s_RemovedList = new List(); + s_ChangedList = new List(); + } +#endif public static List GetAddedList() { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedILPPInitializers.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedILPPInitializers.cs index 9fd42b2ae3..9671d0b66c 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedILPPInitializers.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedILPPInitializers.cs @@ -20,7 +20,7 @@ namespace Unity.Netcode /// public static class NetworkVariableSerializationTypedInitializers { - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)] + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] #if UNITY_EDITOR [InitializeOnLoadMethod] #endif diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index bf1ed0d734..4d272bfb05 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -549,6 +549,16 @@ internal bool RemoveServerClientSceneHandle(NetworkSceneHandle serverHandle, Net /// internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + DisableReSynchronization = false; + IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + SceneUnloadEventHandler.ResetInstances(); + } +#endif + /// /// Client and Server: /// Used for all scene event processing @@ -1564,6 +1574,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 fd3424a79e..f5f74a5ea1 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using Unity.Collections; +using UnityEngine; using UnityEngine.SceneManagement; namespace Unity.Netcode @@ -168,7 +169,7 @@ internal class SceneEventData : IDisposable /// Look for usage to see where /// entries are being added to or removed from the table /// - /// + /// /// internal void AddSceneToSynchronize(uint sceneHash, NetworkSceneHandle sceneHandle) { @@ -315,7 +316,11 @@ private void SortParentedNetworkObjects() } } - internal static bool LogSerializationOrder = false; + internal static bool LogSerializationOrder; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStatics() => LogSerializationOrder = false; +#endif internal void AddSpawnedNetworkObjects() { @@ -1037,8 +1042,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(); 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 b39fe2bc94..e181aafc26 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -370,6 +370,21 @@ private struct PacketLossCache internal static event Action TransportInitialized; internal static event Action TransportDisposed; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + s_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, WebSocketPath = "/", ServerListenAddress = string.Empty }; + 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/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index f747bbbd5d..64d8c3fa32 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -2389,8 +2389,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; From 702bb730b25e1b1ebf380dcdcbf6948231860696 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Fri, 24 Apr 2026 15:41:51 +0200 Subject: [PATCH 02/20] Remove RuntimeInitializeOnLoadMethod from non generic types --- .../Runtime/Messaging/INetworkMessage.cs | 3 --- .../Runtime/Messaging/MessageDelivery.cs | 1 - .../Serialization/CollectionSerializationUtility.cs | 10 ---------- 3 files changed, 14 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs index 7940efa1ab..6517d63675 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs @@ -1,5 +1,3 @@ -using UnityEngine; - namespace Unity.Netcode { /// @@ -50,7 +48,6 @@ internal interface INetworkMessage internal static class MessageDeliveryType where T : INetworkMessage { internal static NetworkDelivery DefaultDelivery { get; private set; } - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] internal static void Initialize() { DefaultDelivery = MessageDelivery.GetDelivery(typeof(T)); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs index e70dfd17f1..114d893203 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 diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs index 101a635be8..d9327f2441 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs @@ -3,7 +3,6 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; -using UnityEngine; namespace Unity.Netcode { @@ -264,15 +263,6 @@ 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(); -#if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStaticsOnLoad() - { - s_AddedList = new List(); - s_RemovedList = new List(); - s_ChangedList = new List(); - } -#endif public static List GetAddedList() { From 8dabded7e57b272411ed57872c5e36ce71982ba6 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Fri, 24 Apr 2026 16:02:27 +0200 Subject: [PATCH 03/20] Fix naming + if editor def on `using UnityEngine` --- .../Runtime/Core/ComponentFactory.cs | 2 ++ .../Runtime/Logging/NetworkLog.cs | 2 +- .../Runtime/Messaging/DeferredMessageManager.cs | 4 +++- .../Runtime/Messaging/ILPPMessageProvider.cs | 7 ++++--- .../Runtime/Messaging/MessageDelivery.cs | 5 +++-- .../Runtime/Metrics/NetworkMetrics.cs | 2 ++ .../Runtime/SceneManagement/SceneEventData.cs | 4 +++- .../Runtime/Transports/UTP/UnityTransport.cs | 1 - 8 files changed, 18 insertions(+), 9 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs index 8798d67858..1616ac727b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +#if UNITY_EDITOR using UnityEngine; +#endif namespace Unity.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs index 07cc3fa499..f64f88db72 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs @@ -60,7 +60,7 @@ public static class NetworkLog internal static NetworkManager NetworkManagerOverride; #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStatics() => NetworkManagerOverride = null; + private static void ResetStaticsOnLoad() => NetworkManagerOverride = null; #endif private static void LogServer(string message, LogType logType) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs index d570970292..c2bdba09c6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using Unity.Collections; +#if UNITY_EDITOR using UnityEngine; +#endif namespace Unity.Netcode { @@ -99,7 +101,7 @@ public virtual unsafe void CleanupStaleTriggers() internal static bool IncludeMessageType = true; #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStatics() => IncludeMessageType = true; + 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 fa3c125a7d..c2a973fb4b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using UnityEngine; #if UNITY_EDITOR +using UnityEngine; using UnityEditor; #endif @@ -54,9 +54,10 @@ internal struct ILPPMessageProvider : INetworkMessageProvider // Enable this for integration tests that need no message types defined internal static bool IntegrationTestNoMessages; - +#if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStatics() => IntegrationTestNoMessages = false; + private static void ResetStaticsOnLoad() => IntegrationTestNoMessages = false; +#endif /// /// Returns a table of message type to NetworkMessageTypes enum value diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs index 114d893203..3a6f34b274 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs @@ -18,11 +18,12 @@ internal static class MessageDelivery NetworkMessageTypes.NamedMessage, NetworkMessageTypes.Unnamed}; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void OnSubsystemRegistration() + private static void OnApplicationStart() { +#if UNITY_EDITOR s_MessageToDelivery = new Dictionary(); s_MessageToMessageType = new Dictionary(); - +#endif UpdateMessageTypes(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs index 727508b116..06b15d9368 100644 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs @@ -1,7 +1,9 @@ #if MULTIPLAYER_TOOLS using System; using System.Collections.Generic; +#if UNITY_EDITOR using UnityEngine; +#endif using Unity.Multiplayer.Tools; using Unity.Multiplayer.Tools.MetricTypes; using Unity.Multiplayer.Tools.NetStats; diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index f5f74a5ea1..eeb4d98c48 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -3,7 +3,9 @@ using System.Linq; using System.Text; using Unity.Collections; +#if UNITY_EDITOR using UnityEngine; +#endif using UnityEngine.SceneManagement; namespace Unity.Netcode @@ -319,7 +321,7 @@ private void SortParentedNetworkObjects() internal static bool LogSerializationOrder; #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStatics() => LogSerializationOrder = false; + private static void ResetStaticsOnLoad() => LogSerializationOrder = false; #endif internal void AddSpawnedNetworkObjects() diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index e181aafc26..ef26d9cc59 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -369,7 +369,6 @@ private struct PacketLossCache #endif internal static event Action TransportInitialized; internal static event Action TransportDisposed; - #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticsOnLoad() From 5ab98dc4d83d64b0af25437d3fa6908866617cb7 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Fri, 24 Apr 2026 16:46:09 +0200 Subject: [PATCH 04/20] Styling, renaming and made a field readonly --- .../Runtime/Components/NetworkTransform.cs | 4 +-- .../Configuration/CommandLineOptions.cs | 1 - .../Runtime/Core/NetworkUpdateLoop.cs | 34 +++++++++---------- .../SceneManagement/NetworkSceneManager.cs | 1 - .../Runtime/Transports/UTP/UnityTransport.cs | 5 ++- 5 files changed, 21 insertions(+), 24 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 618e9723b6..68eeafa790 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -1361,7 +1361,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. @@ -4692,7 +4692,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) diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs index 49bfe092ff..8a550b0542 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs @@ -32,7 +32,6 @@ private set // Contains the current application instance domain's command line arguments private static List s_CommandLineArguments = new List(Environment.GetCommandLineArgs()); - #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticsOnLoad() diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs index eff98799c2..9d44394393 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++) { @@ -292,12 +292,12 @@ public static PlayerLoopSystem CreateLoopSystem() private static void Initialize() { #if UNITY_EDITOR - 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]); } UpdateStage = default; #endif diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 6bcdfd91fb..7b7f9cae00 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -548,7 +548,6 @@ 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() diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index ef26d9cc59..1cd059fab8 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 @@ -373,7 +373,6 @@ private struct PacketLossCache [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticsOnLoad() { - s_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, WebSocketPath = "/", ServerListenAddress = string.Empty }; s_DriverConstructor = null; #if UNITY_6000_2_OR_NEWER OnDriverInitialized = null; From 638855fef782703ace0826f3a89ebc3d23091bc5 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Tue, 28 Apr 2026 14:29:23 +0200 Subject: [PATCH 05/20] Fix code formatting issues (redundant UnityEngine) --- .../Runtime/SceneManagement/SceneEventData.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 3de3eccadc..a0d7bc4996 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -3,9 +3,7 @@ using System.Linq; using System.Text; using Unity.Collections; -#if UNITY_EDITOR using UnityEngine; -#endif using UnityEngine.SceneManagement; namespace Unity.Netcode @@ -477,7 +475,7 @@ private void LogArray(byte[] data, int start = 0, int stop = 0, StringBuilder bu if (!usingExternalBuilder) { - UnityEngine.Debug.Log(builder.ToString()); + Debug.Log(builder.ToString()); } } @@ -643,7 +641,7 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) } if (EnableSerializationLogs) { - UnityEngine.Debug.Log(builder.ToString()); + Debug.Log(builder.ToString()); } // Write the number of despawned in-scene placed NetworkObjects @@ -1049,12 +1047,12 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() } else { - UnityEngine.Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative local scene handle {localSceneHandle}!"); + Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative local scene handle {localSceneHandle}!"); } } else { - UnityEngine.Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative NetworkSceneHandle {networkSceneHandle}!"); + Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative NetworkSceneHandle {networkSceneHandle}!"); } } else // Use the cached NetworkObjects if they exist @@ -1083,7 +1081,7 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() } else { - UnityEngine.Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) could not be found!"); + Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) could not be found!"); } } } @@ -1142,7 +1140,7 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) } if (EnableSerializationLogs) { - UnityEngine.Debug.Log(builder.ToString()); + Debug.Log(builder.ToString()); } // Notify that all in-scene placed NetworkObjects have been spawned @@ -1160,8 +1158,8 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) } catch (Exception ex) { - UnityEngine.Debug.LogException(ex); - UnityEngine.Debug.Log(builder.ToString()); + Debug.LogException(ex); + Debug.Log(builder.ToString()); } finally { From 0ecf34c68393975fefe3c6e305a814f14308fb2f Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 6 May 2026 16:35:15 +0200 Subject: [PATCH 06/20] Add AutoStaticsCleanup + fix typo --- .../Runtime/Messaging/INetworkMessage.cs | 10 ++++++++-- .../Serialization/CollectionSerializationUtility.cs | 10 ++++++++-- .../Serialization/NetworkVariableSerialization.cs | 8 +++++++- .../Serialization/UserNetworkVariableSerialization.cs | 9 ++++++++- .../Runtime/SceneManagement/NetworkSceneManager.cs | 2 +- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs index 6517d63675..060f275460 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs @@ -1,3 +1,7 @@ +#if UNITY_6000_6_OR_NEWER +using Unity.Scripting.LifecycleManagement; +#endif + namespace Unity.Netcode { /// @@ -44,8 +48,10 @@ internal interface INetworkMessage public int Version { get; } } - - internal static class MessageDeliveryType where T : INetworkMessage +#if UNITY_6000_6_OR_NEWER + [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/NetworkVariable/Serialization/CollectionSerializationUtility.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs index d9327f2441..1b3ad8b1ff 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs @@ -3,10 +3,13 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; +#if UNITY_6000_6_OR_NEWER +using Unity.Scripting.LifecycleManagement; +#endif 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 { @@ -258,7 +261,10 @@ public static void ReadListDelta(FastBufferReader reader, ref List value) // For HashSet and Dictionary, we need to have some local space to hold lists we need to serialize. // We don't want to do allocations all the time and we know each one needs a maximum of three lists, // so we're going to keep static lists that we can reuse in these methods. - private static class ListCache +#if UNITY_6000_6_OR_NEWER + [AutoStaticsCleanup] +#endif + private static partial class ListCache { private static List s_AddedList = new List(); private static List s_RemovedList = new List(); diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs index 1a00d2c86a..634865847c 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs @@ -1,4 +1,7 @@ using System; +#if UNITY_6000_6_OR_NEWER +using Unity.Scripting.LifecycleManagement; +#endif namespace Unity.Netcode { @@ -9,8 +12,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 + [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..3da82099e5 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs @@ -1,3 +1,7 @@ +#if UNITY_6000_6_OR_NEWER +using Unity.Scripting.LifecycleManagement; +#endif + namespace Unity.Netcode { /// @@ -7,7 +11,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 + [AutoStaticsCleanup] +#endif + public partial class UserNetworkVariableSerialization { /// /// The write value delegate handler definition diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 7b7f9cae00..a65275d5d1 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -599,7 +599,7 @@ internal bool HasSceneAuthority() } /// - /// Handle NetworkSeneManager clean up + /// Handle NetworkSceneManager clean up /// public void Dispose() { From e65351c1c1f4c7ed29aaab55883647eb511f238f Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 6 May 2026 17:03:46 +0200 Subject: [PATCH 07/20] Remove possible NullReferenceException --- .../Runtime/SceneManagement/DefaultSceneManagerHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 551b9893044e0d83c4120892cb4ca8fcbfe766db Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 7 May 2026 17:22:33 +0200 Subject: [PATCH 08/20] Unused parented children list cleanup --- .../Runtime/Components/NetworkTransform.cs | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 68eeafa790..257fc06768 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -2062,19 +2062,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); - } - } - } } } } @@ -3361,19 +3348,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 @@ -3632,8 +3606,6 @@ internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManag /// public override void OnNetworkSpawn() { - m_ParentedChildren.Clear(); - Initialize(); if (CanCommitToTransform && !SwitchTransformSpaceWhenParented) @@ -3645,7 +3617,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 @@ -3859,7 +3830,6 @@ protected override void OnOwnershipChanged(ulong previous, ulong current) } internal bool IsNested; - private List m_ParentedChildren = new List(); private bool m_IsFirstNetworkTransform; From a8891664eb39cf6680b6885473491023a1576829 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Mon, 18 May 2026 17:54:05 +0200 Subject: [PATCH 09/20] Review --- .../Runtime/Components/NetworkTransform.cs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 257fc06768..8a7ea12cab 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 } @@ -4767,21 +4779,6 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) } } } - private static int s_TickSynchPosition; - -#if UNITY_EDITOR - [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 private int m_NextTickSync; From 46e09abf9681f84dee8d07b5abdc7ac924f82447 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Mon, 18 May 2026 20:16:23 +0200 Subject: [PATCH 10/20] Finalize addressing review comments --- .../RigidbodyContactEventManager.cs | 6 +- .../Configuration/CommandLineOptions.cs | 21 +------ .../Runtime/Core/ComponentFactory.cs | 5 +- .../Runtime/Core/NetworkManager.cs | 32 +++++----- .../Runtime/Core/NetworkUpdateLoop.cs | 1 + .../Runtime/Logging/NetworkLog.cs | 12 +++- .../Messaging/DeferredMessageManager.cs | 5 +- .../Runtime/Messaging/ILPPMessageProvider.cs | 14 ++--- .../Runtime/Messaging/INetworkMessage.cs | 6 +- .../Runtime/Metrics/NetworkMetrics.cs | 24 +------- .../NetworkVariable/NetworkVariableBase.cs | 4 -- .../CollectionSerializationUtility.cs | 5 +- .../NetworkVariableSerialization.cs | 5 +- .../Serialization/TypedILPPInitializers.cs | 2 +- .../UserNetworkVariableSerialization.cs | 6 +- .../SceneManagement/NetworkSceneManager.cs | 5 +- .../Runtime/SceneManagement/SceneEventData.cs | 58 +++++++++---------- 17 files changed, 75 insertions(+), 136 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs index ac54c3b6e5..5a6fa6f7d0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs @@ -76,10 +76,6 @@ public interface IContactEventHandlerWithInfo : IContactEventHandler public class RigidbodyContactEventManager : MonoBehaviour { public static RigidbodyContactEventManager Instance { get; private set; } -#if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStaticsOnLoad() => Instance = null; -#endif private struct JobResultStruct { @@ -113,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 8a550b0542..e333577c4e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs @@ -13,33 +13,16 @@ 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; + public static CommandLineOptions Instance { get; private set; } // Contains the current application instance domain's command line arguments private static List s_CommandLineArguments = new List(Environment.GetCommandLineArgs()); + #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticsOnLoad() { Instance = new CommandLineOptions(); - s_Instance = new CommandLineOptions(); - // Get all the command line arguments to be parsed later and/or modified - // prior to being parsed (for testing purposes). s_CommandLineArguments = new List(Environment.GetCommandLineArgs()); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs index 1616ac727b..6733429ee2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -#if UNITY_EDITOR -using UnityEngine; -#endif namespace Unity.Netcode { @@ -18,7 +15,7 @@ internal static class ComponentFactory private static Dictionary s_Delegates = new Dictionary(); #if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticsOnLoad() => s_Delegates = new Dictionary(); #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 0e4fdf1754..59ab063406 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] @@ -1796,20 +1809,11 @@ internal abstract class NetcodeAnalytics internal delegate void ResetNetworkManagerDelegate(NetworkManager manager); internal static ResetNetworkManagerDelegate OnNetworkManagerReset; - //We already are in an #if UNITY_ENGINE def - [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; - } + + /// + /// This is called by the Unity Editor reset button. See "OnNetworkManagerReset" from "NetworkManagerHelper.cs" file. + /// private void Reset() { OnNetworkManagerReset?.Invoke(this); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs index 9d44394393..5ac60e2cd4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs @@ -292,6 +292,7 @@ public static PlayerLoopSystem CreateLoopSystem() 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))) 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 c2bdba09c6..4d0cd8fec8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs @@ -1,8 +1,5 @@ using System.Collections.Generic; using Unity.Collections; -#if UNITY_EDITOR -using UnityEngine; -#endif namespace Unity.Netcode { @@ -100,7 +97,7 @@ public virtual unsafe void CleanupStaleTriggers() ///
internal static bool IncludeMessageType = true; #if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticsOnLoad() => IncludeMessageType = true; #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs index c2a973fb4b..26f33c1045 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs @@ -1,9 +1,5 @@ using System; using System.Collections.Generic; -#if UNITY_EDITOR -using UnityEngine; -using UnityEditor; -#endif namespace Unity.Netcode { @@ -55,7 +51,7 @@ internal struct ILPPMessageProvider : INetworkMessageProvider // Enable this for integration tests that need no message types defined internal static bool IntegrationTestNoMessages; #if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticsOnLoad() => IntegrationTestNoMessages = false; #endif @@ -146,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 060f275460..7417bd6b85 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs @@ -1,7 +1,3 @@ -#if UNITY_6000_6_OR_NEWER -using Unity.Scripting.LifecycleManagement; -#endif - namespace Unity.Netcode { /// @@ -49,7 +45,7 @@ internal interface INetworkMessage } #if UNITY_6000_6_OR_NEWER - [AutoStaticsCleanup] + [Scripting.LifecycleManagement.AutoStaticsCleanup] #endif internal static partial class MessageDeliveryType where T : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs index 06b15d9368..eb0478bc4a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs @@ -1,9 +1,6 @@ #if MULTIPLAYER_TOOLS using System; using System.Collections.Generic; -#if UNITY_EDITOR -using UnityEngine; -#endif using Unity.Multiplayer.Tools; using Unity.Multiplayer.Tools.MetricTypes; using Unity.Multiplayer.Tools.NetStats; @@ -14,20 +11,8 @@ namespace Unity.Netcode internal class NetworkMetrics : INetworkMetrics { private const ulong k_MaxMetricsPerFrame = 1000L; - private static Dictionary s_SceneEventTypeNames; + private readonly static Dictionary s_SceneEventTypeNames; private static ProfilerMarker s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); -#if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStaticsOnLoad() - { - s_SceneEventTypeNames = new Dictionary(); - foreach (SceneEventType type in Enum.GetValues(typeof(SceneEventType))) - { - s_SceneEventTypeNames[(uint)type] = type.ToString(); - } - s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); - } -#endif static NetworkMetrics() { @@ -40,12 +25,7 @@ static NetworkMetrics() private static string GetSceneEventTypeName(uint typeCode) { - if (!s_SceneEventTypeNames.TryGetValue(typeCode, out string name)) - { - name = "Unknown"; - } - - return name; + return s_SceneEventTypeNames.GetValueOrDefault(typeCode, "Unknown"); } private readonly Counter m_TransportBytesSent = new Counter(NetworkMetricTypes.TotalBytesSent.Id) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs index 52b3359867..25abeb20e9 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -307,10 +307,6 @@ internal void UpdateLastSentTime() } internal static bool IgnoreInitializeWarning; -#if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStaticsOnLoad() => IgnoreInitializeWarning = false; -#endif /// /// Marks the associated NetworkBehaviour as dirty, indicating it needs synchronization diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs index 1b3ad8b1ff..c9bcc9c02c 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs @@ -3,9 +3,6 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; -#if UNITY_6000_6_OR_NEWER -using Unity.Scripting.LifecycleManagement; -#endif namespace Unity.Netcode { @@ -262,7 +259,7 @@ public static void ReadListDelta(FastBufferReader reader, ref List value) // We don't want to do allocations all the time and we know each one needs a maximum of three lists, // so we're going to keep static lists that we can reuse in these methods. #if UNITY_6000_6_OR_NEWER - [AutoStaticsCleanup] + [Scripting.LifecycleManagement.AutoStaticsCleanup] #endif private static partial class ListCache { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs index 634865847c..8b35317ceb 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs @@ -1,7 +1,4 @@ using System; -#if UNITY_6000_6_OR_NEWER -using Unity.Scripting.LifecycleManagement; -#endif namespace Unity.Netcode { @@ -13,7 +10,7 @@ namespace Unity.Netcode /// /// The type the associated NetworkVariable is templated on #if UNITY_6000_6_OR_NEWER - [AutoStaticsCleanup] + [Scripting.LifecycleManagement.AutoStaticsCleanup] #endif [Serializable] public static partial class NetworkVariableSerialization diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedILPPInitializers.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedILPPInitializers.cs index 9671d0b66c..9fd42b2ae3 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedILPPInitializers.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedILPPInitializers.cs @@ -20,7 +20,7 @@ namespace Unity.Netcode /// public static class NetworkVariableSerializationTypedInitializers { - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)] #if UNITY_EDITOR [InitializeOnLoadMethod] #endif diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs index 3da82099e5..fe6a7acbba 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs @@ -1,7 +1,3 @@ -#if UNITY_6000_6_OR_NEWER -using Unity.Scripting.LifecycleManagement; -#endif - namespace Unity.Netcode { /// @@ -12,7 +8,7 @@ namespace Unity.Netcode /// /// The type of value being serialized #if UNITY_6000_6_OR_NEWER - [AutoStaticsCleanup] + [Scripting.LifecycleManagement.AutoStaticsCleanup] #endif public partial class UserNetworkVariableSerialization { diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index a65275d5d1..545a906e8d 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 @@ -1579,9 +1581,8 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc internal class SceneUnloadEventHandler { private static Dictionary> s_Instances = new Dictionary>(); -#if UNITY_EDITOR + [Conditional("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 a0d7bc4996..945d6819ef 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text; using Unity.Collections; -using UnityEngine; using UnityEngine.SceneManagement; namespace Unity.Netcode @@ -166,7 +165,7 @@ 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 ///
/// @@ -317,12 +316,6 @@ private void SortParentedNetworkObjects() } } - internal static bool LogSerializationOrder; -#if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStaticsOnLoad() => LogSerializationOrder = false; -#endif - internal void AddSpawnedNetworkObjects() { m_NetworkObjectsSync.Clear(); @@ -358,7 +351,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:"); @@ -451,8 +444,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; @@ -475,7 +466,7 @@ private void LogArray(byte[] data, int start = 0, int stop = 0, StringBuilder bu if (!usingExternalBuilder) { - Debug.Log(builder.ToString()); + UnityEngine.Debug.Log(builder.ToString()); } } @@ -534,7 +525,7 @@ internal void Serialize(FastBufferWriter writer) WriteSceneSynchronizationData(writer); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { LogArray(writer.ToArray(), 0, writer.Length); } @@ -584,7 +575,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:"); @@ -599,7 +590,7 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) { writer.WriteValueSafe(m_InternalBufferSize); CopyInternalBuffer(ref writer); - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { LogArray(writer.ToArray(), positionStart); } @@ -614,7 +605,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:"); } @@ -631,7 +622,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)); @@ -639,9 +630,9 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) LogArray(writer.ToArray(), noStart, noStop, builder); } } - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { - Debug.Log(builder.ToString()); + UnityEngine.Debug.Log(builder.ToString()); } // Write the number of despawned in-scene placed NetworkObjects @@ -663,7 +654,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); } @@ -783,7 +774,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); } @@ -853,7 +844,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()); } @@ -1047,12 +1038,12 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() } else { - Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative local scene handle {localSceneHandle}!"); + UnityEngine.Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative local scene handle {localSceneHandle}!"); } } else { - Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative NetworkSceneHandle {networkSceneHandle}!"); + UnityEngine.Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative NetworkSceneHandle {networkSceneHandle}!"); } } else // Use the cached NetworkObjects if they exist @@ -1096,7 +1087,7 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) { var builder = (StringBuilder)null; - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { builder = new StringBuilder(); } @@ -1105,7 +1096,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:"); } @@ -1124,12 +1115,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)) @@ -1138,9 +1129,9 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) } } } - if (EnableSerializationLogs) + if (NetworkLog.Config.EnableSerializationLogs) { - Debug.Log(builder.ToString()); + UnityEngine.Debug.Log(builder.ToString()); } // Notify that all in-scene placed NetworkObjects have been spawned @@ -1158,8 +1149,11 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) } catch (Exception ex) { - Debug.LogException(ex); - Debug.Log(builder.ToString()); + UnityEngine.Debug.LogException(ex); + if (NetworkLog.Config.EnableSerializationLogs) + { + UnityEngine.Debug.Log(builder.ToString()); + } } finally { From ff2e26a73d4d547c8088d31154460b682e23528e Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 12:03:34 +0200 Subject: [PATCH 11/20] revert field removal --- .../Runtime/Components/NetworkTransform.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 8a7ea12cab..7254cac5cc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -4780,6 +4780,7 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) } } + private static int s_TickSynchPosition; private int m_NextTickSync; internal void RegisterForTickSynchronization() From be40aa3f70315b189607f6fbc2a253693b07a37a Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 16:12:25 +0200 Subject: [PATCH 12/20] Readonly, missing MessageTypes, replace quat... --- .../Runtime/Components/NetworkTransform.cs | 2 +- .../Components/QuaternionCompressor.cs | 34 ++++++------------- .../Runtime/Core/NetworkManager.cs | 2 +- .../Runtime/Messaging/MessageDelivery.cs | 4 +++ .../Runtime/Metrics/NetworkMetrics.cs | 15 +++++--- 5 files changed, 26 insertions(+), 31 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 7254cac5cc..e2825a7d02 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -2102,7 +2102,7 @@ internal NetworkTransformState ApplyLocalNetworkState() ///
internal bool ApplyTransformToNetworkState(ref NetworkTransformState networkState, double dirtyTime, Transform transformToUse) { - CachedTransform = transformToUse; + m_CachedTransform = transformToUse; m_CachedNetworkManager = NetworkManager; // Apply the interpolate and PostionDeltaCompression flags, otherwise we get false positives whether something changed or not. networkState.FlagStates.UseInterpolation = Interpolate; diff --git a/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs b/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs index 3c6af11529..91941c812e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs @@ -42,13 +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; -#if UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStaticsOnLoad() => s_QuatAbsValues = Quaternion.identity; -#endif - /// /// Compresses a Quaternion into an unsigned integer /// @@ -58,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/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 59ab063406..b915e21d4b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1812,7 +1812,7 @@ internal abstract class NetcodeAnalytics /// - /// This is called by the Unity Editor reset button. See "OnNetworkManagerReset" from "NetworkManagerHelper.cs" file. + /// This is called by the Unity Editor reset button. See which is handled in "NetworkManagerHelper.cs". /// private void Reset() { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs index 3a6f34b274..cc54d9d102 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs @@ -59,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(); diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs index eb0478bc4a..09325be701 100644 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs @@ -11,21 +11,26 @@ namespace Unity.Netcode internal class NetworkMetrics : INetworkMetrics { private const ulong k_MaxMetricsPerFrame = 1000L; - private readonly 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) { - return s_SceneEventTypeNames.GetValueOrDefault(typeCode, "Unknown"); + return k_SceneEventTypeNames.GetValueOrDefault(typeCode, "Unknown"); } private readonly Counter m_TransportBytesSent = new Counter(NetworkMetricTypes.TotalBytesSent.Id) From dbeccdf9cae794f353ae54ca813547439dba56ce Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 17:56:56 +0200 Subject: [PATCH 13/20] Small fixes --- .../Runtime/Components/RigidbodyContactEventManager.cs | 2 +- .../Runtime/Configuration/CommandLineOptions.cs | 6 +++--- .../Runtime/SceneManagement/NetworkSceneManager.cs | 3 ++- .../Runtime/SceneManagement/SceneEventData.cs | 2 -- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs index 5a6fa6f7d0..7239da4e7a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs @@ -109,7 +109,7 @@ private void OnEnable() { m_ResultsArray = new NativeArray(16, Allocator.Persistent); Physics.ContactEvent += Physics_ContactEvent; - if (Instance != null || Instance != this) + 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 e333577c4e..f6266db2a5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs @@ -18,14 +18,14 @@ public class CommandLineOptions // Contains the current application instance domain's command line arguments private static List s_CommandLineArguments = new List(Environment.GetCommandLineArgs()); -#if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ResetStaticsOnLoad() + private static void InitializeOnLoad() { Instance = new CommandLineOptions(); +#if UNITY_EDITOR s_CommandLineArguments = new List(Environment.GetCommandLineArgs()); - } #endif + } /// /// Returns the value of an argument or null if the argument is not present diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 545a906e8d..3d46ae52c3 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -1581,8 +1581,9 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc internal class SceneUnloadEventHandler { private static Dictionary> s_Instances = new Dictionary>(); - [Conditional("UNITY_EDITOR")] +#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 945d6819ef..f528783ec4 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: @@ -1005,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>(); From f184559164cdfa31af804ebaa9e0bca8e225ff8d Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 18:07:55 +0200 Subject: [PATCH 14/20] Diable Domain Reload + remove warning as errors --- testproject/ProjectSettings/EditorSettings.asset | 2 +- testproject/ProjectSettings/ProjectSettings.asset | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) 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: {} From 99b4f1807df381d5df6a2967b3b1fe76857be722 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 18:22:43 +0200 Subject: [PATCH 15/20] Adding NetworkManager singleton reset test --- .../Tests/Editor/FastEnterPlayModeTests.cs | 28 +++++++++++++++++++ .../Editor/FastEnterPlayModeTests.cs.meta | 3 ++ 2 files changed, 31 insertions(+) create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs.meta 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..08db2a1b6d --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs @@ -0,0 +1,28 @@ +using System.Collections; +using NUnit.Framework; +using UnityEditor; +using UnityEngine.TestTools; + +namespace Unity.Netcode.EditorTests +{ + public 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 From 935ebe129ffc6e9065ecd14bc2f99d8dc5fc2a93 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 18:33:44 +0200 Subject: [PATCH 16/20] Changelog update --- com.unity.netcode.gameobjects/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) 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 From e315369f9bc138909ee820a69a42e24e3c9782db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noellie=20V=C3=A9lez?= Date: Thu, 21 May 2026 18:40:55 +0200 Subject: [PATCH 17/20] Apply suggestion from @noellie-velez --- .../Serialization/CollectionSerializationUtility.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs index c9bcc9c02c..e57f1a9344 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs @@ -265,7 +265,11 @@ private static partial 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 class ListCache +{ +private static readonly List s_AddedList = new List(); +private static readonly List s_RemovedList = new List(); +private static readonly List s_ChangedList = new List(); public static List GetAddedList() { From 2ac827f6027dbe6184fda8792fdc43e12549318b Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 18:45:13 +0200 Subject: [PATCH 18/20] fix indentation + naming convention --- .../CollectionSerializationUtility.cs | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs index e57f1a9344..7c1769d9d8 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs @@ -258,33 +258,26 @@ public static void ReadListDelta(FastBufferReader reader, ref List value) // For HashSet and Dictionary, we need to have some local space to hold lists we need to serialize. // We don't want to do allocations all the time and we know each one needs a maximum of three lists, // so we're going to keep static lists that we can reuse in these methods. -#if UNITY_6000_6_OR_NEWER - [Scripting.LifecycleManagement.AutoStaticsCleanup] -#endif - private static partial class ListCache + private static class ListCache { - private static List s_AddedList = new List(); - private static List s_RemovedList = new List(); - private static class ListCache -{ -private static readonly List s_AddedList = new List(); -private static readonly List s_RemovedList = new List(); -private static readonly 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; } } From 157502917ca196067b71f9efb01ff9b631c53d05 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 18:53:20 +0200 Subject: [PATCH 19/20] Fix test accesibility --- .../Tests/Editor/FastEnterPlayModeTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs index 08db2a1b6d..64468ba2d3 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/FastEnterPlayModeTests.cs @@ -5,7 +5,8 @@ namespace Unity.Netcode.EditorTests { - public class FastEnterPlayModeTests + + internal class FastEnterPlayModeTests { [UnityTest] public IEnumerator NetworkManagerSingletonResetsOnPlayModeEnter( From 9dc2b336e2c845288c349fbe924bced4738fe8be Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 21 May 2026 19:30:47 +0200 Subject: [PATCH 20/20] Small missed rename fix --- .../Runtime/Components/NetworkTransform.cs | 2 +- .../Runtime/SceneManagement/SceneEventData.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index e2825a7d02..7254cac5cc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -2102,7 +2102,7 @@ internal NetworkTransformState ApplyLocalNetworkState() /// internal bool ApplyTransformToNetworkState(ref NetworkTransformState networkState, double dirtyTime, Transform transformToUse) { - m_CachedTransform = transformToUse; + CachedTransform = transformToUse; m_CachedNetworkManager = NetworkManager; // Apply the interpolate and PostionDeltaCompression flags, otherwise we get false positives whether something changed or not. networkState.FlagStates.UseInterpolation = Interpolate; diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index f528783ec4..90443abfde 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -1070,7 +1070,7 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() } else { - Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) could not be found!"); + UnityEngine.Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) could not be found!"); } } }