Skip to content
Open
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 @@ -22,6 +22,8 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

- Fixed issue where spawning a player in distributed authority mode via a client, typically session owner, other than the newly connected client and scene management is disabled then the already spawned players will not properly get synchronized by each owning client due to the newly connected client's identifier already being added prior to synchronization. (#3816)


### Security

Expand Down
21 changes: 15 additions & 6 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,15 @@ public void SetSceneObjectStatus(bool isSceneObject = false)

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AddObserver(ulong clientId)
{
#if NETCODE_DEBUG_OBSERVERS
Debug.Log($"[{nameof(NetworkObject)}][{name}-{NetworkObjectId}] Adding Client-{clientId} as an observer.");
#endif
Observers.Add(clientId);
}

#if MULTIPLAYER_TOOLS
private string m_CachedNameForMetrics;
#endif
Expand Down Expand Up @@ -1469,7 +1478,7 @@ public void NetworkShow(ulong clientId)
return;
}
NetworkManager.SpawnManager.MarkObjectForShowingTo(this, clientId);
Observers.Add(clientId);
AddObserver(clientId);
}


Expand Down Expand Up @@ -3340,7 +3349,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
{
foreach (var observer in sceneObject.Observers)
{
networkObject.Observers.Add(observer);
networkObject.AddObserver(observer);
}
}

Expand All @@ -3360,11 +3369,11 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
if (networkObject.IsPlayerObject)
{
// If it is another player, then make sure the local player is aware of the player
playerObject.Observers.Add(networkObject.OwnerClientId);
playerObject.AddObserver(networkObject.OwnerClientId);
}

// Assure the local player has observability
networkObject.Observers.Add(playerObject.OwnerClientId);
networkObject.AddObserver(playerObject.OwnerClientId);

// If it is a player object, then add it to all known spawned NetworkObjects that spawn with observers
if (networkObject.IsPlayerObject)
Expand All @@ -3373,7 +3382,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
{
if (netObject.Value.SpawnWithObservers)
{
netObject.Value.Observers.Add(networkObject.OwnerClientId);
netObject.Value.AddObserver(networkObject.OwnerClientId);
}
}
}
Expand All @@ -3385,7 +3394,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
// Add all known players to the observers list if they don't already exist
foreach (var player in networkManager.SpawnManager.PlayerObjects)
{
networkObject.Observers.Add(player.OwnerClientId);
networkObject.AddObserver(player.OwnerClientId);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public void Serialize(FastBufferWriter writer, int targetVersion)
{
if (sobj.SpawnWithObservers && (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(OwnerClientId)))
{
sobj.Observers.Add(OwnerClientId);
sobj.AddObserver(OwnerClientId);
// In distributed authority mode, we send the currently known observers of each NetworkObject to the client being synchronized.
var sceneObject = sobj.GetMessageSceneObject(OwnerClientId, IsDistributedAuthority);
sceneObject.Serialize(writer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ internal static void CreateObject(ref NetworkManager networkManager, ulong sende
// Update the observers for this instance
for (int i = 0; i < clientList.Count; i++)
{
networkObject.Observers.Add(clientList[i]);
networkObject.AddObserver(clientList[i]);
}

// Mock CMB Service and forward to all clients
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,33 @@ private void AddPlayerObject(NetworkObject playerObject)
}
}

var cmbService = NetworkManager.CMBServiceConnection;
var sceneManagement = NetworkManager.NetworkConfig.EnableSceneManagement;

foreach (var player in m_PlayerObjects)
{
// When connected to the CMB service, scene management is disabled, and it is the local player, we do
// not want to add the newly connected player to the observers list of the local player as that will be
// done when the local client shows the NetworkObject to the newly connected client.
var shouldAddObserver = cmbService && !sceneManagement ? !player.IsLocalPlayer : true;
// If the player's SpawnWithObservers is not set then do not add the new player object's owner as an observer.
if (player.SpawnWithObservers)
if (player.SpawnWithObservers && shouldAddObserver)
{
player.Observers.Add(playerObject.OwnerClientId);
player.AddObserver(playerObject.OwnerClientId);
}

// If the new player object's SpawnWithObservers is not set then do not add this player as an observer to the new player object.
if (playerObject.SpawnWithObservers)
{
playerObject.Observers.Add(player.OwnerClientId);
playerObject.AddObserver(player.OwnerClientId);
}
}

// Only if spawn with observers is set or we are using a distributed authority network topology and this is the client's player should we add
// the owner as an observer.
if (playerObject.SpawnWithObservers || (NetworkManager.DistributedAuthorityMode && NetworkManager.LocalClientId == playerObject.OwnerClientId))
{
playerObject.Observers.Add(playerObject.OwnerClientId);
playerObject.AddObserver(playerObject.OwnerClientId);
}

m_PlayerObjects.Add(playerObject);
Expand Down Expand Up @@ -1066,7 +1073,7 @@ internal void AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n
// (authority should not take into consideration networkObject.CheckObjectVisibility when SpawnWithObservers is false)
if (!networkObject.SpawnWithObservers)
{
networkObject.Observers.Add(ownerClientId);
networkObject.AddObserver(ownerClientId);
}
else
{
Expand All @@ -1077,15 +1084,15 @@ internal void AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n
{
continue;
}
networkObject.Observers.Add(clientId);
networkObject.AddObserver(clientId);
}

// Sanity check to make sure the owner is always included
// Itentionally checking as opposed to just assigning in order to generate notification.
if (!networkObject.Observers.Contains(ownerClientId))
{
Debug.LogError($"Client-{ownerClientId} is the owner of {networkObject.name} but is not an observer! Adding owner, but there is a bug in observer synchronization!");
networkObject.Observers.Add(ownerClientId);
networkObject.AddObserver(ownerClientId);
}
}
}
Expand Down Expand Up @@ -1174,7 +1181,7 @@ internal void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong
// If running as a server only, then make sure to always add the server's client identifier
if (!NetworkManager.IsHost)
{
networkObject.Observers.Add(NetworkManager.LocalClientId);
networkObject.AddObserver(NetworkManager.LocalClientId);
}

// Add client observers
Expand All @@ -1185,7 +1192,7 @@ internal void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong
{
continue;
}
networkObject.Observers.Add(NetworkManager.ConnectedClientsIds[i]);
networkObject.AddObserver(NetworkManager.ConnectedClientsIds[i]);
}
}

Expand Down Expand Up @@ -1738,15 +1745,15 @@ internal void UpdateObservedNetworkObjects(ulong clientId)
// If the client is not part of the observers and spawn with observers is enabled on this instance or the clientId is the server/host/DAHost
if (!sobj.Observers.Contains(clientId) && (sobj.SpawnWithObservers || clientId == NetworkManager.ServerClientId))
{
sobj.Observers.Add(clientId);
sobj.AddObserver(clientId);
}
}
else
{
// CheckObject visibility overrides SpawnWithObservers under this condition
if (sobj.CheckObjectVisibility(clientId))
{
sobj.Observers.Add(clientId);
sobj.AddObserver(clientId);
}
else // Otherwise, if the observers contains the clientId (shouldn't happen) then remove it since CheckObjectVisibility returned false
{
Expand Down
Loading