From b7ee6ef8e9dd5a60b694a98b37268a117d16c5ce Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 12 Dec 2024 08:55:40 -0600 Subject: [PATCH 01/11] update updating package --- com.unity.netcode.gameobjects/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/package.json b/com.unity.netcode.gameobjects/package.json index edbce0938e..810105020d 100644 --- a/com.unity.netcode.gameobjects/package.json +++ b/com.unity.netcode.gameobjects/package.json @@ -2,11 +2,11 @@ "name": "com.unity.netcode.gameobjects", "displayName": "Netcode for GameObjects", "description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.", - "version": "2.1.1", - "unity": "6000.0", + "version": "2.2.0", + "unity": "6000.1", "dependencies": { "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.transport": "2.3.0" + "com.unity.transport": "2.4.0" }, "samples": [ { From 4a77fb20720366ada79031f1fa1706ee2e1e7aa5 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 12 Dec 2024 08:55:54 -0600 Subject: [PATCH 02/11] update updating changelog --- com.unity.netcode.gameobjects/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 2d16bbc30e..229b506e5c 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -6,7 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). -[Unreleased] + +[2.2.0 2024-12-12] ### Added From 088755ea3a5c6ad2fa000c97b52833857badc1a4 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 12 Dec 2024 09:23:25 -0600 Subject: [PATCH 03/11] update moving minimum editor back down to 6000.0 --- com.unity.netcode.gameobjects/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/package.json b/com.unity.netcode.gameobjects/package.json index 810105020d..c20e366c3c 100644 --- a/com.unity.netcode.gameobjects/package.json +++ b/com.unity.netcode.gameobjects/package.json @@ -3,7 +3,7 @@ "displayName": "Netcode for GameObjects", "description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.", "version": "2.2.0", - "unity": "6000.1", + "unity": "6000.0", "dependencies": { "com.unity.nuget.mono-cecil": "1.11.4", "com.unity.transport": "2.4.0" From 004a2206b27dbeb30c01bbe6204f96987053b8ec Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 12 Dec 2024 10:54:05 -0600 Subject: [PATCH 04/11] fix Fixing the changelog version goof of mine. Making the license copyright just a year and not year range. --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 +- com.unity.netcode.gameobjects/LICENSE.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 229b506e5c..3b068b5c21 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -7,7 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). -[2.2.0 2024-12-12] +## [2.2.0] 2024-12-12 ### Added diff --git a/com.unity.netcode.gameobjects/LICENSE.md b/com.unity.netcode.gameobjects/LICENSE.md index 031978c204..6e26290e2d 100644 --- a/com.unity.netcode.gameobjects/LICENSE.md +++ b/com.unity.netcode.gameobjects/LICENSE.md @@ -1,6 +1,6 @@ Unity Companion License (UCL License) -com.unity.netcode.gameobjects copyright © 2021-2024 Unity Technologies +com.unity.netcode.gameobjects copyright © 2024 Unity Technologies Licensed under the Unity Companion License for Unity-dependent projects (see https://unity3d.com/legal/licenses/unity_companion_license). Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. From 64195d57a8e6a154e4620428eef2099e67f7ff39 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 12 Dec 2024 11:11:07 -0600 Subject: [PATCH 05/11] style removing whitespace from copyright notice. adding dash to changelog header --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 +- com.unity.netcode.gameobjects/LICENSE.md | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 3b068b5c21..c3137ad6be 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -7,7 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). -## [2.2.0] 2024-12-12 +## [2.2.0] - 2024-12-12 ### Added diff --git a/com.unity.netcode.gameobjects/LICENSE.md b/com.unity.netcode.gameobjects/LICENSE.md index 6e26290e2d..ee8cecf4bf 100644 --- a/com.unity.netcode.gameobjects/LICENSE.md +++ b/com.unity.netcode.gameobjects/LICENSE.md @@ -1,7 +1,5 @@ Unity Companion License (UCL License) -com.unity.netcode.gameobjects copyright © 2024 Unity Technologies +com.unity.netcode.gameobjects copyright © 2024 Unity Technologies Licensed under the Unity Companion License for Unity-dependent projects (see https://unity3d.com/legal/licenses/unity_companion_license). Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. - - From 1914d3c62f6b0d703d852397d334cb992e60e477 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 12 Dec 2024 14:03:04 -0600 Subject: [PATCH 06/11] style Removing whitespaces for PVP-124-2 issues. --- com.unity.netcode.gameobjects/CHANGELOG.md | 8 +- .../Editor/NetworkManagerEditor.cs | 2 +- .../Editor/NetworkObjectEditor.cs | 6 +- .../Runtime/Components/NetworkAnimator.cs | 33 ++--- .../Components/NetworkRigidBodyBase.cs | 44 +++---- .../Runtime/Components/NetworkTransform.cs | 116 +++++++++--------- .../RigidbodyContactEventManager.cs | 10 +- .../Connection/NetworkConnectionManager.cs | 2 +- .../Runtime/Core/NetworkManager.cs | 2 +- .../Runtime/Core/NetworkObject.cs | 2 +- .../Runtime/Core/NetworkObjectRefreshTool.cs | 2 +- .../Runtime/Messaging/ILPPMessageProvider.cs | 2 +- .../Messages/ChangeOwnershipMessage.cs | 10 +- .../Messaging/Messages/CreateObjectMessage.cs | 2 +- .../Messages/NetworkTransformMessage.cs | 2 +- .../Runtime/Serialization/BitWriter.cs | 2 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 16 +-- .../Bootstrap/Scripts/Bootstrap.asmdef | 4 +- .../NetworkTransformOwnershipTests.cs | 2 +- .../NetworkTransform/NetworkTransformTests.cs | 2 +- .../NetworkVariableCollectionsTests.cs | 12 +- .../Prefabs/NetworkPrefabOverrideTests.cs | 4 +- 22 files changed, 146 insertions(+), 139 deletions(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index c3137ad6be..accae93b14 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -215,8 +215,8 @@ Additional documentation and release notes are available at [Multiplayer Documen ## [2.0.0-exp.2] - 2024-04-02 ### Added -- Added updates to all internal messages to account for a distributed authority network session connection. (#2863) -- Added `NetworkRigidbodyBase` that provides users with a more customizable network rigidbody, handles both `Rigidbody` and `Rigidbody2D`, and provides an option to make `NetworkTransform` use the rigid body for motion. (#2863) +- Added updates to all internal messages to account for a distributed authority network session connection. (#2863) +- Added `NetworkRigidbodyBase` that provides users with a more customizable network rigidbody, handles both `Rigidbody` and `Rigidbody2D`, and provides an option to make `NetworkTransform` use the rigid body for motion. (#2863) - For a customized `NetworkRigidbodyBase` class: - `NetworkRigidbodyBase.AutoUpdateKinematicState` provides control on whether the kinematic setting will be automatically set or not when ownership changes. - `NetworkRigidbodyBase.AutoSetKinematicOnDespawn` provides control on whether isKinematic will automatically be set to true when the associated `NetworkObject` is despawned. @@ -352,6 +352,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Fixed issue where you could not have multiple source network prefab overrides targeting the same network prefab as their override. (#2710) ### Changed + - Changed the server or host shutdown so it will now perform a "soft shutdown" when `NetworkManager.Shutdown` is invoked. This will send a disconnect notification to all connected clients and the server-host will wait for all connected clients to disconnect or timeout after a 5 second period before completing the shutdown process. (#2789) - Changed `OnClientDisconnectedCallback` will now return the assigned client identifier on the local client side if the client was approved and assigned one prior to being disconnected. (#2789) - Changed `NetworkTransform.SetState` (and related methods) now are cumulative during a fractional tick period and sent on the next pending tick. (#2777) @@ -364,6 +365,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Changed in-scene placed `NetworkObject`s now set their `IsSceneObject` value when generating their `GlobalObjectIdHash` value. (#2710) - Changed the default `NetworkConfig.SpawnTimeout` value from 1.0s to 10.0s. (#2710) + ## [1.7.1] - 2023-11-15 ### Added @@ -413,7 +415,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added - Added a protected virtual method `NetworkTransform.OnInitialize(ref NetworkTransformState replicatedState)` that just returns the replicated state reference. - + ### Fixed - Fixed issue where invoking `NetworkManager.Shutdown` within `NetworkManager.OnClientStopped` or `NetworkManager.OnServerStopped` would force `NetworkManager.ShutdownInProgress` to remain true after completing the shutdown process. (#2661) diff --git a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs index 0684d74849..8ba4128b07 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs @@ -237,7 +237,7 @@ private void DisplayNetworkManagerProperties() if (m_NetworkManager.NetworkConfig.NetworkTopology == NetworkTopologyTypes.DistributedAuthority) { EditorGUILayout.PropertyField(m_AutoSpawnPlayerPrefabClientSide, new GUIContent("Auto Spawn Player Prefab")); - } + } #endif EditorGUILayout.PropertyField(m_PlayerPrefabProperty, new GUIContent("Default Player Prefab")); diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs index bd18473b27..6adb7b8223 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs @@ -193,9 +193,9 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten // The below can cause visual anomalies and/or throws an exception within the EditorGUI itself (index out of bounds of the array). and has // The visual anomaly is when you select one field it is set in the drop down but then the flags selection in the popup menu selects more items - // even though if you exit the popup menu the flag setting is correct. - //var ownership = (NetworkObject.OwnershipStatus)EditorGUI.EnumFlagsField(position, label, (NetworkObject.OwnershipStatus)property.enumValueFlag); - //property.enumValueFlag = (int)ownership; + // even though if you exit the popup menu the flag setting is correct. + // var ownership = (NetworkObject.OwnershipStatus)EditorGUI.EnumFlagsField(position, label, (NetworkObject.OwnershipStatus)property.enumValueFlag); + // property.enumValueFlag = (int)ownership; EditorGUI.EndDisabledGroup(); EditorGUI.EndProperty(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index 7f2c4ae23a..f6f40ea2b7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -416,8 +416,8 @@ internal struct AnimationMessage : INetworkSerializable internal bool HasBeenProcessed; // This is preallocated/populated in OnNetworkSpawn for all instances in the event ownership or - // authority changes. When serializing, IsDirtyCount determines how many AnimationState entries - // should be serialized from the list. When deserializing the list is created and populated with + // authority changes. When serializing, IsDirtyCount determines how many AnimationState entries + // should be serialized from the list. When deserializing the list is created and populated with // only the number of AnimationStates received which is dictated by the deserialized IsDirtyCount. internal List AnimationStates; @@ -493,7 +493,7 @@ internal bool IsServerAuthoritative() } /// - /// Override this method and return false to switch to owner authoritative mode + /// Override this method and return false to switch to owner authoritative mode. /// /// /// When using a distributed authority network topology, this will default to @@ -731,7 +731,7 @@ public override void OnNetworkDespawn() } /// - /// Wries all parameter and state information needed to initially synchronize a client + /// Writes all parameter and state information needed to initially synchronize a client /// private void WriteSynchronizationData(ref BufferSerializer serializer) where T : IReaderWriter { @@ -806,8 +806,10 @@ private void WriteSynchronizationData(ref BufferSerializer serializer) whe } } - animationState.Transition = isInTransition; // The only time this could be set to true - animationState.StateHash = stateHash; // When a transition, this is the originating/starting state + // The only time this could be set to true + animationState.Transition = isInTransition; + // When a transition, this is the originating/starting state + animationState.StateHash = stateHash; animationState.NormalizedTime = normalizedTime; animationState.Layer = layer; animationState.Weight = m_LayerWeights[layer]; @@ -881,7 +883,8 @@ private void CheckForStateChange(int layer) { m_TransitionHash[layer] = nt.fullPathHash; m_AnimationHash[layer] = 0; - animState.DestinationStateHash = nt.fullPathHash; // Next state is the destination state for cross fade + // Next state is the destination state for cross fade + animState.DestinationStateHash = nt.fullPathHash; animState.CrossFade = true; animState.Transition = true; animState.Duration = tt.duration; @@ -899,7 +902,8 @@ private void CheckForStateChange(int layer) // first time in this transition for this layer m_TransitionHash[layer] = tt.fullPathHash; m_AnimationHash[layer] = 0; - animState.StateHash = tt.fullPathHash; // Transitioning from state + // Transitioning from state + animState.StateHash = tt.fullPathHash; animState.CrossFade = false; animState.Transition = true; animState.NormalizedTime = tt.normalizedTime; @@ -1115,7 +1119,7 @@ private unsafe void WriteParameters(ref FastBufferWriter writer) { writer.Seek(0); writer.Truncate(); - // Write how many parameter entries we are going to write + // Write out how many parameter entries to read BytePacker.WriteValuePacked(writer, (uint)m_ParametersToUpdate.Count); foreach (var parameterIndex in m_ParametersToUpdate) { @@ -1264,7 +1268,7 @@ internal void UpdateAnimationState(AnimationState animationState) NetworkLog.LogError($"[DestinationState To Transition Info] Layer ({animationState.Layer}) sub-table does not contain destination state ({animationState.DestinationStateHash})!"); } } - // For reference, it is valid to have no transition information + // For reference, it is valid to have no transition information //else if (NetworkManager.LogLevel == LogLevel.Developer) //{ // NetworkLog.LogError($"[DestinationState To Transition Info] Layer ({animationState.Layer}) does not exist!"); @@ -1471,7 +1475,7 @@ private void InternalSetTrigger(int hash, bool isSet = true) /// /// Distributed Authority: Internally-called RPC client receiving function to update a trigger when the server wants to forward - /// a trigger for a client to play / reset + /// a trigger to a client /// /// the payload containing the trigger data to apply [Rpc(SendTo.NotAuthority)] @@ -1482,7 +1486,7 @@ internal void SendAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage /// /// Client Server: Internally-called RPC client receiving function to update a trigger when the server wants to forward - /// a trigger for a client to play / reset + /// a trigger to a client /// /// the payload containing the trigger data to apply /// unused @@ -1548,7 +1552,7 @@ public void SetTrigger(int hash, bool setTrigger = true) } /// - /// Resets the trigger for the associated animation. See SetTrigger for more on how triggers are special + /// Resets the trigger for the associated animation. See SetTrigger for more on how triggers are special /// /// The string name of the trigger to reset public void ResetTrigger(string triggerName) @@ -1564,4 +1568,5 @@ public void ResetTrigger(int hash) } } } -#endif // COM_UNITY_MODULES_ANIMATION +// COM_UNITY_MODULES_ANIMATION +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index 7e8808171a..18b393bec0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -180,7 +180,7 @@ internal Vector3 GetAdjustedRotationThreshold() /// /// /// For , only the x and y components of the are applied. - /// + /// public void SetLinearVelocity(Vector3 linearVelocity) { if (m_IsRigidbody2D) @@ -546,7 +546,7 @@ public void SetIsKinematic(bool isKinematic) { if (IsKinematic()) { - // If not already set to interpolate then set the Rigidbody to interpolate + // If not already set to interpolate then set the Rigidbody to interpolate if (m_InternalRigidbody.interpolation == RigidbodyInterpolation.Extrapolate) { // Sleep until the next fixed update when switching from extrapolation to interpolation @@ -849,36 +849,30 @@ public void DetachFromFixedJoint() } if (UseRigidBodyForMotion) { - if (m_IsRigidbody2D) + if (m_IsRigidbody2D && FixedJoint2D != null) { - if (FixedJoint2D != null) + if (!m_FixedJoint2DUsingGravity) { - if (!m_FixedJoint2DUsingGravity) - { - FixedJoint2D.connectedBody.gravityScale = m_OriginalGravityScale; - } - FixedJoint2D.connectedBody = null; - Destroy(FixedJoint2D); - FixedJoint2D = null; - ResetInterpolation(); - RemoveFromParentBody(); + FixedJoint2D.connectedBody.gravityScale = m_OriginalGravityScale; } + FixedJoint2D.connectedBody = null; + Destroy(FixedJoint2D); + FixedJoint2D = null; + ResetInterpolation(); + RemoveFromParentBody(); } - else + else if (FixedJoint != null) { - if (FixedJoint != null) - { - FixedJoint.connectedBody = null; - m_InternalRigidbody.useGravity = m_OriginalGravitySetting; - Destroy(FixedJoint); - FixedJoint = null; - ResetInterpolation(); - RemoveFromParentBody(); - } + FixedJoint.connectedBody = null; + m_InternalRigidbody.useGravity = m_OriginalGravitySetting; + Destroy(FixedJoint); + FixedJoint = null; + ResetInterpolation(); + RemoveFromParentBody(); } } } } } -#endif // COM_UNITY_MODULES_PHYSICS - +// COM_UNITY_MODULES_PHYSICS +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index a103b0fc4b..3d2be47c63 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -32,7 +32,8 @@ public class NetworkTransform : NetworkBehaviour /// public struct NetworkTransformState : INetworkSerializable { - private const int k_InLocalSpaceBit = 0x00000001; // Persists between state updates (authority dictates if this is set) + // Persists between state updates (authority dictates if this is set) + private const int k_InLocalSpaceBit = 0x00000001; private const int k_PositionXBit = 0x00000002; private const int k_PositionYBit = 0x00000004; private const int k_PositionZBit = 0x00000008; @@ -43,18 +44,25 @@ public struct NetworkTransformState : INetworkSerializable private const int k_ScaleYBit = 0x00000100; private const int k_ScaleZBit = 0x00000200; private const int k_TeleportingBit = 0x00000400; - private const int k_Interpolate = 0x00000800; // Persists between state updates (authority dictates if this is set) - private const int k_QuaternionSync = 0x00001000; // Persists between state updates (authority dictates if this is set) - private const int k_QuaternionCompress = 0x00002000; // Persists between state updates (authority dictates if this is set) - private const int k_UseHalfFloats = 0x00004000; // Persists between state updates (authority dictates if this is set) + // Persists between state updates (authority dictates if this is set) + private const int k_Interpolate = 0x00000800; + // Persists between state updates (authority dictates if this is set) + private const int k_QuaternionSync = 0x00001000; + // Persists between state updates (authority dictates if this is set) + private const int k_QuaternionCompress = 0x00002000; + // Persists between state updates (authority dictates if this is set) + private const int k_UseHalfFloats = 0x00004000; private const int k_Synchronization = 0x00008000; - private const int k_PositionSlerp = 0x00010000; // Persists between state updates (authority dictates if this is set) - private const int k_IsParented = 0x00020000; // When parented and synchronizing, we need to have both lossy and local scale due to varying spawn order + // Persists between state updates (authority dictates if this is set) + private const int k_PositionSlerp = 0x00010000; + // When parented and synchronizing, we need to have both lossy and local scale due to varying spawn order + private const int k_IsParented = 0x00020000; private const int k_SynchBaseHalfFloat = 0x00040000; private const int k_ReliableSequenced = 0x00080000; private const int k_UseUnreliableDeltas = 0x00100000; private const int k_UnreliableFrameSync = 0x00200000; - private const int k_TrackStateId = 0x10000000; // (Internal Debugging) When set each state update will contain a state identifier + // (Internal Debugging) When set each state update will contain a state identifier + private const int k_TrackStateId = 0x10000000; // Stores persistent and state relative flags private uint m_Bitset; @@ -409,8 +417,8 @@ internal set } /// - /// Returns whether this state update was a frame synchronization when - /// UseUnreliableDeltas is enabled. When set, the entire transform will + /// Returns whether this state update was a frame synchronization when + /// UseUnreliableDeltas is enabled. When set, the entire transform will /// be or has been synchronized. /// public bool IsUnreliableFrameSync() @@ -929,8 +937,6 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade #endregion #region PROPERTIES AND GENERAL METHODS - - public enum AuthorityModes { Server, @@ -1370,7 +1376,8 @@ internal NetworkTransformState LocalAuthoritativeNetworkState private BufferedLinearInterpolatorVector3 m_PositionInterpolator; private BufferedLinearInterpolatorVector3 m_ScaleInterpolator; - private BufferedLinearInterpolatorQuaternion m_RotationInterpolator; // rotation is a single Quaternion since each Euler axis will affect the quaternion's final value + // rotation is a single Quaternion since each Euler axis will affect the quaternion's final value + private BufferedLinearInterpolatorQuaternion m_RotationInterpolator; // The previous network state private NetworkTransformState m_OldState = new NetworkTransformState(); @@ -1643,11 +1650,11 @@ private void TryCommitTransform(ref Transform transformToCommit, bool synchroniz Debug.LogException(ex); } - // The below is part of assuring we only send a frame synch, when sending unreliable deltas, if + // The below is part of assuring we only send a frame synch, when sending unreliable deltas, if // we have already sent at least one unreliable delta state update. At this point in the callstack, // a delta state update has just been sent in the above UpdateTransformState() call and as long as // we didn't send a frame synch and we are not synchronizing then we know at least one unreliable - // delta has been sent. Under this scenario, we should start checking for this instance's alloted + // delta has been sent. Under this scenario, we should start checking for this instance's alloted // frame synch "tick slot". Once we send a frame synch, if no other deltas occur after that // (i.e. the object is at rest) then we will stop sending frame synch's until the object begins // moving, rotating, or scaling again. @@ -1964,7 +1971,7 @@ private bool CheckForStateChange(ref NetworkTransformState networkState, ref Tra networkState.NetworkDeltaPosition = m_HalfPositionState; - // If ownership offset is greater or we are doing an axial synchronization then synchronize the base position + // If ownership offset is greater or we are doing an axial synchronization then synchronize the base position if ((m_HalfFloatTargetTickOwnership > m_CachedNetworkManager.ServerTime.Tick || isAxisSync) && !networkState.IsTeleportingNextFrame) { networkState.SynchronizeBaseHalfFloat = true; @@ -3403,7 +3410,7 @@ internal void ChildRegistration(NetworkObject child, bool isAdding) /// - Local space to local space ( parent to parent) /// Will all smoothly transition while interpolation is enabled. /// (Does not work if using a or for motion) - /// + /// /// When a parent changes, non-authoritative instances should:
/// - Apply the resultant position, rotation, and scale from the parenting action.
/// - Clear interpolators (even if not enabled on this frame)
@@ -3575,7 +3582,7 @@ private void SetStateInternal(Vector3 pos, Quaternion rot, Vector3 scale, bool s var transformToCommit = transform; - // Explicit set states are cumulative during a fractional tick period of time (i.e. each SetState invocation will + // Explicit set states are cumulative during a fractional tick period of time (i.e. each SetState invocation will // update the axial deltas to whatever changes are applied). As such, we need to preserve the dirty and explicit // state flags. var stateWasDirty = m_LocalAuthoritativeNetworkState.IsDirty; @@ -3658,7 +3665,7 @@ private void UpdateInterpolation() var serverTime = m_CachedNetworkManager.ServerTime; var cachedServerTime = serverTime.Time; - //var offset = (float)serverTime.TickOffset; + // var offset = (float)serverTime.TickOffset; #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D var cachedDeltaTime = m_UseRigidbodyForMotion ? m_CachedNetworkManager.RealTimeProvider.FixedDeltaTime : m_CachedNetworkManager.RealTimeProvider.DeltaTime; #else @@ -3669,7 +3676,7 @@ private void UpdateInterpolation() // is to make their cachedRenderTime run 2 ticks behind. // TODO: This could most likely just always be 2 - //var ticksAgo = ((!IsServerAuthoritative() && !IsServer) || m_CachedNetworkManager.DistributedAuthorityMode) && !m_CachedNetworkManager.DAHost ? 2 : 1; + // var ticksAgo = ((!IsServerAuthoritative() && !IsServer) || m_CachedNetworkManager.DistributedAuthorityMode) && !m_CachedNetworkManager.DAHost ? 2 : 1; var ticksAgo = 2; var cachedRenderTime = serverTime.TimeTicksAgo(ticksAgo).Time; @@ -3746,7 +3753,7 @@ public virtual void OnFixedUpdate() /// /// Determines whether the is or based on the property. - /// You can override this method to control this logic. + /// You can override this method to control this logic. /// /// or protected virtual bool OnIsServerAuthoritative() @@ -3772,7 +3779,6 @@ public bool IsServerAuthoritative() return OnIsServerAuthoritative(); } } - #endregion #region MESSAGE HANDLING @@ -3964,7 +3970,7 @@ internal float TicksAgoInSeconds() { return 2 * m_TickFrequency; // TODO: We need an RTT that updates regularly and not just when the client sends packets - //return Mathf.Max(1.0f, TicksAgo) * m_TickFrequency; + // return Mathf.Max(1.0f, TicksAgo) * m_TickFrequency; } /// @@ -3974,25 +3980,25 @@ internal float TicksAgoInSeconds() private void TickUpdate() { // TODO: We need an RTT that updates regularly and not just when the client sends packets - //if (m_UnityTransport != null) - //{ - // // Determine the desired ticks ago by the RTT (this really should be the combination of the - // // authority and non-authority 1/2 RTT but in the end anything beyond 300ms is considered very poor - // // network quality so latent interpolation is going to be expected). - // var rtt = Mathf.Max(m_TickInMS, m_UnityTransport.GetCurrentRtt(NetworkManager.ServerClientId)); - // m_TicksAgoSamples[m_TickSampleIndex] = Mathf.Max(1, (int)(rtt * m_TickFrequency)); - // var tickAgoSum = 0.0f; - // foreach (var tickAgo in m_TicksAgoSamples) - // { - // tickAgoSum += tickAgo; - // } - // m_PreviousTicksAgo = TicksAgo; - // TicksAgo = Mathf.Lerp(m_PreviousTicksAgo, tickAgoSum / m_TickRate, m_TickFrequency); - // m_TickSampleIndex = (m_TickSampleIndex + 1) % m_TickRate; - // // Get the partial tick value for when this is all calculated to provide an offset for determining - // // the relative starting interpolation point for the next update - // Offset = m_OffsetTickFrequency * (Mathf.Max(2, TicksAgo) - (int)TicksAgo); - //} + // if (m_UnityTransport != null) + // { + // // Determine the desired ticks ago by the RTT (this really should be the combination of the + // // authority and non-authority 1/2 RTT but in the end anything beyond 300ms is considered very poor + // // network quality so latent interpolation is going to be expected). + // var rtt = Mathf.Max(m_TickInMS, m_UnityTransport.GetCurrentRtt(NetworkManager.ServerClientId)); + // m_TicksAgoSamples[m_TickSampleIndex] = Mathf.Max(1, (int)(rtt * m_TickFrequency)); + // var tickAgoSum = 0.0f; + // foreach (var tickAgo in m_TicksAgoSamples) + // { + // tickAgoSum += tickAgo; + // } + // m_PreviousTicksAgo = TicksAgo; + // TicksAgo = Mathf.Lerp(m_PreviousTicksAgo, tickAgoSum / m_TickRate, m_TickFrequency); + // m_TickSampleIndex = (m_TickSampleIndex + 1) % m_TickRate; + // // Get the partial tick value for when this is all calculated to provide an offset for determining + // // the relative starting interpolation point for the next update + // Offset = m_OffsetTickFrequency * (Mathf.Max(2, TicksAgo) - (int)TicksAgo); + // } // TODO FIX: The local NetworkTickSystem can invoke with the same network tick as before if (m_NetworkManager.ServerTime.Tick <= m_LastTick) @@ -4012,13 +4018,13 @@ private void TickUpdate() private UnityTransport m_UnityTransport; private float m_TickFrequency; - //private float m_OffsetTickFrequency; - //private ulong m_TickInMS; - //private int m_TickSampleIndex; + // private float m_OffsetTickFrequency; + // private ulong m_TickInMS; + // private int m_TickSampleIndex; private int m_TickRate; public float TicksAgo { get; private set; } - //public float Offset { get; private set; } - //private float m_PreviousTicksAgo; + // public float Offset { get; private set; } + // private float m_PreviousTicksAgo; private List m_TicksAgoSamples = new List(); @@ -4032,16 +4038,16 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) //// For the offset, it uses the fractional remainder of the tick to determine the offset. //// In order to keep within tick boundaries, we increment the tick rate by 1 to assure it //// will always be < the tick frequency. - //m_OffsetTickFrequency = 1.0f / (m_TickRate + 1); - //m_TickInMS = (ulong)(1000 * m_TickFrequency); - //m_UnityTransport = m_NetworkManager.NetworkConfig.NetworkTransport as UnityTransport; + // m_OffsetTickFrequency = 1.0f / (m_TickRate + 1); + // m_TickInMS = (ulong)(1000 * m_TickFrequency); + // m_UnityTransport = m_NetworkManager.NetworkConfig.NetworkTransport as UnityTransport; //// Fill the sample with a starting value of 1 - //for (int i = 0; i < m_TickRate; i++) - //{ - // m_TicksAgoSamples.Add(1f); - //} + // for (int i = 0; i < m_TickRate; i++) + // { + // m_TicksAgoSamples.Add(1f); + // } TicksAgo = 2f; - //m_PreviousTicksAgo = 1f; + // m_PreviousTicksAgo = 1f; if (networkManager.IsServer) { networkManager.OnServerStopped += OnNetworkManagerStopped; diff --git a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs index d0808d2886..02f9c98e7e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs @@ -27,7 +27,7 @@ public struct ContactEventHandlerInfo } /// - /// Default implementation required to register a with a instance. + /// Default implementation required to register a with a instance. /// /// /// Recommended to implement this method on a component @@ -52,7 +52,7 @@ public interface IContactEventHandler } /// - /// This is an extended version of and can be used to register a with a instance.
+ /// This is an extended version of and can be used to register a with a instance.
/// This provides additional information to the for each set of contact events it is processing. ///
public interface IContactEventHandlerWithInfo : IContactEventHandler @@ -66,9 +66,9 @@ public interface IContactEventHandlerWithInfo : IContactEventHandler /// /// Add this component to an in-scene placed GameObject to provide faster collision event processing between instances and optionally static colliders. - ///
- ///
- ///
+ ///
+ ///
+ ///
///
[AddComponentMenu("Netcode/Rigidbody Contact Event Manager")] public class RigidbodyContactEventManager : MonoBehaviour diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index 09119e8076..e297375ec1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -11,7 +11,7 @@ namespace Unity.Netcode { /// - /// The connection event type set within to signify the type of connection event notification received. + /// The connection event type set within to signify the type of connection event notification received. /// /// /// is returned as a parameter of the event notification. diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 6a7d71f159..75db75b9f2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -416,7 +416,7 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) // Metrics update needs to be driven by NetworkConnectionManager's update to assure metrics are dispatched after the send queue is processed. MetricsManager.UpdateMetrics(); - // Handle sending any pending transport messages + // Handle sending any pending transport messages NetworkConfig.NetworkTransport.PostLateUpdate(); // TODO: Determine a better way to handle this diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 5d94f4fd3c..2ff4e27eb3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -182,7 +182,7 @@ private static void PrefabStageOpened(PrefabStage prefabStage) /// /// InContext: Typically means a are in prefab edit mode for an in-scene placed network prefab instance. /// (currently no such thing as a network prefab with nested network prefab instances) - /// + /// /// InIsolation: Typically means we are in prefb edit mode for a prefab asset. /// /// diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObjectRefreshTool.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObjectRefreshTool.cs index 63d48e914d..5309377475 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObjectRefreshTool.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObjectRefreshTool.cs @@ -11,7 +11,7 @@ namespace Unity.Netcode { /// - /// This is a helper tool to update all in-scene placed instances of a prefab that + /// This is a helper tool to update all in-scene placed instances of a prefab that /// originally did not have a NetworkObject component but one was added to the prefab /// later. /// diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs index b7f90f658f..4e697acc0d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs @@ -16,7 +16,7 @@ internal struct ILPPMessageProvider : INetworkMessageProvider /// /// Enum representing the different types of messages that can be sent over the network. /// The values cannot be changed, as they are used to serialize and deserialize messages. - /// Adding new messages should be done by adding new values to the end of the enum + /// Adding new messages should be done by adding new values to the end of the enum /// using the next free value. /// /// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs index cb4f114b91..998e84d640 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs @@ -17,7 +17,7 @@ internal struct ChangeOwnershipMessage : INetworkMessage, INetworkSerializeByMem /// /// When requesting, RequestClientId is the requestor. /// When approving, RequestClientId is the owner that approved. - /// When responding (only for denied), RequestClientId is the requestor + /// When responding (only for denied), RequestClientId is the requestor /// internal ulong RequestClientId; internal int ClientIdCount; @@ -272,7 +272,7 @@ public void Handle(ref NetworkContext context) networkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.Reliable, clientId); } } - // If the NetworkObject is not visible to the DAHost client, then exit early + // If the NetworkObject is not visible to the DAHost client, then exit early if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId)) { return; @@ -294,7 +294,7 @@ public void Handle(ref NetworkContext context) } /// - /// Handle the + /// Handle the extended distributed authority ownership updates /// /// private void HandleExtendedOwnershipUpdate(ref NetworkContext context) @@ -351,10 +351,10 @@ private void HandleOwnershipChange(ref NetworkContext context) networkObject.InvokeBehaviourOnLostOwnership(); } - // If in distributed authority mode + // If in distributed authority mode if (networkManager.DistributedAuthorityMode) { - // Always update the network properties in distributed authority mode + // Always update the network properties in distributed authority mode for (int i = 0; i < networkObject.ChildNetworkBehaviours.Count; i++) { networkObject.ChildNetworkBehaviours[i].UpdateNetworkProperties(); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs index 6c0656b90a..02f4263e9d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs @@ -257,7 +257,7 @@ internal static void CreateObject(ref NetworkManager networkManager, ulong sende { // DA - NGO CMB SERVICE NOTES: // (*** See above notes fist ***) - // If it is a player object freshly spawning and one or more clients all connect at the exact same time (i.e. received on effectively + // If it is a player object freshly spawning and one or more clients all connect at the exact same time (i.e. received on effectively // the same frame), then we need to check the observers list to make sure all players are visible upon first spawning. At a later date, // for area of interest we will need to have some form of follow up "observer update" message to cull out players not within each // player's AOI. diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkTransformMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkTransformMessage.cs index 56f25f22c2..cf4013f469 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkTransformMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkTransformMessage.cs @@ -128,7 +128,7 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int } else if (networkManager.DAHost) { - // Specific to distributed authority mode, the only sender of state updates will be the owner + // Specific to distributed authority mode, the only sender of state updates will be the owner ownerClientId = context.SenderId; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 9eacd5601f..0e3ccfb7e3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -62,7 +62,7 @@ public void Dispose() /// When you know you will be writing multiple fields back-to-back and you know the total size, /// you can call TryBeginWriteBits() once on the total size, and then follow it with calls to /// WriteBit() or WriteBits(). - /// + /// /// Bitwise write operations will throw OverflowException in editor and development builds if you /// go past the point you've marked using TryBeginWriteBits(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 9ce6595c5f..85ff9a89e2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -771,7 +771,7 @@ internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networ } /// - /// Gets the right NetworkObject prefab instance to spawn. If a handler is registered or there is an override assigned to the + /// Gets the right NetworkObject prefab instance to spawn. If a handler is registered or there is an override assigned to the /// passed in globalObjectIdHash value, then that is what will be instantiated, spawned, and returned. /// internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ownerId, Vector3? position, Quaternion? rotation, bool isScenePlaced = false) @@ -803,8 +803,8 @@ internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ow case NetworkPrefabOverride.Hash: case NetworkPrefabOverride.Prefab: { - // When scene management is disabled and this is an in-scene placed NetworkObject, we want to always use the - // SourcePrefabToOverride and not any possible prefab override as a user might want to spawn overrides dynamically + // When scene management is disabled and this is an in-scene placed NetworkObject, we want to always use the + // SourcePrefabToOverride and not any possible prefab override as a user might want to spawn overrides dynamically // but might want to use the same source network prefab as an in-scene placed NetworkObject. // (When scene management is enabled, clients don't delete their in-scene placed NetworkObjects prior to dynamically // spawning them so the original prefab placed is preserved and this is not needed) @@ -998,7 +998,7 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneO /// - NetworkObject when spawning a newly instantiated NetworkObject for the first time. /// - NetworkSceneManager after a server/session-owner has loaded a scene to locally spawn the newly instantiated in-scene placed NetworkObjects. /// - NetworkSpawnManager when spawning any already loaded in-scene placed NetworkObjects (client-server or session owner). - /// + /// /// Client-Server: /// Server is the only instance that invokes this method. /// @@ -1376,7 +1376,7 @@ internal void DespawnAndDestroyNetworkObjects() } } - // If spawned, then despawn and potentially destroy. + // If spawned, then despawn and potentially destroy. if (networkObjects[i].IsSpawned) { OnDespawnObject(networkObjects[i], shouldDestroy); @@ -1805,7 +1805,7 @@ internal void DistributeNetworkObjects(ulong clientId) } - // DA-NGO CMB SERVICE NOTES: + // DA-NGO CMB SERVICE NOTES: // The most basic object distribution should be broken up into a table of spawned object types // where each type contains a list of each client's owned objects of that type that can be // distributed. @@ -1824,7 +1824,7 @@ internal void DistributeNetworkObjects(ulong clientId) var clientCount = NetworkManager.ConnectedClientsIds.Count; - // Cycle through each prefab type + // Cycle through each prefab type foreach (var objectTypeEntry in distributedNetworkObjects) { // Calculate the number of objects that should be distributed amongst the clients @@ -1894,7 +1894,7 @@ internal void DistributeNetworkObjects(ulong clientId) objectTypeCount.Clear(); GetObjectDistribution(ref distributedNetworkObjects, ref objectTypeCount); builder.AppendLine($"Client Relative Distributed Object Count: (distribution follows)"); - // Cycle through each prefab type + // Cycle through each prefab type foreach (var objectTypeEntry in distributedNetworkObjects) { builder.AppendLine($"[GID: {objectTypeEntry.Key} | {objectTypeEntry.Value.First().Value.First().name}][Total Count: {objectTypeCount[objectTypeEntry.Key]}]"); diff --git a/com.unity.netcode.gameobjects/Samples~/Bootstrap/Scripts/Bootstrap.asmdef b/com.unity.netcode.gameobjects/Samples~/Bootstrap/Scripts/Bootstrap.asmdef index 07a8ac0096..54cb675609 100644 --- a/com.unity.netcode.gameobjects/Samples~/Bootstrap/Scripts/Bootstrap.asmdef +++ b/com.unity.netcode.gameobjects/Samples~/Bootstrap/Scripts/Bootstrap.asmdef @@ -1,7 +1,7 @@ { - "name": "Bootstrap", + "name": "Bootstrap", "rootNamespace": "Unity.Netcode.Samples", "references": [ "Unity.Netcode.Runtime" ] -} +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformOwnershipTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformOwnershipTests.cs index ca402c6095..229a067758 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformOwnershipTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformOwnershipTests.cs @@ -869,7 +869,7 @@ public IEnumerator NestedNetworkTransformSpawnPositionTest() foreach (var networkManager in m_NetworkManagers) { - // Randomize the position + // Randomize the position RandomizeObjectTransformPositions(m_SpawnObject); // Create an instance owned by the specified networkmanager diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs index 95ca7e0f20..7e12934a67 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs @@ -123,7 +123,7 @@ private void UpdateTransformWorld(Components.NetworkTransform networkTransformTe } /// - /// This test validates the SwitchTransformSpaceWhenParented setting under all network topologies + /// This test validates the SwitchTransformSpaceWhenParented setting under all network topologies /// [Test] public void SwitchTransformSpaceWhenParentedTest([Values(0.5f, 1.0f, 5.0f)] float scale) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableCollectionsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableCollectionsTests.cs index 0c8a2ffdf9..3276b3d3df 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableCollectionsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableCollectionsTests.cs @@ -20,7 +20,7 @@ namespace Unity.Netcode.RuntimeTests /// - HashSet /// This also does some testing on nested collections, but does /// not test every possible combination. - ///
+ /// [TestFixture(HostOrServer.Host)] [TestFixture(HostOrServer.Server)] public class NetworkVariableCollectionsTests : NetcodeIntegrationTest @@ -218,7 +218,7 @@ public IEnumerator TestListBuiltInTypeCollections() yield return WaitForConditionOrTimeOut(() => compInt.CompareTrackedChanges(ListTestHelperBase.Targets.Owner)); AssertOnTimeout($"Client-{client.LocalClientId} full set failed to synchronize on {nameof(ListTestHelperInt)} {compInt.name}!"); ////////////////////////////////// - // Server Full Set + // Server Full Set compIntServer.FullSet(GetRandomIntList(5), ListTestHelperBase.Targets.Server); yield return WaitForConditionOrTimeOut(() => compIntServer.CompareTrackedChanges(ListTestHelperBase.Targets.Server)); AssertOnTimeout($"Server full set failed to synchronize on {nameof(ListTestHelperInt)} {compIntServer.name}!"); @@ -285,7 +285,7 @@ public IEnumerator TestListBuiltInTypeCollections() // Only test restore on non-host clients (otherwise a host is both server and client/owner) if (!client.IsServer) { - // No Write Client Remove List item with CheckDirtyState restore + // No Write Client Remove List item with CheckDirtyState restore compListInt.Remove(compListInt.ListCollectionServer.Value[index], ListTestHelperBase.Targets.Server); yield return WaitForConditionOrTimeOut(() => compListInt.CompareTrackedChanges(ListTestHelperBase.Targets.Server)); AssertOnTimeout($"Client-{client.LocalClientId} remove failed to restore on {nameof(ListTestHelperListInt)} {compListIntServer.name}! {compListIntServer.GetLog()}"); @@ -474,7 +474,7 @@ public IEnumerator TestListSerializableObjectCollections() yield return WaitForConditionOrTimeOut(() => compObjectServer.CompareTrackedChanges(ListTestHelperBase.Targets.Server)); AssertOnTimeout($"Client-{client.LocalClientId} change failed to restore on {nameof(ListTestHelperSerializableObject)} {compObjectServer.name}!"); - // No Write Client Remove Serializable item with owner state update restore + // No Write Client Remove Serializable item with owner state update restore compObject.ListCollectionServer.Value[index] = SerializableObject.GetRandomObject(); } compObjectServer.ListCollectionServer.Value[index] = SerializableObject.GetRandomObject(); @@ -838,7 +838,7 @@ public IEnumerator TestDictionaryCollections() compDictionaryServer.ListCollectionOwner.IsDirty(); yield return WaitForConditionOrTimeOut(() => compDictionaryServer.CompareTrackedChanges(ListTestHelperBase.Targets.Owner)); AssertOnTimeout($"Server add to owner write collection property failed to restore on {className} {compDictionaryServer.name}! {compDictionaryServer.GetLog()}"); - // Server-side add the same key and SerializableObject to owner write permission (would throw key exists exception too if previous failed) + // Server-side add the same key and SerializableObject to owner write permission (would throw key exists exception too if previous failed) compDictionaryServer.ListCollectionOwner.Value.Add(newEntry.Item1, newEntry.Item2); // Server-side add a completely new key and SerializableObject to to owner write permission property compDictionaryServer.ListCollectionOwner.Value.Add(GetNextKey(), SerializableObject.GetRandomObject()); @@ -864,7 +864,7 @@ public IEnumerator TestDictionaryCollections() compDictionary.ListCollectionServer.IsDirty(); yield return WaitForConditionOrTimeOut(() => compDictionary.CompareTrackedChanges(ListTestHelperBase.Targets.Server)); AssertOnTimeout($"Client-{client.LocalClientId} add to server write collection property failed to restore on {className} {compDictionary.name}! {compDictionary.GetLog()}"); - // Client-side add the same key and SerializableObject to server write permission property (would throw key exists exception too if previous failed) + // Client-side add the same key and SerializableObject to server write permission property (would throw key exists exception too if previous failed) compDictionary.ListCollectionServer.Value.Add(newEntry.Item1, newEntry.Item2); // Client-side add a completely new key and SerializableObject to to server write permission property compDictionary.ListCollectionServer.Value.Add(GetNextKey(), SerializableObject.GetRandomObject()); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs index b7d86e3356..84d9c56fec 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs @@ -109,7 +109,7 @@ protected override void OnServerAndClientsCreated() playerPrefabOverrideHandler.ServerSideInstance = m_PlayerPrefab; playerPrefabOverrideHandler.ClientSideInstance = m_ClientSidePlayerPrefab.Prefab; - // Add the NetworkPrefab with override + // Add the NetworkPrefab with override m_ServerNetworkManager.NetworkConfig.Prefabs.Add(m_PrefabOverride); // Add the client player prefab that will be used on clients (and the host) m_ServerNetworkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab); @@ -121,7 +121,7 @@ protected override void OnServerAndClientsCreated() playerPrefabOverrideHandler.ServerSideInstance = m_PlayerPrefab; playerPrefabOverrideHandler.ClientSideInstance = m_ClientSidePlayerPrefab.Prefab; - // Add the NetworkPrefab with override + // Add the NetworkPrefab with override networkManager.NetworkConfig.Prefabs.Add(m_PrefabOverride); // Add the client player prefab that will be used on clients (and the host) networkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab); From 658ef01f0f96ed33bb7e17a3e9b4c202a6aba7c2 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 12 Dec 2024 14:53:54 -0600 Subject: [PATCH 07/11] style missed one whitespace... --- .../Runtime/Messaging/RpcTargets/ServerRpcTarget.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs index 0db593f23b..87bfbfb1c4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs @@ -43,7 +43,7 @@ internal override void Send(NetworkBehaviour behaviour, ref RpcMessage message, using var tempBuffer = new FastBufferReader(message.WriteBuffer, Allocator.None); message.ReadBuffer = tempBuffer; message.Handle(ref context); - // If enabled, then add the RPC metrics for this + // If enabled, then add the RPC metrics for this #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE int length = tempBuffer.Length; if (NetworkBehaviour.__rpc_name_table[behaviour.GetType()].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName)) From 6de550bfee76ba014cd7ad37c7b0f7aa6c50230d Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Fri, 13 Dec 2024 03:36:39 -0600 Subject: [PATCH 08/11] chore: merge develop 2 0 0 updates with SessionOwner permissions (#3176) * fix: clamp spawntimeout to recommended range (#3174) * update Clamping spawntimeout * update improving parenting failed message when either the child or parent NetworkObject is not spawnd. * update Update the local SceneEventData.SceneEventType on the authority side for SceneLoadComplete. * style removing whitespaces * feat: Add a SessionOwner ObjectStatus and allow InScenePlaced network objects to be distributed (#3175) * Initial pass on SessionOwner ownership flag * feat: Add SessionOwner OwnershipStatus flag * update removing additional session owner accessor. * Add OwnershipPermissions tests * fix client connect * Revert "fix client connect" This reverts commit 3c3b35444200da308dd07e1dd2e7b591fa0842d5. * update object distribution for in-scene placed NetworkObjects needs to use the InScenePlacedSourceGlobalObjectIdHash when building an object distribution list. * Add changelog * Remove unnecessary change * Remove Settings.json * Reword CHANGELOG * fix DAHost should not distribute session owner permission NetworkObjects. When client is promoted to session owner, for now newly promoted client takes ownership of NetworkObjects that have the SessionOwner permission set. Only prevent non-session owners from taking ownership of a NetworkObject with the SessionOwner permission set. * test fix Avoid the RemoveOwnership client-server only method. * style Visual studio code cleanup likes to sort by alpha... fixing for our standards. * update Adding check for session owner trying to change ownership to a non-session owner client. * test Adding an additional validation that a non-session owner cannot change ownership and that a session owner cannot change ownership to a non-session owner when the NetworkObject in question has the SessionOwner permissions set. --------- Co-authored-by: NoelStephensUnity --------- Co-authored-by: Emma --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 + .../Editor/NetworkObjectEditor.cs | 4 + .../Runtime/Configuration/NetworkConfig.cs | 25 ++++ .../Connection/NetworkConnectionManager.cs | 7 +- .../Runtime/Core/NetworkManager.cs | 9 +- .../Runtime/Core/NetworkObject.cs | 68 ++++++++--- .../SceneManagement/NetworkSceneManager.cs | 4 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 67 +++++------ .../OwnershipPermissionsTests.cs | 112 ++++++++++++++---- .../NetworkObjectOwnershipTests.cs | 13 +- 10 files changed, 231 insertions(+), 80 deletions(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index accae93b14..ff93d501ee 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -11,6 +11,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added +- Added `NetworkObject.OwnershipStatus.SessionOwner` to allow Network Objects to be distributable and only owned by the Session Owner. This flag will override all other `OwnershipStatus` flags. (#3175) - Added `UnityTransport.GetEndpoint` method to provide a way to obtain `NetworkEndpoint` information of a connection via client identifier. (#3130) - Added `NetworkTransport.OnEarlyUpdate` and `NetworkTransport.OnPostLateUpdate` methods to provide more control over handling transport related events at the start and end of each frame. (#3113) @@ -31,6 +32,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed +- In-scene placed `NetworkObject`s have been made distributable when balancing object distribution after a connection event. (#3175) - Optimised `NetworkVariable` and `NetworkTransform` related packets when in Distributed Authority mode. - The Debug Simulator section of the Unity Transport component was removed. This section was not functional anymore and users are now recommended to use the more featureful [Network Simulator](https://docs-multiplayer.unity3d.com/tools/current/tools-network-simulator/) tool from the Multiplayer Tools package instead. (#3121) diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs index 6adb7b8223..0860fd9c92 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs @@ -115,6 +115,10 @@ public override void OnInspectorGUI() EditorGUI.BeginChangeCheck(); serializedObject.UpdateIfRequiredOrScript(); DrawPropertiesExcluding(serializedObject, k_HiddenFields); + if (m_NetworkObject.IsOwnershipSessionOwner) + { + m_NetworkObject.Ownership = NetworkObject.OwnershipStatus.SessionOwner; + } serializedObject.ApplyModifiedProperties(); EditorGUI.EndChangeCheck(); diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs index 79035a895f..a9668084bd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs @@ -13,6 +13,14 @@ namespace Unity.Netcode [Serializable] public class NetworkConfig { + // Clamp spawn time outs to prevent dropping messages during scene events + // Note: The legacy versions of NGO defaulted to 1s which was too low. As + // well, the SpawnTimeOut is now being clamped to within this recommended + // range both via UI and when NetworkManager is validated. + internal const float MinSpawnTimeout = 10.0f; + // Clamp spawn time outs to no more than 1 hour (really that is a bit high) + internal const float MaxSpawnTimeout = 3600.0f; + /// /// The protocol version. Different versions doesn't talk to each other. /// @@ -132,6 +140,8 @@ public class NetworkConfig /// The amount of time a message will be held (deferred) if the destination NetworkObject needed to process the message doesn't exist yet. If the NetworkObject is not spawned within this time period, all deferred messages for that NetworkObject will be dropped. /// [Tooltip("The amount of time a message will be held (deferred) if the destination NetworkObject needed to process the message doesn't exist yet. If the NetworkObject is not spawned within this time period, all deferred messages for that NetworkObject will be dropped.")] + + [Range(MinSpawnTimeout, MaxSpawnTimeout)] public float SpawnTimeout = 10f; /// @@ -176,6 +186,21 @@ public class NetworkConfig [Tooltip("Enable (default) if you want to profile network messages with development builds and defaults to being disabled in release builds. When disabled, network messaging profiling will be disabled in development builds.")] public bool NetworkProfilingMetrics = true; + /// + /// Invoked by when it is validated. + /// + /// + /// Used to check for potential legacy values that have already been serialized and/or + /// runtime modifications to a property outside of the recommended range. + /// For each property checked below, provide a brief description of the reason. + /// + internal void OnValidate() + { + // Legacy NGO versions defaulted this value to 1 second that has since been determiend + // any range less than 10 seconds can lead to dropped messages during scene events. + SpawnTimeout = Mathf.Clamp(SpawnTimeout, MinSpawnTimeout, MaxSpawnTimeout); + } + /// /// Returns a base64 encoded version of the configuration /// diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index e297375ec1..fe6bc4816d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -1212,7 +1212,7 @@ internal void OnClientDisconnectFromServer(ulong clientId) { // Only NetworkObjects that have the OwnershipStatus.Distributable flag set and no parent // (ownership is transferred to all children) will have their ownership redistributed. - if (ownedObject.IsOwnershipDistributable && ownedObject.GetCachedParent() == null) + if (ownedObject.IsOwnershipDistributable && ownedObject.GetCachedParent() == null && !ownedObject.IsOwnershipSessionOwner) { if (ownedObject.IsOwnershipLocked) { @@ -1249,6 +1249,11 @@ internal void OnClientDisconnectFromServer(ulong clientId) childObject.SetOwnershipLock(false); } + // Ignore session owner marked objects + if (childObject.IsOwnershipSessionOwner) + { + continue; + } NetworkManager.SpawnManager.ChangeOwnership(childObject, targetOwner, true); if (EnableDistributeLogging) { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 75db75b9f2..9b3ef047a0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -225,11 +225,7 @@ internal void SetSessionOwner(ulong sessionOwner) foreach (var networkObjectEntry in SpawnManager.SpawnedObjects) { var networkObject = networkObjectEntry.Value; - if (networkObject.IsSceneObject == null || !networkObject.IsSceneObject.Value) - { - continue; - } - if (networkObject.OwnerClientId != LocalClientId) + if (networkObject.IsOwnershipSessionOwner && LocalClient.IsSessionOwner) { SpawnManager.ChangeOwnership(networkObject, LocalClientId, true); } @@ -955,6 +951,9 @@ internal void OnValidate() return; // May occur when the component is added } + // Do a validation pass on NetworkConfig properties + NetworkConfig.OnValidate(); + if (GetComponentInChildren() != null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 2ff4e27eb3..c449c9aadb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -439,6 +439,13 @@ public void DeferDespawn(int tickOffset, bool destroy = true) /// public bool IsOwnershipDistributable => Ownership.HasFlag(OwnershipStatus.Distributable); + /// + /// When true, the can only be owned by the current Session Owner. + /// To set during runtime, use to ensure the session owner owns the object. + /// Once the session owner owns the object, then use . + /// + public bool IsOwnershipSessionOwner => Ownership.HasFlag(OwnershipStatus.SessionOwner); + /// /// Returns true if the is has ownership locked. /// When locked, the cannot be redistributed nor can it be transferred by another client. @@ -481,7 +488,8 @@ public void DeferDespawn(int tickOffset, bool destroy = true) /// : If nothing is set, then ownership is considered "static" and cannot be redistributed, requested, or transferred (i.e. a Player would have this). /// : When set, this instance will be automatically redistributed when a client joins (if not locked or no request is pending) or leaves. /// : When set, a non-owner can obtain ownership immediately (without requesting and as long as it is not locked). - /// : When set, When set, a non-owner must request ownership from the owner (will always get locked once ownership is transferred). + /// : When set, a non-owner must request ownership from the owner (will always get locked once ownership is transferred). + /// : When set, only the current session owner may have ownership over this object. /// // Ranges from 1 to 8 bits [Flags] @@ -491,6 +499,7 @@ public enum OwnershipStatus Distributable = 1 << 0, Transferable = 1 << 1, RequestRequired = 1 << 2, + SessionOwner = 1 << 3, } /// @@ -549,7 +558,7 @@ public bool SetOwnershipLock(bool lockOwnership = true) } // If we don't have the Transferable flag set and it is not a player object, then it is the same as having a static lock on ownership - if (!IsOwnershipTransferable && !IsPlayerObject) + if (!(IsOwnershipTransferable || IsPlayerObject) || IsOwnershipSessionOwner) { NetworkLog.LogWarning($"Trying to add or remove ownership lock on [{name}] which does not have the {nameof(OwnershipStatus.Transferable)} flag set!"); return false; @@ -582,13 +591,15 @@ public bool SetOwnershipLock(bool lockOwnership = true) /// : The requires an ownership request via . /// : The is already processing an ownership request and ownership cannot be acquired at this time. /// : The does not have the flag set and ownership cannot be acquired. + /// : The has the flag set and ownership cannot be acquired. /// public enum OwnershipPermissionsFailureStatus { Locked, RequestRequired, RequestInProgress, - NotTransferrable + NotTransferrable, + SessionOwnerOnly } /// @@ -610,6 +621,7 @@ public enum OwnershipPermissionsFailureStatus /// : The flag is not set on this /// : The current owner has locked ownership which means requests are not available at this time. /// : There is already a known request in progress. You can scan for ownership changes and try upon + /// : This object is marked as SessionOwnerOnly and therefore cannot be requested /// a change in ownership or just try again after a specific period of time or no longer attempt to request ownership. /// public enum OwnershipRequestStatus @@ -619,6 +631,7 @@ public enum OwnershipRequestStatus RequestRequiredNotSet, Locked, RequestInProgress, + SessionOwnerOnly, } /// @@ -631,6 +644,7 @@ public enum OwnershipRequestStatus /// : The flag is not set on this /// : The current owner has locked ownership which means requests are not available at this time. /// : There is already a known request in progress. You can scan for ownership changes and try upon + /// : This object can only belong the the session owner and so cannot be requested /// a change in ownership or just try again after a specific period of time or no longer attempt to request ownership. /// /// @@ -660,6 +674,12 @@ public OwnershipRequestStatus RequestOwnership() return OwnershipRequestStatus.RequestInProgress; } + // Exit early if it has the SessionOwner flag + if (IsOwnershipSessionOwner) + { + return OwnershipRequestStatus.SessionOwnerOnly; + } + // Otherwise, send the request ownership message var changeOwnership = new ChangeOwnershipMessage { @@ -716,7 +736,7 @@ internal void OwnershipRequest(ulong clientRequestingOwnership) { response = OwnershipRequestResponseStatus.RequestInProgress; } - else if (!IsOwnershipRequestRequired && !IsOwnershipTransferable) + else if (!(IsOwnershipRequestRequired || IsOwnershipTransferable) || IsOwnershipSessionOwner) { response = OwnershipRequestResponseStatus.CannotRequest; } @@ -836,6 +856,12 @@ public enum OwnershipLockActions /// public bool SetOwnershipStatus(OwnershipStatus status, bool clearAndSet = false, OwnershipLockActions lockAction = OwnershipLockActions.None) { + if (status.HasFlag(OwnershipStatus.SessionOwner) && !NetworkManager.LocalClient.IsSessionOwner) + { + NetworkLog.LogWarning("Only the session owner is allowed to set the ownership status to session owner only."); + return false; + } + // If it already has the flag do nothing if (!clearAndSet && Ownership.HasFlag(status)) { @@ -847,13 +873,25 @@ public bool SetOwnershipStatus(OwnershipStatus status, bool clearAndSet = false, Ownership = OwnershipStatus.None; } - // Faster to just OR a None status than to check - // if it is !None before "OR'ing". - Ownership |= status; - - if (lockAction != OwnershipLockActions.None) + if (status.HasFlag(OwnershipStatus.SessionOwner)) { - SetOwnershipLock(lockAction == OwnershipLockActions.SetAndLock); + Ownership = OwnershipStatus.SessionOwner; + } + else if (Ownership.HasFlag(OwnershipStatus.SessionOwner)) + { + NetworkLog.LogWarning("No other ownership statuses may be set while SessionOwner is set."); + return false; + } + else + { + // Faster to just OR a None status than to check + // if it is !None before "OR'ing". + Ownership |= status; + + if (lockAction != OwnershipLockActions.None) + { + SetOwnershipLock(lockAction == OwnershipLockActions.SetAndLock); + } } SendOwnershipStatusUpdate(); @@ -1629,7 +1667,7 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla // DANGO-TODO: Review over don't destroy with owner being set but DistributeOwnership not being set if (NetworkManager.LogLevel == LogLevel.Developer) { - NetworkLog.LogWarning("DANGO-TODO: Review over don't destroy with owner being set but DistributeOwnership not being set. For now, if the NetworkObject does not destroy with the owner it will automatically set DistributeOwnership."); + NetworkLog.LogWarning("DANGO-TODO: Review over don't destroy with owner being set but DistributeOwnership not being set. For now, if the NetworkObject does not destroy with the owner it will set ownership to SessionOwner."); } } } @@ -2007,12 +2045,14 @@ public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true) internal bool InternalTrySetParent(NetworkObject parent, bool worldPositionStays = true) { - if (parent != null && (IsSpawned ^ parent.IsSpawned)) + if (parent != null && (IsSpawned ^ parent.IsSpawned) && NetworkManager != null && !NetworkManager.ShutdownInProgress) { - if (NetworkManager != null && !NetworkManager.ShutdownInProgress) + if (NetworkManager.LogLevel <= LogLevel.Developer) { - return false; + var nameOfNotSpawnedObject = IsSpawned ? $" the parent ({parent.name})" : $"the child ({name})"; + NetworkLog.LogWarning($"Parenting failed because {nameOfNotSpawnedObject} is not spawned!"); } + return false; } m_CachedWorldPositionStays = worldPositionStays; diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index acffc411f8..8996dfcfd7 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -1903,10 +1903,12 @@ private void OnSessionOwnerLoadedScene(uint sceneEventId, Scene scene) SendSceneEventData(sceneEventData.SceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != sessionOwner).ToArray()); m_IsSceneEventActive = false; + + sceneEventData.SceneEventType = SceneEventType.LoadComplete; //First, notify local server that the scene was loaded OnSceneEvent?.Invoke(new SceneEvent() { - SceneEventType = SceneEventType.LoadComplete, + SceneEventType = sceneEventData.SceneEventType, LoadSceneMode = sceneEventData.LoadSceneMode, SceneName = SceneNameFromHash(sceneEventData.SceneHash), ClientId = NetworkManager.LocalClientId, diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 85ff9a89e2..ed0b0348b7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -422,27 +422,8 @@ internal void RemoveOwnership(NetworkObject networkObject) { if (NetworkManager.DistributedAuthorityMode && !NetworkManager.ShutdownInProgress) { - if (networkObject.IsOwnershipDistributable || networkObject.IsOwnershipTransferable) - { - if (networkObject.IsOwner || NetworkManager.DAHost) - { - NetworkLog.LogWarning("DANGO-TODO: Determine if removing ownership should make the CMB Service redistribute ownership or if this just isn't a valid thing in DAMode."); - return; - } - else - { - NetworkLog.LogError($"Only the owner is allowed to remove ownership in distributed authority mode!"); - return; - } - } - else - { - if (!NetworkManager.DAHost) - { - Debug.LogError($"Only {nameof(NetworkObject)}s with {nameof(NetworkObject.IsOwnershipDistributable)} or {nameof(NetworkObject.IsOwnershipTransferable)} set can perform ownership changes!"); - } - return; - } + Debug.LogError($"Removing ownership is invalid in Distributed Authority Mode. Use {nameof(ChangeOwnership)} instead."); + return; } ChangeOwnership(networkObject, NetworkManager.ServerClientId, true); } @@ -474,6 +455,18 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool if (NetworkManager.DistributedAuthorityMode) { + // Ensure only the session owner can change ownership (i.e. acquire) and that the session owner is not trying to assign a non-session owner client + // ownership of a NetworkObject with SessionOwner permissions. + if (networkObject.IsOwnershipSessionOwner && (!NetworkManager.LocalClient.IsSessionOwner || clientId != NetworkManager.CurrentSessionOwner)) + { + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogErrorServer($"[{networkObject.name}][Session Owner Only] You cannot change ownership of a {nameof(NetworkObject)} that has the {NetworkObject.OwnershipStatus.SessionOwner} flag set!"); + } + networkObject.OnOwnershipPermissionsFailure?.Invoke(NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly); + return; + } + // If are not authorized and this is not an approved ownership change, then check to see if we can change ownership if (!isAuthorized && !isRequestApproval) { @@ -1746,6 +1739,11 @@ internal void GetObjectDistribution(ref Dictionary>()); + objectByTypeAndOwner.Add(globalOjectIdHash, new Dictionary>()); } // Sub-divide each type by owner - if (!objectByTypeAndOwner[networkObject.GlobalObjectIdHash].ContainsKey(networkObject.OwnerClientId)) + if (!objectByTypeAndOwner[globalOjectIdHash].ContainsKey(networkObject.OwnerClientId)) { - objectByTypeAndOwner[networkObject.GlobalObjectIdHash].Add(networkObject.OwnerClientId, new List()); + objectByTypeAndOwner[globalOjectIdHash].Add(networkObject.OwnerClientId, new List()); } // Add to the client's spawned object list - objectByTypeAndOwner[networkObject.GlobalObjectIdHash][networkObject.OwnerClientId].Add(networkObject); + objectByTypeAndOwner[globalOjectIdHash][networkObject.OwnerClientId].Add(networkObject); } } } @@ -1872,7 +1869,7 @@ internal void DistributeNetworkObjects(ulong clientId) if ((i % offsetCount) == 0) { ChangeOwnership(ownerList.Value[i], clientId, true); - if (EnableDistributeLogging) + //if (EnableDistributeLogging) { Debug.Log($"[Client-{ownerList.Key}][NetworkObjectId-{ownerList.Value[i].NetworkObjectId} Distributed to Client-{clientId}"); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/OwnershipPermissionsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/OwnershipPermissionsTests.cs index beccbdd9db..dcf44f7d8f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/OwnershipPermissionsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/OwnershipPermissionsTests.cs @@ -128,7 +128,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() yield return WaitForConditionOrTimeOut(ValidateObjectSpawnedOnAllClients); AssertOnTimeout($"[Failed To Spawn] {firstInstance.name}: \n {m_ErrorLog}"); - // Validate the base non-assigned persmissions value for all instances are the same. + // Validate the base non-assigned permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}"); @@ -141,9 +141,15 @@ public IEnumerator ValidateOwnershipPermissionsTest() foreach (var permissionObject in Enum.GetValues(typeof(NetworkObject.OwnershipStatus))) { var permission = (NetworkObject.OwnershipStatus)permissionObject; + // Adding the SessionOwner flag here should fail as this NetworkObject is not owned by the Session Owner + if (permission == NetworkObject.OwnershipStatus.SessionOwner) + { + Assert.IsFalse(firstInstance.SetOwnershipStatus(permission), $"[Add][IncorrectPermissions] Setting {NetworkObject.OwnershipStatus.SessionOwner} is not valid when the client is not the Session Owner: \n {m_ErrorLog}!"); + continue; + } // Add the status firstInstance.SetOwnershipStatus(permission); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Add][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}"); @@ -153,7 +159,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() continue; } firstInstance.RemoveOwnershipStatus(permission); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Remove][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}"); } @@ -163,7 +169,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() firstInstance.SetOwnershipStatus(multipleFlags, true); Assert.IsTrue(firstInstance.HasOwnershipStatus(multipleFlags), $"[Set][Multi-flag Failure] Expected: {(ushort)multipleFlags} but was {(ushort)firstInstance.Ownership}!"); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Set Multiple][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}"); @@ -175,7 +181,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() // Validate that the Distributable flag is still set Assert.IsTrue(firstInstance.HasOwnershipStatus(NetworkObject.OwnershipStatus.Distributable), $"[Remove][Multi-flag Failure] Expected: {(ushort)NetworkObject.OwnershipStatus.Distributable} but was {(ushort)firstInstance.Ownership}!"); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Set Multiple][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}"); @@ -186,7 +192,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() // Clear the flags, set the permissions to transferrable, and lock ownership in one pass. firstInstance.SetOwnershipStatus(NetworkObject.OwnershipStatus.Transferable, true, NetworkObject.OwnershipLockActions.SetAndLock); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Reset][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}"); @@ -199,7 +205,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() $" status is {secondInstanceHelper.OwnershipPermissionsFailureStatus}!"); firstInstance.SetOwnershipLock(false); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Unlock][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}"); @@ -208,7 +214,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() // Now try to acquire ownership secondInstance.ChangeOwnership(m_ClientNetworkManagers[1].LocalClientId); - // Validate the persmissions value for all instances are the same + // Validate the permissions value for all instances are the same yield return WaitForConditionOrTimeOut(() => secondInstance.IsOwner); AssertOnTimeout($"[Acquire Ownership Failed] Client-{m_ClientNetworkManagers[1].LocalClientId} failed to get ownership!"); @@ -220,7 +226,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() // Clear the flags, set the permissions to RequestRequired, and lock ownership in one pass. secondInstance.SetOwnershipStatus(NetworkObject.OwnershipStatus.RequestRequired, true); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Unlock][Permissions Mismatch] {secondInstance.name}: \n {m_ErrorLog}"); @@ -238,7 +244,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() // Start with a request for the client we expect to be given ownership var requestStatus = firstInstance.RequestOwnership(); - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); // Get the 3rd client to send a request at the "relatively" same time var thirdInstance = m_ClientNetworkManagers[2].SpawnManager.SpawnedObjects[networkObjectId]; @@ -248,7 +254,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() requestStatus = thirdInstance.RequestOwnership(); // We expect the 3rd client's request should be able to be sent at this time as well (i.e. creates the race condition between two clients) - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{m_ServerNetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{m_ServerNetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); // We expect the first requesting client to be given ownership yield return WaitForConditionOrTimeOut(() => firstInstance.IsOwner); @@ -263,7 +269,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() yield return WaitForConditionOrTimeOut(() => thirdInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.RequestInProgress); AssertOnTimeout($"[Request In Progress Failed] Client-{thirdInstanceHelper.NetworkManager.LocalClientId} did not get the right request denied reponse!"); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Unlock][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}"); @@ -278,11 +284,11 @@ public IEnumerator ValidateOwnershipPermissionsTest() // Send out a request from three clients at the same time // The first one sent (and received for this test) gets ownership requestStatus = secondInstance.RequestOwnership(); - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{secondInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{secondInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); requestStatus = thirdInstance.RequestOwnership(); - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); requestStatus = fourthInstance.RequestOwnership(); - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{fourthInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{fourthInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); // The 2nd and 3rd client should be denied and the 4th client should be approved yield return WaitForConditionOrTimeOut(() => @@ -296,7 +302,7 @@ public IEnumerator ValidateOwnershipPermissionsTest() yield return WaitForConditionOrTimeOut(() => ValidateAllInstancesAreOwnedByClient(secondInstance.NetworkManager.LocalClientId)); AssertOnTimeout($"[Ownership Mismatch] {secondInstance.name}: \n {m_ErrorLog}"); - // Validate the persmissions value for all instances are the same. + // Validate the permissions value for all instances are the same. yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); AssertOnTimeout($"[Unlock][Permissions Mismatch] {secondInstance.name}: \n {m_ErrorLog}"); @@ -314,22 +320,84 @@ public IEnumerator ValidateOwnershipPermissionsTest() // Send out a request from all three clients requestStatus = firstInstance.RequestOwnership(); - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); requestStatus = thirdInstance.RequestOwnership(); - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); requestStatus = fourthInstance.RequestOwnership(); - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{fourthInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{fourthInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); requestStatus = daHostInstance.RequestOwnership(); - Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{daHostInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!"); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{daHostInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); - // The server and the 2nd client should be denied and the third client should be approved + // Only the client marked as ClientToAllowOwnership (daHost) should be approved. All others should be denied. yield return WaitForConditionOrTimeOut(() => (firstInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Denied) && (thirdInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Denied) && (fourthInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Denied) && (daHostInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Approved) ); - AssertOnTimeout($"[Targeted Owner] Client-{daHostInstance.NetworkManager.LocalClientId} did not get the right request reponse: {daHostInstanceHelper.OwnershipRequestResponseStatus} Expecting: {NetworkObject.OwnershipRequestResponseStatus.Approved}!"); + AssertOnTimeout($"[Targeted Owner] Client-{daHostInstance.NetworkManager.LocalClientId} did not get the right request response: {daHostInstanceHelper.OwnershipRequestResponseStatus} Expecting: {NetworkObject.OwnershipRequestResponseStatus.Approved}!"); + + /////////////////////////////////////////////// + // Test OwnershipStatus.SessionOwner: + /////////////////////////////////////////////// + + OwnershipPermissionsTestHelper.CurrentOwnedInstance = daHostInstance; + m_ObjectToValidate = OwnershipPermissionsTestHelper.CurrentOwnedInstance; + + // Add multiple statuses + daHostInstance.SetOwnershipStatus(NetworkObject.OwnershipStatus.Transferable | NetworkObject.OwnershipStatus.SessionOwner); + // Validate the permissions value for all instances are the same. + yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); + AssertOnTimeout($"[Add][Permissions Mismatch] {daHostInstance.name}: \n {m_ErrorLog}"); + + // Trying to set SessionOwner flag should override any other flags. + Assert.IsFalse(daHostInstance.HasOwnershipStatus(NetworkObject.OwnershipStatus.Transferable), $"[Set][SessionOwner flag Failure] Expected: {NetworkObject.OwnershipStatus.Transferable} not to be set!"); + + // Add another status. Should fail as SessionOwner should be exclusive + daHostInstance.SetOwnershipStatus(NetworkObject.OwnershipStatus.Distributable); + Assert.IsFalse(daHostInstance.HasOwnershipStatus(NetworkObject.OwnershipStatus.Distributable), $"[Add][SessionOwner flag Failure] Expected: {NetworkObject.OwnershipStatus.Transferable} not to be set!"); + + // Request ownership of the SessionOwner flag instance + requestStatus = firstInstance.RequestOwnership(); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestRequiredNotSet, $"Client-{firstInstance.NetworkManager.LocalClientId} should not be able to send a request for ownership because object is marked as owned by the session owner. {requestStatus}!"); + + // Set ownership directly on local object. This will allow the request to be sent + firstInstance.Ownership = NetworkObject.OwnershipStatus.RequestRequired; + requestStatus = firstInstance.RequestOwnership(); + Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!"); + + // Request should be denied with CannotRequest + yield return WaitForConditionOrTimeOut(() => firstInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.CannotRequest); + AssertOnTimeout($"[Targeted Owner] Client-{firstInstance.NetworkManager.LocalClientId} did not get the right request response: {daHostInstanceHelper.OwnershipRequestResponseStatus} Expecting: {NetworkObject.OwnershipRequestResponseStatus.CannotRequest}!"); + + // Try changing the ownership explicitly + // Get the cloned daHostInstance instance on a client side + var clientInstance = m_ClientNetworkManagers[2].SpawnManager.SpawnedObjects[daHostInstance.NetworkObjectId]; + + // Get the client instance of the OwnershipPermissionsTestHelper component + var clientInstanceHelper = clientInstance.GetComponent(); + + // Have the client attempt to change ownership + clientInstance.ChangeOwnership(m_ClientNetworkManagers[2].LocalClientId); + + // Verify the client side gets a permission failure status of NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly + Assert.IsTrue(clientInstanceHelper.OwnershipPermissionsFailureStatus == NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly, + $"Expected {clientInstance.name} to return {NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly} but its permission failure" + + $" status is {clientInstanceHelper.OwnershipPermissionsFailureStatus}!"); + + // Have the session owner attempt to change ownership to a non-session owner + daHostInstance.ChangeOwnership(m_ClientNetworkManagers[2].LocalClientId); + + // Verify the session owner cannot assign a SessionOwner permission NetworkObject to a non-sessionowner client + Assert.IsTrue(daHostInstanceHelper.OwnershipPermissionsFailureStatus == NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly, + $"Expected {daHostInstance.name} to return {NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly} but its permission failure" + + $" status is {daHostInstanceHelper.OwnershipPermissionsFailureStatus}!"); + + // Remove status + daHostInstance.RemoveOwnershipStatus(NetworkObject.OwnershipStatus.SessionOwner); + // Validate the permissions value for all instances are the same. + yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients); + AssertOnTimeout($"[Remove][Permissions Mismatch] {daHostInstance.name}: \n {m_ErrorLog}"); } internal class OwnershipPermissionsTestHelper : NetworkBehaviour diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOwnershipTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOwnershipTests.cs index bda9d15fcb..6c1fe6bba7 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOwnershipTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOwnershipTests.cs @@ -127,7 +127,11 @@ public IEnumerator TestOwnershipCallbacks([Values] OwnershipChecks ownershipChec } // Verifies that removing the ownership when the default (server) is already set does not cause a Key Not Found Exception - m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject); + // Distributed authority does not allow remove ownership (users are instructed to use change ownership) + if (!m_DistributedAuthority) + { + m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject); + } var serverObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[ownershipNetworkObjectId]; var clientObject = m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects[ownershipNetworkObjectId]; @@ -237,7 +241,12 @@ bool WaitForClientsToSpawnNetworkObject() Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for all clients to change ownership!"); // Verifies that removing the ownership when the default (server) is already set does not cause a Key Not Found Exception - m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject); + // Distributed authority does not allow remove ownership (users are instructed to use change ownership) + if (!m_DistributedAuthority) + { + m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject); + } + var serverObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[ownershipNetworkObjectId]; Assert.That(serverObject, Is.Not.Null); var clientObject = (NetworkObject)null; From c0e65f46cfe65a90b805351d596152dd273471a6 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Sun, 15 Dec 2024 18:23:50 -0600 Subject: [PATCH 09/11] fix NetworkAnimator started throwing this exception: "Objects are trying to be loaded during a domain backup. This is not allowed as it will lead to undefined behaviour!" When MPPM was being used. This has to do with the OnBeforeSerialize method and potentially an order of operations issue. We can accomplish the same serialization within OnValidate, but went ahead and made it virtual in order to provide users with the ability to override and handle their own validation. --- .../Runtime/Components/NetworkAnimator.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index f6f40ea2b7..583e7bda4a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -298,14 +298,11 @@ private void ParseStateMachineStates(int layerIndex, ref AnimatorController anim } } -#endif - /// /// Creates the TransitionStateInfoList table /// private void BuildTransitionStateInfoList() { -#if UNITY_EDITOR if (m_Animator == null) { return; @@ -323,9 +320,18 @@ private void BuildTransitionStateInfoList() var stateMachine = animatorController.layers[x].stateMachine; ParseStateMachineStates(x, ref animatorController, ref stateMachine); } -#endif } + /// + /// In-Editor Only + /// Virtual OnValidate method for custom derived NetworkAnimator classes. + /// + protected virtual void OnValidate() + { + BuildTransitionStateInfoList(); + } +#endif + public void OnAfterDeserialize() { BuildDestinationToTransitionInfoTable(); @@ -333,7 +339,7 @@ public void OnAfterDeserialize() public void OnBeforeSerialize() { - BuildTransitionStateInfoList(); + // Do nothing when serializing (handled during OnValidate) } internal struct AnimationState : INetworkSerializable From ba490651a8c6cf2f98c2bbacef8b24a1170860b3 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Mon, 16 Dec 2024 00:38:19 -0600 Subject: [PATCH 10/11] fix: server, host, or session owner not populating in-scene placed object table when started (#3177) * fix This fixes the issue with the server, host, or session owner not adding in-scene placed NetworkObjects to its internal table if the scene was loaded prior to starting the NetworkManager. * update Adding change log entry * update Adding PR# to this entry. * style remove whitespace --- com.unity.netcode.gameobjects/CHANGELOG.md | 1 + .../Runtime/Spawning/NetworkSpawnManager.cs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index ff93d501ee..e6930d1077 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -17,6 +17,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Fixed issue where the server, host, or session owner would not populate the in-scene place `NetworkObject` table if the scene was loaded prior to starting the `NetworkManager`. (#3177) - Fixed issue where the `NetworkObjectIdHash` value could be incorrect when entering play mode while still in prefab edit mode with pending changes and using MPPM. (#3162) - Fixed issue where a sever only `NetworkManager` instance would spawn the actual `NetworkPrefab`'s `GameObject` as opposed to creating an instance of it. (#3160) - Fixed issue where only the session owner (as opposed to all clients) would handle spawning prefab overrides properly when using a distributed authority network topology. (#3160) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index ed0b0348b7..854bc8fba2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1449,6 +1449,15 @@ internal void ServerSpawnSceneObjectsOnStartSweep() } } + // Since we are spawing in-scene placed NetworkObjects for already loaded scenes, + // we need to add any in-scene placed NetworkObject to our tracking table + var clearFirst = true; + foreach (var sceneLoaded in NetworkManager.SceneManager.ScenesLoaded) + { + NetworkManager.SceneManager.PopulateScenePlacedObjects(sceneLoaded.Value, clearFirst); + clearFirst = false; + } + // Notify all in-scene placed NetworkObjects have been spawned foreach (var networkObject in networkObjectsToSpawn) { From a6103e369a51a859fea128612eb35c30387e2c5a Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Mon, 16 Dec 2024 10:30:17 -0600 Subject: [PATCH 11/11] update adding unreleased change log --- com.unity.netcode.gameobjects/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index e6930d1077..423096ac35 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). +## [Unreleased] + +### Added + +### Fixed + +### Changed + ## [2.2.0] - 2024-12-12