Skip to content

Commit c7dc103

Browse files
fix: NetworkBehaviour Property Initialization Updates (#1785)
* fix Updating to more stable and performant NetworkBehaviour properties through changing the how and when the properties are set. * test fix Really MessageOrdering and the SpawnRpcDespawn classes need to be reviewed and updated. This fixes a few timing related issues that were exposed with this update. * fix Removing the update to HasNetworkObject * update really only ever need to wait for almost 1 tic interval to make sure the GameObject and associated components are destroyed and the handler destroyed. * style clarity in a comment * update removing the NetworkManager.IsListening since that is part of how the property values are determined (host, client, server). * update handling an unlikely condition where the ownership or property update is invoked and NetworkObject is null. * update Includes all of Fatih's suggested adjustments Also, went ahead and just moved the invocation of OnNetworkSpawn and OnNetworkDespawn into the internal methods. * update Coming to alternate solution of IsLocalPlayer to reduce the processing cost of the other implementation.
1 parent 14e7546 commit c7dc103

File tree

4 files changed

+83
-22
lines changed

4 files changed

+83
-22
lines changed

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

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -235,42 +235,42 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth
235235
public NetworkManager NetworkManager => NetworkObject.NetworkManager;
236236

237237
/// <summary>
238-
/// Gets if the object is the the personal clients player object
238+
/// If a NetworkObject is assigned, it will return whether or not this NetworkObject
239+
/// is the local player object. If no NetworkObject is assigned it will always return false.
239240
/// </summary>
240-
public bool IsLocalPlayer => NetworkObject.IsLocalPlayer;
241+
public bool IsLocalPlayer { get; private set; }
241242

242243
/// <summary>
243244
/// Gets if the object is owned by the local player or if the object is the local player object
244245
/// </summary>
245-
public bool IsOwner => NetworkObject.IsOwner;
246+
public bool IsOwner { get; internal set; }
246247

247248
/// <summary>
248249
/// Gets if we are executing as server
249250
/// </summary>
250-
protected bool IsServer => IsRunning && NetworkManager.IsServer;
251+
protected bool IsServer { get; private set; }
251252

252253
/// <summary>
253254
/// Gets if we are executing as client
254255
/// </summary>
255-
protected bool IsClient => IsRunning && NetworkManager.IsClient;
256+
protected bool IsClient { get; private set; }
257+
256258

257259
/// <summary>
258260
/// Gets if we are executing as Host, I.E Server and Client
259261
/// </summary>
260-
protected bool IsHost => IsRunning && NetworkManager.IsHost;
261-
262-
private bool IsRunning => NetworkManager && NetworkManager.IsListening;
262+
protected bool IsHost { get; private set; }
263263

264264
/// <summary>
265265
/// Gets Whether or not the object has a owner
266266
/// </summary>
267-
public bool IsOwnedByServer => NetworkObject.IsOwnedByServer;
267+
public bool IsOwnedByServer { get; internal set; }
268268

269269
/// <summary>
270270
/// Used to determine if it is safe to access NetworkObject and NetworkManager from within a NetworkBehaviour component
271271
/// Primarily useful when checking NetworkObject/NetworkManager properties within FixedUpate
272272
/// </summary>
273-
public bool IsSpawned => HasNetworkObject ? NetworkObject.IsSpawned : false;
273+
public bool IsSpawned { get; internal set; }
274274

275275
internal bool IsBehaviourEditable()
276276
{
@@ -327,12 +327,12 @@ public NetworkObject NetworkObject
327327
/// <summary>
328328
/// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour
329329
/// </summary>
330-
public ulong NetworkObjectId => NetworkObject.NetworkObjectId;
330+
public ulong NetworkObjectId { get; internal set; }
331331

332332
/// <summary>
333333
/// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject
334334
/// </summary>
335-
public ushort NetworkBehaviourId => NetworkObject.GetNetworkBehaviourOrderIndex(this);
335+
public ushort NetworkBehaviourId { get; internal set; }
336336

337337
/// <summary>
338338
/// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster
@@ -352,7 +352,47 @@ protected NetworkBehaviour GetNetworkBehaviour(ushort behaviourId)
352352
/// <summary>
353353
/// Gets the ClientId that owns the NetworkObject
354354
/// </summary>
355-
public ulong OwnerClientId => NetworkObject.OwnerClientId;
355+
public ulong OwnerClientId { get; internal set; }
356+
357+
/// <summary>
358+
/// Updates properties with network session related
359+
/// dependencies such as a NetworkObject's spawned
360+
/// state or NetworkManager's session state.
361+
/// </summary>
362+
internal void UpdateNetworkProperties()
363+
{
364+
// Set NetworkObject dependent properties
365+
if (NetworkObject != null)
366+
{
367+
// Set identification related properties
368+
NetworkObjectId = NetworkObject.NetworkObjectId;
369+
IsLocalPlayer = NetworkObject.IsLocalPlayer;
370+
371+
// This is "OK" because GetNetworkBehaviourOrderIndex uses the order of
372+
// NetworkObject.ChildNetworkBehaviours which is set once when first
373+
// accessed.
374+
NetworkBehaviourId = NetworkObject.GetNetworkBehaviourOrderIndex(this);
375+
376+
// Set ownership related properties
377+
IsOwnedByServer = NetworkObject.IsOwnedByServer;
378+
IsOwner = NetworkObject.IsOwner;
379+
OwnerClientId = NetworkObject.OwnerClientId;
380+
381+
// Set NetworkManager dependent properties
382+
if (NetworkManager != null)
383+
{
384+
IsHost = NetworkManager.IsListening && NetworkManager.IsHost;
385+
IsClient = NetworkManager.IsListening && NetworkManager.IsClient;
386+
IsServer = NetworkManager.IsListening && NetworkManager.IsServer;
387+
}
388+
}
389+
else // Shouldn't happen, but if so then set the properties to their default value;
390+
{
391+
OwnerClientId = NetworkObjectId = default;
392+
IsOwnedByServer = IsOwner = IsHost = IsClient = IsServer = default;
393+
NetworkBehaviourId = default;
394+
}
395+
}
356396

357397
/// <summary>
358398
/// Gets called when the <see cref="NetworkObject"/> gets spawned, message handlers are ready to be registered and the network is setup.
@@ -366,21 +406,41 @@ public virtual void OnNetworkDespawn() { }
366406

367407
internal void InternalOnNetworkSpawn()
368408
{
409+
IsSpawned = true;
369410
InitializeVariables();
411+
UpdateNetworkProperties();
412+
OnNetworkSpawn();
370413
}
371414

372-
internal void InternalOnNetworkDespawn() { }
415+
internal void InternalOnNetworkDespawn()
416+
{
417+
IsSpawned = false;
418+
UpdateNetworkProperties();
419+
OnNetworkDespawn();
420+
}
373421

374422
/// <summary>
375423
/// Gets called when the local client gains ownership of this object
376424
/// </summary>
377425
public virtual void OnGainedOwnership() { }
378426

427+
internal void InternalOnGainedOwnership()
428+
{
429+
UpdateNetworkProperties();
430+
OnGainedOwnership();
431+
}
432+
379433
/// <summary>
380434
/// Gets called when we loose ownership of this object
381435
/// </summary>
382436
public virtual void OnLostOwnership() { }
383437

438+
internal void InternalOnLostOwnership()
439+
{
440+
UpdateNetworkProperties();
441+
OnLostOwnership();
442+
}
443+
384444
/// <summary>
385445
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
386446
/// </summary>

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -549,15 +549,15 @@ internal void InvokeBehaviourOnLostOwnership()
549549
{
550550
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
551551
{
552-
ChildNetworkBehaviours[i].OnLostOwnership();
552+
ChildNetworkBehaviours[i].InternalOnLostOwnership();
553553
}
554554
}
555555

556556
internal void InvokeBehaviourOnGainedOwnership()
557557
{
558558
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
559559
{
560-
ChildNetworkBehaviours[i].OnGainedOwnership();
560+
ChildNetworkBehaviours[i].InternalOnGainedOwnership();
561561
}
562562
}
563563

@@ -796,7 +796,6 @@ internal void InvokeBehaviourNetworkSpawn()
796796
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
797797
{
798798
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
799-
ChildNetworkBehaviours[i].OnNetworkSpawn();
800799
}
801800
}
802801

@@ -805,7 +804,6 @@ internal void InvokeBehaviourNetworkDespawn()
805804
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
806805
{
807806
ChildNetworkBehaviours[i].InternalOnNetworkDespawn();
808-
ChildNetworkBehaviours[i].OnNetworkDespawn();
809807
}
810808
}
811809

testproject/Assets/Tests/Runtime/MessageOrdering.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,14 @@ public IEnumerator SpawnRpcDespawn()
146146
var serverObject = Object.Instantiate(m_Prefab, Vector3.zero, Quaternion.identity);
147147
NetworkObject serverNetworkObject = serverObject.GetComponent<NetworkObject>();
148148
serverNetworkObject.NetworkManagerOwner = server;
149+
serverNetworkObject.Spawn();
150+
149151
SpawnRpcDespawn srdComponent = serverObject.GetComponent<SpawnRpcDespawn>();
150152
srdComponent.Activate();
151153

152154
// Wait until all objects have spawned.
153155
int expectedCount = Support.SpawnRpcDespawn.ClientUpdateCount + 1;
154-
const int maxFrames = 240;
156+
int maxFrames = 240 + Time.frameCount;
155157
var doubleCheckTime = Time.realtimeSinceStartup + 5.0f;
156158
while (Support.SpawnRpcDespawn.ClientUpdateCount < expectedCount && !handler.WasSpawned)
157159
{
@@ -171,8 +173,10 @@ public IEnumerator SpawnRpcDespawn()
171173

172174
Assert.AreEqual(NetworkUpdateStage.EarlyUpdate, Support.SpawnRpcDespawn.StageExecutedByReceiver);
173175
Assert.AreEqual(Support.SpawnRpcDespawn.ServerUpdateCount, Support.SpawnRpcDespawn.ClientUpdateCount);
174-
var lastFrameNumber = Time.frameCount + 1;
175-
yield return new WaitUntil(() => Time.frameCount >= lastFrameNumber);
176+
177+
// Wait 1 tic for the GameObjet and associated components to be destroyed
178+
yield return new WaitForSeconds(1.0f / server.NetworkConfig.TickRate);
179+
176180
Assert.True(handler.WasDestroyed);
177181
}
178182

testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawn.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ public override void OnDestroy()
8686
private void RunTest()
8787
{
8888
Debug.Log("Running test...");
89-
GetComponent<NetworkObject>().Spawn();
9089
IncrementUpdateCount();
9190
Destroy(gameObject);
9291
m_Active = false;

0 commit comments

Comments
 (0)