Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c6f0d4b
fix: reset static fields for Fast Enter Play Mode
noellie-velez Apr 24, 2026
702bb73
Remove RuntimeInitializeOnLoadMethod from non generic types
noellie-velez Apr 24, 2026
8dabded
Fix naming + if editor def on `using UnityEngine`
noellie-velez Apr 24, 2026
aa33ed6
Merge branch 'develop-2.0.0' into chore/fast-enter-playmode
noellie-velez Apr 24, 2026
5ab98dc
Styling, renaming and made a field readonly
noellie-velez Apr 24, 2026
69dce92
Merge branch 'develop-2.0.0' into chore/fast-enter-playmode
noellie-velez Apr 28, 2026
638855f
Fix code formatting issues (redundant UnityEngine)
noellie-velez Apr 28, 2026
4cbec9b
Merge branch 'develop-2.0.0' into chore/fast-enter-playmode
noellie-velez May 6, 2026
0ecf34c
Add AutoStaticsCleanup + fix typo
noellie-velez May 6, 2026
e65351c
Remove possible NullReferenceException
noellie-velez May 6, 2026
551b989
Unused parented children list cleanup
noellie-velez May 7, 2026
689ecd2
Merge branch 'develop-2.0.0' into chore/fast-enter-playmode
NoelStephensUnity May 8, 2026
25e456b
Merge branch 'develop-2.0.0' into chore/fast-enter-playmode
NoelStephensUnity May 12, 2026
70c90eb
Merge branch 'develop-2.0.0' into chore/fast-enter-playmode
NoelStephensUnity May 18, 2026
a889166
Review
noellie-velez May 18, 2026
9febb5e
Merge branch 'chore/fast-enter-playmode' of github.com:Unity-Technolo…
noellie-velez May 18, 2026
46e09ab
Finalize addressing review comments
noellie-velez May 18, 2026
ff2e26a
revert field removal
noellie-velez May 21, 2026
be40aa3
Readonly, missing MessageTypes, replace quat...
noellie-velez May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,6 @@ protected virtual bool OnIsServerAuthoritative()
private int[] m_TransitionHash;
private int[] m_AnimationHash;
private float[] m_LayerWeights;
private static byte[] s_EmptyArray = new byte[] { };
private List<int> m_ParametersToUpdate;
private RpcParams m_RpcParams;
private IGroupRpcTarget m_TargetGroup;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ public class NetworkTransform : NetworkBehaviour
[HideInInspector]
[SerializeField]
internal bool NetworkTransformExpanded;

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStaticsOnLoad()
{
CurrentTick = 0;
TrackStateUpdateId = false;
AssignDefaultInterpolationType = false;
DefaultInterpolationType = default;
s_NetworkTickRegistration = new Dictionary<NetworkManager, NetworkTransformTickRegistration>();
InterpolationBufferTickOffset = 0;
s_TickSynchPosition = 0;
}
#endif

internal enum Axis { X, Y, Z }
Expand Down Expand Up @@ -1361,7 +1373,7 @@ public enum AuthorityModes
/// <summary>
/// When set each state update will contain a state identifier
/// </summary>
internal static bool TrackStateUpdateId = false;
internal static bool TrackStateUpdateId;

/// <summary>
/// Enabled by default.
Expand Down Expand Up @@ -2062,19 +2074,6 @@ private void TryCommitTransform(bool synchronize = false, bool settingState = fa
childNetworkTransform.OnNetworkTick(true);
}
}

// Synchronize any parented children with the parent's motion
foreach (var child in m_ParentedChildren)
{
// Synchronize any nested NetworkTransforms of the child with the parent's
foreach (var childNetworkTransform in child.NetworkTransforms)
{
if (childNetworkTransform.CanCommitToTransform)
{
childNetworkTransform.OnNetworkTick(true);
}
}
}
}
}
}
Expand Down Expand Up @@ -2103,7 +2102,7 @@ internal NetworkTransformState ApplyLocalNetworkState()
/// </summary>
internal bool ApplyTransformToNetworkState(ref NetworkTransformState networkState, double dirtyTime, Transform transformToUse)
{
CachedTransform = transformToUse;
m_CachedTransform = transformToUse;
m_CachedNetworkManager = NetworkManager;
// Apply the interpolate and PostionDeltaCompression flags, otherwise we get false positives whether something changed or not.
networkState.FlagStates.UseInterpolation = Interpolate;
Expand Down Expand Up @@ -2221,13 +2220,11 @@ private bool CheckForStateChange(ref NetworkTransformState networkState, bool is
// values are applied.
var hasParentNetworkObject = false;

var parentNetworkObject = (NetworkObject)null;

// If the NetworkObject belonging to this NetworkTransform instance has a parent
// (i.e. this handles nested NetworkTransforms under a parent at some layer above)
if (NetworkObject.transform.parent != null)
{
parentNetworkObject = NetworkObject.transform.parent.GetComponent<NetworkObject>();
var parentNetworkObject = NetworkObject.transform.parent.GetComponent<NetworkObject>();

// In-scene placed NetworkObjects parented under a GameObject with no
// NetworkObject preserve their lossyScale when synchronizing.
Expand Down Expand Up @@ -3363,19 +3360,6 @@ private void OnNetworkStateChanged(NetworkTransformState oldState, NetworkTransf
childNetworkTransform.OnNetworkTick(true);
}
}

// Synchronize any parented children with the parent's motion
foreach (var child in m_ParentedChildren)
{
// Synchronize any nested NetworkTransforms of the child with the parent's
foreach (var childNetworkTransform in child.NetworkTransforms)
{
if (childNetworkTransform.CanCommitToTransform)
{
childNetworkTransform.OnNetworkTick(true);
}
}
}
}

// Provide notifications when the state has been updated
Expand Down Expand Up @@ -3634,8 +3618,6 @@ internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManag
/// <inheritdoc/>
public override void OnNetworkSpawn()
{
m_ParentedChildren.Clear();

Initialize();

if (CanCommitToTransform && !SwitchTransformSpaceWhenParented)
Expand All @@ -3647,7 +3629,6 @@ public override void OnNetworkSpawn()

private void CleanUpOnDestroyOrDespawn()
{
m_ParentedChildren.Clear();
#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D
var forUpdate = !m_UseRigidbodyForMotion;
#else
Expand Down Expand Up @@ -3861,7 +3842,6 @@ protected override void OnOwnershipChanged(ulong previous, ulong current)
}

internal bool IsNested;
private List<NetworkObject> m_ParentedChildren = new List<NetworkObject>();

private bool m_IsFirstNetworkTransform;

Expand Down Expand Up @@ -4694,7 +4674,7 @@ internal static void UpdateNetworkTick(NetworkManager networkManager)
/// The default value is 1 tick (plus the tick latency). When running on a local network, reducing this to 0 is recommended.<br />
/// <see cref="NetworkTimeSystem.TickLatency"/>
/// </remarks>
public static int InterpolationBufferTickOffset = 0;
public static int InterpolationBufferTickOffset;
internal static float GetTickLatency(NetworkManager networkManager)
{
if (networkManager.IsListening)
Expand Down Expand Up @@ -4799,6 +4779,7 @@ public NetworkTransformTickRegistration(NetworkManager networkManager)
}
}
}

private static int s_TickSynchPosition;
private int m_NextTickSync;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ public static class QuaternionCompressor
private const ushort k_True = 1;
private const ushort k_False = 0;

// Used to store the absolute value of the 4 quaternion elements
private static Quaternion s_QuatAbsValues = Quaternion.identity;

/// <summary>
/// Compresses a Quaternion into an unsigned integer
/// </summary>
Expand All @@ -54,38 +51,31 @@ public static class QuaternionCompressor
public static uint CompressQuaternion(ref Quaternion quaternion)
{
// Store off the absolute value for each Quaternion element
s_QuatAbsValues[0] = Mathf.Abs(quaternion[0]);
s_QuatAbsValues[1] = Mathf.Abs(quaternion[1]);
s_QuatAbsValues[2] = Mathf.Abs(quaternion[2]);
s_QuatAbsValues[3] = Mathf.Abs(quaternion[3]);
var quatAbsValue0 = Mathf.Abs(quaternion[0]);
var quatAbsValue1 = Mathf.Abs(quaternion[1]);
var quatAbsValue2 = Mathf.Abs(quaternion[2]);
var quatAbsValue3 = Mathf.Abs(quaternion[3]);

// Get the largest element value of the quaternion to know what the remaining "Smallest Three" values are
var quatMax = Mathf.Max(s_QuatAbsValues[0], s_QuatAbsValues[1], s_QuatAbsValues[2], s_QuatAbsValues[3]);
var quatMax = Mathf.Max(quatAbsValue0, quatAbsValue1, quatAbsValue2, quatAbsValue3);

// Find the index of the largest element so we can skip that element while compressing and decompressing
var indexToSkip = (ushort)(s_QuatAbsValues[0] == quatMax ? 0 : s_QuatAbsValues[1] == quatMax ? 1 : s_QuatAbsValues[2] == quatMax ? 2 : 3);
var indexToSkip = (ushort)(quatAbsValue0 == quatMax ? 0 : quatAbsValue1 == quatMax ? 1 : quatAbsValue2 == quatMax ? 2 : 3);

// Get the sign of the largest element which is all that is needed when calculating the sum of squares of a normalized quaternion.

var quatMaxSign = (quaternion[indexToSkip] < 0 ? k_True : k_False);

// Start with the index to skip which will be shifted to the highest two bits
var compressed = (uint)indexToSkip;

// Step 1: Start with the first element
var currentIndex = 0;

// Step 2: If we are on the index to skip preserve the current compressed value, otherwise proceed to step 3 and 4
// Step 3: Get the sign of the element we are processing. If it is the not the same as the largest value's sign bit then we set the bit
// Step 4: Get the compressed and encoded value by multiplying the absolute value of the current element by k_CompressionEcodingMask and round that result up
compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed;
currentIndex++;
compressed = 0 != indexToSkip ? (compressed << 10) | (uint)((quaternion[0] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * quatAbsValue0) : compressed;
// Repeat the last 3 steps for the remaining elements
compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed;
currentIndex++;
compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed;
currentIndex++;
compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed;
compressed = 1 != indexToSkip ? (compressed << 10) | (uint)((quaternion[1] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * quatAbsValue1) : compressed;
compressed = 2 != indexToSkip ? (compressed << 10) | (uint)((quaternion[2] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * quatAbsValue2) : compressed;
compressed = 3 != indexToSkip ? (compressed << 10) | (uint)((quaternion[3] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * quatAbsValue3) : compressed;

// Return the compress quaternion
return compressed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public struct ContactEventHandlerInfo
/// </summary>
public bool ProvideNonRigidBodyContactEvents;
/// <summary>
/// When set to true, the <see cref="RigidbodyContactEventManager"/> will prioritize invoking <see cref="IContactEventHandler.ContactEvent(ulong, Vector3, Rigidbody, Vector3, bool, Vector3)"/> <br /></br>
/// When set to true, the <see cref="RigidbodyContactEventManager"/> will prioritize invoking <see cref="IContactEventHandler.ContactEvent(ulong, Vector3, Rigidbody, Vector3, bool, Vector3)"/> <br />
/// if it is the 2nd colliding body in the contact pair being processed. With distributed authority, setting this value to true when a <see cref="NetworkObject"/> is owned by the local client <br />
/// will assure <see cref="IContactEventHandler.ContactEvent(ulong, Vector3, Rigidbody, Vector3, bool, Vector3)"/> is only invoked on the authoritative side.
/// </summary>
Expand Down Expand Up @@ -109,7 +109,7 @@ private void OnEnable()
{
m_ResultsArray = new NativeArray<JobResultStruct>(16, Allocator.Persistent);
Physics.ContactEvent += Physics_ContactEvent;
if (Instance != null)
if (Instance != null || Instance != this)
{
NetworkLog.LogError($"[Invalid][Multiple Instances] Found more than one instance of {nameof(RigidbodyContactEventManager)}: {name} and {Instance.name}");
NetworkLog.LogError($"[Disable][Additional Instance] Disabling {name} instance!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,31 @@ public class CommandLineOptions
/// <summary>
/// Command-line options singleton
/// </summary>
public static CommandLineOptions Instance
{
get
{
if (s_Instance == null)
{
s_Instance = new CommandLineOptions();
}
return s_Instance;
}
private set
{
s_Instance = value;
}
}
private static CommandLineOptions s_Instance;

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void RuntimeInitializeOnLoad() => Instance = new CommandLineOptions();
public static CommandLineOptions Instance { get; private set; }

// Contains the current application instance domain's command line arguments
internal static List<string> CommandLineArguments = new List<string>();
private static List<string> s_CommandLineArguments = new List<string>(Environment.GetCommandLineArgs());

// Invoked upon application start, after scene load
[RuntimeInitializeOnLoadMethod]
private static void ParseCommandLineArguments()
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStaticsOnLoad()
{
// Get all the command line arguments to be parsed later and/or modified
// prior to being parsed (for testing purposes).
CommandLineArguments = new List<string>(Environment.GetCommandLineArgs());
Instance = new CommandLineOptions();
s_CommandLineArguments = new List<string>(Environment.GetCommandLineArgs());
}
#endif

/// <summary>
/// Returns the value of an argument or null if there the argument is not present
/// Returns the value of an argument or null if the argument is not present
/// </summary>
/// <param name="arg">The name of the argument</param>
/// <returns><see cref="string"/>Value of the command line argument passed in.</returns>
public string GetArg(string arg)
{
var argIndex = CommandLineArguments.IndexOf(arg);
if (argIndex >= 0 && argIndex < CommandLineArguments.Count - 1)
var argIndex = s_CommandLineArguments.IndexOf(arg);
if (argIndex >= 0 && argIndex < s_CommandLineArguments.Count - 1)
{
return CommandLineArguments[argIndex + 1];
return s_CommandLineArguments[argIndex + 1];
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ internal static class ComponentFactory
internal delegate object CreateObjectDelegate(NetworkManager networkManager);

private static Dictionary<Type, CreateObjectDelegate> s_Delegates = new Dictionary<Type, CreateObjectDelegate>();
#if UNITY_EDITOR
[UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStaticsOnLoad() => s_Delegates = new Dictionary<Type, CreateObjectDelegate>();
#endif

/// <summary>
/// Instantiates an instance of a given interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1299,8 +1299,6 @@ internal void NetworkVariableUpdate(ulong targetClientId, bool forceSend = false
}
}

internal static bool LogSentVariableUpdateMessage;

private bool CouldHaveDirtyNetworkVariables()
{
// TODO: There should be a better way by reading one dirty variable vs. 'n'
Expand Down
19 changes: 18 additions & 1 deletion com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ namespace Unity.Netcode
[HelpURL(HelpUrls.NetworkManager)]
public class NetworkManager : MonoBehaviour, INetworkUpdateSystem
{
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStaticsOnLoad()
{
Singleton = null;
OnInstantiated = null;
OnDestroying = null;
OnSingletonReady = null;
OnNetworkManagerReset = null;
IsDistributedAuthority = false;
s_SerializedType = new List<Type>();
DisableNotOptimizedSerializedType = false;
}
#endif
/// <summary>
/// Subscribe to this static event to get notifications when a <see cref="NetworkManager"/> instance has been instantiated.
/// </summary>
Expand All @@ -31,7 +45,6 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem
/// </summary>
public static event Action<NetworkManager> OnDestroying;


#if UNITY_EDITOR
// Inspector view expand/collapse settings for this derived child class
[HideInInspector]
Expand Down Expand Up @@ -1797,6 +1810,10 @@ internal abstract class NetcodeAnalytics

internal static ResetNetworkManagerDelegate OnNetworkManagerReset;


/// <summary>
/// This is called by the Unity Editor reset button. See <see cref="OnNetworkManagerReset"/> which is handled in "NetworkManagerHelper.cs".
/// </summary>
private void Reset()
Comment thread
noellie-velez marked this conversation as resolved.
{
OnNetworkManagerReset?.Invoke(this);
Expand Down
9 changes: 9 additions & 0 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,11 @@ internal void SetIsDestroying()
IsDestroying = true;
}

private void OnDisable()
{
SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged;
}

private void OnDestroy()
{
// Apply the is destroying flag
Expand Down Expand Up @@ -2506,6 +2511,10 @@ private void OnTransformParentChanged()
// If you couldn't find your parent, we put you into OrphanChildren set and every time we spawn another NetworkObject locally due to replication,
// we call CheckOrphanChildren() method and quickly iterate over OrphanChildren set and see if we can reparent/adopt one.
internal static HashSet<NetworkObject> OrphanChildren = new HashSet<NetworkObject>();
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStaticsOnLoad() => OrphanChildren = new HashSet<NetworkObject>();
#endif

internal bool ApplyNetworkParenting(bool removeParent = false, bool ignoreNotSpawned = false, bool orphanedChildPass = false, bool enableNotification = true)
{
Expand Down
Loading
Loading