From 0a417e1ba81f9098babca3b450ada647c392631c Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 7 Oct 2025 16:59:26 -0400 Subject: [PATCH 01/19] ensure internal state is completely reset on behaviour despawn --- .../Runtime/Core/NetworkBehaviour.cs | 66 ++++++++++++++----- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 332694af7d..2f1e748be4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -496,23 +496,11 @@ public NetworkManager NetworkManager /// public bool HasAuthority { get; internal set; } - internal NetworkClient LocalClient { get; private set; } /// /// Gets whether the client is the distributed authority mode session owner. /// - public bool IsSessionOwner - { - get - { - if (LocalClient == null) - { - return false; - } - - return LocalClient.IsSessionOwner; - } - } + public bool IsSessionOwner { get; private set; } /// /// Gets whether the server (local or remote) is a host. @@ -677,12 +665,41 @@ internal void UpdateNetworkProperties() IsHost = networkManager.IsListening && networkManager.IsHost; IsClient = networkManager.IsListening && networkManager.IsClient; IsServer = networkManager.IsListening && networkManager.IsServer; - LocalClient = networkManager.LocalClient; + IsSessionOwner = networkManager.IsListening && networkManager.LocalClient.IsSessionOwner; HasAuthority = networkObject.HasAuthority; ServerIsHost = networkManager.IsListening && networkManager.ServerIsHost; } } + private void ResetAllFields() + { + m_NetworkObject = null; + m_NetworkManager = null; + RpcTarget = null; + + // Set identification related properties + NetworkObjectId = default; + IsLocalPlayer = false; + + // This is "OK" because GetNetworkBehaviourOrderIndex uses the order of + // NetworkObject.ChildNetworkBehaviours which is set once when first + // accessed. + NetworkBehaviourId = default; + + // Set ownership related properties + IsOwnedByServer = false; + IsOwner = false; + OwnerClientId = default; + + // Set NetworkManager dependent properties + IsHost = false; + IsClient = false; + IsServer = false; + IsSessionOwner = false; + HasAuthority = false; + ServerIsHost = false; + } + /// /// Only for use in distributed authority mode. /// Invoked only on the authority instance when a is deferring its despawn on non-authoritative instances. @@ -763,6 +780,9 @@ public virtual void OnNetworkPreDespawn() { } internal void NetworkPreSpawn(ref NetworkManager networkManager, NetworkObject networkObject) { m_NetworkObject = networkObject; + m_NetworkManager = networkManager; + RpcTarget = networkManager.RpcTarget; + UpdateNetworkProperties(); try @@ -872,6 +892,8 @@ internal void InternalOnNetworkDespawn() { NetworkVariableFields[i].Deinitialize(); } + + ResetAllFields(); } /// @@ -1553,7 +1575,15 @@ internal virtual void InternalOnDestroy() /// public virtual void OnDestroy() { - InternalOnDestroy(); + try + { + InternalOnDestroy(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + if (m_NetworkObject != null && m_NetworkObject.IsSpawned && IsSpawned) { // If the associated NetworkObject is still spawned then this @@ -1574,12 +1604,12 @@ public virtual void OnDestroy() } - for (int i = 0; i < NetworkVariableFields.Count; i++) + foreach (var networkVar in NetworkVariableFields) { - NetworkVariableFields[i].Dispose(); + networkVar.Dispose(); } - m_NetworkObject = null; + ResetAllFields(); } } } From aea56feb38bbb16632cd08b40668a9fe75a57ba9 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 7 Oct 2025 16:59:42 -0400 Subject: [PATCH 02/19] Fix tests --- .../Runtime/Components/NetworkAnimator.cs | 40 ++++++++++++++----- .../Components/ObjectNameIdentifier.cs | 5 +-- .../TestHelpers/NetcodeIntegrationTest.cs | 1 + .../TestHelpers/NetworkManagerHelper.cs | 23 ++++++++--- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index 2fe642162a..a3e264bd4d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -213,6 +213,11 @@ internal class TransitionStateinfo // [Layer][DestinationState][TransitionStateInfo] private Dictionary> m_DestinationStateToTransitioninfo = new Dictionary>(); + // Named differently to avoid serialization conflicts with NetworkBehaviour + private NetworkManager m_LocalNetworkManager; + + internal bool DistributedAuthorityMode; + /// /// Builds the m_DestinationStateToTransitioninfo lookup table /// @@ -509,7 +514,12 @@ internal bool IsServerAuthoritative() /// protected virtual bool OnIsServerAuthoritative() { - return NetworkManager ? !NetworkManager.DistributedAuthorityMode : true; + if (!m_LocalNetworkManager) + { + return true; + } + + return !DistributedAuthorityMode; } private int[] m_TransitionHash; @@ -713,6 +723,10 @@ internal AnimationMessage GetAnimationMessage() /// public override void OnNetworkSpawn() { + // Save internal state references + m_LocalNetworkManager = NetworkManager; + DistributedAuthorityMode = m_LocalNetworkManager.DistributedAuthorityMode; + // If there is no assigned Animator then generate a server network warning (logged locally and if applicable on the server-host side as well). if (m_Animator == null) { @@ -963,7 +977,7 @@ internal void CheckForAnimatorChanges() if (m_Animator.runtimeAnimatorController == null) { - if (NetworkManager.LogLevel == LogLevel.Developer) + if (m_LocalNetworkManager.LogLevel == LogLevel.Developer) { Debug.LogError($"[{GetType().Name}] Could not find an assigned {nameof(RuntimeAnimatorController)}! Cannot check {nameof(Animator)} for changes in state!"); } @@ -998,9 +1012,9 @@ internal void CheckForAnimatorChanges() { // Just notify all remote clients and not the local server m_ClientSendList.Clear(); - foreach (var clientId in NetworkManager.ConnectionManager.ConnectedClientIds) + foreach (var clientId in m_LocalNetworkManager.ConnectionManager.ConnectedClientIds) { - if (clientId == NetworkManager.LocalClientId || !NetworkObject.Observers.Contains(clientId)) + if (clientId == m_LocalNetworkManager.LocalClientId || !NetworkObject.Observers.Contains(clientId)) { continue; } @@ -1028,7 +1042,7 @@ private void SendParametersUpdate(ClientRpcParams clientRpcParams = default, boo } else { - Debug.LogError($"[{name}][Client-{NetworkManager.LocalClientId}] Attempting to send parameter updates but not the owner!"); + Debug.LogError($"[{name}][Client-{m_LocalNetworkManager.LocalClientId}] Attempting to send parameter updates but not the owner!"); } } else @@ -1266,12 +1280,12 @@ internal void UpdateAnimationState(AnimationState animationState) // Cross fade from the current to the destination state for the transitions duration while starting at the server's current normalized time of the transition m_Animator.CrossFade(transitionStateInfo.DestinationState, transitionStateInfo.TransitionDuration, transitionStateInfo.Layer, 0.0f, animationState.NormalizedTime); } - else if (NetworkManager.LogLevel == LogLevel.Developer) + else if (m_LocalNetworkManager.LogLevel == LogLevel.Developer) { NetworkLog.LogWarning($"Current State Hash ({currentState.fullPathHash}) != AnimationState.StateHash ({animationState.StateHash})"); } } - else if (NetworkManager.LogLevel == LogLevel.Developer) + else if (m_LocalNetworkManager.LogLevel == LogLevel.Developer) { NetworkLog.LogError($"[DestinationState To Transition Info] Layer ({animationState.Layer}) sub-table does not contain destination state ({animationState.DestinationStateHash})!"); } @@ -1314,7 +1328,8 @@ private unsafe void SendParametersUpdateServerRpc(ParametersUpdateMessage parame return; } UpdateParameters(ref parametersUpdate); - if (NetworkManager.ConnectedClientsIds.Count > (IsHost ? 2 : 1)) + var connectedClientIds = m_LocalNetworkManager.ConnectionManager.ConnectedClientIds; + if (connectedClientIds.Count <= (IsHost ? 2 : 1)) { m_ClientSendList.Clear(); foreach (var clientId in NetworkManager.ConnectionManager.ConnectedClientIds) @@ -1377,7 +1392,8 @@ private void SendAnimStateServerRpc(AnimationMessage animationMessage, ServerRpc UpdateAnimationState(animationState); } - if (NetworkManager.ConnectedClientsIds.Count > (IsHost ? 2 : 1)) + var connectedClientIds = m_LocalNetworkManager.ConnectionManager.ConnectedClientIds; + if (connectedClientIds.Count <= (IsHost ? 2 : 1)) { m_ClientSendList.Clear(); foreach (var clientId in NetworkManager.ConnectionManager.ConnectedClientIds) @@ -1416,7 +1432,7 @@ private void ProcessAnimStates(AnimationMessage animationMessage) { if (HasAuthority) { - if (NetworkManager.LogLevel == LogLevel.Developer) + if (m_LocalNetworkManager.LogLevel == LogLevel.Developer) { var hostOrOwner = NetworkManager.DistributedAuthorityMode ? "Owner" : "Host"; var clientServerOrDAMode = NetworkManager.DistributedAuthorityMode ? "distributed authority" : "client-server"; @@ -1443,7 +1459,7 @@ internal void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerM // Ignore if a non-owner sent this. if (serverRpcParams.Receive.SenderClientId != OwnerClientId) { - if (NetworkManager.LogLevel == LogLevel.Developer) + if (m_LocalNetworkManager.LogLevel == LogLevel.Developer) { NetworkLog.LogWarning($"[Owner Authoritative] Detected the a non-authoritative client is sending the server animation trigger updates. If you recently changed ownership of the {name} object, then this could be the reason."); } @@ -1453,6 +1469,8 @@ internal void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerM // set the trigger locally on the server InternalSetTrigger(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet); + var connectedClientIds = m_LocalNetworkManager.ConnectionManager.ConnectedClientIds; + m_ClientSendList.Clear(); foreach (var clientId in NetworkManager.ConnectionManager.ConnectedClientIds) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/Components/ObjectNameIdentifier.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/Components/ObjectNameIdentifier.cs index 17978b49ec..3209d84ec2 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/Components/ObjectNameIdentifier.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/Components/ObjectNameIdentifier.cs @@ -108,10 +108,7 @@ public override void OnDestroy() DeRegisterNetworkObject(); // This is required otherwise it will try to continue to update the NetworkBehaviour even if // it has been destroyed (most likely integration test specific) - if (m_NetworkObject.ChildNetworkBehaviours != null && m_NetworkObject.ChildNetworkBehaviours.Contains(this)) - { - NetworkObject.ChildNetworkBehaviours.Remove(this); - } + m_NetworkObject.ChildNetworkBehaviours?.Remove(this); m_NetworkObject = null; } base.OnDestroy(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index fedc6a05da..bd4b0d7d8f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -576,6 +576,7 @@ private void InternalOnOneTimeSetup() IsRunning = true; m_EnableVerboseDebug = OnSetVerboseDebug(); IntegrationTestSceneHandler.VerboseDebugMode = m_EnableVerboseDebug; + NetworkManagerHelper.VerboseDebugMode = m_EnableVerboseDebug; VerboseDebug($"Entering {nameof(OneTimeSetup)}"); m_NetworkManagerInstatiationMode = OnSetIntegrationTestMode(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetworkManagerHelper.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetworkManagerHelper.cs index 7a1a4e5afa..8e4505b413 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetworkManagerHelper.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetworkManagerHelper.cs @@ -44,6 +44,11 @@ public static class NetworkManagerHelper /// public static NetworkManagerOperatingMode CurrentNetworkManagerMode; + /// + /// When true, logs will be generated for lifecycle events. + /// + public static bool VerboseDebugMode; + /// /// This provides the ability to start NetworkManager in various modes /// @@ -97,7 +102,7 @@ public static bool StartNetworkManager(out NetworkManager networkManager, Networ return false; } - Debug.Log($"{nameof(NetworkManager)} Instantiated."); + VerboseLog($"{nameof(NetworkManager)} Instantiated."); var unityTransport = NetworkManagerGameObject.AddComponent(); if (networkConfig == null) @@ -212,7 +217,7 @@ private static void StartNetworkManagerMode(NetworkManagerOperatingMode managerM } // Only log this if we started an netcode session - Debug.Log($"{CurrentNetworkManagerMode} started."); + VerboseLog($"{CurrentNetworkManagerMode} started."); } } @@ -223,7 +228,7 @@ private static void StopNetworkManagerMode() { NetworkManagerObject.Shutdown(); - Debug.Log($"{CurrentNetworkManagerMode} stopped."); + VerboseLog($"{CurrentNetworkManagerMode} stopped."); CurrentNetworkManagerMode = NetworkManagerOperatingMode.None; } @@ -243,11 +248,11 @@ public static void ShutdownNetworkManager() if (NetworkManagerGameObject != null) { - Debug.Log($"{nameof(NetworkManager)} shutdown."); + VerboseLog($"{nameof(NetworkManager)} shutdown."); StopNetworkManagerMode(); UnityEngine.Object.DestroyImmediate(NetworkManagerGameObject); - Debug.Log($"{nameof(NetworkManager)} destroyed."); + VerboseLog($"{nameof(NetworkManager)} destroyed."); } NetworkManagerGameObject = null; NetworkManagerObject = null; @@ -304,5 +309,13 @@ public static bool BuffersMatch(int indexOffset, long targetSize, byte[] sourceA } return true; } + + private static void VerboseLog(string message) + { + if (VerboseDebugMode) + { + Debug.unityLogger.Log(message); + } + } } } From da502024893f6ff784686814fa02e7b9fd177d12 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 18:46:28 -0400 Subject: [PATCH 03/19] Ensure NetworkManagerOwner is always correctly set --- .../Connection/NetworkConnectionManager.cs | 2 + .../Runtime/Core/NetworkBehaviour.cs | 49 +------- .../Runtime/Core/NetworkObject.cs | 115 ++++++++++-------- .../SceneManagement/NetworkSceneManager.cs | 2 +- .../Runtime/SceneManagement/SceneEventData.cs | 18 ++- .../Runtime/Spawning/NetworkSpawnManager.cs | 36 ++---- .../IntegrationTestSceneHandler.cs | 4 - .../TestHelpers/NetcodeIntegrationTest.cs | 11 +- .../NetcodeIntegrationTestHelpers.cs | 10 -- 9 files changed, 101 insertions(+), 146 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index ccaab404e4..4ecf5453b7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -805,6 +805,7 @@ internal void HandleConnectionApproval(ulong ownerClientId, NetworkManager.Conne // Spawn the player NetworkObject locally NetworkManager.SpawnManager.SpawnNetworkObjectLocally( playerObject, + NetworkManager, NetworkManager.SpawnManager.GetNetworkObjectId(), sceneObject: false, playerObject: true, @@ -954,6 +955,7 @@ internal void CreateAndSpawnPlayer(ulong ownerId) var globalObjectIdHash = playerPrefab.GetComponent().GlobalObjectIdHash; var networkObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(globalObjectIdHash, ownerId, playerPrefab.transform.position, playerPrefab.transform.rotation); networkObject.IsSceneObject = false; + networkObject.NetworkManagerOwner = NetworkManager; networkObject.SpawnAsPlayerObject(ownerId, networkObject.DestroyWithScene); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 2f1e748be4..5863d2f0f5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -548,9 +548,10 @@ internal bool IsBehaviourEditable() ((networkManager.DistributedAuthorityMode && m_NetworkObject.IsOwner) || (!networkManager.DistributedAuthorityMode && networkManager.IsServer)); } - internal void SetNetworkObject(NetworkObject networkObject) + internal void SetNetworkObject(NetworkObject networkObject, ushort behaviourId) { m_NetworkObject = networkObject; + NetworkBehaviourId = behaviourId; } // TODO: this needs an overhaul. It's expensive, it's ja little naive in how it looks for networkObject in @@ -615,11 +616,6 @@ public NetworkObject NetworkObject /// public ushort NetworkBehaviourId { get; internal set; } - /// - /// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster - /// - internal ushort NetworkBehaviourIdCache = 0; - /// /// Returns the NetworkBehaviour with a given BehaviourId for the current NetworkObject. /// @@ -649,11 +645,6 @@ internal void UpdateNetworkProperties() NetworkObjectId = networkObject.NetworkObjectId; IsLocalPlayer = networkObject.IsLocalPlayer; - // This is "OK" because GetNetworkBehaviourOrderIndex uses the order of - // NetworkObject.ChildNetworkBehaviours which is set once when first - // accessed. - NetworkBehaviourId = networkObject.GetNetworkBehaviourOrderIndex(this); - // Set ownership related properties IsOwnedByServer = networkObject.IsOwnedByServer; IsOwner = networkObject.IsOwner; @@ -671,35 +662,6 @@ internal void UpdateNetworkProperties() } } - private void ResetAllFields() - { - m_NetworkObject = null; - m_NetworkManager = null; - RpcTarget = null; - - // Set identification related properties - NetworkObjectId = default; - IsLocalPlayer = false; - - // This is "OK" because GetNetworkBehaviourOrderIndex uses the order of - // NetworkObject.ChildNetworkBehaviours which is set once when first - // accessed. - NetworkBehaviourId = default; - - // Set ownership related properties - IsOwnedByServer = false; - IsOwner = false; - OwnerClientId = default; - - // Set NetworkManager dependent properties - IsHost = false; - IsClient = false; - IsServer = false; - IsSessionOwner = false; - HasAuthority = false; - ServerIsHost = false; - } - /// /// Only for use in distributed authority mode. /// Invoked only on the authority instance when a is deferring its despawn on non-authoritative instances. @@ -892,8 +854,6 @@ internal void InternalOnNetworkDespawn() { NetworkVariableFields[i].Deinitialize(); } - - ResetAllFields(); } /// @@ -1130,7 +1090,6 @@ internal void NetworkVariableUpdate(ulong targetClientId, bool forceSend = false // Getting these ahead of time actually improves performance var networkManager = NetworkManager; var networkObject = m_NetworkObject; - var behaviourIndex = networkObject.GetNetworkBehaviourOrderIndex(this); var messageManager = networkManager.MessageManager; var connectionManager = networkManager.ConnectionManager; @@ -1170,7 +1129,7 @@ internal void NetworkVariableUpdate(ulong targetClientId, bool forceSend = false var message = new NetworkVariableDeltaMessage { NetworkObjectId = NetworkObjectId, - NetworkBehaviourIndex = behaviourIndex, + NetworkBehaviourIndex = NetworkBehaviourId, NetworkBehaviour = this, TargetClientId = targetClientId, DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j], @@ -1608,8 +1567,6 @@ public virtual void OnDestroy() { networkVar.Dispose(); } - - ResetAllFields(); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 4c0d97c82e..4c167417c8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1119,8 +1119,7 @@ private bool InternalHasAuthority() } /// - /// The NetworkManager that owns this NetworkObject. - /// This property controls where this NetworkObject belongs. + /// The NetworkManager that is responsible for this NetworkObject instance. /// This property is null by default currently, which means that the above NetworkManager getter will return the Singleton. /// In the future this is the path where alternative NetworkManagers should be injected for running multi NetworkManagers /// @@ -1771,6 +1770,9 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla { if (NetworkManagerOwner == null) { +#if TEST_NO_SINGLETON + Debug.LogError("NetworkObject has no owner client! setting as singleton owner"); +#endif NetworkManagerOwner = NetworkManager.Singleton; } if (!NetworkManager.IsListening) @@ -1825,7 +1827,7 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla } } - NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene); + NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManagerOwner, NetworkManager.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene); if ((NetworkManager.DistributedAuthorityMode && NetworkManager.DAHost) || (!NetworkManager.DistributedAuthorityMode && NetworkManager.IsServer)) { @@ -2534,7 +2536,7 @@ internal static void CheckOrphanChildren() internal void InvokeBehaviourNetworkPreSpawn() { - var networkManager = NetworkManager; + var networkManager = NetworkManagerOwner; for (int i = 0; i < ChildNetworkBehaviours.Count; i++) { if (ChildNetworkBehaviours[i].gameObject.activeInHierarchy) @@ -2611,58 +2613,74 @@ internal void InvokeBehaviourNetworkDespawn() } } - private List m_ChildNetworkBehaviours; + internal List m_ChildNetworkBehaviours; internal List ChildNetworkBehaviours { get { - if (m_ChildNetworkBehaviours != null) + if (m_ChildNetworkBehaviours == null) { - return m_ChildNetworkBehaviours; + m_ChildNetworkBehaviours = BuildChildBehavioursList(); } - m_ChildNetworkBehaviours = new List(); - var networkBehaviours = GetComponentsInChildren(true); - for (int i = 0; i < networkBehaviours.Length; i++) + return m_ChildNetworkBehaviours; + } + } + + private List BuildChildBehavioursList() + { +#if UNITY_EDITOR + if (NetworkManagerOwner == null) + { + Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); + NetworkManagerOwner = NetworkManager.Singleton; + } +#endif + + var networkBehaviours = GetComponentsInChildren(true); + var childBehaviours = new List(networkBehaviours.Length); + + foreach (var behaviour in networkBehaviours) + { + // Find the first parent NetworkObject of this child + // if it's not ourselves, this childBehaviour belongs to a different NetworkObject. + var networkObj = behaviour.GetComponentInParent(); + if (networkObj != this) { - // Find the first parent NetworkObject of this child - // if it's not ourselves, this childBehaviour belongs to a different NetworkObject. - var networkObj = networkBehaviours[i].GetComponentInParent(); - if (networkObj != this) - { - continue; - } + continue; + } - // Set ourselves as the NetworkObject that this behaviour belongs to and add it to the child list - networkBehaviours[i].SetNetworkObject(this); - m_ChildNetworkBehaviours.Add(networkBehaviours[i]); + // Set ourselves as the NetworkObject that this behaviour belongs to and add it to the child list + var nextIndex = childBehaviours.Count; + childBehaviours.Add(behaviour); + behaviour.SetNetworkObject(this, (ushort)nextIndex); - var type = networkBehaviours[i].GetType(); - if (type == typeof(NetworkTransform) || type.IsInstanceOfType(typeof(NetworkTransform)) || type.IsSubclassOf(typeof(NetworkTransform))) + var type = behaviour.GetType(); + if (type == typeof(NetworkTransform) || type.IsAssignableFrom(typeof(NetworkTransform)) || type.IsSubclassOf(typeof(NetworkTransform))) + { + if (NetworkTransforms == null) { - if (NetworkTransforms == null) - { - NetworkTransforms = new List(); - } - var networkTransform = networkBehaviours[i] as NetworkTransform; - networkTransform.IsNested = i != 0 && networkTransform.gameObject != gameObject; - NetworkTransforms.Add(networkTransform); + NetworkTransforms = new List(); } + var networkTransform = behaviour as NetworkTransform; + networkTransform.IsNested = networkTransform.gameObject != gameObject; + NetworkTransforms.Add(networkTransform); + } #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D - else if (type.IsSubclassOf(typeof(NetworkRigidbodyBase))) + else if (type.IsSubclassOf(typeof(NetworkRigidbodyBase))) + { + if (NetworkRigidbodies == null) { - if (NetworkRigidbodies == null) - { - NetworkRigidbodies = new List(); - } - NetworkRigidbodies.Add(networkBehaviours[i] as NetworkRigidbodyBase); + NetworkRigidbodies = new List(); } -#endif + NetworkRigidbodies.Add(behaviour as NetworkRigidbodyBase); } - - return m_ChildNetworkBehaviours; +#endif } + + childBehaviours.TrimExcess(); + return childBehaviours; } /// @@ -2744,25 +2762,14 @@ internal static void VerifyParentingStatus() public ushort GetNetworkBehaviourOrderIndex(NetworkBehaviour instance) { // read the cached index, and verify it first - if (instance.NetworkBehaviourIdCache < ChildNetworkBehaviours.Count) + if (instance.NetworkBehaviourId < ChildNetworkBehaviours.Count) { - if (ChildNetworkBehaviours[instance.NetworkBehaviourIdCache] == instance) + if (ChildNetworkBehaviours[instance.NetworkBehaviourId] == instance) { - return instance.NetworkBehaviourIdCache; + return instance.NetworkBehaviourId; } - // invalid cached id reset - instance.NetworkBehaviourIdCache = default; - } - - for (ushort i = 0; i < ChildNetworkBehaviours.Count; i++) - { - if (ChildNetworkBehaviours[i] == instance) - { - // cache the id, for next query - instance.NetworkBehaviourIdCache = i; - return i; - } + Debug.LogError("Network behaviour at index has changed. This should not be possible."); } return 0; @@ -3246,6 +3253,8 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf return null; } + networkObject.NetworkManagerOwner = networkManager; + // This will get set again when the NetworkObject is spawned locally, but we set it here ahead of spawning // in order to be able to determine which NetworkVariables the client will be allowed to read. networkObject.OwnerClientId = sceneObject.OwnerClientId; diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 0ef1840253..f01c8f0581 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -1822,7 +1822,7 @@ private void OnSessionOwnerLoadedScene(uint sceneEventId, Scene scene) if (!keyValuePairBySceneHandle.Value.IsPlayerObject) { // All in-scene placed NetworkObjects default to being owned by the server - NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, + NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, NetworkManager, NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, NetworkManager.LocalClientId, true); } } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index b456197a47..7286a9202a 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -376,8 +376,15 @@ internal void AddDespawnedInSceneNetworkObjects() #endif foreach (var sobj in inSceneNetworkObjects) { + // For integration tests, don't collect objects that don't belong to us. + if (sobj.NetworkManagerOwner != null && sobj.NetworkManagerOwner != m_NetworkManager) + { + continue; + } + if (sobj.IsSceneObject.HasValue && sobj.IsSceneObject.Value && !sobj.IsSpawned) { + sobj.NetworkManagerOwner = m_NetworkManager; m_DespawnedInSceneObjectsSync.Add(sobj); } } @@ -1083,19 +1090,22 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() } // Now find the in-scene NetworkObject with the current GlobalObjectIdHash we are looking for - if (sceneRelativeNetworkObjects.ContainsKey(globalObjectIdHash)) + if (sceneRelativeNetworkObjects.TryGetValue(globalObjectIdHash, out var despawnedObject)) { + // Set the owner of this network object + despawnedObject.NetworkManagerOwner = m_NetworkManager; + // Since this is a NetworkObject that was never spawned, we just need to send a notification // out that it was despawned so users can make adjustments - sceneRelativeNetworkObjects[globalObjectIdHash].InvokeBehaviourNetworkDespawn(); + despawnedObject.InvokeBehaviourNetworkDespawn(); if (!m_NetworkManager.SceneManager.ScenePlacedObjects.ContainsKey(globalObjectIdHash)) { m_NetworkManager.SceneManager.ScenePlacedObjects.Add(globalObjectIdHash, new Dictionary()); } - if (!m_NetworkManager.SceneManager.ScenePlacedObjects[globalObjectIdHash].ContainsKey(sceneRelativeNetworkObjects[globalObjectIdHash].GetSceneOriginHandle())) + if (!m_NetworkManager.SceneManager.ScenePlacedObjects[globalObjectIdHash].ContainsKey(despawnedObject.GetSceneOriginHandle())) { - m_NetworkManager.SceneManager.ScenePlacedObjects[globalObjectIdHash].Add(sceneRelativeNetworkObjects[globalObjectIdHash].GetSceneOriginHandle(), sceneRelativeNetworkObjects[globalObjectIdHash]); + m_NetworkManager.SceneManager.ScenePlacedObjects[globalObjectIdHash].Add(despawnedObject.GetSceneOriginHandle(), despawnedObject); } } else diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 6e4b561db6..92a445ba77 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -807,7 +807,6 @@ internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ow { // Let the handler spawn the NetworkObject var prefabHandlerObject = NetworkManager.PrefabHandler.HandleNetworkPrefabSpawn(globalObjectIdHash, ownerId, position ?? default, rotation ?? default, instantiationData); - prefabHandlerObject.NetworkManagerOwner = NetworkManager; return prefabHandlerObject; } @@ -879,7 +878,6 @@ internal NetworkObject InstantiateNetworkPrefab(GameObject networkPrefab, uint p { var networkObject = UnityEngine.Object.Instantiate(networkPrefab).GetComponent(); networkObject.transform.SetPositionAndRotation(position ?? networkObject.transform.position, rotation ?? networkObject.transform.rotation); - networkObject.NetworkManagerOwner = NetworkManager; networkObject.PrefabGlobalObjectIdHash = prefabGlobalObjectIdHash; return networkObject; } @@ -1030,7 +1028,7 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneO /// Distributed Authority: /// DAHost client and standard DA clients invoke this method. /// - internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene) + internal void SpawnNetworkObjectLocally(NetworkObject networkObject, NetworkManager networkManager, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene) { if (networkObject == null) { @@ -1052,7 +1050,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo } } // Invoke NetworkBehaviour.OnPreSpawn methods - networkObject.NetworkManagerOwner = NetworkManager; + networkObject.NetworkManagerOwner = networkManager; networkObject.InvokeBehaviourNetworkPreSpawn(); // DANGO-TODO: It would be nice to allow users to specify which clients are observers prior to spawning @@ -1114,13 +1112,6 @@ internal void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkObject.SceneOrigin = networkObject.gameObject.scene; } - // For integration testing, this makes sure that the appropriate NetworkManager is assigned to - // the NetworkObject since it uses the NetworkManager.Singleton when not set - if (networkObject.NetworkManagerOwner != NetworkManager) - { - networkObject.NetworkManagerOwner = NetworkManager; - } - networkObject.NetworkObjectId = networkId; networkObject.DestroyWithScene = sceneObject || destroyWithScene; @@ -1476,22 +1467,19 @@ internal void ServerSpawnSceneObjectsOnStartSweep() var networkObjectsToSpawn = new List(); for (int i = 0; i < networkObjects.Length; i++) { - if (networkObjects[i].NetworkManager == NetworkManager) + // This used to be two loops. + // The first added all NetworkObjects to a list and the second spawned all NetworkObjects in the list. + // Now, a parent will set its children's IsSceneObject value when spawned, so we check for null or for true. + if (networkObjects[i].IsSceneObject == null || (networkObjects[i].IsSceneObject.HasValue && networkObjects[i].IsSceneObject.Value)) { - // This used to be two loops. - // The first added all NetworkObjects to a list and the second spawned all NetworkObjects in the list. - // Now, a parent will set its children's IsSceneObject value when spawned, so we check for null or for true. - if (networkObjects[i].IsSceneObject == null || (networkObjects[i].IsSceneObject.HasValue && networkObjects[i].IsSceneObject.Value)) + var ownerId = networkObjects[i].OwnerClientId; + if (NetworkManager.DistributedAuthorityMode) { - var ownerId = networkObjects[i].OwnerClientId; - if (NetworkManager.DistributedAuthorityMode) - { - ownerId = NetworkManager.LocalClientId; - } - - SpawnNetworkObjectLocally(networkObjects[i], GetNetworkObjectId(), true, false, ownerId, true); - networkObjectsToSpawn.Add(networkObjects[i]); + ownerId = NetworkManager.LocalClientId; } + + SpawnNetworkObjectLocally(networkObjects[i], NetworkManager, GetNetworkObjectId(), true, false, ownerId, true); + networkObjectsToSpawn.Add(networkObjects[i]); } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/IntegrationTestSceneHandler.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/IntegrationTestSceneHandler.cs index 5ca6253026..7786dba6c2 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/IntegrationTestSceneHandler.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/IntegrationTestSceneHandler.cs @@ -176,10 +176,6 @@ private static void ProcessInSceneObjects(Scene scene, NetworkManager networkMan /// private static void ProcessInSceneObject(NetworkObject networkObject, NetworkManager networkManager) { - if (networkObject.NetworkManagerOwner != networkManager) - { - networkObject.NetworkManagerOwner = networkManager; - } if (networkObject.GetComponent() == null) { networkObject.gameObject.AddComponent(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index bd4b0d7d8f..596304a5a5 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -1736,7 +1736,6 @@ protected void DestroySceneNetworkObjects() if (CanDestroyNetworkObject(networkObject)) { - networkObject.NetworkManagerOwner = m_ServerNetworkManager; // Destroy the GameObject that holds the NetworkObject component Object.DestroyImmediate(networkObject.gameObject); } @@ -2561,10 +2560,14 @@ public static void SimulateOneFrame() { var method = obj.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); method?.Invoke(obj, new object[] { }); - foreach (var behaviour in obj.ChildNetworkBehaviours) + + if (obj.m_ChildNetworkBehaviours != null) { - var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - behaviourMethod?.Invoke(behaviour, new object[] { }); + foreach (var behaviour in obj.m_ChildNetworkBehaviours) + { + var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + behaviourMethod?.Invoke(behaviour, new object[] { }); + } } } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs index b33f47cd01..e7a07a16fb 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs @@ -697,7 +697,6 @@ internal static GameObject CreateNetworkObject(string baseName, NetworkManager o name = baseName }; var networkObject = gameObject.AddComponent(); - networkObject.NetworkManagerOwner = owner; MakeNetworkObjectTestPrefab(networkObject); if (moveToDDOL) { @@ -754,10 +753,6 @@ public static void MarkAsSceneObjectRoot(GameObject networkObjectRoot, NetworkMa NetworkObject[] serverNetworkObjects = networkObjectRoot.GetComponentsInChildren(); - for (int i = 0; i < serverNetworkObjects.Length; i++) - { - serverNetworkObjects[i].NetworkManagerOwner = server; - } for (int i = 0; i < clients.Length; i++) { @@ -765,11 +760,6 @@ public static void MarkAsSceneObjectRoot(GameObject networkObjectRoot, NetworkMa root.name += " - Client - " + i; NetworkObject[] clientNetworkObjects = root.GetComponentsInChildren(); - - for (int j = 0; j < clientNetworkObjects.Length; j++) - { - clientNetworkObjects[j].NetworkManagerOwner = clients[i]; - } } } From f30953a09d023670e38af85cb10cc58390ed2fa5 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 18:47:39 -0400 Subject: [PATCH 04/19] Update tests to explicitly set NetworkManagerOwner --- .../Tests/Runtime/NetworkBehaviourGenericTests.cs | 2 ++ .../Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs | 4 +++- .../Runtime/Serialization/NetworkObjectReferenceTests.cs | 1 + .../Tests/Runtime/TestHelpers/NetworkManagerHelper.cs | 7 ++++--- .../Tests/Runtime/UniversalRpcTests.cs | 1 + .../InSceneObjectDestroyTests.cs | 1 + .../InScenePlacedNetworkObject/InSceneObjectTests.cs | 2 ++ .../NetworkObjectSceneMigrationTests.cs | 4 ++++ .../NetworkSceneManager/NetworkSceneManagerDDOLTests.cs | 1 + .../ObjectParenting/ParentDynamicUnderInScenePlaced.cs | 5 +++-- .../ObjectParenting/ParentingWorldPositionStaysTests.cs | 2 ++ testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs | 1 + testproject/Assets/Tests/Runtime/RpcObserverTests.cs | 1 + 13 files changed, 26 insertions(+), 6 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs index 91e3124932..ce756cc6ca 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs @@ -69,6 +69,7 @@ public IEnumerator ValidatedDisableddNetworkBehaviourWarning() LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during spawn!"); + parentNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; parentNetworkObject.Spawn(); yield return s_DefaultWaitForTick; } @@ -134,6 +135,7 @@ public IEnumerator ValidateDeleteChildNetworkBehaviour() var parentNetworkObject = parentObject.AddComponent(); childObject.AddComponent(); + parentNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; parentNetworkObject.Spawn(); yield return s_DefaultWaitForTick; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs index 7b06e49576..1fa1724a45 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs @@ -105,8 +105,10 @@ private void RegisterPrefabHandler(NetworkManager manager, out PrefabInstanceHan private NetworkObject SpawnPrefabWithData(NetworkSerializableTest data) { + var authority = GetAuthorityNetworkManager(); var instance = UnityEngine.Object.Instantiate(m_Prefab).GetComponent(); - GetAuthorityNetworkManager().PrefabHandler.SetInstantiationData(instance, data); + instance.NetworkManagerOwner = authority; + authority.PrefabHandler.SetInstantiationData(instance, data); instance.Spawn(); return instance; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs index 1fbce803fc..19a57f1fb0 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs @@ -418,6 +418,7 @@ public static UnityObjectContext CreateNetworkObject(string name { var gameObject = new GameObject(name); var networkObject = gameObject.AddComponent(); + networkObject.NetworkManagerOwner = NetworkManager.Singleton; return new UnityObjectContext(networkObject, gameObject); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetworkManagerHelper.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetworkManagerHelper.cs index 8e4505b413..035d11dfb5 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetworkManagerHelper.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetworkManagerHelper.cs @@ -170,10 +170,11 @@ public static T AddComponentToObject(Guid gameObjectIdentifier) where T : Net /// ID returned to reference the game object public static void SpawnNetworkObject(Guid gameObjectIdentifier) { - Assert.IsTrue(InstantiatedNetworkObjects.ContainsKey(gameObjectIdentifier)); - if (!InstantiatedNetworkObjects[gameObjectIdentifier].IsSpawned) + Assert.IsTrue(InstantiatedNetworkObjects.TryGetValue(gameObjectIdentifier, out var objToSpawn)); + if (!objToSpawn.IsSpawned) { - InstantiatedNetworkObjects[gameObjectIdentifier].Spawn(); + objToSpawn.NetworkManagerOwner = NetworkManager.Singleton; + objToSpawn.Spawn(); } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/UniversalRpcTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/UniversalRpcTests.cs index bccb9aae40..9c056af126 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/UniversalRpcTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/UniversalRpcTests.cs @@ -601,6 +601,7 @@ protected override void OnOneTimeTearDown() protected override void OnTimeTravelServerAndClientsConnected() { + m_ServerObject.GetComponent().NetworkManagerOwner = m_ServerNetworkManager; m_ServerObject.GetComponent().Spawn(); WaitForMessageReceivedWithTimeTravel(m_ClientNetworkManagers.ToList()); } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs index 9a01ceb5c1..5ec7f69e28 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs @@ -140,6 +140,7 @@ public IEnumerator InSceneNetworkObjectDespawnSyncAndSpawn() yield return LoadSceneAndDespawnObject(DestroyMode.DespawnGameObject); var serverObject = NetworkObjectTestComponent.ServerNetworkObjectInstance; + serverObject.NetworkManagerOwner = m_ServerNetworkManager; Assert.IsNotNull(serverObject, "Could not find server-side in-scene placed NetworkObject!"); diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectTests.cs index ba384e7800..50270582fa 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectTests.cs @@ -67,6 +67,7 @@ public IEnumerator EnableDisableInSceneObjectTests() // Test #3: Now spawn the same in-scene placed NetworkObject serverInSceneObjectInstance.gameObject.SetActive(true); + serverInSceneObjectInstance.NetworkManagerOwner = m_ServerNetworkManager; serverInSceneObjectInstance.Spawn(); yield return WaitForConditionOrTimeOut(HaveAllClientsSpawnedInSceneObject); @@ -104,6 +105,7 @@ public IEnumerator EnableDisableInSceneObjectTests() // Test #5: Now spawn the in-scene placed NetworkObject serverInSceneObjectInstance.gameObject.SetActive(true); + serverInSceneObjectInstance.NetworkManagerOwner = m_ServerNetworkManager; serverInSceneObjectInstance.Spawn(); // Verify all clients spawned their in-scene NetworkObject relative instance diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs index d5eb5f427c..b73e821f78 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs @@ -159,6 +159,7 @@ public IEnumerator MigrateIntoNewSceneTest() { var serverInstance = Object.Instantiate(m_TestPrefab); var serverNetworkObject = serverInstance.GetComponent(); + serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverNetworkObject.Spawn(); m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); } @@ -276,6 +277,7 @@ public IEnumerator ActiveSceneSynchronizationTest() // the active scene and marked to destroy with scene =are destroyed= if // the scene being unloaded is currently the active scene and the scene that // the NetworkObjects reside within. + serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverNetworkObject.Spawn(true); m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); } @@ -291,6 +293,7 @@ public IEnumerator ActiveSceneSynchronizationTest() // spawned with DestroyWithScene set to false will migrate into the current // active scene if the scene they currently reside within is destroyed and // is not the currently active scene. + serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverNetworkObject.Spawn(); m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); } @@ -304,6 +307,7 @@ public IEnumerator ActiveSceneSynchronizationTest() // This set of NetworkObjects will be used to verify that NetworkObjets // spawned with DestroyWithScene == true will get destroyed when the scene // is unloaded + serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverNetworkObject.Spawn(true); m_ServerSpawnedDestroyWithSceneInstances.Add(serverNetworkObject); } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs index f38a311ea8..3fbaf065c4 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs @@ -112,6 +112,7 @@ public IEnumerator InSceneNetworkObjectState([Values(DefaultState.IsEnabled, Def // Sets whether we are in-scene or dynamically spawned NetworkObject ddolBehaviour.SetInScene(isInScene); + networkObject.NetworkManagerOwner = m_ServerNetworkManager; networkObject.Spawn(); yield return waitForFullNetworkTick; diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs index 3bf12e7011..3e8f68b03e 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs @@ -129,8 +129,9 @@ public IEnumerator ParentUnderInSceneplaced() // Now dynamically spawn a NetworkObject to also test dynamically spawned NetworkObjects being parented // under in-scene placed NetworkObjects - var dynamicallySpawnedServerSide = Object.Instantiate(m_DynamicallySpawned); - dynamicallySpawnedServerSide.GetComponent().Spawn(true); + var dynamicallySpawnedServerSide = Object.Instantiate(m_DynamicallySpawned).GetComponent(); + dynamicallySpawnedServerSide.NetworkManagerOwner = m_ServerNetworkManager; + dynamicallySpawnedServerSide.Spawn(true); for (int i = 0; i < 5; i++) { diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingWorldPositionStaysTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingWorldPositionStaysTests.cs index 04eb18c713..6d8f6739f5 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingWorldPositionStaysTests.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingWorldPositionStaysTests.cs @@ -291,6 +291,7 @@ public IEnumerator WorldPositionStaysTest([Values] ParentingTestModes mode, [Val } var serverSideParentNetworkObject = m_ServerSideParent.GetComponent(); + serverSideParentNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverSideParentNetworkObject.Spawn(); // Instantiate the children @@ -321,6 +322,7 @@ public IEnumerator WorldPositionStaysTest([Values] ParentingTestModes mode, [Val serverSideChild.transform.localScale = childScaleList[i]; VerboseDebug($"[Server][PreSpawn] Set scale of NetworkObject to ({childScaleList[i]})"); + serverSideChildNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverSideChildNetworkObject.Spawn(); VerboseDebug($"[Server] Set scale of NetworkObjectID ({serverSideChildNetworkObject.NetworkObjectId}) to ({childScaleList[i]}) and is currently {serverSideChild.transform.localScale}"); diff --git a/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs b/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs index 665590d342..a12705e077 100644 --- a/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs +++ b/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs @@ -258,6 +258,7 @@ public IEnumerator TestPrefabsSpawning([Values] InstantiateAndSpawnMethods insta } var gameObjectInstance = Object.Instantiate(prefabOverride.gameObject); spawnedNetworkObject = gameObjectInstance.GetComponent(); + spawnedNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; spawnedNetworkObject.Spawn(); manualSpawnCount++; } diff --git a/testproject/Assets/Tests/Runtime/RpcObserverTests.cs b/testproject/Assets/Tests/Runtime/RpcObserverTests.cs index 0db86ccb1a..e7ebed1c49 100644 --- a/testproject/Assets/Tests/Runtime/RpcObserverTests.cs +++ b/testproject/Assets/Tests/Runtime/RpcObserverTests.cs @@ -179,6 +179,7 @@ public IEnumerator DespawnRespawnObserverTest() yield return s_DefaultWaitForTick; m_ServerRpcObserverObject.ResetTest(); + m_ServerRpcObserverObject.NetworkObject.NetworkManagerOwner = m_ServerNetworkManager; m_ServerRpcObserverObject.NetworkObject.Spawn(); m_ServerRpcObserverObject.ObserverMessageClientRpc(); yield return WaitForConditionOrTimeOut(m_ServerRpcObserverObject.AllObserversReceivedRPC); From add43397edb4f03392a1506d8bd6fa8e9f40e261 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 19:03:32 -0400 Subject: [PATCH 05/19] Fix InstantiateAndSpawn --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 2 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 4c167417c8..b8c1d37b3c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1929,7 +1929,7 @@ public NetworkObject InstantiateAndSpawn(NetworkManager networkManager, ulong ow return null; } - return networkManager.SpawnManager.InstantiateAndSpawnNoParameterChecks(this, ownerClientId, destroyWithScene, isPlayerObject, forceOverride, position, rotation); + return networkManager.SpawnManager.InstantiateAndSpawnNoParameterChecks(this, ownerClientId, destroyWithScene, isPlayerObject, forceOverride, position, rotation, networkManager); } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 92a445ba77..d47a387d73 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -760,7 +760,7 @@ public NetworkObject InstantiateAndSpawn(NetworkObject networkPrefab, ulong owne /// /// !!! Does not perform any parameter checks prior to attempting to instantiate and spawn the NetworkObject !!! /// - internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default) + internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default, NetworkManager networkManager = null) { NetworkObject networkObject; // - Host and clients always instantiate the override if one exists. @@ -782,6 +782,8 @@ internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networ Debug.LogError($"Failed to instantiate and spawn {networkPrefab.name}!"); return null; } + + networkObject.NetworkManagerOwner = networkManager; networkObject.IsPlayerObject = isPlayerObject; networkObject.transform.SetPositionAndRotation(position, rotation); // If spawning as a player, then invoke SpawnAsPlayerObject From bb3770b8dc357495dc367a19e5e5e84fc4db0768 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 19:14:13 -0400 Subject: [PATCH 06/19] fixes --- .../Runtime/Core/NetworkObject.cs | 2 +- .../NetworkTransform/NetworkTransformParentingTests.cs | 1 + .../Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs | 9 +++------ 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index b8c1d37b3c..7e347801ac 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -2613,7 +2613,7 @@ internal void InvokeBehaviourNetworkDespawn() } } - internal List m_ChildNetworkBehaviours; + private List m_ChildNetworkBehaviours; internal List ChildNetworkBehaviours { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs index 896e7ddda2..9bdea9be25 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs @@ -103,6 +103,7 @@ public override void OnNetworkSpawn() private void RequestPlayerObjectSpawnServerRpc(ServerRpcParams rpcParams = default) { SpawnedPlayer = Instantiate(PlayerPrefab); + SpawnedPlayer.NetworkManagerOwner = NetworkManager; SpawnedPlayer.SpawnAsPlayerObject(rpcParams.Receive.SenderClientId); SpawnedPlayer.TrySetParent(NetworkObject, false); State = MoveState.PlayerSpawned; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index 596304a5a5..af62de0458 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -2561,13 +2561,10 @@ public static void SimulateOneFrame() var method = obj.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); method?.Invoke(obj, new object[] { }); - if (obj.m_ChildNetworkBehaviours != null) + foreach (var behaviour in obj.ChildNetworkBehaviours) { - foreach (var behaviour in obj.m_ChildNetworkBehaviours) - { - var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - behaviourMethod?.Invoke(behaviour, new object[] { }); - } + var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + behaviourMethod?.Invoke(behaviour, new object[] { }); } } } From 8d0ccd3401255c46d293927e51f7e1650886e0fa Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 20:02:39 -0400 Subject: [PATCH 07/19] Hide error for now --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 7e347801ac..94467fde69 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -2630,13 +2630,13 @@ internal List ChildNetworkBehaviours private List BuildChildBehavioursList() { -#if UNITY_EDITOR if (NetworkManagerOwner == null) { +#if TEST_NO_SINGLETON Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); +#endif NetworkManagerOwner = NetworkManager.Singleton; } -#endif var networkBehaviours = GetComponentsInChildren(true); var childBehaviours = new List(networkBehaviours.Length); From 9d79e57a7a5e11f82e44331720f7f059025921ce Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 20:09:13 -0400 Subject: [PATCH 08/19] Put error back and fix timetravel --- .../Runtime/Core/NetworkObject.cs | 2 -- .../Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs | 9 ++++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 94467fde69..71fdcc6fb8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -2632,9 +2632,7 @@ private List BuildChildBehavioursList() { if (NetworkManagerOwner == null) { -#if TEST_NO_SINGLETON Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); -#endif NetworkManagerOwner = NetworkManager.Singleton; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index af62de0458..d1d0410e94 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -2561,10 +2561,13 @@ public static void SimulateOneFrame() var method = obj.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); method?.Invoke(obj, new object[] { }); - foreach (var behaviour in obj.ChildNetworkBehaviours) + if (obj.IsSpawned) { - var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - behaviourMethod?.Invoke(behaviour, new object[] { }); + foreach (var behaviour in obj.ChildNetworkBehaviours) + { + var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + behaviourMethod?.Invoke(behaviour, new object[] { }); + } } } } From 36a250a2468b5204e81e4d3b242f325d61ae9562 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 21:38:49 -0400 Subject: [PATCH 09/19] try things --- .../Runtime/Core/NetworkObject.cs | 34 +++++++++++++++++++ .../Runtime/Spawning/NetworkSpawnManager.cs | 10 +++++- .../Components/ObjectNameIdentifier.cs | 5 ++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 71fdcc6fb8..252ec22fd9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -2060,6 +2060,13 @@ internal void InvokeOwnershipChanged(ulong previous, ulong next) internal void InvokeBehaviourOnNetworkObjectParentChanged(NetworkObject parentNetworkObject) { + if (NetworkManagerOwner == null) + { +#if TEST_NO_SINGLETON + Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); +#endif + NetworkManagerOwner = NetworkManager.Singleton; + } for (int i = 0; i < ChildNetworkBehaviours.Count; i++) { // Invoke internal notification @@ -2288,6 +2295,13 @@ private void OnTransformParentChanged() if (!IsSpawned) { + if (NetworkManagerOwner == null) + { +#if TEST_NO_SINGLETON + Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); +#endif + NetworkManagerOwner = NetworkManager; + } AuthorityAppliedParenting = false; // and we are removing the parent, then go ahead and allow parenting to occur if (transform.parent == null) @@ -2632,7 +2646,9 @@ private List BuildChildBehavioursList() { if (NetworkManagerOwner == null) { +#if TEST_NO_SINGLETON Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); +#endif NetworkManagerOwner = NetworkManager.Singleton; } @@ -2759,6 +2775,15 @@ internal static void VerifyParentingStatus() /// public ushort GetNetworkBehaviourOrderIndex(NetworkBehaviour instance) { + if (!IsSpawned) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) + { + Debug.LogWarning($"{nameof(NetworkObject)} is not spawned yet. Cannot get index of NetworkBehaviour."); + } + return 0; + } + // read the cached index, and verify it first if (instance.NetworkBehaviourId < ChildNetworkBehaviours.Count) { @@ -2780,6 +2805,15 @@ public ushort GetNetworkBehaviourOrderIndex(NetworkBehaviour instance) /// The at the ordered index value or null if it does not exist. public NetworkBehaviour GetNetworkBehaviourAtOrderIndex(ushort index) { + if (!IsSpawned) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) + { + Debug.LogWarning($"{nameof(NetworkObject)} is not spawned yet. Cannot get NetworkBehaviour at index."); + } + return null; + } + if (index >= ChildNetworkBehaviours.Count) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index d47a387d73..d6c90bfa04 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -783,7 +783,7 @@ internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networ return null; } - networkObject.NetworkManagerOwner = networkManager; + networkObject.NetworkManagerOwner = NetworkManager; networkObject.IsPlayerObject = isPlayerObject; networkObject.transform.SetPositionAndRotation(position, rotation); // If spawning as a player, then invoke SpawnAsPlayerObject @@ -1052,6 +1052,10 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, NetworkMana } } // Invoke NetworkBehaviour.OnPreSpawn methods + if (networkObject.NetworkManagerOwner != networkManager || NetworkManager != networkManager) + { + Debug.LogWarning("overriding network manager"); + } networkObject.NetworkManagerOwner = networkManager; networkObject.InvokeBehaviourNetworkPreSpawn(); @@ -1469,6 +1473,10 @@ internal void ServerSpawnSceneObjectsOnStartSweep() var networkObjectsToSpawn = new List(); for (int i = 0; i < networkObjects.Length; i++) { + if (networkObjects[i].NetworkManager != NetworkManager) + { + Debug.LogWarning("Well this is strange"); + } // This used to be two loops. // The first added all NetworkObjects to a list and the second spawned all NetworkObjects in the list. // Now, a parent will set its children's IsSceneObject value when spawned, so we check for null or for true. diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/Components/ObjectNameIdentifier.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/Components/ObjectNameIdentifier.cs index 3209d84ec2..17978b49ec 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/Components/ObjectNameIdentifier.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/Components/ObjectNameIdentifier.cs @@ -108,7 +108,10 @@ public override void OnDestroy() DeRegisterNetworkObject(); // This is required otherwise it will try to continue to update the NetworkBehaviour even if // it has been destroyed (most likely integration test specific) - m_NetworkObject.ChildNetworkBehaviours?.Remove(this); + if (m_NetworkObject.ChildNetworkBehaviours != null && m_NetworkObject.ChildNetworkBehaviours.Contains(this)) + { + NetworkObject.ChildNetworkBehaviours.Remove(this); + } m_NetworkObject = null; } base.OnDestroy(); From 3b6bd42c129dc73cf8a4db1bd0a627f0932865c5 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 22:52:00 -0400 Subject: [PATCH 10/19] Fix onStartSweep --- .../Connection/NetworkConnectionManager.cs | 1 - .../Runtime/Core/NetworkObject.cs | 2 +- .../SceneManagement/NetworkSceneManager.cs | 2 +- .../Runtime/SceneManagement/SceneEventData.cs | 2 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 28 ++++++++++--------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index 4ecf5453b7..5123c4b2c1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -805,7 +805,6 @@ internal void HandleConnectionApproval(ulong ownerClientId, NetworkManager.Conne // Spawn the player NetworkObject locally NetworkManager.SpawnManager.SpawnNetworkObjectLocally( playerObject, - NetworkManager, NetworkManager.SpawnManager.GetNetworkObjectId(), sceneObject: false, playerObject: true, diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 252ec22fd9..ba9e37ad03 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1827,7 +1827,7 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla } } - NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManagerOwner, NetworkManager.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene); + NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene); if ((NetworkManager.DistributedAuthorityMode && NetworkManager.DAHost) || (!NetworkManager.DistributedAuthorityMode && NetworkManager.IsServer)) { diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index f01c8f0581..0ef1840253 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -1822,7 +1822,7 @@ private void OnSessionOwnerLoadedScene(uint sceneEventId, Scene scene) if (!keyValuePairBySceneHandle.Value.IsPlayerObject) { // All in-scene placed NetworkObjects default to being owned by the server - NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, NetworkManager, + NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, NetworkManager.LocalClientId, true); } } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 7286a9202a..843041e5d7 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -377,7 +377,7 @@ internal void AddDespawnedInSceneNetworkObjects() foreach (var sobj in inSceneNetworkObjects) { // For integration tests, don't collect objects that don't belong to us. - if (sobj.NetworkManagerOwner != null && sobj.NetworkManagerOwner != m_NetworkManager) + if (sobj.NetworkManager != m_NetworkManager) { continue; } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index d6c90bfa04..8d0d4bbc9f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1030,7 +1030,7 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneO /// Distributed Authority: /// DAHost client and standard DA clients invoke this method. /// - internal void SpawnNetworkObjectLocally(NetworkObject networkObject, NetworkManager networkManager, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene) + internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene) { if (networkObject == null) { @@ -1051,12 +1051,9 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, NetworkMana Debug.LogError("Spawning NetworkObjects with nested NetworkObjects is only supported for scene objects. Child NetworkObjects will not be spawned over the network!"); } } + // Invoke NetworkBehaviour.OnPreSpawn methods - if (networkObject.NetworkManagerOwner != networkManager || NetworkManager != networkManager) - { - Debug.LogWarning("overriding network manager"); - } - networkObject.NetworkManagerOwner = networkManager; + networkObject.NetworkManagerOwner = NetworkManager; networkObject.InvokeBehaviourNetworkPreSpawn(); // DANGO-TODO: It would be nice to allow users to specify which clients are observers prior to spawning @@ -1103,6 +1100,11 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, NetworkMana internal void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene) { + if (networkObject.NetworkManagerOwner == null) + { + Debug.LogError("NetworkManagerOwner should not be null!"); + } + if (SpawnedObjects.ContainsKey(networkId)) { Debug.LogWarning($"[{NetworkManager.name}] Trying to spawn {networkObject.name} with a {nameof(NetworkObject.NetworkObjectId)} of {networkId} but it is already in the spawned list!"); @@ -1471,25 +1473,25 @@ internal void ServerSpawnSceneObjectsOnStartSweep() var networkObjects = UnityEngine.Object.FindObjectsOfType(); #endif var networkObjectsToSpawn = new List(); - for (int i = 0; i < networkObjects.Length; i++) + foreach (var networkObject in networkObjects) { - if (networkObjects[i].NetworkManager != NetworkManager) + if (networkObject.NetworkManager != NetworkManager) { - Debug.LogWarning("Well this is strange"); + continue; } // This used to be two loops. // The first added all NetworkObjects to a list and the second spawned all NetworkObjects in the list. // Now, a parent will set its children's IsSceneObject value when spawned, so we check for null or for true. - if (networkObjects[i].IsSceneObject == null || (networkObjects[i].IsSceneObject.HasValue && networkObjects[i].IsSceneObject.Value)) + if (networkObject.IsSceneObject == null || (networkObject.IsSceneObject.HasValue && networkObject.IsSceneObject.Value)) { - var ownerId = networkObjects[i].OwnerClientId; + var ownerId = networkObject.OwnerClientId; if (NetworkManager.DistributedAuthorityMode) { ownerId = NetworkManager.LocalClientId; } - SpawnNetworkObjectLocally(networkObjects[i], NetworkManager, GetNetworkObjectId(), true, false, ownerId, true); - networkObjectsToSpawn.Add(networkObjects[i]); + SpawnNetworkObjectLocally(networkObject, GetNetworkObjectId(), true, false, ownerId, true); + networkObjectsToSpawn.Add(networkObject); } } From aa95b19ac92fd47eaf9ec2a19ddd130ddae10ef0 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:05:47 -0400 Subject: [PATCH 11/19] put back changed test code --- .../Tests/Runtime/NetworkBehaviourGenericTests.cs | 2 -- .../NetworkTransform/NetworkTransformParentingTests.cs | 1 - .../Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs | 4 +--- .../Runtime/Serialization/NetworkObjectReferenceTests.cs | 1 - .../Tests/Runtime/UniversalRpcTests.cs | 1 - .../InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs | 1 - .../InScenePlacedNetworkObject/InSceneObjectTests.cs | 2 -- .../NetworkSceneManager/NetworkObjectSceneMigrationTests.cs | 4 ---- .../NetworkSceneManager/NetworkSceneManagerDDOLTests.cs | 1 - .../ObjectParenting/ParentDynamicUnderInScenePlaced.cs | 5 ++--- .../ObjectParenting/ParentingWorldPositionStaysTests.cs | 2 -- testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs | 1 - testproject/Assets/Tests/Runtime/RpcObserverTests.cs | 1 - 13 files changed, 3 insertions(+), 23 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs index ce756cc6ca..91e3124932 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs @@ -69,7 +69,6 @@ public IEnumerator ValidatedDisableddNetworkBehaviourWarning() LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during spawn!"); - parentNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; parentNetworkObject.Spawn(); yield return s_DefaultWaitForTick; } @@ -135,7 +134,6 @@ public IEnumerator ValidateDeleteChildNetworkBehaviour() var parentNetworkObject = parentObject.AddComponent(); childObject.AddComponent(); - parentNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; parentNetworkObject.Spawn(); yield return s_DefaultWaitForTick; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs index 9bdea9be25..896e7ddda2 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs @@ -103,7 +103,6 @@ public override void OnNetworkSpawn() private void RequestPlayerObjectSpawnServerRpc(ServerRpcParams rpcParams = default) { SpawnedPlayer = Instantiate(PlayerPrefab); - SpawnedPlayer.NetworkManagerOwner = NetworkManager; SpawnedPlayer.SpawnAsPlayerObject(rpcParams.Receive.SenderClientId); SpawnedPlayer.TrySetParent(NetworkObject, false); State = MoveState.PlayerSpawned; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs index 1fa1724a45..7b06e49576 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs @@ -105,10 +105,8 @@ private void RegisterPrefabHandler(NetworkManager manager, out PrefabInstanceHan private NetworkObject SpawnPrefabWithData(NetworkSerializableTest data) { - var authority = GetAuthorityNetworkManager(); var instance = UnityEngine.Object.Instantiate(m_Prefab).GetComponent(); - instance.NetworkManagerOwner = authority; - authority.PrefabHandler.SetInstantiationData(instance, data); + GetAuthorityNetworkManager().PrefabHandler.SetInstantiationData(instance, data); instance.Spawn(); return instance; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs index 19a57f1fb0..1fbce803fc 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs @@ -418,7 +418,6 @@ public static UnityObjectContext CreateNetworkObject(string name { var gameObject = new GameObject(name); var networkObject = gameObject.AddComponent(); - networkObject.NetworkManagerOwner = NetworkManager.Singleton; return new UnityObjectContext(networkObject, gameObject); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/UniversalRpcTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/UniversalRpcTests.cs index 9c056af126..bccb9aae40 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/UniversalRpcTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/UniversalRpcTests.cs @@ -601,7 +601,6 @@ protected override void OnOneTimeTearDown() protected override void OnTimeTravelServerAndClientsConnected() { - m_ServerObject.GetComponent().NetworkManagerOwner = m_ServerNetworkManager; m_ServerObject.GetComponent().Spawn(); WaitForMessageReceivedWithTimeTravel(m_ClientNetworkManagers.ToList()); } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs index 5ec7f69e28..9a01ceb5c1 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectDestroyTests.cs @@ -140,7 +140,6 @@ public IEnumerator InSceneNetworkObjectDespawnSyncAndSpawn() yield return LoadSceneAndDespawnObject(DestroyMode.DespawnGameObject); var serverObject = NetworkObjectTestComponent.ServerNetworkObjectInstance; - serverObject.NetworkManagerOwner = m_ServerNetworkManager; Assert.IsNotNull(serverObject, "Could not find server-side in-scene placed NetworkObject!"); diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectTests.cs index 50270582fa..ba384e7800 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InSceneObjectTests.cs @@ -67,7 +67,6 @@ public IEnumerator EnableDisableInSceneObjectTests() // Test #3: Now spawn the same in-scene placed NetworkObject serverInSceneObjectInstance.gameObject.SetActive(true); - serverInSceneObjectInstance.NetworkManagerOwner = m_ServerNetworkManager; serverInSceneObjectInstance.Spawn(); yield return WaitForConditionOrTimeOut(HaveAllClientsSpawnedInSceneObject); @@ -105,7 +104,6 @@ public IEnumerator EnableDisableInSceneObjectTests() // Test #5: Now spawn the in-scene placed NetworkObject serverInSceneObjectInstance.gameObject.SetActive(true); - serverInSceneObjectInstance.NetworkManagerOwner = m_ServerNetworkManager; serverInSceneObjectInstance.Spawn(); // Verify all clients spawned their in-scene NetworkObject relative instance diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs index b73e821f78..d5eb5f427c 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs @@ -159,7 +159,6 @@ public IEnumerator MigrateIntoNewSceneTest() { var serverInstance = Object.Instantiate(m_TestPrefab); var serverNetworkObject = serverInstance.GetComponent(); - serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverNetworkObject.Spawn(); m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); } @@ -277,7 +276,6 @@ public IEnumerator ActiveSceneSynchronizationTest() // the active scene and marked to destroy with scene =are destroyed= if // the scene being unloaded is currently the active scene and the scene that // the NetworkObjects reside within. - serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverNetworkObject.Spawn(true); m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); } @@ -293,7 +291,6 @@ public IEnumerator ActiveSceneSynchronizationTest() // spawned with DestroyWithScene set to false will migrate into the current // active scene if the scene they currently reside within is destroyed and // is not the currently active scene. - serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverNetworkObject.Spawn(); m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); } @@ -307,7 +304,6 @@ public IEnumerator ActiveSceneSynchronizationTest() // This set of NetworkObjects will be used to verify that NetworkObjets // spawned with DestroyWithScene == true will get destroyed when the scene // is unloaded - serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverNetworkObject.Spawn(true); m_ServerSpawnedDestroyWithSceneInstances.Add(serverNetworkObject); } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs index 3fbaf065c4..f38a311ea8 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs @@ -112,7 +112,6 @@ public IEnumerator InSceneNetworkObjectState([Values(DefaultState.IsEnabled, Def // Sets whether we are in-scene or dynamically spawned NetworkObject ddolBehaviour.SetInScene(isInScene); - networkObject.NetworkManagerOwner = m_ServerNetworkManager; networkObject.Spawn(); yield return waitForFullNetworkTick; diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs index 3e8f68b03e..3bf12e7011 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs @@ -129,9 +129,8 @@ public IEnumerator ParentUnderInSceneplaced() // Now dynamically spawn a NetworkObject to also test dynamically spawned NetworkObjects being parented // under in-scene placed NetworkObjects - var dynamicallySpawnedServerSide = Object.Instantiate(m_DynamicallySpawned).GetComponent(); - dynamicallySpawnedServerSide.NetworkManagerOwner = m_ServerNetworkManager; - dynamicallySpawnedServerSide.Spawn(true); + var dynamicallySpawnedServerSide = Object.Instantiate(m_DynamicallySpawned); + dynamicallySpawnedServerSide.GetComponent().Spawn(true); for (int i = 0; i < 5; i++) { diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingWorldPositionStaysTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingWorldPositionStaysTests.cs index 6d8f6739f5..04eb18c713 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingWorldPositionStaysTests.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingWorldPositionStaysTests.cs @@ -291,7 +291,6 @@ public IEnumerator WorldPositionStaysTest([Values] ParentingTestModes mode, [Val } var serverSideParentNetworkObject = m_ServerSideParent.GetComponent(); - serverSideParentNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverSideParentNetworkObject.Spawn(); // Instantiate the children @@ -322,7 +321,6 @@ public IEnumerator WorldPositionStaysTest([Values] ParentingTestModes mode, [Val serverSideChild.transform.localScale = childScaleList[i]; VerboseDebug($"[Server][PreSpawn] Set scale of NetworkObject to ({childScaleList[i]})"); - serverSideChildNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; serverSideChildNetworkObject.Spawn(); VerboseDebug($"[Server] Set scale of NetworkObjectID ({serverSideChildNetworkObject.NetworkObjectId}) to ({childScaleList[i]}) and is currently {serverSideChild.transform.localScale}"); diff --git a/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs b/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs index a12705e077..665590d342 100644 --- a/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs +++ b/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs @@ -258,7 +258,6 @@ public IEnumerator TestPrefabsSpawning([Values] InstantiateAndSpawnMethods insta } var gameObjectInstance = Object.Instantiate(prefabOverride.gameObject); spawnedNetworkObject = gameObjectInstance.GetComponent(); - spawnedNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; spawnedNetworkObject.Spawn(); manualSpawnCount++; } diff --git a/testproject/Assets/Tests/Runtime/RpcObserverTests.cs b/testproject/Assets/Tests/Runtime/RpcObserverTests.cs index e7ebed1c49..0db86ccb1a 100644 --- a/testproject/Assets/Tests/Runtime/RpcObserverTests.cs +++ b/testproject/Assets/Tests/Runtime/RpcObserverTests.cs @@ -179,7 +179,6 @@ public IEnumerator DespawnRespawnObserverTest() yield return s_DefaultWaitForTick; m_ServerRpcObserverObject.ResetTest(); - m_ServerRpcObserverObject.NetworkObject.NetworkManagerOwner = m_ServerNetworkManager; m_ServerRpcObserverObject.NetworkObject.Spawn(); m_ServerRpcObserverObject.ObserverMessageClientRpc(); yield return WaitForConditionOrTimeOut(m_ServerRpcObserverObject.AllObserversReceivedRPC); From 12978312343aca8f54a3e6ec975cee1bcc3a5032 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:13:33 -0400 Subject: [PATCH 12/19] Remove unneeded changes --- .../Runtime/Connection/NetworkConnectionManager.cs | 1 - .../Runtime/SceneManagement/SceneEventData.cs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index 5123c4b2c1..ccaab404e4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -954,7 +954,6 @@ internal void CreateAndSpawnPlayer(ulong ownerId) var globalObjectIdHash = playerPrefab.GetComponent().GlobalObjectIdHash; var networkObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(globalObjectIdHash, ownerId, playerPrefab.transform.position, playerPrefab.transform.rotation); networkObject.IsSceneObject = false; - networkObject.NetworkManagerOwner = NetworkManager; networkObject.SpawnAsPlayerObject(ownerId, networkObject.DestroyWithScene); } } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 843041e5d7..0a91e583c7 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -376,12 +376,6 @@ internal void AddDespawnedInSceneNetworkObjects() #endif foreach (var sobj in inSceneNetworkObjects) { - // For integration tests, don't collect objects that don't belong to us. - if (sobj.NetworkManager != m_NetworkManager) - { - continue; - } - if (sobj.IsSceneObject.HasValue && sobj.IsSceneObject.Value && !sobj.IsSpawned) { sobj.NetworkManagerOwner = m_NetworkManager; From 6a4a85e4ee877e018f391a3c51bd6af8aa7a9124 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:26:59 -0400 Subject: [PATCH 13/19] Simplify changes --- .../Runtime/Core/NetworkBehaviour.cs | 5 + .../Runtime/Core/NetworkObject.cs | 138 +++++++----------- .../Runtime/Spawning/NetworkSpawnManager.cs | 34 ++--- .../TestHelpers/NetcodeIntegrationTest.cs | 10 +- 4 files changed, 73 insertions(+), 114 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 5863d2f0f5..d2555fa90f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -616,6 +616,11 @@ public NetworkObject NetworkObject /// public ushort NetworkBehaviourId { get; internal set; } + /// + /// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster + /// + internal ushort NetworkBehaviourIdCache = 0; + /// /// Returns the NetworkBehaviour with a given BehaviourId for the current NetworkObject. /// diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index ba9e37ad03..27db26f7b1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1119,7 +1119,8 @@ private bool InternalHasAuthority() } /// - /// The NetworkManager that is responsible for this NetworkObject instance. + /// The NetworkManager that owns this NetworkObject. + /// This property controls where this NetworkObject belongs. /// This property is null by default currently, which means that the above NetworkManager getter will return the Singleton. /// In the future this is the path where alternative NetworkManagers should be injected for running multi NetworkManagers /// @@ -1770,9 +1771,6 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla { if (NetworkManagerOwner == null) { -#if TEST_NO_SINGLETON - Debug.LogError("NetworkObject has no owner client! setting as singleton owner"); -#endif NetworkManagerOwner = NetworkManager.Singleton; } if (!NetworkManager.IsListening) @@ -1929,7 +1927,7 @@ public NetworkObject InstantiateAndSpawn(NetworkManager networkManager, ulong ow return null; } - return networkManager.SpawnManager.InstantiateAndSpawnNoParameterChecks(this, ownerClientId, destroyWithScene, isPlayerObject, forceOverride, position, rotation, networkManager); + return networkManager.SpawnManager.InstantiateAndSpawnNoParameterChecks(this, ownerClientId, destroyWithScene, isPlayerObject, forceOverride, position, rotation); } /// @@ -2060,13 +2058,6 @@ internal void InvokeOwnershipChanged(ulong previous, ulong next) internal void InvokeBehaviourOnNetworkObjectParentChanged(NetworkObject parentNetworkObject) { - if (NetworkManagerOwner == null) - { -#if TEST_NO_SINGLETON - Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); -#endif - NetworkManagerOwner = NetworkManager.Singleton; - } for (int i = 0; i < ChildNetworkBehaviours.Count; i++) { // Invoke internal notification @@ -2295,13 +2286,6 @@ private void OnTransformParentChanged() if (!IsSpawned) { - if (NetworkManagerOwner == null) - { -#if TEST_NO_SINGLETON - Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); -#endif - NetworkManagerOwner = NetworkManager; - } AuthorityAppliedParenting = false; // and we are removing the parent, then go ahead and allow parenting to occur if (transform.parent == null) @@ -2550,7 +2534,7 @@ internal static void CheckOrphanChildren() internal void InvokeBehaviourNetworkPreSpawn() { - var networkManager = NetworkManagerOwner; + var networkManager = NetworkManager; for (int i = 0; i < ChildNetworkBehaviours.Count; i++) { if (ChildNetworkBehaviours[i].gameObject.activeInHierarchy) @@ -2633,68 +2617,53 @@ internal List ChildNetworkBehaviours { get { - if (m_ChildNetworkBehaviours == null) + if (m_ChildNetworkBehaviours != null) { - m_ChildNetworkBehaviours = BuildChildBehavioursList(); + return m_ChildNetworkBehaviours; } - return m_ChildNetworkBehaviours; - } - } - - private List BuildChildBehavioursList() - { - if (NetworkManagerOwner == null) - { -#if TEST_NO_SINGLETON - Debug.LogError("NetworkManagerOwner should be set! Setting owner to NetworkManager.Singleton"); -#endif - NetworkManagerOwner = NetworkManager.Singleton; - } - - var networkBehaviours = GetComponentsInChildren(true); - var childBehaviours = new List(networkBehaviours.Length); - - foreach (var behaviour in networkBehaviours) - { - // Find the first parent NetworkObject of this child - // if it's not ourselves, this childBehaviour belongs to a different NetworkObject. - var networkObj = behaviour.GetComponentInParent(); - if (networkObj != this) + m_ChildNetworkBehaviours = new List(); + var networkBehaviours = GetComponentsInChildren(true); + for (int i = 0; i < networkBehaviours.Length; i++) { - continue; - } + // Find the first parent NetworkObject of this child + // if it's not ourselves, this childBehaviour belongs to a different NetworkObject. + var networkObj = networkBehaviours[i].GetComponentInParent(); + if (networkObj != this) + { + continue; + } - // Set ourselves as the NetworkObject that this behaviour belongs to and add it to the child list - var nextIndex = childBehaviours.Count; - childBehaviours.Add(behaviour); - behaviour.SetNetworkObject(this, (ushort)nextIndex); + // Set ourselves as the NetworkObject that this behaviour belongs to and add it to the child list + var nextIndex = (ushort)m_ChildNetworkBehaviours.Count; + networkBehaviours[i].SetNetworkObject(this, nextIndex); + m_ChildNetworkBehaviours.Add(networkBehaviours[i]); - var type = behaviour.GetType(); - if (type == typeof(NetworkTransform) || type.IsAssignableFrom(typeof(NetworkTransform)) || type.IsSubclassOf(typeof(NetworkTransform))) - { - if (NetworkTransforms == null) + var type = networkBehaviours[i].GetType(); + if (type == typeof(NetworkTransform) || type.IsInstanceOfType(typeof(NetworkTransform)) || type.IsSubclassOf(typeof(NetworkTransform))) { - NetworkTransforms = new List(); + if (NetworkTransforms == null) + { + NetworkTransforms = new List(); + } + var networkTransform = networkBehaviours[i] as NetworkTransform; + networkTransform.IsNested = i != 0 && networkTransform.gameObject != gameObject; + NetworkTransforms.Add(networkTransform); } - var networkTransform = behaviour as NetworkTransform; - networkTransform.IsNested = networkTransform.gameObject != gameObject; - NetworkTransforms.Add(networkTransform); - } #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D - else if (type.IsSubclassOf(typeof(NetworkRigidbodyBase))) - { - if (NetworkRigidbodies == null) + else if (type.IsSubclassOf(typeof(NetworkRigidbodyBase))) { - NetworkRigidbodies = new List(); + if (NetworkRigidbodies == null) + { + NetworkRigidbodies = new List(); + } + NetworkRigidbodies.Add(networkBehaviours[i] as NetworkRigidbodyBase); } - NetworkRigidbodies.Add(behaviour as NetworkRigidbodyBase); - } #endif - } + } - childBehaviours.TrimExcess(); - return childBehaviours; + return m_ChildNetworkBehaviours; + } } /// @@ -2775,24 +2744,26 @@ internal static void VerifyParentingStatus() /// public ushort GetNetworkBehaviourOrderIndex(NetworkBehaviour instance) { - if (!IsSpawned) + // read the cached index, and verify it first + if (instance.NetworkBehaviourIdCache < ChildNetworkBehaviours.Count) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) + if (ChildNetworkBehaviours[instance.NetworkBehaviourIdCache] == instance) { - Debug.LogWarning($"{nameof(NetworkObject)} is not spawned yet. Cannot get index of NetworkBehaviour."); + return instance.NetworkBehaviourIdCache; } - return 0; + + // invalid cached id reset + instance.NetworkBehaviourIdCache = default; } - // read the cached index, and verify it first - if (instance.NetworkBehaviourId < ChildNetworkBehaviours.Count) + for (ushort i = 0; i < ChildNetworkBehaviours.Count; i++) { - if (ChildNetworkBehaviours[instance.NetworkBehaviourId] == instance) + if (ChildNetworkBehaviours[i] == instance) { - return instance.NetworkBehaviourId; + // cache the id, for next query + instance.NetworkBehaviourIdCache = i; + return i; } - - Debug.LogError("Network behaviour at index has changed. This should not be possible."); } return 0; @@ -2805,15 +2776,6 @@ public ushort GetNetworkBehaviourOrderIndex(NetworkBehaviour instance) /// The at the ordered index value or null if it does not exist. public NetworkBehaviour GetNetworkBehaviourAtOrderIndex(ushort index) { - if (!IsSpawned) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) - { - Debug.LogWarning($"{nameof(NetworkObject)} is not spawned yet. Cannot get NetworkBehaviour at index."); - } - return null; - } - if (index >= ChildNetworkBehaviours.Count) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 8d0d4bbc9f..4939dba75f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -760,7 +760,7 @@ public NetworkObject InstantiateAndSpawn(NetworkObject networkPrefab, ulong owne /// /// !!! Does not perform any parameter checks prior to attempting to instantiate and spawn the NetworkObject !!! /// - internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default, NetworkManager networkManager = null) + internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default) { NetworkObject networkObject; // - Host and clients always instantiate the override if one exists. @@ -782,8 +782,6 @@ internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networ Debug.LogError($"Failed to instantiate and spawn {networkPrefab.name}!"); return null; } - - networkObject.NetworkManagerOwner = NetworkManager; networkObject.IsPlayerObject = isPlayerObject; networkObject.transform.SetPositionAndRotation(position, rotation); // If spawning as a player, then invoke SpawnAsPlayerObject @@ -1051,7 +1049,6 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo Debug.LogError("Spawning NetworkObjects with nested NetworkObjects is only supported for scene objects. Child NetworkObjects will not be spawned over the network!"); } } - // Invoke NetworkBehaviour.OnPreSpawn methods networkObject.NetworkManagerOwner = NetworkManager; networkObject.InvokeBehaviourNetworkPreSpawn(); @@ -1473,25 +1470,24 @@ internal void ServerSpawnSceneObjectsOnStartSweep() var networkObjects = UnityEngine.Object.FindObjectsOfType(); #endif var networkObjectsToSpawn = new List(); - foreach (var networkObject in networkObjects) + for (int i = 0; i < networkObjects.Length; i++) { - if (networkObject.NetworkManager != NetworkManager) - { - continue; - } - // This used to be two loops. - // The first added all NetworkObjects to a list and the second spawned all NetworkObjects in the list. - // Now, a parent will set its children's IsSceneObject value when spawned, so we check for null or for true. - if (networkObject.IsSceneObject == null || (networkObject.IsSceneObject.HasValue && networkObject.IsSceneObject.Value)) + if (networkObjects[i].NetworkManager == NetworkManager) { - var ownerId = networkObject.OwnerClientId; - if (NetworkManager.DistributedAuthorityMode) + // This used to be two loops. + // The first added all NetworkObjects to a list and the second spawned all NetworkObjects in the list. + // Now, a parent will set its children's IsSceneObject value when spawned, so we check for null or for true. + if (networkObjects[i].IsSceneObject == null || (networkObjects[i].IsSceneObject.HasValue && networkObjects[i].IsSceneObject.Value)) { - ownerId = NetworkManager.LocalClientId; - } + var ownerId = networkObjects[i].OwnerClientId; + if (NetworkManager.DistributedAuthorityMode) + { + ownerId = NetworkManager.LocalClientId; + } - SpawnNetworkObjectLocally(networkObject, GetNetworkObjectId(), true, false, ownerId, true); - networkObjectsToSpawn.Add(networkObject); + SpawnNetworkObjectLocally(networkObjects[i], GetNetworkObjectId(), true, false, ownerId, true); + networkObjectsToSpawn.Add(networkObjects[i]); + } } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index d1d0410e94..0756d94f38 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -2560,14 +2560,10 @@ public static void SimulateOneFrame() { var method = obj.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); method?.Invoke(obj, new object[] { }); - - if (obj.IsSpawned) + foreach (var behaviour in obj.ChildNetworkBehaviours) { - foreach (var behaviour in obj.ChildNetworkBehaviours) - { - var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - behaviourMethod?.Invoke(behaviour, new object[] { }); - } + var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + behaviourMethod?.Invoke(behaviour, new object[] { }); } } } From e116b81f11aabc8ebbd24299937efad844ae0e0b Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:43:04 -0400 Subject: [PATCH 14/19] Put back needed change --- .../Runtime/Connection/NetworkConnectionManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index ccaab404e4..5123c4b2c1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -954,6 +954,7 @@ internal void CreateAndSpawnPlayer(ulong ownerId) var globalObjectIdHash = playerPrefab.GetComponent().GlobalObjectIdHash; var networkObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(globalObjectIdHash, ownerId, playerPrefab.transform.position, playerPrefab.transform.rotation); networkObject.IsSceneObject = false; + networkObject.NetworkManagerOwner = NetworkManager; networkObject.SpawnAsPlayerObject(ownerId, networkObject.DestroyWithScene); } } From 56f8b71b6f0eee3dfb4217a98cf2afbfa76de63e Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:48:36 -0400 Subject: [PATCH 15/19] Set NetworkManager in InstantiateAnd Spawn --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 2 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 27db26f7b1..c70d42ce2a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1927,7 +1927,7 @@ public NetworkObject InstantiateAndSpawn(NetworkManager networkManager, ulong ow return null; } - return networkManager.SpawnManager.InstantiateAndSpawnNoParameterChecks(this, ownerClientId, destroyWithScene, isPlayerObject, forceOverride, position, rotation); + return networkManager.SpawnManager.InstantiateAndSpawnNoParameterChecks(this, networkManager, ownerClientId, destroyWithScene, isPlayerObject, forceOverride, position, rotation); } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 4939dba75f..8f9c2b10f6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -754,13 +754,13 @@ public NetworkObject InstantiateAndSpawn(NetworkObject networkPrefab, ulong owne return null; } - return InstantiateAndSpawnNoParameterChecks(networkPrefab, ownerClientId, destroyWithScene, isPlayerObject, forceOverride, position, rotation); + return InstantiateAndSpawnNoParameterChecks(networkPrefab, NetworkManager, ownerClientId, destroyWithScene, isPlayerObject, forceOverride, position, rotation); } /// /// !!! Does not perform any parameter checks prior to attempting to instantiate and spawn the NetworkObject !!! /// - internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default) + internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networkPrefab, NetworkManager networkManager, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default) { NetworkObject networkObject; // - Host and clients always instantiate the override if one exists. @@ -782,6 +782,8 @@ internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networ Debug.LogError($"Failed to instantiate and spawn {networkPrefab.name}!"); return null; } + + networkObject.NetworkManagerOwner = networkManager; networkObject.IsPlayerObject = isPlayerObject; networkObject.transform.SetPositionAndRotation(position, rotation); // If spawning as a player, then invoke SpawnAsPlayerObject From 96eba851d26f6404b06789e27296bed00148e96e Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 15 Oct 2025 00:00:32 -0400 Subject: [PATCH 16/19] Check for IsSpawned on despawn --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 6 ++++++ .../Runtime/Spawning/NetworkSpawnManager.cs | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index c70d42ce2a..5b7b8b02a7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1966,6 +1966,12 @@ public void SpawnAsPlayerObject(ulong clientId, bool destroyWithScene = false) /// (true) the will be destroyed (false) the will persist after being despawned public void Despawn(bool destroy = true) { + if (!IsSpawned) + { + NetworkLog.LogErrorServer("Object is not spawned!"); + return; + } + foreach (var behavior in ChildNetworkBehaviours) { behavior.MarkVariablesDirty(false); diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 8f9c2b10f6..ef6bca09ce 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1318,12 +1318,6 @@ internal void SendSpawnCallForObserverUpdate(ulong[] newObservers, NetworkObject internal void DespawnObject(NetworkObject networkObject, bool destroyObject = false, bool authorityOverride = false) { - if (!networkObject.IsSpawned) - { - NetworkLog.LogErrorServer("Object is not spawned!"); - return; - } - if (!NetworkManager.IsServer && !NetworkManager.DistributedAuthorityMode) { NetworkLog.LogErrorServer("Only server can despawn objects"); From 685dd00ea00e4f6d48238de6f97fd65839b0f9a2 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 15 Oct 2025 00:04:03 -0400 Subject: [PATCH 17/19] Add recreating test --- .../Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs index a85d26dd88..a719a68908 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs @@ -43,6 +43,10 @@ internal class NetworkBehaviourPreSpawn : NetworkBehaviour protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) { + if (NetworkObject.NetworkManagerOwner == null) + { + Assert.Fail("NetworkManagerOwner should be set before calling OnNetworkPreSpawn"); + } OnNetworkPreSpawnCalled = true; // If we are the server, then set the randomly generated value (1-200). // Otherwise, just set the value to 0. From d725198c569ba4f5d20329e2fb82bd75d719f006 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 15 Oct 2025 00:10:37 -0400 Subject: [PATCH 18/19] Clean up test helpers some --- .../NetworkBehaviourPrePostSpawnTests.cs | 6 ++---- .../Prefabs/NetworkPrefabOverrideTests.cs | 4 ++-- .../NetcodeIntegrationTestHelpers.cs | 19 +++---------------- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs index a719a68908..06496ac129 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs @@ -43,10 +43,8 @@ internal class NetworkBehaviourPreSpawn : NetworkBehaviour protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) { - if (NetworkObject.NetworkManagerOwner == null) - { - Assert.Fail("NetworkManagerOwner should be set before calling OnNetworkPreSpawn"); - } + Assert.That(NetworkObject.NetworkManagerOwner, Is.Not.Null, "NetworkManagerOwner should be set before calling OnNetworkPreSpawn"); + OnNetworkPreSpawnCalled = true; // If we are the server, then set the randomly generated value (1-200). // Otherwise, just set the value to 0. diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs index cbaffa4470..502146fa24 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs @@ -135,9 +135,9 @@ protected override void OnServerAndClientsCreated() { var authorityNetworkManager = GetAuthorityNetworkManager(); // Create a NetworkPrefab with an override - var basePrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-base", authorityNetworkManager, true); + var basePrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-base", true); basePrefab.AddComponent(); - var targetPrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-over", authorityNetworkManager, true); + var targetPrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-over", true); targetPrefab.AddComponent(); m_PrefabOverride = new NetworkPrefab() { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs index e7a07a16fb..92fb80e265 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs @@ -686,11 +686,10 @@ public static void MakeNetworkObjectTestPrefab(NetworkObject networkObject, uint /// /// Creates a to be used with integration testing /// - /// namr of the object - /// owner of the object + /// name of the object /// when true, the instance is automatically migrated into the DDOL /// - internal static GameObject CreateNetworkObject(string baseName, NetworkManager owner, bool moveToDDOL = false) + internal static GameObject CreateNetworkObject(string baseName, bool moveToDDOL = false) { var gameObject = new GameObject { @@ -723,7 +722,7 @@ public static GameObject CreateNetworkObjectPrefab(string baseName, NetworkManag Assert.IsNotNull(authorityNetworkManager, prefabCreateAssertError); Assert.IsFalse(authorityNetworkManager.IsListening, prefabCreateAssertError); - var gameObject = CreateNetworkObject(baseName, authorityNetworkManager); + var gameObject = CreateNetworkObject(baseName); var networkPrefab = new NetworkPrefab() { Prefab = gameObject }; // We could refactor this test framework to share a NetworkPrefabList instance, but at this point it's @@ -749,18 +748,6 @@ public static GameObject CreateNetworkObjectPrefab(string baseName, NetworkManag [Obsolete("This method is no longer valid or used.", false)] public static void MarkAsSceneObjectRoot(GameObject networkObjectRoot, NetworkManager server, NetworkManager[] clients) { - networkObjectRoot.name += " - Server"; - - NetworkObject[] serverNetworkObjects = networkObjectRoot.GetComponentsInChildren(); - - - for (int i = 0; i < clients.Length; i++) - { - GameObject root = Object.Instantiate(networkObjectRoot); - root.name += " - Client - " + i; - - NetworkObject[] clientNetworkObjects = root.GetComponentsInChildren(); - } } /// From 861e62320a4153fd7d785aee25abb3b9bbb17693 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 15 Oct 2025 01:47:14 -0400 Subject: [PATCH 19/19] Fix the DistributedAuthorityCodecTests --- .../DistributedAuthorityCodecTests.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DistributedAuthorityCodecTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DistributedAuthorityCodecTests.cs index 1f4b31affa..585c3b24d8 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DistributedAuthorityCodecTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DistributedAuthorityCodecTests.cs @@ -124,7 +124,11 @@ protected override void OnServerAndClientsCreated() m_Client.NetworkConfig.EnsureNetworkVariableLengthSafety = m_EnsureVariableLengthSafety; utpTransport.ConnectionData.Address = Dns.GetHostAddresses(m_TransportHost).First().ToString(); utpTransport.ConnectionData.Port = k_TransportPort; - m_Client.LogLevel = LogLevel.Developer; + + if (m_EnableVerboseDebug) + { + m_Client.LogLevel = LogLevel.Developer; + } // Validate we are in distributed authority mode with client side spawning and using CMB Service Assert.True(m_Client.NetworkConfig.NetworkTopology == NetworkTopologyTypes.DistributedAuthority, "Distributed authority topology is not set!"); @@ -376,6 +380,10 @@ public IEnumerator SceneEventMessageLoadWithObjects() m_Client.SceneManager.SkipSceneHandling = true; var prefabNetworkObject = m_SpawnObject.GetComponent(); + // We need to preSpawn the behaviours to set the internal data for the synchronize methods + prefabNetworkObject.NetworkManagerOwner = m_Client; + prefabNetworkObject.InvokeBehaviourNetworkPreSpawn(); + m_Client.SceneManager.ScenePlacedObjects.Add(0, new Dictionary() { { new NetworkSceneHandle(1, true), prefabNetworkObject }