diff --git a/Assets/Mirror/Core/NetworkClient.cs b/Assets/Mirror/Core/NetworkClient.cs index 4a014b6bb..77b69dd17 100644 --- a/Assets/Mirror/Core/NetworkClient.cs +++ b/Assets/Mirror/Core/NetworkClient.cs @@ -1036,6 +1036,7 @@ internal static void ApplySpawnPayload(NetworkIdentity identity, SpawnMessage me } spawned[message.netId] = identity; + if (identity.isOwned) connection?.owned.Add(identity); // the initial spawn with OnObjectSpawnStarted/Finished calls all // object's OnStartClient/OnStartLocalPlayer after they were all @@ -1233,6 +1234,11 @@ static void ClearNullFromSpawned() static void OnHostClientObjectDestroy(ObjectDestroyMessage message) { //Debug.Log($"NetworkClient.OnLocalObjectObjDestroy netId:{message.netId}"); + + // remove from owned (if any) + if (spawned.TryGetValue(message.netId, out NetworkIdentity identity)) + connection.owned.Remove(identity); + spawned.Remove(message.netId); } @@ -1254,6 +1260,7 @@ internal static void OnHostClientSpawn(SpawnMessage message) if (NetworkServer.spawned.TryGetValue(message.netId, out NetworkIdentity localObject) && localObject != null) { spawned[message.netId] = localObject; + if (message.isOwner) connection.owned.Add(localObject); // now do the actual 'spawning' on host mode if (message.isLocalPlayer) @@ -1394,6 +1401,7 @@ static void DestroyObject(uint netId) } // remove from dictionary no matter how it is unspawned + connection.owned.Remove(localObject); // if any spawned.Remove(netId); } //else Debug.LogWarning($"Did not find target for destroy message for {netId}"); @@ -1497,6 +1505,7 @@ public static void DestroyAllClientObjects() } } spawned.Clear(); + connection?.owned.Clear(); } catch (InvalidOperationException e) { @@ -1521,6 +1530,7 @@ public static void Shutdown() DestroyAllClientObjects(); spawned.Clear(); + connection?.owned.Clear(); handlers.Clear(); spawnableObjects.Clear(); diff --git a/Assets/Mirror/Core/NetworkConnection.cs b/Assets/Mirror/Core/NetworkConnection.cs index 11204ff7d..1356a37ad 100644 --- a/Assets/Mirror/Core/NetworkConnection.cs +++ b/Assets/Mirror/Core/NetworkConnection.cs @@ -41,14 +41,17 @@ public abstract class NetworkConnection /// This connection's main object (usually the player object). public NetworkIdentity identity { get; internal set; } + // DEPRECATED 2022-02-05 + [Obsolete("Cast to NetworkConnectionToClient to access .owned")] + public HashSet clientOwnedObjects => owned; + /// All NetworkIdentities owned by this connection. Can be main player, pets, etc. + // .owned is now valid both on server and on client. // IMPORTANT: this needs to be , not . // fixes a bug where DestroyOwnedObjects wouldn't find the // netId anymore: https://github.com/vis2k/Mirror/issues/1380 // Works fine with NetworkIdentity pointers though. - // DEPRECATED 2022-02-05 - [Obsolete("Cast to NetworkConnectionToClient to access .owned")] - public HashSet clientOwnedObjects => ((NetworkConnectionToClient)this).owned; + public readonly HashSet owned = new HashSet(); // batching from server to client & client to server. // fewer transport calls give us significantly better performance/scale. diff --git a/Assets/Mirror/Core/NetworkConnectionToClient.cs b/Assets/Mirror/Core/NetworkConnectionToClient.cs index 6436be2f7..72887f586 100644 --- a/Assets/Mirror/Core/NetworkConnectionToClient.cs +++ b/Assets/Mirror/Core/NetworkConnectionToClient.cs @@ -13,13 +13,6 @@ public class NetworkConnectionToClient : NetworkConnection // TODO move to server's NetworkConnectionToClient? public new readonly HashSet observing = new HashSet(); - /// All NetworkIdentities owned by this connection. Can be main player, pets, etc. - // IMPORTANT: this needs to be , not . - // fixes a bug where DestroyOwnedObjects wouldn't find the - // netId anymore: https://github.com/vis2k/Mirror/issues/1380 - // Works fine with NetworkIdentity pointers though. - public readonly HashSet owned = new HashSet(); - [Obsolete(".clientOwnedObjects was renamed to .owned :)")] // 2022-10-13 public new HashSet clientOwnedObjects => owned; diff --git a/Assets/Mirror/Core/NetworkServer.cs b/Assets/Mirror/Core/NetworkServer.cs index 8a3fc3c05..ada04d43c 100644 --- a/Assets/Mirror/Core/NetworkServer.cs +++ b/Assets/Mirror/Core/NetworkServer.cs @@ -1364,6 +1364,7 @@ static void DestroyObject(NetworkIdentity identity, DestroyMode mode) identity.NotifyAuthority(); // remove from NetworkClient dictionary + NetworkClient.connection.owned.Remove(identity); NetworkClient.spawned.Remove(identity.netId); } diff --git a/Assets/Mirror/Tests/Editor/NetworkClientTests.cs b/Assets/Mirror/Tests/Editor/NetworkClientTests.cs index 729d23256..756647fbf 100644 --- a/Assets/Mirror/Tests/Editor/NetworkClientTests.cs +++ b/Assets/Mirror/Tests/Editor/NetworkClientTests.cs @@ -97,6 +97,36 @@ public void DisconnectCallsOnClientDisconnected_HostMode() Assert.That(called, Is.True); } + [Test] + public void OwnedObjects() + { + // create a scene object and set inactive before spawning + // CreateNetworked(out GameObject go, out NetworkIdentity identity); + + // listen & connect + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // spawn main player. should be added to .owned. + CreateNetworkedAndSpawnPlayer(out _, out NetworkIdentity player, NetworkServer.localConnection); + Assert.That(NetworkClient.connection.owned.Count, Is.EqualTo(1)); + Assert.That(NetworkClient.connection.owned.Contains(NetworkClient.localPlayer)); + + // spawn an object which is not owned. shouldn't add anything. + CreateNetworkedAndSpawn(out _, out NetworkIdentity other); + Assert.That(NetworkClient.connection.owned.Count, Is.EqualTo(1)); + + // spawn an owned object. should add to client's .owned. + CreateNetworkedAndSpawn(out _, out NetworkIdentity pet, NetworkServer.localConnection); + Assert.That(NetworkClient.connection.owned.Count, Is.EqualTo(2)); + + // despawn should remove from .owned + NetworkServer.Destroy(pet.gameObject); + ProcessMessages(); + Assert.That(NetworkClient.connection.owned.Count, Is.EqualTo(1)); + Assert.That(NetworkClient.connection.owned.Contains(NetworkClient.localPlayer)); + } + [Test] public void ShutdownCleanup() {