Skip to content

Commit

Permalink
chore: object synchronization distributed authority mode when scene m…
Browse files Browse the repository at this point in the history
…anagement disabled (#3135)

* fix

This fixes most of the synchronization issues with using a distributed authority network topology and scene management is disabled.
(There still is some form of issue with changing ownership when distributing objects)

* update

A better way to handle object redistribution and NetworkObject synchronization when scene management is disabled,
  • Loading branch information
NoelStephensUnity authored Nov 23, 2024
1 parent 1845a33 commit 86ddd7d
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,14 @@ internal NetworkClient AddClient(ulong clientId)
}

var distributedAuthority = NetworkManager.DistributedAuthorityMode;

// If not using DA return early or if using DA and scene management is disabled then exit early Since we use NetworkShow to spawn
// objects on the newly connected client side.
if (!distributedAuthority || distributedAuthority && !NetworkManager.NetworkConfig.EnableSceneManagement)
{
return networkClient;
}

var sessionOwnerId = NetworkManager.CurrentSessionOwner;
var isSessionOwner = NetworkManager.LocalClient.IsSessionOwner;
foreach (var networkObject in NetworkManager.SpawnManager.SpawnedObjectsList)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ private void NetworkBehaviourUpdater_Tick()

// Then show any NetworkObjects queued to be made visible/shown
m_NetworkManager.SpawnManager.HandleNetworkObjectShow();

// Handle object redistribution (DA + disabled scene management only)
m_NetworkManager.HandleRedistributionToClients();
}
}
}
38 changes: 24 additions & 14 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,30 @@ public bool DAHost
}
}

// DANGO-TODO-MVP: Remove these properties once the service handles object distribution
internal ulong ClientToRedistribute;
internal bool RedistributeToClient;
internal int TickToRedistribute;
// DANGO-TODO: Determine if this needs to be removed once the service handles object distribution
internal List<ulong> ClientsToRedistribute = new List<ulong>();
internal bool RedistributeToClients;

/// <summary>
/// Handles object redistribution when scene management is disabled.
/// <see cref="NetworkBehaviourUpdater.NetworkBehaviourUpdater_Tick"/>
/// DANGO-TODO: Determine if this needs to be removed once the service handles object distribution
/// </summary>
internal void HandleRedistributionToClients()
{
if (!DistributedAuthorityMode || !RedistributeToClients || NetworkConfig.EnableSceneManagement || ShutdownInProgress)
{
return;
}

foreach (var clientId in ClientsToRedistribute)
{
SpawnManager.DistributeNetworkObjects(clientId);
}
RedistributeToClients = false;
ClientsToRedistribute.Clear();
}


internal List<NetworkObject> DeferredDespawnObjects = new List<NetworkObject>();

Expand Down Expand Up @@ -393,16 +413,6 @@ public void NetworkUpdate(NetworkUpdateStage updateStage)
// This is "ok" to invoke when not processing messages since it is just cleaning up messages that never got handled within their timeout period.
DeferredMessageManager.CleanupStaleTriggers();

// DANGO-TODO-MVP: Remove this once the service handles object distribution
// NOTE: This needs to be the last thing done and should happen exactly at this point
// in the update
if (RedistributeToClient && ServerTime.Tick <= TickToRedistribute)
{
RedistributeToClient = false;
SpawnManager.DistributeNetworkObjects(ClientToRedistribute);
ClientToRedistribute = 0;
}

if (m_ShuttingDown)
{
// Host-server will disconnect any connected clients prior to finalizing its shutdown
Expand Down
10 changes: 5 additions & 5 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2894,7 +2894,7 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager
SyncObservers = syncObservers,
Observers = syncObservers ? Observers.ToArray() : null,
NetworkSceneHandle = NetworkSceneHandle,
Hash = HostCheckForGlobalObjectIdHashOverride(),
Hash = CheckForGlobalObjectIdHashOverride(),
OwnerObject = this,
TargetClientId = targetClientId
};
Expand Down Expand Up @@ -3246,14 +3246,14 @@ internal void UpdateForSceneChanges()
}

/// <summary>
/// Only applies to Host mode.
/// Only applies to Hosts or session owners (for now)
/// Will return the registered source NetworkPrefab's GlobalObjectIdHash if one exists.
/// Server and Clients will always return the NetworkObject's GlobalObjectIdHash.
/// </summary>
/// <returns></returns>
internal uint HostCheckForGlobalObjectIdHashOverride()
/// <returns>appropriate hash value</returns>
internal uint CheckForGlobalObjectIdHashOverride()
{
if (NetworkManager.IsServer)
if (NetworkManager.IsServer || (NetworkManager.DistributedAuthorityMode && NetworkManager.LocalClient.IsSessionOwner))
{
if (NetworkManager.PrefabHandler.ContainsHandler(this))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ private void HandleOwnershipChange(ref NetworkContext context)
// Sanity check that we are not sending duplicated change ownership messages
if (networkObject.OwnerClientId == OwnerClientId)
{
UnityEngine.Debug.LogError($"Unnecessary ownership changed message for {NetworkObjectId}.");
// Ignore the message
// Log error and then ignore the message
UnityEngine.Debug.LogError($"Client-{context.SenderId} ({RequestClientId}) sent unnecessary ownership changed message for {NetworkObjectId}.");
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,24 @@ public void Handle(ref NetworkContext context)
// Don't redistribute for the local instance
if (ClientId != networkManager.LocalClientId)
{
// Synchronize the client with spawned objects (relative to each client)
networkManager.SpawnManager.SynchronizeObjectsToNewlyJoinedClient(ClientId);

// Keeping for reference in case the above doesn't resolve for hidden objects (theoretically it should)
// Show any NetworkObjects that are:
// - Hidden from the session owner
// - Owned by this client
// - Has NetworkObject.SpawnWithObservers set to true (the default)
if (!networkManager.LocalClient.IsSessionOwner)
{
networkManager.SpawnManager.ShowHiddenObjectsToNewlyJoinedClient(ClientId);
}
//if (!networkManager.LocalClient.IsSessionOwner)
//{
// networkManager.SpawnManager.ShowHiddenObjectsToNewlyJoinedClient(ClientId);
//}

// We defer redistribution to the end of the NetworkUpdateStage.PostLateUpdate
networkManager.RedistributeToClient = true;
networkManager.ClientToRedistribute = ClientId;
networkManager.TickToRedistribute = networkManager.ServerTime.Tick + 20;
/// We defer redistribution to happen after NetworkShow has been invoked
/// <see cref="NetworkBehaviourUpdater.NetworkBehaviourUpdater_Tick"/>
/// DANGO-TODO: Determine if this needs to be removed once the service handles object distribution
networkManager.RedistributeToClients = true;
networkManager.ClientsToRedistribute.Add(ClientId);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,6 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool
{
networkObject.ChildNetworkBehaviours[i].UpdateNetworkProperties();
}

size = NetworkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, NetworkManager.ServerClientId);
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(NetworkManager.LocalClientId, networkObject, size);
}
Expand Down Expand Up @@ -1982,14 +1981,14 @@ internal void NotifyNetworkObjectsSynchronized()
/// synchronizing in order to "show" (spawn) anything that might be currently hidden from
/// the session owner.
/// </summary>
/// <remarks>
/// Replacement is: SynchronizeObjectsToNewlyJoinedClient
/// </remarks>
internal void ShowHiddenObjectsToNewlyJoinedClient(ulong newClientId)
{
if (!NetworkManager.DistributedAuthorityMode)
if (NetworkManager == null || NetworkManager.ShutdownInProgress && NetworkManager.LogLevel <= LogLevel.Developer)
{
if (NetworkManager == null || !NetworkManager.ShutdownInProgress && NetworkManager.LogLevel <= LogLevel.Developer)
{
Debug.LogWarning($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} invoked while !");
}
Debug.LogWarning($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} invoked while shutdown is in progress!");
return;
}

Expand Down Expand Up @@ -2025,5 +2024,46 @@ internal void ShowHiddenObjectsToNewlyJoinedClient(ulong newClientId)
}
}
}

internal void SynchronizeObjectsToNewlyJoinedClient(ulong newClientId)
{
if (NetworkManager == null || NetworkManager.ShutdownInProgress && NetworkManager.LogLevel <= LogLevel.Developer)
{
Debug.LogWarning($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} invoked while shutdown is in progress!");
return;
}

if (!NetworkManager.DistributedAuthorityMode)
{
Debug.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!");
return;
}

if (NetworkManager.NetworkConfig.EnableSceneManagement)
{
Debug.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when scene management is disabled!");
return;
}

var localClientId = NetworkManager.LocalClient.ClientId;
foreach (var networkObject in SpawnedObjectsList)
{
if (networkObject.SpawnWithObservers && networkObject.OwnerClientId == localClientId)
{
if (networkObject.Observers.Contains(newClientId))
{
if (NetworkManager.LogLevel <= LogLevel.Developer)
{
// Temporary tracking to make sure we are not showing something already visibile (should never be the case for this)
Debug.LogWarning($"[{nameof(SynchronizeObjectsToNewlyJoinedClient)}][{networkObject.name}] New client as already an observer!");
}
// For now, remove the client (impossible for the new client to have an instance since the session owner doesn't) to make sure newly added
// code to handle this edge case works.
networkObject.Observers.Remove(newClientId);
}
networkObject.NetworkShow(newClientId);
}
}
}
}
}

0 comments on commit 86ddd7d

Please sign in to comment.