From e73ddb42a99cf4550abffe47606d92119c9f15fc Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Tue, 18 Feb 2025 15:15:14 -0600 Subject: [PATCH 1/7] fix This fixes the issue with setting flags on NetworkObjects and the default Everything/All flag causing session owner to be grouped with the rest of the flags. Now all will initially select all flags but the session owner but if the all flag is selected again it toggles to just the SessionOwner flag. This also assures that all root in-scene placed NetworkObjects have the Distributable or SessionOwner flag set (if neither then it defaults to Distributable). --- .../Editor/NetworkObjectEditor.cs | 28 +++++++++++++++++-- .../Runtime/Core/NetworkObject.cs | 11 ++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs index 0860fd9c92..14b29be402 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs @@ -114,10 +114,34 @@ public override void OnInspectorGUI() { EditorGUI.BeginChangeCheck(); serializedObject.UpdateIfRequiredOrScript(); + var ownershipProperty = serializedObject.FindProperty(nameof(NetworkObject.Ownership)); + var sceneObjectProperty = serializedObject.FindProperty(nameof(NetworkObject.IsSceneObject)); + var previousOwnership = (NetworkObject.OwnershipStatus)ownershipProperty.intValue; + var allFilter = NetworkObject.OwnershipStatus.RequestRequired | NetworkObject.OwnershipStatus.Transferable | NetworkObject.OwnershipStatus.Distributable; + var hadAll = previousOwnership == allFilter; + var wasNone = previousOwnership == 0; + var hadSessionOwner = ownershipProperty.intValue == (int)NetworkObject.OwnershipStatus.SessionOwner; DrawPropertiesExcluding(serializedObject, k_HiddenFields); - if (m_NetworkObject.IsOwnershipSessionOwner) + + var currentOwnership = (NetworkObject.OwnershipStatus)ownershipProperty.intValue; + if (currentOwnership != previousOwnership) { - m_NetworkObject.Ownership = NetworkObject.OwnershipStatus.SessionOwner; + var hasSessionOwner = currentOwnership.HasFlag(NetworkObject.OwnershipStatus.SessionOwner); + if (hasSessionOwner) + { + if (ownershipProperty.intValue == -1 && !hadAll) + { + ownershipProperty.intValue = (int)allFilter; + } + else if ((hadAll && !hadSessionOwner) || (!hadAll && !hadSessionOwner)) + { + ownershipProperty.intValue = (int)NetworkObject.OwnershipStatus.SessionOwner; + } + else if (hadSessionOwner && hasSessionOwner) + { + ownershipProperty.intValue &= (int)~NetworkObject.OwnershipStatus.SessionOwner; + } + } } serializedObject.ApplyModifiedProperties(); EditorGUI.EndChangeCheck(); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 2b0900a166..7300580e5d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -322,6 +322,15 @@ private void CheckForInScenePlaced() // Default scene migration synchronization to false for in-scene placed NetworkObjects SceneMigrationSynchronization = false; + + // Root In-scene placed NetworkObjects have to either have the SessionOwner or Distributable permission flag set. + if (transform.parent == null) + { + if (!Ownership.HasFlag(OwnershipStatus.SessionOwner) && !Ownership.HasFlag(OwnershipStatus.Distributable)) + { + Ownership |= OwnershipStatus.Distributable; + } + } } } #endif // UNITY_EDITOR @@ -493,6 +502,7 @@ public void DeferDespawn(int tickOffset, bool destroy = true) /// : When set, a non-owner can obtain ownership immediately (without requesting and as long as it is not locked). /// : 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. + /// : Used within the inspector view only. When selected it will set the Distributable, Transferable, and RequestRequired flags or if those flags are already set it will select the SessionOwner flag by itself. /// // Ranges from 1 to 8 bits [Flags] @@ -503,6 +513,7 @@ public enum OwnershipStatus Transferable = 1 << 1, RequestRequired = 1 << 2, SessionOwner = 1 << 3, + All = ~0, } /// From 6770fde6253a69546435b46ea3a3dc9ec364a1c7 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Tue, 18 Feb 2025 15:16:12 -0600 Subject: [PATCH 2/7] fix Fixing some logic that would only skip over children if it was logging a message. --- .../Runtime/Spawning/NetworkSpawnManager.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index aa277735c3..9f219e4c35 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1967,9 +1967,12 @@ internal void DistributeNetworkObjects(ulong clientId) { continue; } - if ((!child.IsOwnershipDistributable || !child.IsOwnershipTransferable) && NetworkManager.LogLevel == LogLevel.Developer) + if (!child.IsOwnershipDistributable || !child.IsOwnershipTransferable) { - NetworkLog.LogWarning($"Sibling {child.name} of root parent {ownerList.Value[i].name} is neither transferrable or distributable! Object distribution skipped and could lead to a potentially un-owned or owner-mismatched {nameof(NetworkObject)}!"); + if (NetworkManager.LogLevel == LogLevel.Developer) + { + NetworkLog.LogWarning($"Sibling {child.name} of root parent {ownerList.Value[i].name} is neither transferrable or distributable! Object distribution skipped and could lead to a potentially un-owned or owner-mismatched {nameof(NetworkObject)}!"); + } continue; } // Transfer ownership of all distributable =or= transferrable children with the same owner to the same client to preserve the sibling ownership tree. From 1e4f9912d1d850b4cbd3550dfb0ce1acac830b1c Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Tue, 18 Feb 2025 15:41:14 -0600 Subject: [PATCH 3/7] update adding changelog entries. --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index d2572fcc89..8f9ba1eb70 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -12,6 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Fixed issue where the `NetworkObject.Ownership` custom editor did not take the default "Everything" flag into consideration. (#3305) - Fixed DestroyObject flow on non-authority game clients. (#3291) - Fixed exception being thrown when a `GameObject` with an associated `NetworkTransform` is disabled. (#3243) - Fixed issue where the scene migration synchronization table was not cleaned up if the `GameObject` of a `NetworkObject` is destroyed before it should have been. (#3230) @@ -24,6 +25,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed +- Changed root in-scene placed `NetworkObject` instances now will always have either the `Distributable` permission set unless the `SessionOwer` permission is set. (#3305) - Changed the `NetworkTimeSystem.Sync` method to use half RTT to calculate the desired local time offset as opposed to the full RTT. (#3212) ## [2.2.0] - 2024-12-12 From 76edc256c22bd1b8b95f303b0ddb6979e0dba666 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Tue, 18 Feb 2025 15:50:22 -0600 Subject: [PATCH 4/7] style adding API comments --- .../Runtime/Core/NetworkObject.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 7300580e5d..4ec53b2fca 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -509,10 +509,25 @@ public void DeferDespawn(int tickOffset, bool destroy = true) public enum OwnershipStatus { None = 0, + /// + /// When set, this instance will be automatically redistributed when a client joins (if not locked or no request is pending) or leaves. + /// Distributable = 1 << 0, + /// + /// When set, a non-owner can obtain ownership immediately (without requesting and as long as it is not locked). + /// Transferable = 1 << 1, + /// + /// When set, a non-owner must request ownership from the owner (will always get locked once ownership is transferred). + /// RequestRequired = 1 << 2, + /// + /// When set, only the current session owner may have ownership over this object. + /// SessionOwner = 1 << 3, + /// + /// Used within the inspector view only. When selected it will set the Distributable, Transferable, and RequestRequired flags or if those flags are already set it will select the SessionOwner flag by itself. + /// All = ~0, } From 38b2792f6c9ab933f78124ff771ad6ac83de6f34 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Tue, 18 Feb 2025 15:52:19 -0600 Subject: [PATCH 5/7] style Adding one for "None". --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 4ec53b2fca..a14bd79a2b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -508,6 +508,9 @@ public void DeferDespawn(int tickOffset, bool destroy = true) [Flags] public enum OwnershipStatus { + /// + /// When set, this instance will have no permissions (i.e. cannot distribute, transfer, etc). + /// None = 0, /// /// When set, this instance will be automatically redistributed when a client joins (if not locked or no request is pending) or leaves. From 5ffd0afc456b0d1f105b94cf3d008457a048e36d Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 20 Feb 2025 17:03:56 -0600 Subject: [PATCH 6/7] update Cleaning up some of the script. --- .../Editor/NetworkObjectEditor.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs index 14b29be402..80399f1fb3 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs @@ -14,6 +14,9 @@ namespace Unity.Netcode.Editor [CanEditMultipleObjects] public class NetworkObjectEditor : UnityEditor.Editor { + private const NetworkObject.OwnershipStatus k_AllOwnershipFlags = NetworkObject.OwnershipStatus.RequestRequired | NetworkObject.OwnershipStatus.Transferable | NetworkObject.OwnershipStatus.Distributable; + private const int k_SessionOwnerFlagAsInt = (int)NetworkObject.OwnershipStatus.SessionOwner; + private bool m_Initialized; private NetworkObject m_NetworkObject; private bool m_ShowObservers; @@ -115,12 +118,9 @@ public override void OnInspectorGUI() EditorGUI.BeginChangeCheck(); serializedObject.UpdateIfRequiredOrScript(); var ownershipProperty = serializedObject.FindProperty(nameof(NetworkObject.Ownership)); - var sceneObjectProperty = serializedObject.FindProperty(nameof(NetworkObject.IsSceneObject)); var previousOwnership = (NetworkObject.OwnershipStatus)ownershipProperty.intValue; - var allFilter = NetworkObject.OwnershipStatus.RequestRequired | NetworkObject.OwnershipStatus.Transferable | NetworkObject.OwnershipStatus.Distributable; - var hadAll = previousOwnership == allFilter; - var wasNone = previousOwnership == 0; - var hadSessionOwner = ownershipProperty.intValue == (int)NetworkObject.OwnershipStatus.SessionOwner; + var hadAll = previousOwnership == k_AllOwnershipFlags; + var hadSessionOwner = ownershipProperty.intValue == k_SessionOwnerFlagAsInt; DrawPropertiesExcluding(serializedObject, k_HiddenFields); var currentOwnership = (NetworkObject.OwnershipStatus)ownershipProperty.intValue; @@ -131,15 +131,15 @@ public override void OnInspectorGUI() { if (ownershipProperty.intValue == -1 && !hadAll) { - ownershipProperty.intValue = (int)allFilter; + ownershipProperty.intValue = (int)k_AllOwnershipFlags; } else if ((hadAll && !hadSessionOwner) || (!hadAll && !hadSessionOwner)) { - ownershipProperty.intValue = (int)NetworkObject.OwnershipStatus.SessionOwner; + ownershipProperty.intValue = k_SessionOwnerFlagAsInt; } else if (hadSessionOwner && hasSessionOwner) { - ownershipProperty.intValue &= (int)~NetworkObject.OwnershipStatus.SessionOwner; + ownershipProperty.intValue &= ~k_SessionOwnerFlagAsInt; } } } From 00c27db4a5180d5228784a910b3d9369dd82c673 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Thu, 20 Feb 2025 17:08:30 -0600 Subject: [PATCH 7/7] style adding comments --- .../Editor/NetworkObjectEditor.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs index 80399f1fb3..04e0d1f698 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs @@ -117,15 +117,22 @@ public override void OnInspectorGUI() { EditorGUI.BeginChangeCheck(); serializedObject.UpdateIfRequiredOrScript(); + + // Get the current ownership property and precalculate values in order to handle + // the exclusion or inclusion of "all" or just the session owner flags. var ownershipProperty = serializedObject.FindProperty(nameof(NetworkObject.Ownership)); var previousOwnership = (NetworkObject.OwnershipStatus)ownershipProperty.intValue; var hadAll = previousOwnership == k_AllOwnershipFlags; var hadSessionOwner = ownershipProperty.intValue == k_SessionOwnerFlagAsInt; + DrawPropertiesExcluding(serializedObject, k_HiddenFields); + // If the ownership flags were changed var currentOwnership = (NetworkObject.OwnershipStatus)ownershipProperty.intValue; if (currentOwnership != previousOwnership) { + // Determine if we need to handle setting or removing the session owner flag specifically + // when a user selects the "All" enum flag value. var hasSessionOwner = currentOwnership.HasFlag(NetworkObject.OwnershipStatus.SessionOwner); if (hasSessionOwner) {