From 5aab06d8012aad8a153c96f910620469e4800c29 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 18 Mar 2026 18:52:59 +0100 Subject: [PATCH 01/10] Use cached network manager instead of property --- .../Runtime/Components/NetworkTransform.cs | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 64e9bc0b95..4d9467a893 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -1621,7 +1621,7 @@ internal bool SynchronizeScale public bool CanCommitToTransform { get; protected set; } /// - /// Internally used by to keep track of the instance assigned to this + /// Internally used by to keep track of the instance assigned to /// this derived class instance. /// protected NetworkManager m_CachedNetworkManager; @@ -1852,6 +1852,7 @@ private bool ShouldSynchronizeHalfFloat(ulong targetClientId) if (!IsServerAuthoritative() && NetworkObject.OwnerClientId == targetClientId) { // In distributed authority mode we want to synchronize the half float if we are the owner. + // TODO do we have a cached NetworkManager here? Should we create one? return (!NetworkManager.DistributedAuthorityMode && NetworkObject.IsOwnedByServer) || (NetworkManager.DistributedAuthorityMode); } return true; @@ -3537,7 +3538,7 @@ private void ApplyPlayerTransformState() /// /// For dynamically spawned NetworkObjects, when the non-authority instance's client is already connected and - /// the SynchronizeState is still pending synchronization then we want to finalize the synchornization at this time. + /// the SynchronizeState is still pending synchronization then we want to finalize the synchronization at this time. /// protected internal override void InternalOnNetworkPostSpawn() { @@ -3550,7 +3551,7 @@ protected internal override void InternalOnNetworkPostSpawn() // Then we want to: // - Force the "IsSynchronizing" flag so the NetworkTransform has its state updated properly and runs through the initialization again. // - Make sure the SynchronizingState is updated to the instantiated prefab's default flags/settings. - if (NetworkManager.IsServer && !NetworkManager.DistributedAuthorityMode && !IsOwner && !OnIsServerAuthoritative() && !SynchronizeState.IsSynchronizing) + if (m_CachedNetworkManager.IsServer && !m_CachedNetworkManager.DistributedAuthorityMode && !IsOwner && !OnIsServerAuthoritative() && !SynchronizeState.IsSynchronizing) { // Handle the first/root NetworkTransform slightly differently to have a sequenced synchronization of like authority nested NetworkTransform components if (m_IsFirstNetworkTransform) @@ -3578,7 +3579,7 @@ protected internal override void InternalOnNetworkPostSpawn() } // Standard non-authority synchronization is handled here - if (!CanCommitToTransform && NetworkManager.IsConnectedClient && SynchronizeState.IsSynchronizing) + if (!CanCommitToTransform && m_CachedNetworkManager.IsConnectedClient && SynchronizeState.IsSynchronizing) { NonAuthorityFinalizeSynchronization(); } @@ -3631,7 +3632,6 @@ internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManag public override void OnNetworkSpawn() { m_ParentedChildren.Clear(); - m_CachedNetworkManager = NetworkManager; Initialize(); @@ -3639,6 +3639,7 @@ public override void OnNetworkSpawn() { SetState(GetSpaceRelativePosition(), GetSpaceRelativeRotation(), GetScale(), false); } + base.OnNetworkSpawn(); } private void CleanUpOnDestroyOrDespawn() @@ -3651,7 +3652,7 @@ private void CleanUpOnDestroyOrDespawn() #endif if (m_CachedNetworkObject != null) { - NetworkManager?.NetworkTransformRegistration(m_CachedNetworkObject, forUpdate, false); + m_CachedNetworkManager.NetworkTransformRegistration(m_CachedNetworkObject, forUpdate, false); } DeregisterForTickUpdate(this); @@ -3697,7 +3698,7 @@ protected virtual void OnInitialize(ref NetworkVariable r /// private void ResetInterpolatedStateToCurrentAuthoritativeState() { - var serverTime = NetworkManager.ServerTime.Time; + var serverTime = m_CachedNetworkManager.ServerTime.Time; #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D var position = m_UseRigidbodyForMotion ? m_NetworkRigidbodyInternal.GetPosition() : GetSpaceRelativePosition(); var rotation = m_UseRigidbodyForMotion ? m_NetworkRigidbodyInternal.GetRotation() : GetSpaceRelativeRotation(); @@ -3719,7 +3720,7 @@ private void ResetInterpolatedStateToCurrentAuthoritativeState() } private NetworkObject m_CachedNetworkObject; /// - /// The internal initialzation method to allow for internal API adjustments + /// The internal initialization method to allow for internal API adjustments /// /// private void InternalInitialization(bool isOwnershipChange = false) @@ -3731,7 +3732,7 @@ private void InternalInitialization(bool isOwnershipChange = false) m_CachedNetworkObject = NetworkObject; // Determine if this is the first NetworkTransform in the associated NetworkObject's list - m_IsFirstNetworkTransform = NetworkObject.NetworkTransforms[0] == this; + m_IsFirstNetworkTransform = m_CachedNetworkObject.NetworkTransforms[0] == this; if (m_CachedNetworkManager && m_CachedNetworkManager.DistributedAuthorityMode) { @@ -3755,9 +3756,9 @@ private void InternalInitialization(bool isOwnershipChange = false) var currentPosition = GetSpaceRelativePosition(); var currentRotation = GetSpaceRelativeRotation(); - if (NetworkManager.DistributedAuthorityMode) + if (m_CachedNetworkManager.DistributedAuthorityMode) { - RegisterNetworkManagerForTickUpdate(NetworkManager); + RegisterNetworkManagerForTickUpdate(m_CachedNetworkManager); } #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D @@ -3941,7 +3942,7 @@ internal override void InternalOnNetworkObjectParentChanged(NetworkObject parent if (LastTickSync == m_LocalAuthoritativeNetworkState.GetNetworkTick()) { m_InternalCurrentPosition = m_LastStateTargetPosition = GetSpaceRelativePosition(); - m_PositionInterpolator.ResetTo(m_PositionInterpolator.Parent, m_InternalCurrentPosition, NetworkManager.ServerTime.Time); + m_PositionInterpolator.ResetTo(m_PositionInterpolator.Parent, m_InternalCurrentPosition, m_CachedNetworkManager.ServerTime.Time); if (InLocalSpace) { transform.localPosition = m_InternalCurrentPosition; @@ -3973,7 +3974,7 @@ internal override void InternalOnNetworkObjectParentChanged(NetworkObject parent { m_InternalCurrentRotation = GetSpaceRelativeRotation(); m_TargetRotation = m_InternalCurrentRotation.eulerAngles; - m_RotationInterpolator.ResetTo(m_RotationInterpolator.Parent, m_InternalCurrentRotation, NetworkManager.ServerTime.Time); + m_RotationInterpolator.ResetTo(m_RotationInterpolator.Parent, m_InternalCurrentRotation, m_CachedNetworkManager.ServerTime.Time); if (InLocalSpace) { transform.localRotation = m_InternalCurrentRotation; @@ -4596,7 +4597,7 @@ internal void TransformStateUpdate() { // TODO: Investigate where this state should be applied or just discarded. // For now, discard the state if we assumed ownership. - // Debug.Log($"[Client-{NetworkManager.LocalClientId}] Ignoring inbound update from Client-{0} and parentUpdated:{isParentingDirective}!"); + // Debug.Log($"[Client-{m_CachedNetworkManager.LocalClientId}] Ignoring inbound update from Client-{0} and parentUpdated:{isParentingDirective}!"); return; } // Store the previous/old state @@ -4653,17 +4654,17 @@ private void UpdateTransformState() { continue; } - if (!NetworkObject.Observers.Contains(clientId)) + if (!m_CachedNetworkObject.Observers.Contains(clientId)) { continue; } - NetworkManager.MessageManager.SendMessage(ref m_OutboundMessage, networkDelivery, clientId); + m_CachedNetworkManager.MessageManager.SendMessage(ref m_OutboundMessage, networkDelivery, clientId); } } else { // Clients (owner authoritative) send messages to the server-host - NetworkManager.MessageManager.SendMessage(ref m_OutboundMessage, networkDelivery, NetworkManager.ServerClientId); + m_CachedNetworkManager.MessageManager.SendMessage(ref m_OutboundMessage, networkDelivery, NetworkManager.ServerClientId); } m_LocalAuthoritativeNetworkState.LastSerializedSize = m_OutboundMessage.BytesWritten; } @@ -4802,7 +4803,7 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) internal void RegisterForTickSynchronization() { s_TickSynchPosition++; - m_NextTickSync = NetworkManager.ServerTime.Tick + (s_TickSynchPosition % (int)NetworkManager.NetworkConfig.TickRate); + m_NextTickSync = m_CachedNetworkManager.ServerTime.Tick + (s_TickSynchPosition % (int)NetworkManager.NetworkConfig.TickRate); } private static void RegisterNetworkManagerForTickUpdate(NetworkManager networkManager) @@ -4821,14 +4822,15 @@ private static void RegisterNetworkManagerForTickUpdate(NetworkManager networkMa /// private static void RegisterForTickUpdate(NetworkTransform networkTransform) { - - if (!networkTransform.NetworkManager.DistributedAuthorityMode && !s_NetworkTickRegistration.ContainsKey(networkTransform.NetworkManager)) + //CHECK can we use NetworkManager.Singleton instead as we are in static context? + var networkManager = networkTransform.NetworkManager; + if (!networkManager.DistributedAuthorityMode && !s_NetworkTickRegistration.ContainsKey(networkManager)) { - s_NetworkTickRegistration.Add(networkTransform.NetworkManager, new NetworkTransformTickRegistration(networkTransform.NetworkManager)); + s_NetworkTickRegistration.Add(networkManager, new NetworkTransformTickRegistration(networkManager)); } networkTransform.RegisterForTickSynchronization(); - s_NetworkTickRegistration[networkTransform.NetworkManager].NetworkTransforms.Add(networkTransform); + s_NetworkTickRegistration[networkManager].NetworkTransforms.Add(networkTransform); } /// @@ -4838,16 +4840,18 @@ private static void RegisterForTickUpdate(NetworkTransform networkTransform) /// private static void DeregisterForTickUpdate(NetworkTransform networkTransform) { - if (networkTransform.NetworkManager == null) + //CHECK static context, can we use NetworkManager.Singleton? + var networkManager = networkTransform.NetworkManager; + if (!networkManager) { return; } - if (s_NetworkTickRegistration.ContainsKey(networkTransform.NetworkManager)) + if (s_NetworkTickRegistration.ContainsKey(networkManager)) { - s_NetworkTickRegistration[networkTransform.NetworkManager].NetworkTransforms.Remove(networkTransform); - if (!networkTransform.NetworkManager.DistributedAuthorityMode && s_NetworkTickRegistration[networkTransform.NetworkManager].NetworkTransforms.Count == 0) + s_NetworkTickRegistration[networkManager].NetworkTransforms.Remove(networkTransform); + if (!networkManager.DistributedAuthorityMode && s_NetworkTickRegistration[networkManager].NetworkTransforms.Count == 0) { - var registrationEntry = s_NetworkTickRegistration[networkTransform.NetworkManager]; + var registrationEntry = s_NetworkTickRegistration[networkManager]; registrationEntry.Remove(); } } From 17416f788d24e806d156e7cb2704618bb07e0817 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 18 Mar 2026 19:11:33 +0100 Subject: [PATCH 02/10] Update changelog --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 +- .../Runtime/Components/NetworkTransform.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 190981fad4..19c0add8e9 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -14,7 +14,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - `NetworkTransport.EarlyUpdate` and `NetworkTransport.PostLateUpdate` are now public. For the vast majority of users, there's really no point in ever calling those methods directly (the `NetworkManager` handles it). It's only useful if wrapping transports outside of NGO. (#3890) ### Changed - +- Improve performance of `NetworkTransform`. (#3907) ### Deprecated diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 4d9467a893..c2c2c6f8a3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -4822,7 +4822,6 @@ private static void RegisterNetworkManagerForTickUpdate(NetworkManager networkMa /// private static void RegisterForTickUpdate(NetworkTransform networkTransform) { - //CHECK can we use NetworkManager.Singleton instead as we are in static context? var networkManager = networkTransform.NetworkManager; if (!networkManager.DistributedAuthorityMode && !s_NetworkTickRegistration.ContainsKey(networkManager)) { @@ -4840,7 +4839,6 @@ private static void RegisterForTickUpdate(NetworkTransform networkTransform) /// private static void DeregisterForTickUpdate(NetworkTransform networkTransform) { - //CHECK static context, can we use NetworkManager.Singleton? var networkManager = networkTransform.NetworkManager; if (!networkManager) { From 555f9f8c781f8800d4b58be87a3c9cac42a7d6ed Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 19 Mar 2026 15:56:54 +0100 Subject: [PATCH 03/10] comments cleanup --- .../Runtime/Components/NetworkTransform.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index c2c2c6f8a3..7169683ae0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -1852,7 +1852,6 @@ private bool ShouldSynchronizeHalfFloat(ulong targetClientId) if (!IsServerAuthoritative() && NetworkObject.OwnerClientId == targetClientId) { // In distributed authority mode we want to synchronize the half float if we are the owner. - // TODO do we have a cached NetworkManager here? Should we create one? return (!NetworkManager.DistributedAuthorityMode && NetworkObject.IsOwnedByServer) || (NetworkManager.DistributedAuthorityMode); } return true; From 122c5938730de0b24a3bd560988192796ab8e058 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Mon, 23 Mar 2026 16:10:24 +0100 Subject: [PATCH 04/10] Use local network manager instead of field + Add comments --- .../Runtime/Core/NetworkBehaviour.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 3d3c919986..c858320aca 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -90,6 +90,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 { + // Getting this ahead of time actually improves performance var networkManager = m_NetworkManager; var serverRpcMessage = new ServerRpcMessage { @@ -169,6 +170,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 { + // Getting this ahead of time actually improves performance var networkManager = m_NetworkManager; var clientRpcMessage = new ClientRpcMessage { @@ -206,7 +208,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth { foreach (var targetClientId in clientRpcParams.Send.TargetClientIds) { - if (targetClientId == NetworkManager.ServerClientId) + if (targetClientId == networkManager.ServerClientId) { shouldInvokeLocally = true; continue; @@ -217,13 +219,13 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth NetworkLog.LogError(GenerateObserverErrorMessage(clientRpcParams, targetClientId)); } } - rpcWriteSize = m_NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds); + rpcWriteSize = networkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds); } else if (clientRpcParams.Send.TargetClientIdsNativeArray != null) { foreach (var targetClientId in clientRpcParams.Send.TargetClientIdsNativeArray) { - if (targetClientId == NetworkManager.ServerClientId) + if (targetClientId == networkManager.ServerClientId) { shouldInvokeLocally = true; continue; @@ -652,6 +654,7 @@ protected NetworkBehaviour GetNetworkBehaviour(ushort behaviourId) /// internal void UpdateNetworkProperties() { + // Getting these ahead of time actually improves performance var networkObject = m_NetworkObject; var networkManager = m_NetworkManager; From 0ef61354435d8b7dd981888eee1dcb1466ea76f6 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Mon, 23 Mar 2026 16:44:03 +0100 Subject: [PATCH 05/10] Changelog update --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 7f561d4349..538c102588 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -12,7 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed - +- Changed: Improve performance of `NetworkBehavior`. ### Deprecated From 719f8443d2d35c89a24cf309d3ee8e4a2b91e0ff Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 25 Mar 2026 15:41:24 +0100 Subject: [PATCH 06/10] Review --- .../Runtime/Core/NetworkBehaviour.cs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index c858320aca..a710aa91d3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -170,8 +170,6 @@ 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 { - // Getting this ahead of time actually improves performance - var networkManager = m_NetworkManager; var clientRpcMessage = new ClientRpcMessage { Metadata = new RpcMetadata @@ -191,7 +189,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth networkDelivery = MessageDeliveryType.DefaultDelivery; 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."); } @@ -208,35 +206,35 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth { foreach (var targetClientId in clientRpcParams.Send.TargetClientIds) { - if (targetClientId == networkManager.ServerClientId) + if (targetClientId == NetworkManager.ServerClientId) { shouldInvokeLocally = true; continue; } // Check to make sure we are sending to only observers, if not log an error. - if (networkManager.LogLevel >= LogLevel.Error && !m_NetworkObject.Observers.Contains(targetClientId)) + if (m_NetworkManager.LogLevel >= LogLevel.Error && !m_NetworkObject.Observers.Contains(targetClientId)) { 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) { foreach (var targetClientId in clientRpcParams.Send.TargetClientIdsNativeArray) { - if (targetClientId == networkManager.ServerClientId) + if (targetClientId == NetworkManager.ServerClientId) { shouldInvokeLocally = true; continue; } // Check to make sure we are sending to only observers, if not log an error. - if (networkManager.LogLevel >= LogLevel.Error && !m_NetworkObject.Observers.Contains(targetClientId)) + if (m_NetworkManager.LogLevel >= LogLevel.Error && !m_NetworkObject.Observers.Contains(targetClientId)) { NetworkLog.LogError(GenerateObserverErrorMessage(clientRpcParams, targetClientId)); } } - rpcWriteSize = networkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value); + rpcWriteSize = m_NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value); } else { @@ -244,12 +242,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 == m_NetworkManager.LocalClientId) { shouldInvokeLocally = true; continue; } - rpcWriteSize = networkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, observerEnumerator.Current); + rpcWriteSize = m_NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, observerEnumerator.Current); } } @@ -260,8 +258,8 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth var context = new NetworkContext { SenderId = NetworkManager.ServerClientId, - Timestamp = networkManager.RealTimeProvider.RealTimeSinceStartup, - SystemOwner = networkManager, + Timestamp = m_NetworkManager.RealTimeProvider.RealTimeSinceStartup, + SystemOwner = m_NetworkManager, // header information isn't valid since it's not a real message. // RpcMessage doesn't access this stuff so it's just left empty. Header = new NetworkMessageHeader(), @@ -284,7 +282,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth { foreach (var targetClientId in clientRpcParams.Send.TargetClientIds) { - networkManager.NetworkMetrics.TrackRpcSent( + m_NetworkManager.NetworkMetrics.TrackRpcSent( targetClientId, m_NetworkObject, rpcMethodName, @@ -296,7 +294,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth { foreach (var targetClientId in clientRpcParams.Send.TargetClientIdsNativeArray) { - networkManager.NetworkMetrics.TrackRpcSent( + m_NetworkManager.NetworkMetrics.TrackRpcSent( targetClientId, m_NetworkObject, rpcMethodName, @@ -309,7 +307,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth var observerEnumerator = m_NetworkObject.Observers.GetEnumerator(); while (observerEnumerator.MoveNext()) { - networkManager.NetworkMetrics.TrackRpcSent( + m_NetworkManager.NetworkMetrics.TrackRpcSent( observerEnumerator.Current, m_NetworkObject, rpcMethodName, @@ -327,7 +325,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth internal FastBufferWriter __beginSendRpc(uint rpcMethodId, RpcParams rpcParams, RpcAttribute.RpcAttributeParams attributeParams, SendTo defaultTarget, RpcDelivery rpcDelivery) #pragma warning restore IDE1006 // restore naming rule violation check { - if (m_NetworkObject == null && !IsSpawned) + if (!IsSpawned) { throw new RpcException("The NetworkBehaviour must be spawned before calling this method."); } From ecb27587d32acf0659dbe7b5469f5a5d54f36074 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 25 Mar 2026 17:54:22 +0100 Subject: [PATCH 07/10] remove static from method --- .../Runtime/Components/NetworkTransform.cs | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 7169683ae0..c16d3dfc39 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -2606,7 +2606,7 @@ private void OnNetworkTick(bool isCalledFromParent = false) } else // If we are no longer authority, unsubscribe to the tick event { - DeregisterForTickUpdate(this); + DeregisterForTickUpdate(); } } #endregion @@ -3654,7 +3654,7 @@ private void CleanUpOnDestroyOrDespawn() m_CachedNetworkManager.NetworkTransformRegistration(m_CachedNetworkObject, forUpdate, false); } - DeregisterForTickUpdate(this); + DeregisterForTickUpdate(); CanCommitToTransform = false; } @@ -3793,7 +3793,7 @@ private void InternalInitialization(bool isOwnershipChange = false) m_InternalCurrentPosition = currentPosition; m_LastStateTargetPosition = currentPosition; - RegisterForTickUpdate(this); + RegisterForTickUpdate(); if (UseHalfFloatPrecision && isOwnershipChange && !IsServerAuthoritative() && Interpolate) { @@ -3813,7 +3813,7 @@ private void InternalInitialization(bool isOwnershipChange = false) // Non-authority needs to be added to updates for interpolation and applying state purposes m_CachedNetworkManager.NetworkTransformRegistration(NetworkObject, forUpdate, true); // Remove this instance from the tick update - DeregisterForTickUpdate(this); + DeregisterForTickUpdate(); ResetInterpolatedStateToCurrentAuthoritativeState(); m_InternalCurrentPosition = currentPosition; m_LastStateTargetPosition = currentPosition; @@ -4818,37 +4818,34 @@ private static void RegisterNetworkManagerForTickUpdate(NetworkManager networkMa /// If a NetworkTransformTickRegistration has not yet been registered for the NetworkManager /// instance, then create an entry. /// - /// - private static void RegisterForTickUpdate(NetworkTransform networkTransform) + private void RegisterForTickUpdate() { - var networkManager = networkTransform.NetworkManager; - if (!networkManager.DistributedAuthorityMode && !s_NetworkTickRegistration.ContainsKey(networkManager)) + + if (!m_CachedNetworkManager.DistributedAuthorityMode && !s_NetworkTickRegistration.ContainsKey(m_CachedNetworkManager)) { - s_NetworkTickRegistration.Add(networkManager, new NetworkTransformTickRegistration(networkManager)); + s_NetworkTickRegistration.Add(m_CachedNetworkManager, new NetworkTransformTickRegistration(m_CachedNetworkManager)); } - networkTransform.RegisterForTickSynchronization(); - s_NetworkTickRegistration[networkManager].NetworkTransforms.Add(networkTransform); + RegisterForTickSynchronization(); + s_NetworkTickRegistration[m_CachedNetworkManager].NetworkTransforms.Add(this); } /// /// If a NetworkTransformTickRegistration exists for the NetworkManager instance, then this will /// remove the NetworkTransform instance from the single tick update entry point. /// - /// - private static void DeregisterForTickUpdate(NetworkTransform networkTransform) + private void DeregisterForTickUpdate() { - var networkManager = networkTransform.NetworkManager; - if (!networkManager) + if (m_CachedNetworkManager == null) { return; } - if (s_NetworkTickRegistration.ContainsKey(networkManager)) + if (s_NetworkTickRegistration.ContainsKey(m_CachedNetworkManager)) { - s_NetworkTickRegistration[networkManager].NetworkTransforms.Remove(networkTransform); - if (!networkManager.DistributedAuthorityMode && s_NetworkTickRegistration[networkManager].NetworkTransforms.Count == 0) + s_NetworkTickRegistration[m_CachedNetworkManager].NetworkTransforms.Remove(this); + if (!m_CachedNetworkManager.DistributedAuthorityMode && s_NetworkTickRegistration[m_CachedNetworkManager].NetworkTransforms.Count == 0) { - var registrationEntry = s_NetworkTickRegistration[networkManager]; + var registrationEntry = s_NetworkTickRegistration[m_CachedNetworkManager]; registrationEntry.Remove(); } } From 6a71541b20f7855505cecc2b05543892b926139d Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 25 Mar 2026 19:56:34 +0100 Subject: [PATCH 08/10] Small update --- .../Runtime/Components/NetworkTransform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index c16d3dfc39..75e7191e80 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -4802,7 +4802,7 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) internal void RegisterForTickSynchronization() { s_TickSynchPosition++; - m_NextTickSync = m_CachedNetworkManager.ServerTime.Tick + (s_TickSynchPosition % (int)NetworkManager.NetworkConfig.TickRate); + m_NextTickSync = m_CachedNetworkManager.ServerTime.Tick + (s_TickSynchPosition % (int)m_CachedNetworkManager.NetworkConfig.TickRate); } private static void RegisterNetworkManagerForTickUpdate(NetworkManager networkManager) From 279768ab10f3479b1d0e0d566d01bb751d1b2c8e Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 25 Mar 2026 20:30:59 +0100 Subject: [PATCH 09/10] Caching tickRate --- .../Runtime/Components/NetworkTransform.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 75e7191e80..972cdbd689 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -1625,6 +1625,7 @@ internal bool SynchronizeScale /// this derived class instance. /// protected NetworkManager m_CachedNetworkManager; + private int m_CachedTickRate; /// /// Helper method that returns the space relative position of the transform. @@ -2136,9 +2137,8 @@ private bool CheckForStateChange(ref NetworkTransformState networkState, bool is // We compare against the NetworkTickSystem version since ServerTime is set when updating ticks if (UseUnreliableDeltas && !isSynchronization && m_DeltaSynch && m_NextTickSync <= CurrentTick) { - // TODO-CACHE: m_CachedNetworkManager.NetworkConfig.TickRate value // Increment to the next frame synch tick position for this instance - m_NextTickSync += (int)m_CachedNetworkManager.NetworkConfig.TickRate; + m_NextTickSync += m_CachedTickRate; // If we are teleporting, we do not need to send a frame synch for this tick slot // as a "frame synch" really is effectively just a teleport. isAxisSync = !flagStates.IsTeleportingNextFrame; @@ -3729,6 +3729,7 @@ private void InternalInitialization(bool isOwnershipChange = false) return; } m_CachedNetworkObject = NetworkObject; + m_CachedTickRate = (int)m_CachedNetworkManager.NetworkConfig.TickRate; // Determine if this is the first NetworkTransform in the associated NetworkObject's list m_IsFirstNetworkTransform = m_CachedNetworkObject.NetworkTransforms[0] == this; @@ -4802,7 +4803,7 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) internal void RegisterForTickSynchronization() { s_TickSynchPosition++; - m_NextTickSync = m_CachedNetworkManager.ServerTime.Tick + (s_TickSynchPosition % (int)m_CachedNetworkManager.NetworkConfig.TickRate); + m_NextTickSync = m_CachedNetworkManager.ServerTime.Tick + (s_TickSynchPosition % m_CachedTickRate); } private static void RegisterNetworkManagerForTickUpdate(NetworkManager networkManager) From d0284d7f24e4eecaa5917051104136598b29b947 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 25 Mar 2026 20:38:35 +0100 Subject: [PATCH 10/10] Removing un-needed extra var layer --- .../Runtime/Core/NetworkBehaviour.cs | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index a710aa91d3..049f28070b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -90,8 +90,6 @@ 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 { - // Getting this ahead of time actually improves performance - var networkManager = m_NetworkManager; var serverRpcMessage = new ServerRpcMessage { Metadata = new RpcMetadata @@ -111,7 +109,7 @@ internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMeth networkDelivery = MessageDeliveryType.DefaultDelivery; 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."); } @@ -128,8 +126,8 @@ internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMeth var context = new NetworkContext { SenderId = NetworkManager.ServerClientId, - Timestamp = networkManager.RealTimeProvider.RealTimeSinceStartup, - SystemOwner = networkManager, + Timestamp = m_NetworkManager.RealTimeProvider.RealTimeSinceStartup, + SystemOwner = m_NetworkManager, // header information isn't valid since it's not a real message. // RpcMessage doesn't access this stuff so it's just left empty. Header = new NetworkMessageHeader(), @@ -142,7 +140,7 @@ internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMeth } else { - rpcWriteSize = networkManager.ConnectionManager.SendMessage(ref serverRpcMessage, networkDelivery, NetworkManager.ServerClientId); + rpcWriteSize = m_NetworkManager.ConnectionManager.SendMessage(ref serverRpcMessage, networkDelivery, NetworkManager.ServerClientId); } bufferWriter.Dispose(); @@ -652,28 +650,24 @@ protected NetworkBehaviour GetNetworkBehaviour(ushort behaviourId) /// internal void UpdateNetworkProperties() { - // Getting these ahead of time actually improves performance - var networkObject = m_NetworkObject; - var networkManager = m_NetworkManager; - // Set identification related properties - NetworkObjectId = networkObject.NetworkObjectId; - IsLocalPlayer = networkObject.IsLocalPlayer; + NetworkObjectId = m_NetworkObject.NetworkObjectId; + IsLocalPlayer = m_NetworkObject.IsLocalPlayer; // Set ownership related properties - IsOwnedByServer = networkObject.IsOwnedByServer; - IsOwner = networkObject.IsOwner; - OwnerClientId = networkObject.OwnerClientId; + IsOwnedByServer = m_NetworkObject.IsOwnedByServer; + IsOwner = m_NetworkObject.IsOwner; + OwnerClientId = m_NetworkObject.OwnerClientId; // Set NetworkManager dependent properties - if (networkManager != null) - { - IsHost = networkManager.IsListening && networkManager.IsHost; - IsClient = networkManager.IsListening && networkManager.IsClient; - IsServer = networkManager.IsListening && networkManager.IsServer; - IsSessionOwner = networkManager.IsListening && networkManager.LocalClient.IsSessionOwner; - HasAuthority = networkObject.HasAuthority; - ServerIsHost = networkManager.IsListening && networkManager.ServerIsHost; + if (m_NetworkManager != null) + { + IsHost = m_NetworkManager.IsListening && m_NetworkManager.IsHost; + IsClient = m_NetworkManager.IsListening && m_NetworkManager.IsClient; + IsServer = m_NetworkManager.IsListening && m_NetworkManager.IsServer; + IsSessionOwner = m_NetworkManager.IsListening && m_NetworkManager.LocalClient.IsSessionOwner; + HasAuthority = m_NetworkObject.HasAuthority; + ServerIsHost = m_NetworkManager.IsListening && m_NetworkManager.ServerIsHost; } }