diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md
index 75f3dd780a..6922d1eeda 100644
--- a/com.unity.netcode.gameobjects/CHANGELOG.md
+++ b/com.unity.netcode.gameobjects/CHANGELOG.md
@@ -13,6 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Fixed
- Fixed `OnClientConnectedCallback` passing incorrect `clientId` when scene management is disabled. (#3312)
+- 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)
@@ -25,6 +26,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 `SessionOwner` permission is set. (#3305)
- Changed the `DestroyObject` message to reduce the serialized message size and remove the unnecessary message field. (#3304)
- Changed the `NetworkTimeSystem.Sync` method to use half RTT to calculate the desired local time offset as opposed to the full RTT. (#3212)
diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs
index 0860fd9c92..04e0d1f698 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;
@@ -114,10 +117,38 @@ 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 (m_NetworkObject.IsOwnershipSessionOwner)
+
+ // If the ownership flags were changed
+ var currentOwnership = (NetworkObject.OwnershipStatus)ownershipProperty.intValue;
+ if (currentOwnership != previousOwnership)
{
- m_NetworkObject.Ownership = NetworkObject.OwnershipStatus.SessionOwner;
+ // 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)
+ {
+ if (ownershipProperty.intValue == -1 && !hadAll)
+ {
+ ownershipProperty.intValue = (int)k_AllOwnershipFlags;
+ }
+ else if ((hadAll && !hadSessionOwner) || (!hadAll && !hadSessionOwner))
+ {
+ ownershipProperty.intValue = k_SessionOwnerFlagAsInt;
+ }
+ else if (hadSessionOwner && hasSessionOwner)
+ {
+ ownershipProperty.intValue &= ~k_SessionOwnerFlagAsInt;
+ }
+ }
}
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..a14bd79a2b 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,16 +502,36 @@ 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]
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.
+ ///
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,
}
///
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.