Skip to content

fix: owner authoritative parenting backport #3453

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
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
29 changes: 26 additions & 3 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,24 @@ private GlobalObjectId GetGlobalId()
/// </summary>
public bool AutoObjectParentSync = true;

/// <summary>
/// Determines if the owner will apply transform values sent by the parenting message.
/// </summary>
/// <remarks>
/// When enabled, the resultant parenting transform changes sent by the authority will be applied on all instances. <br />
/// When disabled, the resultant parenting transform changes sent by the authority will not be applied on the owner's instance. <br />
/// When disabled, all non-owner instances will still be synchronized by the authority's transform values when parented.
/// </remarks>
[Tooltip("When disabled (default enabled), the owner will not apply a server or host's transform properties when parenting changes. Primarily useful for client-server network topology configurations.")]
public bool SyncOwnerTransformWhenParented = true;

/// <summary>
/// Client-Server specific, when enabled an owner of a NetworkObject can parent locally as opposed to requiring the owner to notify the server it would like to be parented.
/// This behavior is always true when using a distributed authority network topology and does not require it to be set.
/// </summary>
[Tooltip("When enabled (default disabled), owner's can parent a NetworkObject locally without having to send an RPC to the server or host. Only pertinent when using client-server network topology configurations.")]
public bool AllowOwnerToParent;

internal readonly HashSet<ulong> Observers = new HashSet<ulong>();

#if MULTIPLAYER_TOOLS
Expand Down Expand Up @@ -1086,8 +1104,9 @@ public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true)
{
return false;
}

if (!NetworkManager.IsServer && !NetworkManager.ShutdownInProgress)
// If we don't have authority and we are not shutting down, then don't allow any parenting.
// If we are shutting down and don't have authority then allow it.
if (!(NetworkManager.IsServer || (AllowOwnerToParent && IsOwner)) && !NetworkManager.ShutdownInProgress)
{
return false;
}
Expand All @@ -1102,6 +1121,8 @@ public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true)
return false;
}



m_CachedWorldPositionStays = worldPositionStays;

if (parent == null)
Expand Down Expand Up @@ -1135,7 +1156,9 @@ private void OnTransformParentChanged()
return;
}

if (!NetworkManager.IsServer)
var hasAuthority = NetworkManager.IsServer || (AllowOwnerToParent && IsOwner);

if (!hasAuthority)
{
// Log exception if we are a client and not shutting down.
if (!NetworkManager.ShutdownInProgress)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,42 @@ public void Handle(ref NetworkContext context)
networkObject.SetNetworkParenting(LatestParent, WorldPositionStays);
networkObject.ApplyNetworkParenting(RemoveParent);

// We set all of the transform values after parenting as they are
// the values of the server-side post-parenting transform values
if (!WorldPositionStays)

// This check is primarily for client-server network topologies when the motion model is owner authoritative:
// When SyncOwnerTransformWhenParented is enabled, then always apply the transform values.
// When SyncOwnerTransformWhenParented is disabled, then only synchronize the transform on non-owner instances.
if (networkObject.SyncOwnerTransformWhenParented || (!networkObject.SyncOwnerTransformWhenParented && !networkObject.IsOwner))
{
networkObject.transform.localPosition = Position;
networkObject.transform.localRotation = Rotation;
// We set all of the transform values after parenting as they are
// the values of the server-side post-parenting transform values
if (!WorldPositionStays)
{
networkObject.transform.localPosition = Position;
networkObject.transform.localRotation = Rotation;
}
else
{
networkObject.transform.position = Position;
networkObject.transform.rotation = Rotation;
}
}
else
networkObject.transform.localScale = Scale;

// If client side parenting is enabled and this is the server instance, then notify the rest of the connected clients that parenting has taken place.
if (networkObject.AllowOwnerToParent && context.SenderId == networkObject.OwnerClientId && networkManager.IsServer)
{
networkObject.transform.position = Position;
networkObject.transform.rotation = Rotation;
var size = 0;
var message = this;
foreach (var client in networkManager.ConnectedClients)
{
if (client.Value.ClientId == networkObject.OwnerClientId || client.Value.ClientId == networkManager.LocalClientId || !networkObject.IsNetworkVisibleTo(client.Value.ClientId))
{
continue;
}
size = networkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, client.Value.ClientId);
networkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
}
}
networkObject.transform.localScale = Scale;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,6 @@ protected override void OnNewClientCreated(NetworkManager networkManager)
base.OnNewClientCreated(networkManager);
}


/// <summary>
/// Returns true when the server-host and all clients have
/// instantiated the child object to be used in <see cref="NetworkTransformParentingLocalSpaceOffsetTests"/>
Expand Down Expand Up @@ -378,6 +377,26 @@ protected bool AllChildObjectInstancesAreSpawned()
return true;
}

/// <summary>
/// Conditional check that all child object instances also have a child
/// </summary>
/// <returns>true if they do and false if they do not</returns>
protected bool AllFirstLevelChildObjectInstancesHaveChild()
{
foreach (var instance in ChildObjectComponent.ClientInstances.Values)
{
if (instance.transform.parent == null)
{
return false;
}
}
return true;
}

/// <summary>
/// Conditional check that all child instances have a child.
/// </summary>
/// <returns>true if they do and false if they do not</returns>
protected bool AllChildObjectInstancesHaveChild()
{
foreach (var instance in ChildObjectComponent.ClientInstances.Values)
Expand All @@ -400,6 +419,41 @@ protected bool AllChildObjectInstancesHaveChild()
return true;
}

/// <summary>
/// Conditional check that all first level child objects have no parent.
/// </summary>
/// <returns>true if they do and false if they do not</returns>
protected bool AllFirstLevelChildObjectInstancesHaveNoParent()
{
foreach (var instance in ChildObjectComponent.ClientInstances.Values)
{
if (instance.transform.parent != null)
{
return false;
}
}
return true;
}

/// <summary>
/// Conditional check that all sub-child objects have no parent.
/// </summary>
/// <returns>true if they do and false if they do not</returns>
protected bool AllSubChildObjectInstancesHaveNoParent()
{
if (ChildObjectComponent.HasSubChild)
{
foreach (var instance in ChildObjectComponent.ClientSubChildInstances.Values)
{
if (instance.transform.parent != null)
{
return false;
}
}
}
return true;
}

/// <summary>
/// A wait condition specific method that assures the local space coordinates
/// are not impacted by NetworkTransform when parented.
Expand Down
Loading