From 5dcffc2c2a6d0a573cdb120ffdf1e89f9d217c75 Mon Sep 17 00:00:00 2001 From: Emma Date: Thu, 2 Oct 2025 15:05:56 -0400 Subject: [PATCH 01/20] chore: Optimize NetworkBehaviour.NetworkManager property --- .../Components/AnticipatedNetworkTransform.cs | 52 +++++---- .../Runtime/Components/NetworkAnimator.cs | 103 +++++++++++------- .../Runtime/Core/NetworkBehaviour.cs | 74 ++++++------- 3 files changed, 127 insertions(+), 102 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/AnticipatedNetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/AnticipatedNetworkTransform.cs index d2b22c75a1..db4513ede1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/AnticipatedNetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/AnticipatedNetworkTransform.cs @@ -86,6 +86,8 @@ public struct TransformState private bool m_OutstandingAuthorityChange = false; + private NetworkManager m_NetworkManager; + #if UNITY_EDITOR private void Reset() { @@ -157,7 +159,7 @@ public bool ShouldReanticipate /// The anticipated position public void AnticipateMove(Vector3 newPosition) { - if (NetworkManager.ShutdownInProgress || !NetworkManager.IsListening) + if (m_NetworkManager == null || m_NetworkManager.ShutdownInProgress || !m_NetworkManager.IsListening) { return; } @@ -170,7 +172,7 @@ public void AnticipateMove(Vector3 newPosition) m_PreviousAnticipatedTransform = m_AnticipatedTransform; - m_LastAnticipaionCounter = NetworkManager.AnticipationSystem.AnticipationCounter; + m_LastAnticipaionCounter = m_NetworkManager.AnticipationSystem.AnticipationCounter; m_SmoothDuration = 0; m_CurrentSmoothTime = 0; @@ -183,7 +185,7 @@ public void AnticipateMove(Vector3 newPosition) /// The anticipated rotation public void AnticipateRotate(Quaternion newRotation) { - if (NetworkManager.ShutdownInProgress || !NetworkManager.IsListening) + if (m_NetworkManager == null || m_NetworkManager.ShutdownInProgress || !m_NetworkManager.IsListening) { return; } @@ -196,7 +198,7 @@ public void AnticipateRotate(Quaternion newRotation) m_PreviousAnticipatedTransform = m_AnticipatedTransform; - m_LastAnticipaionCounter = NetworkManager.AnticipationSystem.AnticipationCounter; + m_LastAnticipaionCounter = m_NetworkManager.AnticipationSystem.AnticipationCounter; m_SmoothDuration = 0; m_CurrentSmoothTime = 0; @@ -209,7 +211,7 @@ public void AnticipateRotate(Quaternion newRotation) /// The anticipated scale public void AnticipateScale(Vector3 newScale) { - if (NetworkManager.ShutdownInProgress || !NetworkManager.IsListening) + if (m_NetworkManager == null || m_NetworkManager.ShutdownInProgress || !m_NetworkManager.IsListening) { return; } @@ -222,7 +224,7 @@ public void AnticipateScale(Vector3 newScale) m_PreviousAnticipatedTransform = m_AnticipatedTransform; - m_LastAnticipaionCounter = NetworkManager.AnticipationSystem.AnticipationCounter; + m_LastAnticipaionCounter = m_NetworkManager.AnticipationSystem.AnticipationCounter; m_SmoothDuration = 0; m_CurrentSmoothTime = 0; @@ -235,7 +237,7 @@ public void AnticipateScale(Vector3 newScale) /// The anticipated transform state public void AnticipateState(TransformState newState) { - if (NetworkManager.ShutdownInProgress || !NetworkManager.IsListening) + if (m_NetworkManager == null || m_NetworkManager.ShutdownInProgress || !m_NetworkManager.IsListening) { return; } @@ -265,7 +267,7 @@ private void ProcessSmoothing() if (m_CurrentSmoothTime < m_SmoothDuration) { - m_CurrentSmoothTime += NetworkManager.RealTimeProvider.DeltaTime; + m_CurrentSmoothTime += m_NetworkManager.RealTimeProvider.DeltaTime; var transform_ = transform; var pct = math.min(m_CurrentSmoothTime / m_SmoothDuration, 1f); @@ -401,8 +403,8 @@ protected internal override void InternalOnNetworkSessionSynchronized() ResetAnticipatedState(); m_AnticipatedObject = new AnticipatedObject { Transform = this }; - NetworkManager.AnticipationSystem.RegisterForAnticipationEvents(m_AnticipatedObject); - NetworkManager.AnticipationSystem.AllAnticipatedObjects.Add(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.RegisterForAnticipationEvents(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.AllAnticipatedObjects.Add(m_AnticipatedObject); } } @@ -414,21 +416,23 @@ protected internal override void InternalOnNetworkSessionSynchronized() protected internal override void InternalOnNetworkPostSpawn() { base.InternalOnNetworkPostSpawn(); - if (!CanCommitToTransform && NetworkManager.IsConnectedClient && !SynchronizeState.IsSynchronizing) + if (!CanCommitToTransform && m_NetworkManager.IsConnectedClient && !SynchronizeState.IsSynchronizing) { m_OutstandingAuthorityChange = true; ApplyAuthoritativeState(); ResetAnticipatedState(); m_AnticipatedObject = new AnticipatedObject { Transform = this }; - NetworkManager.AnticipationSystem.RegisterForAnticipationEvents(m_AnticipatedObject); - NetworkManager.AnticipationSystem.AllAnticipatedObjects.Add(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.RegisterForAnticipationEvents(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.AllAnticipatedObjects.Add(m_AnticipatedObject); } } /// public override void OnNetworkSpawn() { - if (NetworkManager.DistributedAuthorityMode) + m_NetworkManager = NetworkManager; + + if (m_NetworkManager.DistributedAuthorityMode) { Debug.LogWarning($"This component is not currently supported in distributed authority."); } @@ -445,8 +449,8 @@ public override void OnNetworkSpawn() ResetAnticipatedState(); m_AnticipatedObject = new AnticipatedObject { Transform = this }; - NetworkManager.AnticipationSystem.RegisterForAnticipationEvents(m_AnticipatedObject); - NetworkManager.AnticipationSystem.AllAnticipatedObjects.Add(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.RegisterForAnticipationEvents(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.AllAnticipatedObjects.Add(m_AnticipatedObject); } /// @@ -454,9 +458,9 @@ public override void OnNetworkDespawn() { if (m_AnticipatedObject != null) { - NetworkManager.AnticipationSystem.DeregisterForAnticipationEvents(m_AnticipatedObject); - NetworkManager.AnticipationSystem.AllAnticipatedObjects.Remove(m_AnticipatedObject); - NetworkManager.AnticipationSystem.ObjectsToReanticipate.Remove(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.DeregisterForAnticipationEvents(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.AllAnticipatedObjects.Remove(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.ObjectsToReanticipate.Remove(m_AnticipatedObject); m_AnticipatedObject = null; } ResetAnticipatedState(); @@ -469,9 +473,9 @@ public override void OnDestroy() { if (m_AnticipatedObject != null) { - NetworkManager.AnticipationSystem.DeregisterForAnticipationEvents(m_AnticipatedObject); - NetworkManager.AnticipationSystem.AllAnticipatedObjects.Remove(m_AnticipatedObject); - NetworkManager.AnticipationSystem.ObjectsToReanticipate.Remove(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.DeregisterForAnticipationEvents(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.AllAnticipatedObjects.Remove(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.ObjectsToReanticipate.Remove(m_AnticipatedObject); m_AnticipatedObject = null; } @@ -520,7 +524,7 @@ public void Smooth(TransformState from, TransformState to, float durationSeconds protected override void OnBeforeUpdateTransformState() { // this is called when new data comes from the server - m_LastAuthorityUpdateCounter = NetworkManager.AnticipationSystem.LastAnticipationAck; + m_LastAuthorityUpdateCounter = m_NetworkManager.AnticipationSystem.LastAnticipationAck; m_OutstandingAuthorityChange = true; } @@ -575,7 +579,7 @@ protected override void OnTransformUpdated() m_AnticipatedTransform = m_AuthoritativeTransform; ShouldReanticipate = true; - NetworkManager.AnticipationSystem.ObjectsToReanticipate.Add(m_AnticipatedObject); + m_NetworkManager.AnticipationSystem.ObjectsToReanticipate.Add(m_AnticipatedObject); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index 2fe642162a..94b247b5cc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -27,7 +27,7 @@ private void FlushMessages() foreach (var animationUpdate in m_SendAnimationUpdates) { - if (m_NetworkAnimator.NetworkManager.DistributedAuthorityMode) + if (m_NetworkAnimator.DistributedAuthorityMode) { m_NetworkAnimator.SendAnimStateRpc(animationUpdate.AnimationMessage); } @@ -41,7 +41,7 @@ private void FlushMessages() foreach (var sendEntry in m_SendParameterUpdates) { - if (m_NetworkAnimator.NetworkManager.DistributedAuthorityMode) + if (m_NetworkAnimator.DistributedAuthorityMode) { m_NetworkAnimator.SendParametersUpdateRpc(sendEntry.ParametersUpdateMessage); } @@ -54,7 +54,7 @@ private void FlushMessages() foreach (var sendEntry in m_SendTriggerUpdates) { - if (m_NetworkAnimator.NetworkManager.DistributedAuthorityMode) + if (m_NetworkAnimator.DistributedAuthorityMode) { m_NetworkAnimator.SendAnimTriggerRpc(sendEntry.AnimationTriggerMessage); } @@ -213,6 +213,10 @@ internal class TransitionStateinfo // [Layer][DestinationState][TransitionStateInfo] private Dictionary> m_DestinationStateToTransitioninfo = new Dictionary>(); + private NetworkManager m_NetworkManager; + + internal bool DistributedAuthorityMode; + /// /// Builds the m_DestinationStateToTransitioninfo lookup table /// @@ -509,7 +513,12 @@ internal bool IsServerAuthoritative() /// protected virtual bool OnIsServerAuthoritative() { - return NetworkManager ? !NetworkManager.DistributedAuthorityMode : true; + if (!m_NetworkManager) + { + return true; + } + + return !DistributedAuthorityMode; } private int[] m_TransitionHash; @@ -713,6 +722,10 @@ internal AnimationMessage GetAnimationMessage() /// public override void OnNetworkSpawn() { + // Save internal state references + m_NetworkManager = NetworkManager; + DistributedAuthorityMode = m_NetworkManager.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 +976,7 @@ internal void CheckForAnimatorChanges() if (m_Animator.runtimeAnimatorController == null) { - if (NetworkManager.LogLevel == LogLevel.Developer) + if (m_NetworkManager.LogLevel == LogLevel.Developer) { Debug.LogError($"[{GetType().Name}] Could not find an assigned {nameof(RuntimeAnimatorController)}! Cannot check {nameof(Animator)} for changes in state!"); } @@ -985,7 +998,7 @@ internal void CheckForAnimatorChanges() // Send an AnimationMessage only if there are dirty AnimationStates to send if (m_AnimationMessage.IsDirtyCount > 0) { - if (NetworkManager.DistributedAuthorityMode) + if (DistributedAuthorityMode) { SendAnimStateRpc(m_AnimationMessage); } @@ -998,9 +1011,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_NetworkManager.ConnectionManager.ConnectedClientIds) { - if (clientId == NetworkManager.LocalClientId || !NetworkObject.Observers.Contains(clientId)) + if (clientId == m_NetworkManager.LocalClientId || !NetworkObject.Observers.Contains(clientId)) { continue; } @@ -1020,7 +1033,7 @@ private void SendParametersUpdate(ClientRpcParams clientRpcParams = default, boo { Parameters = m_ParameterWriter.ToArray() }; - if (NetworkManager.DistributedAuthorityMode) + if (DistributedAuthorityMode) { if (IsOwner) { @@ -1028,7 +1041,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_NetworkManager.LocalClientId}] Attempting to send parameter updates but not the owner!"); } } else @@ -1266,12 +1279,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_NetworkManager.LogLevel == LogLevel.Developer) { NetworkLog.LogWarning($"Current State Hash ({currentState.fullPathHash}) != AnimationState.StateHash ({animationState.StateHash})"); } } - else if (NetworkManager.LogLevel == LogLevel.Developer) + else if (m_NetworkManager.LogLevel == LogLevel.Developer) { NetworkLog.LogError($"[DestinationState To Transition Info] Layer ({animationState.Layer}) sub-table does not contain destination state ({animationState.DestinationStateHash})!"); } @@ -1314,21 +1327,24 @@ private unsafe void SendParametersUpdateServerRpc(ParametersUpdateMessage parame return; } UpdateParameters(ref parametersUpdate); - if (NetworkManager.ConnectedClientsIds.Count > (IsHost ? 2 : 1)) + var connectedClientIds = m_NetworkManager.ConnectionManager.ConnectedClientIds; + if (connectedClientIds.Count <= (IsHost ? 2 : 1)) { - m_ClientSendList.Clear(); - foreach (var clientId in NetworkManager.ConnectionManager.ConnectedClientIds) + return; + } + + m_ClientSendList.Clear(); + foreach (var clientId in connectedClientIds) + { + if (clientId == serverRpcParams.Receive.SenderClientId || clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) { - if (clientId == serverRpcParams.Receive.SenderClientId || clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) - { - continue; - } - m_ClientSendList.Add(clientId); + continue; } - - m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList; - m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersUpdate, m_ClientRpcParams); + m_ClientSendList.Add(clientId); } + + m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList; + m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersUpdate, m_ClientRpcParams); } } @@ -1377,20 +1393,23 @@ private void SendAnimStateServerRpc(AnimationMessage animationMessage, ServerRpc UpdateAnimationState(animationState); } - if (NetworkManager.ConnectedClientsIds.Count > (IsHost ? 2 : 1)) + var connectedClientIds = m_NetworkManager.ConnectionManager.ConnectedClientIds; + if (connectedClientIds.Count <= (IsHost ? 2 : 1)) { - m_ClientSendList.Clear(); - foreach (var clientId in NetworkManager.ConnectionManager.ConnectedClientIds) + return; + } + + m_ClientSendList.Clear(); + foreach (var clientId in connectedClientIds) + { + if (clientId == serverRpcParams.Receive.SenderClientId || clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) { - if (clientId == serverRpcParams.Receive.SenderClientId || clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) - { - continue; - } - m_ClientSendList.Add(clientId); + continue; } - m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList; - m_NetworkAnimatorStateChangeHandler.SendAnimationUpdate(animationMessage, m_ClientRpcParams); + m_ClientSendList.Add(clientId); } + m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList; + m_NetworkAnimatorStateChangeHandler.SendAnimationUpdate(animationMessage, m_ClientRpcParams); } } @@ -1416,10 +1435,10 @@ private void ProcessAnimStates(AnimationMessage animationMessage) { if (HasAuthority) { - if (NetworkManager.LogLevel == LogLevel.Developer) + if (m_NetworkManager.LogLevel == LogLevel.Developer) { - var hostOrOwner = NetworkManager.DistributedAuthorityMode ? "Owner" : "Host"; - var clientServerOrDAMode = NetworkManager.DistributedAuthorityMode ? "distributed authority" : "client-server"; + var hostOrOwner = DistributedAuthorityMode ? "Owner" : "Host"; + var clientServerOrDAMode = DistributedAuthorityMode ? "distributed authority" : "client-server"; NetworkLog.LogWarning($"Detected the {hostOrOwner} is sending itself animation updates in {clientServerOrDAMode} mode! Please report this issue."); } return; @@ -1443,7 +1462,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_NetworkManager.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,8 +1472,10 @@ internal void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerM // set the trigger locally on the server InternalSetTrigger(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet); + var connectedClientIds = m_NetworkManager.ConnectionManager.ConnectedClientIds; + m_ClientSendList.Clear(); - foreach (var clientId in NetworkManager.ConnectionManager.ConnectedClientIds) + foreach (var clientId in connectedClientIds) { if (clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) { @@ -1466,7 +1487,7 @@ internal void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerM { m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animationTriggerMessage, m_ClientRpcParams); } - else if (NetworkManager.ConnectedClientsIds.Count > (IsHost ? 2 : 1)) + else if (connectedClientIds.Count > (IsHost ? 2 : 1)) { m_ClientSendList.Remove(serverRpcParams.Receive.SenderClientId); m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animationTriggerMessage, m_ClientRpcParams); @@ -1531,12 +1552,12 @@ public void SetTrigger(int hash, bool setTrigger = true) // will happen when SendAnimTriggerClientRpc is called. For a client owner, we call the // SendAnimTriggerServerRpc and then trigger locally when running in owner authority mode. var animTriggerMessage = new AnimationTriggerMessage() { Hash = hash, IsTriggerSet = setTrigger }; - if (NetworkManager.DistributedAuthorityMode && HasAuthority) + if (DistributedAuthorityMode && HasAuthority) { m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animTriggerMessage); InternalSetTrigger(hash, setTrigger); } - else if (!NetworkManager.DistributedAuthorityMode && (IsOwner || IsServer)) + else if (!DistributedAuthorityMode && (IsOwner || IsServer)) { if (IsServer) { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 332694af7d..8b6954e283 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -3,7 +3,6 @@ using Unity.Collections; using UnityEngine; - namespace Unity.Netcode { /// @@ -87,7 +86,7 @@ internal FastBufferWriter __beginSendServerRpc(uint rpcMethodId, ServerRpcParams internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMethodId, ServerRpcParams serverRpcParams, RpcDelivery rpcDelivery) #pragma warning restore IDE1006 // restore naming rule violation check { - var networkManager = NetworkManager; + var networkManager = m_NetworkManager; var serverRpcMessage = new ServerRpcMessage { Metadata = new RpcMetadata @@ -138,14 +137,14 @@ internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMeth } else { - rpcWriteSize = NetworkManager.ConnectionManager.SendMessage(ref serverRpcMessage, networkDelivery, NetworkManager.ServerClientId); + rpcWriteSize = networkManager.ConnectionManager.SendMessage(ref serverRpcMessage, networkDelivery, NetworkManager.ServerClientId); } bufferWriter.Dispose(); #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName)) { - NetworkManager.NetworkMetrics.TrackRpcSent( + networkManager.NetworkMetrics.TrackRpcSent( NetworkManager.ServerClientId, m_NetworkObject, rpcMethodName, @@ -173,7 +172,7 @@ internal FastBufferWriter __beginSendClientRpc(uint rpcMethodId, ClientRpcParams internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMethodId, ClientRpcParams clientRpcParams, RpcDelivery rpcDelivery) #pragma warning restore IDE1006 // restore naming rule violation check { - var networkManager = NetworkManager; + var networkManager = m_NetworkManager; var clientRpcMessage = new ClientRpcMessage { Metadata = new RpcMetadata @@ -221,7 +220,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth NetworkLog.LogError(GenerateObserverErrorMessage(clientRpcParams, targetClientId)); } } - rpcWriteSize = NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds); + rpcWriteSize = m_NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds); } else if (clientRpcParams.Send.TargetClientIdsNativeArray != null) { @@ -238,7 +237,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth NetworkLog.LogError(GenerateObserverErrorMessage(clientRpcParams, targetClientId)); } } - rpcWriteSize = NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value); + rpcWriteSize = networkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value); } else { @@ -246,12 +245,12 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth while (observerEnumerator.MoveNext()) { // Skip over the host - if (IsHost && observerEnumerator.Current == NetworkManager.LocalClientId) + if (IsHost && observerEnumerator.Current == networkManager.LocalClientId) { shouldInvokeLocally = true; continue; } - rpcWriteSize = NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, observerEnumerator.Current); + rpcWriteSize = networkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, observerEnumerator.Current); } } @@ -349,7 +348,7 @@ internal void __endSendRpc(ref FastBufferWriter bufferWriter, uint rpcMethodId, NetworkBehaviourId = NetworkBehaviourId, NetworkRpcMethodId = rpcMethodId, }, - SenderClientId = NetworkManager.LocalClientId, + SenderClientId = m_NetworkManager.LocalClientId, WriteBuffer = bufferWriter }; @@ -361,7 +360,7 @@ internal void __endSendRpc(ref FastBufferWriter bufferWriter, uint rpcMethodId, networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; break; case RpcDelivery.Unreliable: - if (bufferWriter.Length > NetworkManager.MessageManager.NonFragmentedMessageMaxSize) + if (bufferWriter.Length > m_NetworkManager.MessageManager.NonFragmentedMessageMaxSize) { throw new OverflowException("RPC parameters are too large for unreliable delivery."); } @@ -436,6 +435,8 @@ internal string GenerateObserverErrorMessage(ClientRpcParams clientRpcParams, ul return $"Sending ClientRpc to non-observer! {containerNameHoldingId} contains clientId {targetClientId} that is not an observer!"; } + private NetworkManager m_NetworkManager; + /// /// Gets the NetworkManager that owns this NetworkBehaviour instance. /// See `NetworkObject` note for how there is a chicken/egg problem when not initialized. @@ -444,9 +445,14 @@ public NetworkManager NetworkManager { get { + if (m_NetworkManager != null) + { + return m_NetworkManager; + } + if (NetworkObject?.NetworkManager != null) { - return NetworkObject?.NetworkManager; + return NetworkObject.NetworkManager; } return NetworkManager.Singleton; @@ -469,7 +475,7 @@ public NetworkManager NetworkManager /// . /// #pragma warning restore IDE0001 - public RpcTarget RpcTarget => NetworkManager.RpcTarget; + public RpcTarget RpcTarget { get; private set; } /// /// If a NetworkObject is assigned, returns whether the NetworkObject @@ -543,21 +549,13 @@ public bool IsSessionOwner internal bool IsBehaviourEditable() { - if (!m_NetworkObject) + if (!m_NetworkObject || !m_NetworkManager || !m_NetworkManager.IsListening) { return true; } - if (!m_NetworkObject.NetworkManager) - { - return true; - } - - var networkManager = m_NetworkObject.NetworkManager; - // Only the authority can MODIFY. So allow modification if network is either not running or we are the authority. - return !networkManager.IsListening || - ((networkManager.DistributedAuthorityMode && m_NetworkObject.IsOwner) || (!networkManager.DistributedAuthorityMode && networkManager.IsServer)); + return HasAuthority; } internal void SetNetworkObject(NetworkObject networkObject) @@ -598,7 +596,7 @@ public NetworkObject NetworkObject // or NetworkBehaviour.IsSpawned (i.e. to early exit if not spawned) which, in turn, could generate several Warning messages // per spawned NetworkObject. Checking for ShutdownInProgress prevents these unnecessary LogWarning messages. // We must check IsSpawned, otherwise a warning will be logged under certain valid conditions (see OnDestroy) - if (IsSpawned && m_NetworkObject == null && (NetworkManager.Singleton == null || !NetworkManager.Singleton.ShutdownInProgress)) + if (IsSpawned && m_NetworkObject == null && (m_NetworkManager == null || !m_NetworkManager.ShutdownInProgress)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { @@ -615,7 +613,7 @@ public NetworkObject NetworkObject /// public bool HasNetworkObject => NetworkObject != null; - private NetworkObject m_NetworkObject = null; + private NetworkObject m_NetworkObject; /// /// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour instance. @@ -655,7 +653,7 @@ protected NetworkBehaviour GetNetworkBehaviour(ushort behaviourId) internal void UpdateNetworkProperties() { var networkObject = m_NetworkObject; - var networkManager = NetworkManager; + var networkManager = m_NetworkManager; // Set identification related properties NetworkObjectId = networkObject.NetworkObjectId; @@ -763,6 +761,8 @@ public virtual void OnNetworkPreDespawn() { } internal void NetworkPreSpawn(ref NetworkManager networkManager, NetworkObject networkObject) { m_NetworkObject = networkObject; + m_NetworkManager = networkManager; + UpdateNetworkProperties(); try @@ -1106,7 +1106,7 @@ internal void NetworkVariableUpdate(ulong targetClientId, bool forceSend = false } // Getting these ahead of time actually improves performance - var networkManager = NetworkManager; + var networkManager = m_NetworkManager; var networkObject = m_NetworkObject; var behaviourIndex = networkObject.GetNetworkBehaviourOrderIndex(this); var messageManager = networkManager.MessageManager; @@ -1193,7 +1193,7 @@ private bool CouldHaveDirtyNetworkVariables() } // If it's dirty but can't be sent yet, we have to keep monitoring it until one of the // conditions blocking its send changes. - NetworkManager.BehaviourUpdater.AddForUpdate(m_NetworkObject); + m_NetworkManager.BehaviourUpdater.AddForUpdate(m_NetworkObject); } } @@ -1259,7 +1259,7 @@ internal void MarkOwnerReadDirtyAndCheckOwnerWriteIsDirty() internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId) { // Create any values that require accessing the NetworkManager locally (it is expensive to access it in NetworkBehaviour) - var networkManager = NetworkManager; + var networkManager = m_NetworkManager; var ensureLengthSafety = networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety; foreach (var field in NetworkVariableFields) @@ -1313,7 +1313,7 @@ internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClie internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId) { // Stack cache any values that requires accessing the NetworkManager (it is expensive to access it in NetworkBehaviour) - var networkManager = NetworkManager; + var networkManager = m_NetworkManager; var ensureLengthSafety = networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety; foreach (var field in NetworkVariableFields) @@ -1358,7 +1358,7 @@ internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId) var totalBytesRead = reader.Position - readStartPos; if (totalBytesRead != expectedBytesToRead) { - if (NetworkManager.LogLevel <= LogLevel.Normal) + if (networkManager.LogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{field.Name}] NetworkVariable read {totalBytesRead} bytes but was expected to read {expectedBytesToRead} bytes during synchronization deserialization!"); } @@ -1375,7 +1375,7 @@ internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId) /// The NetworkObject instance if found, null if no object exists with the specified networkId protected NetworkObject GetNetworkObject(ulong networkId) { - return NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkId, out NetworkObject networkObject) ? networkObject : null; + return m_NetworkManager.SpawnManager.SpawnedObjects.GetValueOrDefault(networkId); } /// @@ -1456,10 +1456,10 @@ internal bool Synchronize(ref BufferSerializer serializer, ulong targetCli catch (Exception ex) { threwException = true; - if (NetworkManager.LogLevel <= LogLevel.Normal) + if (m_NetworkManager.LogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"{name} threw an exception during synchronization serialization, this {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!"); - if (NetworkManager.LogLevel == LogLevel.Developer) + if (m_NetworkManager.LogLevel == LogLevel.Developer) { NetworkLog.LogError($"{ex.Message}\n {ex.StackTrace}"); } @@ -1503,10 +1503,10 @@ internal bool Synchronize(ref BufferSerializer serializer, ulong targetCli } catch (Exception ex) { - if (NetworkManager.LogLevel <= LogLevel.Normal) + if (m_NetworkManager.LogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"{name} threw an exception during synchronization deserialization, this {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!"); - if (NetworkManager.LogLevel == LogLevel.Developer) + if (m_NetworkManager.LogLevel == LogLevel.Developer) { NetworkLog.LogError($"{ex.Message}\n {ex.StackTrace}"); } @@ -1517,7 +1517,7 @@ internal bool Synchronize(ref BufferSerializer serializer, ulong targetCli var totalBytesRead = reader.Position - positionBeforeSynchronize; if (totalBytesRead != expectedBytesToRead) { - if (NetworkManager.LogLevel <= LogLevel.Normal) + if (m_NetworkManager.LogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"{name} read {totalBytesRead} bytes but was expected to read {expectedBytesToRead} bytes during synchronization deserialization! This {nameof(NetworkBehaviour)}({GetType().Name})is being skipped and will not be synchronized!"); } From f6401388c221fe2c18d943bd57eb8dc988771eab Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 7 Oct 2025 16:59:26 -0400 Subject: [PATCH 02/20] ensure internal state is completely reset on behaviour despawn --- .../Runtime/Core/NetworkBehaviour.cs | 64 +++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 8b6954e283..f839704ce8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -502,23 +502,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. @@ -675,12 +663,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. @@ -762,6 +779,7 @@ internal void NetworkPreSpawn(ref NetworkManager networkManager, NetworkObject n { m_NetworkObject = networkObject; m_NetworkManager = networkManager; + RpcTarget = networkManager.RpcTarget; UpdateNetworkProperties(); @@ -872,6 +890,8 @@ internal void InternalOnNetworkDespawn() { NetworkVariableFields[i].Deinitialize(); } + + ResetAllFields(); } /// @@ -1553,7 +1573,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 +1602,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 fa10a911b8d0ccb9777fee8299560e132c333851 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 7 Oct 2025 16:59:42 -0400 Subject: [PATCH 03/20] Fix tests --- .../Runtime/Components/NetworkAnimator.cs | 31 ++++++++++--------- .../Components/ObjectNameIdentifier.cs | 5 +-- .../TestHelpers/NetcodeIntegrationTest.cs | 1 + .../TestHelpers/NetworkManagerHelper.cs | 23 +++++++++++--- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index 94b247b5cc..b4ca574471 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -213,7 +213,8 @@ internal class TransitionStateinfo // [Layer][DestinationState][TransitionStateInfo] private Dictionary> m_DestinationStateToTransitioninfo = new Dictionary>(); - private NetworkManager m_NetworkManager; + // Named differently to avoid serialization conflicts with NetworkBehaviour + private NetworkManager m_LocalNetworkManager; internal bool DistributedAuthorityMode; @@ -513,7 +514,7 @@ internal bool IsServerAuthoritative() /// protected virtual bool OnIsServerAuthoritative() { - if (!m_NetworkManager) + if (!m_LocalNetworkManager) { return true; } @@ -723,8 +724,8 @@ internal AnimationMessage GetAnimationMessage() public override void OnNetworkSpawn() { // Save internal state references - m_NetworkManager = NetworkManager; - DistributedAuthorityMode = m_NetworkManager.DistributedAuthorityMode; + 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) @@ -976,7 +977,7 @@ internal void CheckForAnimatorChanges() if (m_Animator.runtimeAnimatorController == null) { - if (m_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!"); } @@ -1011,9 +1012,9 @@ internal void CheckForAnimatorChanges() { // Just notify all remote clients and not the local server m_ClientSendList.Clear(); - foreach (var clientId in m_NetworkManager.ConnectionManager.ConnectedClientIds) + foreach (var clientId in m_LocalNetworkManager.ConnectionManager.ConnectedClientIds) { - if (clientId == m_NetworkManager.LocalClientId || !NetworkObject.Observers.Contains(clientId)) + if (clientId == m_LocalNetworkManager.LocalClientId || !NetworkObject.Observers.Contains(clientId)) { continue; } @@ -1041,7 +1042,7 @@ private void SendParametersUpdate(ClientRpcParams clientRpcParams = default, boo } else { - Debug.LogError($"[{name}][Client-{m_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 @@ -1279,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 (m_NetworkManager.LogLevel == LogLevel.Developer) + else if (m_LocalNetworkManager.LogLevel == LogLevel.Developer) { NetworkLog.LogWarning($"Current State Hash ({currentState.fullPathHash}) != AnimationState.StateHash ({animationState.StateHash})"); } } - else if (m_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})!"); } @@ -1327,7 +1328,7 @@ private unsafe void SendParametersUpdateServerRpc(ParametersUpdateMessage parame return; } UpdateParameters(ref parametersUpdate); - var connectedClientIds = m_NetworkManager.ConnectionManager.ConnectedClientIds; + var connectedClientIds = m_LocalNetworkManager.ConnectionManager.ConnectedClientIds; if (connectedClientIds.Count <= (IsHost ? 2 : 1)) { return; @@ -1393,7 +1394,7 @@ private void SendAnimStateServerRpc(AnimationMessage animationMessage, ServerRpc UpdateAnimationState(animationState); } - var connectedClientIds = m_NetworkManager.ConnectionManager.ConnectedClientIds; + var connectedClientIds = m_LocalNetworkManager.ConnectionManager.ConnectedClientIds; if (connectedClientIds.Count <= (IsHost ? 2 : 1)) { return; @@ -1435,7 +1436,7 @@ private void ProcessAnimStates(AnimationMessage animationMessage) { if (HasAuthority) { - if (m_NetworkManager.LogLevel == LogLevel.Developer) + if (m_LocalNetworkManager.LogLevel == LogLevel.Developer) { var hostOrOwner = DistributedAuthorityMode ? "Owner" : "Host"; var clientServerOrDAMode = DistributedAuthorityMode ? "distributed authority" : "client-server"; @@ -1462,7 +1463,7 @@ internal void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerM // Ignore if a non-owner sent this. if (serverRpcParams.Receive.SenderClientId != OwnerClientId) { - if (m_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."); } @@ -1472,7 +1473,7 @@ internal void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerM // set the trigger locally on the server InternalSetTrigger(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet); - var connectedClientIds = m_NetworkManager.ConnectionManager.ConnectedClientIds; + var connectedClientIds = m_LocalNetworkManager.ConnectionManager.ConnectedClientIds; m_ClientSendList.Clear(); foreach (var clientId in 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 b73d06bbb48557cfe57782616851183bf17cda3b Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 18:46:28 -0400 Subject: [PATCH 04/20] 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 f839704ce8..c2e6529ecb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -546,9 +546,10 @@ internal bool IsBehaviourEditable() return HasAuthority; } - 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 @@ -613,11 +614,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. /// @@ -647,11 +643,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; @@ -669,35 +660,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. @@ -890,8 +852,6 @@ internal void InternalOnNetworkDespawn() { NetworkVariableFields[i].Deinitialize(); } - - ResetAllFields(); } /// @@ -1128,7 +1088,6 @@ internal void NetworkVariableUpdate(ulong targetClientId, bool forceSend = false // Getting these ahead of time actually improves performance var networkManager = m_NetworkManager; var networkObject = m_NetworkObject; - var behaviourIndex = networkObject.GetNetworkBehaviourOrderIndex(this); var messageManager = networkManager.MessageManager; var connectionManager = networkManager.ConnectionManager; @@ -1168,7 +1127,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], @@ -1606,8 +1565,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 113f46c7ad071f6ef60b595d016ba7398bb00fd5 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 18:47:39 -0400 Subject: [PATCH 05/20] 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 76d4db15a8e6f90eb48a2dd53bab3a820c51172f Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 19:03:32 -0400 Subject: [PATCH 06/20] 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 effcc2f5e0169e19a20a2248967a227bc618b8c3 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 19:14:13 -0400 Subject: [PATCH 07/20] 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 f1d94fd4fbcc301aed56a189a7d0e8cb1607fec1 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 20:02:39 -0400 Subject: [PATCH 08/20] 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 edaf1fe0d670ba45779539d35d262ce0e5fdff89 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 20:09:13 -0400 Subject: [PATCH 09/20] 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 d6fb422eb1480a408e68b54a672262797d7fc9e1 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 21:38:49 -0400 Subject: [PATCH 10/20] 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 9da750a7e2e7d03ddfe1187e24680922540a560c Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 22:52:00 -0400 Subject: [PATCH 11/20] 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 9219b392cb6cf8b280ad6e8215938a6487326c8b Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:05:47 -0400 Subject: [PATCH 12/20] 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 a113a7a90a2dd6a797853e243e4286955e6477f4 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:13:33 -0400 Subject: [PATCH 13/20] 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 49cc8c120fd447794ec76372885a24de34653263 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:26:59 -0400 Subject: [PATCH 14/20] 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 c2e6529ecb..a93164e23d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -614,6 +614,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 1f0e4988d3cdc44b30413d82e24d89a238436333 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:43:04 -0400 Subject: [PATCH 15/20] 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 e968bb5acc82d2ec76e29efe84cad0760761cbc1 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 14 Oct 2025 23:48:36 -0400 Subject: [PATCH 16/20] 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 d8f96d65ab90471893226766401b46059dd054f6 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 15 Oct 2025 00:00:32 -0400 Subject: [PATCH 17/20] 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 2c0d927a173c4e8e9c0be2549f014833d108b9e4 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 15 Oct 2025 00:04:03 -0400 Subject: [PATCH 18/20] 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 5855688a5f14e34c7fec92f3278ff143e7306c8b Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 15 Oct 2025 00:10:37 -0400 Subject: [PATCH 19/20] 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 85545e4e8af440f1ea1090a62c6f498a98fab5e4 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 15 Oct 2025 01:47:14 -0400 Subject: [PATCH 20/20] 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 }