diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md
index 466cc59122..4c160e1771 100644
--- a/com.unity.netcode.gameobjects/CHANGELOG.md
+++ b/com.unity.netcode.gameobjects/CHANGELOG.md
@@ -12,6 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Fixed
+- Fixed issue where `NetworkClient.OwnedObjects` was not returning any owned objects due to the `NetworkClient.IsConnected` not being properly set. (#2631)
- Fixed a crash when calling TrySetParent with a null Transform (#2625)
- Fixed issue where a `NetworkTransform` using full precision state updates was losing transform state updates when interpolation was enabled. (#2624)
- Fixed issue where `NetworkObject.SpawnWithObservers` was not being honored for late joining clients. (#2623)
diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs
index 8a734785f6..370f651a8b 100644
--- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs
@@ -36,15 +36,11 @@ public class NetworkClient
///
/// The ClientId of the NetworkClient
///
- // TODO-2023-Q2: Determine if we want to make this property a public get and internal/private set
- // There is no reason for a user to want to set this, but this will fail the package-validation-suite
public ulong ClientId;
///
/// The PlayerObject of the Client
///
- // TODO-2023-Q2: Determine if we want to make this property a public get and internal/private set
- // There is no reason for a user to want to set this, but this will fail the package-validation-suite
public NetworkObject PlayerObject;
///
diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs
index 6d9275e7b6..ccbe7756bf 100644
--- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs
@@ -17,7 +17,6 @@ namespace Unity.Netcode
/// - Processing s.
/// - Client Disconnection
///
- // TODO 2023-Q2: Discuss what kind of public API exposure we want for this
public sealed class NetworkConnectionManager
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
@@ -656,6 +655,7 @@ internal void HandleConnectionApproval(ulong ownerClientId, NetworkManager.Conne
// If scene management is disabled, then we are done and notify the local host-server the client is connected
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
{
+ NetworkManager.ConnectedClients[ownerClientId].IsConnected = true;
InvokeOnClientConnectedCallback(ownerClientId);
}
else // Otherwise, let NetworkSceneManager handle the initial scene and NetworkObject synchronization
@@ -667,6 +667,7 @@ internal void HandleConnectionApproval(ulong ownerClientId, NetworkManager.Conne
{
LocalClient = client;
NetworkManager.SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
+ LocalClient.IsConnected = true;
}
if (!response.CreatePlayerObject || (response.PlayerPrefabHash == null && NetworkManager.NetworkConfig.PlayerPrefab == null))
@@ -732,12 +733,10 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash)
internal NetworkClient AddClient(ulong clientId)
{
var networkClient = LocalClient;
- if (clientId != NetworkManager.ServerClientId)
- {
- networkClient = new NetworkClient();
- networkClient.SetRole(isServer: false, isClient: true, NetworkManager);
- networkClient.ClientId = clientId;
- }
+
+ networkClient = new NetworkClient();
+ networkClient.SetRole(clientId == NetworkManager.ServerClientId, isClient: true, NetworkManager);
+ networkClient.ClientId = clientId;
ConnectedClients.Add(clientId, networkClient);
ConnectedClientsList.Add(networkClient);
@@ -800,8 +799,7 @@ internal void OnClientDisconnectFromServer(ulong clientId)
}
else
{
- // Handle changing ownership and prefab handlers
- // TODO-2023: Look into whether in-scene placed NetworkObjects could be destroyed if ownership changes to a client
+ // Handle changing ownership and prefab handlers
for (int i = clientOwnedObjects.Count - 1; i >= 0; i--)
{
var ownedObject = clientOwnedObjects[i];
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs
index 0afe462fb0..e6bcfc53c9 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs
@@ -118,7 +118,7 @@ internal void Shutdown()
m_NetworkManager.NetworkTickSystem.Tick -= NetworkBehaviourUpdater_Tick;
}
- // TODO 2023-Q2: Order of operations requires NetworkVariable updates first then showing NetworkObjects
+ // Order of operations requires NetworkVariable updates first then showing NetworkObjects
private void NetworkBehaviourUpdater_Tick()
{
// First update NetworkVariables
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
index a901fec70e..fb9e040aa9 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
@@ -59,13 +59,12 @@ public void NetworkUpdate(NetworkUpdateStage updateStage)
// Metrics update needs to be driven by NetworkConnectionManager's update to assure metrics are dispatched after the send queue is processed.
MetricsManager.UpdateMetrics();
- // TODO 2023-Q2: Determine a better way to handle this
+ // TODO: Determine a better way to handle this
NetworkObject.VerifyParentingStatus();
// 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();
- // TODO 2023-Q2: Determine a better way to handle this
if (m_ShuttingDown)
{
ShutdownInternal();
@@ -834,9 +833,7 @@ public bool StartHost()
}
ConnectionManager.LocalClient.SetRole(true, true, this);
-
Initialize(true);
-
try
{
IsListening = NetworkConfig.NetworkTransport.StartServer();
diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs
index 91fd876187..f7a2f1303a 100644
--- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs
@@ -2191,6 +2191,10 @@ private void HandleServerSceneEvent(uint sceneEventId, ulong clientId)
ClientId = clientId
});
+ // At this point the client is considered fully "connected"
+ NetworkManager.ConnectedClients[clientId].IsConnected = true;
+
+ // All scenes are synchronized, let the server know we are done synchronizing
OnSynchronizeComplete?.Invoke(clientId);
// At this time the client is fully synchronized with all loaded scenes and
diff --git a/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs b/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs
index f31746bd21..16a3c4eade 100644
--- a/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs
@@ -9,9 +9,6 @@ namespace Unity.Netcode
///
public class NetworkTimeSystem
{
- ///
- /// TODO 2023-Q2: Not sure if this just needs to go away, but there is nothing that ever replaces this
- ///
///
/// This was the original comment when it lived in NetworkManager:
/// todo talk with UX/Product, find good default value for this
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOwnershipTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOwnershipTests.cs
index 319415d782..5fa49158e7 100644
--- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOwnershipTests.cs
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOwnershipTests.cs
@@ -294,5 +294,69 @@ bool WaitForClientsToSpawnNetworkObject()
}
serverComponent.ResetFlags();
}
+
+ private const int k_NumberOfSpawnedObjects = 5;
+
+ private bool AllClientsHaveCorrectObjectCount()
+ {
+
+ foreach (var clientNetworkManager in m_ClientNetworkManagers)
+ {
+ if (clientNetworkManager.LocalClient.OwnedObjects.Count < k_NumberOfSpawnedObjects)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private bool ServerHasCorrectClientOwnedObjectCount()
+ {
+ // Only check when we are the host
+ if (m_ServerNetworkManager.IsHost)
+ {
+ if (m_ServerNetworkManager.LocalClient.OwnedObjects.Count < k_NumberOfSpawnedObjects)
+ {
+ return false;
+ }
+ }
+
+ foreach (var connectedClient in m_ServerNetworkManager.ConnectedClients)
+ {
+ if (connectedClient.Value.OwnedObjects.Count < k_NumberOfSpawnedObjects)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ [UnityTest]
+ public IEnumerator TestOwnedObjectCounts()
+ {
+ if (m_ServerNetworkManager.IsHost)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ SpawnObject(m_OwnershipPrefab, m_ServerNetworkManager);
+ }
+ }
+
+ foreach (var clientNetworkManager in m_ClientNetworkManagers)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ SpawnObject(m_OwnershipPrefab, clientNetworkManager);
+ }
+ }
+
+ yield return WaitForConditionOrTimeOut(AllClientsHaveCorrectObjectCount);
+ AssertOnTimeout($"Not all clients spawned {k_NumberOfSpawnedObjects} {nameof(NetworkObject)}s!");
+
+ yield return WaitForConditionOrTimeOut(ServerHasCorrectClientOwnedObjectCount);
+ AssertOnTimeout($"Server does not have the correct count for all clients spawned {k_NumberOfSpawnedObjects} {nameof(NetworkObject)}s!");
+
+ }
}
}