diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index e5fd6013e0..b187b742c8 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -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 diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 311a3df650..6c60540760 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1320,6 +1320,15 @@ public void SetSceneObjectStatus(bool isSceneObject = false) internal readonly HashSet Observers = new HashSet(); + [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 @@ -1469,7 +1478,7 @@ public void NetworkShow(ulong clientId) return; } NetworkManager.SpawnManager.MarkObjectForShowingTo(this, clientId); - Observers.Add(clientId); + AddObserver(clientId); } @@ -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); } } @@ -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) @@ -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); } } } @@ -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); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs index b54f3f7322..06289259de 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs @@ -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); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs index 4ced00b51c..8b4028a184 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs @@ -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 diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 14ea2bc41d..e12d0cd63b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -81,18 +81,25 @@ 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); } } @@ -100,7 +107,7 @@ private void AddPlayerObject(NetworkObject playerObject) // 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); @@ -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 { @@ -1077,7 +1084,7 @@ 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 @@ -1085,7 +1092,7 @@ internal void AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n 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); } } } @@ -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 @@ -1185,7 +1192,7 @@ internal void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong { continue; } - networkObject.Observers.Add(NetworkManager.ConnectedClientsIds[i]); + networkObject.AddObserver(NetworkManager.ConnectedClientsIds[i]); } } @@ -1738,7 +1745,7 @@ 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 @@ -1746,7 +1753,7 @@ internal void UpdateObservedNetworkObjects(ulong clientId) // 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 {