Skip to content

fix: selecting everything selects session owner only #3305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand Down
35 changes: 33 additions & 2 deletions com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
29 changes: 29 additions & 0 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -493,16 +502,36 @@ public void DeferDespawn(int tickOffset, bool destroy = true)
/// <see cref="Transferable"/>: When set, a non-owner can obtain ownership immediately (without requesting and as long as it is not locked).
/// <see cref="RequestRequired"/>: When set, a non-owner must request ownership from the owner (will always get locked once ownership is transferred).
/// <see cref="SessionOwner"/>: When set, only the current session owner may have ownership over this object.
/// <see cref="All"/>: 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.
/// </summary>
// Ranges from 1 to 8 bits
[Flags]
public enum OwnershipStatus
{
/// <summary>
/// When set, this instance will have no permissions (i.e. cannot distribute, transfer, etc).
/// </summary>
None = 0,
/// <summary>
/// When set, this instance will be automatically redistributed when a client joins (if not locked or no request is pending) or leaves.
/// </summary>
Distributable = 1 << 0,
/// <summary>
/// When set, a non-owner can obtain ownership immediately (without requesting and as long as it is not locked).
/// </summary>
Transferable = 1 << 1,
/// <summary>
/// When set, a non-owner must request ownership from the owner (will always get locked once ownership is transferred).
/// </summary>
RequestRequired = 1 << 2,
/// <summary>
/// When set, only the current session owner may have ownership over this object.
/// </summary>
SessionOwner = 1 << 3,
/// <summary>
/// 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.
/// </summary>
All = ~0,
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down