Skip to content

Commit ce8f5b8

Browse files
fix: player networkobject not added to client owned object list (#1801)
* fix This fixes the issue with a player not being added to the client's owned objects list. This also fixes the issue where dynamically spawned NetworkObjects were not getting added to the client's owned object list. This fix migrates the table into NetworkSpawnManager, removes the OwnedObjects list from NetworkClient (replaces it with a method), and provides automatic assignment and de-assignment of ownership to the NetworkSpawnManager's OwnershipToObjectsTable. * tests Updates some of the ownership integration tests to the changes. * Update and Style Log message and exception additions. White space removal. Extra-LF removal. * fix removed extra '}' char. * fix Fix for server ownership invocation. * style whitespace * fix Fixing minor issue with NetworkTransform having ownership applied before OnNetworkSpawn being invoked. * test This is the test for this PR * update `NetworkObjectOwnershipTests` * fix set previous owner whether removing or not. * minor stuff * fix Set the ownership before invoking lost or gained ownership methods. * style * style * fix and update fixing some order of operations issues. reverting back to OwnedObjects get. * style * update removed the internal OwnerClientId. * update Removing nullable client id as a parameter in spawn methods. * test Minor update to the ownership test where it double checks the update to NetworkClient when checking if a client's player is included in the owned objects list. * Update CHANGELOG.md * style Minor clean up. * fix Assure that clients that are neither the current or new owner of the NetworkObject update their properties to reflect the new owner. * test Added a test to verify that switching ownership between several clients works. * style updating small comment for clarity * style updated comments for clarity. * minor update Co-authored-by: Fatih Mar <mfatihmar@gmail.com>
1 parent 58626b3 commit ce8f5b8

File tree

11 files changed

+436
-277
lines changed

11 files changed

+436
-277
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
1313
### Changed
1414

1515
### Fixed
16+
17+
- Fixed PlayerObjects and dynamically spawned NetworkObjects not being added to the NetworkClient's OwnedObjects (#1801)
1618
- Fixed issue where NetworkManager would continue starting even if the NetworkTransport selected failed. (#1780)
1719
- Fixed issue when spawning new player if an already existing player exists it does not remove IsPlayer from the previous player (#1779)
1820
- Fixed lack of notification that NetworkManager and NetworkObject cannot be added to the same GameObject with in-editor notifications (#1777)

com.unity.netcode.gameobjects/Components/NetworkTransform.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ private void Initialize()
785785
{
786786
m_ReplicatedNetworkState.SetDirty(true);
787787
}
788-
else
788+
else if (m_Transform != null)
789789
{
790790
ApplyInterpolatedNetworkStateToTransform(m_ReplicatedNetworkState.Value, m_Transform);
791791
}

com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ public class NetworkClient
2020
/// <summary>
2121
/// The NetworkObject's owned by this Client
2222
/// </summary>
23-
public readonly List<NetworkObject> OwnedObjects = new List<NetworkObject>();
23+
public List<NetworkObject> OwnedObjects
24+
{
25+
get
26+
{
27+
if (PlayerObject != null && PlayerObject.NetworkManager != null && PlayerObject.NetworkManager.IsListening)
28+
{
29+
return PlayerObject.NetworkManager.SpawnManager.GetClientOwnedObjects(ClientId);
30+
}
31+
32+
return new List<NetworkObject>();
33+
}
34+
}
2435
}
2536
}

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,11 @@ public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelive
125125
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
126126
{
127127
if (m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) &&
128-
(client.ConnectionState == PendingClient.State.PendingApproval ||
129-
(client.ConnectionState == PendingClient.State.PendingConnection &&
130-
messageType != typeof(ConnectionRequestMessage))))
128+
(client.ConnectionState == PendingClient.State.PendingApproval || (client.ConnectionState == PendingClient.State.PendingConnection && messageType != typeof(ConnectionRequestMessage))))
131129
{
132130
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
133131
{
134-
NetworkLog.LogWarning($"Message received from {nameof(senderId)}={senderId.ToString()} before it has been accepted");
132+
NetworkLog.LogWarning($"Message received from {nameof(senderId)}={senderId} before it has been accepted");
135133
}
136134

137135
return false;
@@ -365,16 +363,14 @@ public IReadOnlyList<ulong> ConnectedClientsIds
365363
/// <param name="approved">Whether or not the client was approved</param>
366364
/// <param name="position">The position to spawn the client at. If null, the prefab position is used.</param>
367365
/// <param name="rotation">The rotation to spawn the client with. If null, the prefab position is used.</param>
368-
public delegate void ConnectionApprovedDelegate(bool createPlayerObject, uint? playerPrefabHash, bool approved,
369-
Vector3? position, Quaternion? rotation);
366+
public delegate void ConnectionApprovedDelegate(bool createPlayerObject, uint? playerPrefabHash, bool approved, Vector3? position, Quaternion? rotation);
370367

371368
/// <summary>
372369
/// The callback to invoke during connection approval
373370
/// </summary>
374371
public event Action<byte[], ulong, ConnectionApprovedDelegate> ConnectionApprovalCallback = null;
375372

376-
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action) =>
377-
ConnectionApprovalCallback?.Invoke(payload, clientId, action);
373+
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action) => ConnectionApprovalCallback?.Invoke(payload, clientId, action);
378374

379375
/// <summary>
380376
/// The current NetworkConfig
@@ -1612,32 +1608,45 @@ private void OnClientDisconnectFromServer(ulong clientId)
16121608
}
16131609
}
16141610

1615-
for (int i = networkClient.OwnedObjects.Count - 1; i >= 0; i--)
1611+
// Get the NetworkObjects owned by the disconnected client
1612+
var clientOwnedObjects = SpawnManager.GetClientOwnedObjects(clientId);
1613+
if (clientOwnedObjects == null)
16161614
{
1617-
var ownedObject = networkClient.OwnedObjects[i];
1618-
if (ownedObject != null)
1615+
// This could happen if a client is never assigned a player object and is disconnected
1616+
// Only log this in verbose/developer mode
1617+
if (LogLevel == LogLevel.Developer)
16191618
{
1620-
if (!ownedObject.DontDestroyWithOwner)
1619+
NetworkLog.LogWarning($"ClientID {clientId} disconnected with (0) zero owned objects! Was a player prefab not assigned?");
1620+
}
1621+
}
1622+
else
1623+
{
1624+
// Handle changing ownership and prefab handlers
1625+
for (int i = clientOwnedObjects.Count - 1; i >= 0; i--)
1626+
{
1627+
var ownedObject = clientOwnedObjects[i];
1628+
if (ownedObject != null)
16211629
{
1622-
if (PrefabHandler.ContainsHandler(ConnectedClients[clientId].OwnedObjects[i]
1623-
.GlobalObjectIdHash))
1630+
if (!ownedObject.DontDestroyWithOwner)
16241631
{
1625-
PrefabHandler.HandleNetworkPrefabDestroy(ConnectedClients[clientId].OwnedObjects[i]);
1632+
if (PrefabHandler.ContainsHandler(clientOwnedObjects[i].GlobalObjectIdHash))
1633+
{
1634+
PrefabHandler.HandleNetworkPrefabDestroy(clientOwnedObjects[i]);
1635+
}
1636+
else
1637+
{
1638+
Destroy(ownedObject.gameObject);
1639+
}
16261640
}
16271641
else
16281642
{
1629-
Destroy(ownedObject.gameObject);
1643+
ownedObject.RemoveOwnership();
16301644
}
16311645
}
1632-
else
1633-
{
1634-
ownedObject.RemoveOwnership();
1635-
}
16361646
}
16371647
}
16381648

16391649
// TODO: Could(should?) be replaced with more memory per client, by storing the visibility
1640-
16411650
foreach (var sobj in SpawnManager.SpawnedObjectsList)
16421651
{
16431652
sobj.Observers.Remove(clientId);

com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -64,33 +64,7 @@ internal void GenerateGlobalObjectIdHash()
6464
/// <summary>
6565
/// Gets the ClientId of the owner of this NetworkObject
6666
/// </summary>
67-
public ulong OwnerClientId
68-
{
69-
get
70-
{
71-
if (OwnerClientIdInternal == null)
72-
{
73-
return NetworkManager != null ? NetworkManager.ServerClientId : 0;
74-
}
75-
else
76-
{
77-
return OwnerClientIdInternal.Value;
78-
}
79-
}
80-
internal set
81-
{
82-
if (NetworkManager != null && value == NetworkManager.ServerClientId)
83-
{
84-
OwnerClientIdInternal = null;
85-
}
86-
else
87-
{
88-
OwnerClientIdInternal = value;
89-
}
90-
}
91-
}
92-
93-
internal ulong? OwnerClientIdInternal = null;
67+
public ulong OwnerClientId { get; internal set; }
9468

9569
/// <summary>
9670
/// If true, the object will always be replicated as root on clients and the parent will be ignored.
@@ -384,8 +358,8 @@ public static void NetworkHide(List<NetworkObject> networkObjects, ulong clientI
384358

385359
private void OnDestroy()
386360
{
387-
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned
388-
&& (IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true)))
361+
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned &&
362+
(IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true)))
389363
{
390364
throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead.");
391365
}
@@ -461,7 +435,7 @@ internal void SnapshotDespawn(ulong clientId)
461435
}
462436

463437
[MethodImpl(MethodImplOptions.AggressiveInlining)]
464-
private void SpawnInternal(bool destroyWithScene, ulong? ownerClientId, bool playerObject)
438+
private void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool playerObject)
465439
{
466440
if (!NetworkManager.IsListening)
467441
{
@@ -480,7 +454,6 @@ private void SpawnInternal(bool destroyWithScene, ulong? ownerClientId, bool pla
480454
SnapshotSpawn();
481455
}
482456

483-
ulong ownerId = ownerClientId != null ? ownerClientId.Value : NetworkManager.ServerClientId;
484457
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
485458
{
486459
if (Observers.Contains(NetworkManager.ConnectedClientsList[i].ClientId))
@@ -496,7 +469,7 @@ private void SpawnInternal(bool destroyWithScene, ulong? ownerClientId, bool pla
496469
/// <param name="destroyWithScene">Should the object be destroyed when the scene is changed</param>
497470
public void Spawn(bool destroyWithScene = false)
498471
{
499-
SpawnInternal(destroyWithScene, null, false);
472+
SpawnInternal(destroyWithScene, NetworkManager.ServerClientId, false);
500473
}
501474

502475
/// <summary>
@@ -547,6 +520,12 @@ public void ChangeOwnership(ulong newOwnerClientId)
547520

548521
internal void InvokeBehaviourOnLostOwnership()
549522
{
523+
// Server already handles this earlier, hosts should ignore
524+
if (!NetworkManager.IsServer && NetworkManager.LocalClientId == OwnerClientId)
525+
{
526+
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId, true);
527+
}
528+
550529
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
551530
{
552531
ChildNetworkBehaviours[i].InternalOnLostOwnership();
@@ -555,6 +534,12 @@ internal void InvokeBehaviourOnLostOwnership()
555534

556535
internal void InvokeBehaviourOnGainedOwnership()
557536
{
537+
// Server already handles this earlier, hosts should ignore
538+
if (!NetworkManager.IsServer && NetworkManager.LocalClientId == OwnerClientId)
539+
{
540+
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);
541+
}
542+
558543
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
559544
{
560545
ChildNetworkBehaviours[i].InternalOnGainedOwnership();
@@ -793,6 +778,8 @@ internal static void CheckOrphanChildren()
793778

794779
internal void InvokeBehaviourNetworkSpawn()
795780
{
781+
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);
782+
796783
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
797784
{
798785
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
@@ -801,6 +788,8 @@ internal void InvokeBehaviourNetworkSpawn()
801788

802789
internal void InvokeBehaviourNetworkDespawn()
803790
{
791+
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId, true);
792+
804793
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
805794
{
806795
ChildNetworkBehaviours[i].InternalOnNetworkDespawn();

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,33 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
2929

3030
public void Handle(ref NetworkContext context)
3131
{
32-
3332
var networkManager = (NetworkManager)context.SystemOwner;
3433
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
34+
var originalOwner = networkObject.OwnerClientId;
35+
36+
networkObject.OwnerClientId = OwnerClientId;
3537

36-
if (networkObject.OwnerClientId == networkManager.LocalClientId)
38+
// We are current owner.
39+
if (originalOwner == networkManager.LocalClientId)
3740
{
38-
//We are current owner.
3941
networkObject.InvokeBehaviourOnLostOwnership();
4042
}
4143

42-
networkObject.OwnerClientId = OwnerClientId;
43-
44+
// We are new owner.
4445
if (OwnerClientId == networkManager.LocalClientId)
4546
{
46-
//We are new owner.
4747
networkObject.InvokeBehaviourOnGainedOwnership();
4848
}
4949

50+
// For all other clients that are neither the former or current owner, update the behaviours' properties
51+
if (OwnerClientId != networkManager.LocalClientId && originalOwner != networkManager.LocalClientId)
52+
{
53+
for (int i = 0; i < networkObject.ChildNetworkBehaviours.Count; i++)
54+
{
55+
networkObject.ChildNetworkBehaviours[i].UpdateNetworkProperties();
56+
}
57+
}
58+
5059
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(context.SenderId, networkObject, context.MessageSize);
5160
}
5261
}

com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,9 @@ private void OnServerLoadedScene(uint sceneEventId, Scene scene)
12881288
{
12891289
if (!keyValuePairBySceneHandle.Value.IsPlayerObject)
12901290
{
1291-
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, true);
1291+
// All in-scene placed NetworkObjects default to being owned by the server
1292+
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value,
1293+
m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, NetworkManager.ServerClientId, true);
12921294
}
12931295
}
12941296
}

0 commit comments

Comments
 (0)