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()
{