Skip to content

Commit e6ecb65

Browse files
chore: cache HasAuthority to avoid recomputing it on every access (#4026)
refactor: precalculate HasAuthority into cached m_HasAuthority field - Replaced InternalHasAuthority() with private m_HasAuthority field - Set m_HasAuthority on Spawn (before SpawnInternal), cleared on despawn, updated on ownership change - Replaced all internal HasAuthority call sites with direct m_HasAuthority field access - Updated public HasAuthority property to return cached value when spawned - Moved network object setup from NetworkSpawnManager.SpawnNetworkObjectLocallyCommon to NetworkObject.SetupOnSpawn * fix vettng test --------- Co-authored-by: Emma <emma.mcmillan@unity3d.com>
1 parent f7ca618 commit e6ecb65

6 files changed

Lines changed: 165 additions & 247 deletions

File tree

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

Lines changed: 133 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ public void DeferDespawn(int tickOffset, bool destroy = true)
416416
return;
417417
}
418418

419-
if (!HasAuthority)
419+
if (!m_HasAuthority)
420420
{
421421
if (NetworkManagerOwner.LogLevel <= LogLevel.Error)
422422
{
@@ -633,7 +633,7 @@ public bool SetOwnershipLock(bool lockOwnership = true)
633633
}
634634

635635
// If we don't have authority exit early
636-
if (!HasAuthority)
636+
if (!m_HasAuthority)
637637
{
638638
if (NetworkManager.LogLevel <= LogLevel.Error)
639639
{
@@ -909,7 +909,7 @@ internal void OwnershipRequest(ulong clientRequestingOwnership)
909909

910910
// This action is always authorized as long as the client still has authority.
911911
// We need to pass in that this is a request approval ownership change.
912-
NetworkManagerOwner.SpawnManager.ChangeOwnership(this, clientRequestingOwnership, HasAuthority, true);
912+
NetworkManagerOwner.SpawnManager.ChangeOwnership(this, clientRequestingOwnership, m_HasAuthority, true);
913913
}
914914
else
915915
{
@@ -1155,14 +1155,9 @@ public bool HasOwnershipStatus(OwnershipStatus status)
11551155
/// <remarks>
11561156
/// When in client-server mode, authority should is not considered the same as ownership.
11571157
/// </remarks>
1158-
public bool HasAuthority => InternalHasAuthority();
1158+
public bool HasAuthority => IsSpawned ? m_HasAuthority : !NetworkManager.DistributedAuthorityMode && NetworkManager.IsServer;
11591159

1160-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1161-
private bool InternalHasAuthority()
1162-
{
1163-
var networkManager = NetworkManager;
1164-
return networkManager.DistributedAuthorityMode ? OwnerClientId == networkManager.LocalClientId : networkManager.IsServer;
1165-
}
1160+
private bool m_HasAuthority;
11661161

11671162
/// <summary>
11681163
/// The NetworkManager that owns this NetworkObject.
@@ -1448,10 +1443,7 @@ public bool IsNetworkVisibleTo(ulong clientId)
14481443
/// </summary>
14491444
internal Scene SceneOrigin
14501445
{
1451-
get
1452-
{
1453-
return m_SceneOrigin;
1454-
}
1446+
get => m_SceneOrigin;
14551447

14561448
set
14571449
{
@@ -1471,13 +1463,8 @@ internal Scene SceneOrigin
14711463
/// </summary>
14721464
internal NetworkSceneHandle GetSceneOriginHandle()
14731465
{
1474-
if (SceneOriginHandle.IsEmpty() && IsSpawned && InScenePlaced)
1475-
{
1476-
if (NetworkManager.LogLevel <= LogLevel.Error)
1477-
{
1478-
NetworkLog.LogErrorServer($"{nameof(GetSceneOriginHandle)} called when {nameof(SceneOriginHandle)} is still zero but the {nameof(NetworkObject)} is already spawned!");
1479-
}
1480-
}
1466+
NetworkLog.InternalAssert(!(IsSpawned && InScenePlaced && SceneOriginHandle.IsEmpty()), $"Spawned in scene placed NetworkObject {name} should always have a valid SceneOriginHandle");
1467+
14811468
return !SceneOriginHandle.IsEmpty() ? SceneOriginHandle : gameObject.scene.handle;
14821469
}
14831470

@@ -1506,7 +1493,7 @@ public void NetworkShow(ulong clientId)
15061493
return;
15071494
}
15081495

1509-
if (!HasAuthority)
1496+
if (!m_HasAuthority)
15101497
{
15111498
if (NetworkManagerOwner.DistributedAuthorityMode)
15121499
{
@@ -1601,7 +1588,7 @@ public void NetworkHide(ulong clientId)
16011588
return;
16021589
}
16031590

1604-
if (!HasAuthority)
1591+
if (!m_HasAuthority)
16051592
{
16061593
if (NetworkManagerOwner.DistributedAuthorityMode)
16071594
{
@@ -1760,7 +1747,7 @@ private void OnDestroy()
17601747
{
17611748
// An authorized destroy is when done by the authority instance or done due to a scene event and the NetworkObject
17621749
// was marked as destroy pending scene event (which means the destroy with scene property was set).
1763-
var isAuthorityDestroy = HasAuthority || NetworkManager.DAHost || DestroyPendingSceneEvent;
1750+
var isAuthorityDestroy = m_HasAuthority || NetworkManager.DAHost || DestroyPendingSceneEvent;
17641751

17651752
// If the NetworkObject's GameObject is still valid and the scene is still valid and loaded, then we are still valid
17661753
var isStillValid = gameObject != null && gameObject.scene.IsValid() && gameObject.scene.isLoaded;
@@ -2005,8 +1992,7 @@ public NetworkObject InstantiateAndSpawn(NetworkManager networkManager, ulong ow
20051992
/// <param name="destroyWithScene">Should the object be destroyed when the scene is changed</param>
20061993
public void Spawn(bool destroyWithScene = false)
20071994
{
2008-
var networkManager = NetworkManager;
2009-
var clientId = networkManager.DistributedAuthorityMode ? networkManager.LocalClientId : NetworkManager.ServerClientId;
1995+
var clientId = NetworkManager.DistributedAuthorityMode ? NetworkManager.LocalClientId : NetworkManager.ServerClientId;
20101996
SpawnInternal(destroyWithScene, clientId, false);
20111997
}
20121998

@@ -2058,17 +2044,123 @@ public void Despawn(bool destroy = true)
20582044
NetworkManagerOwner.SpawnManager.DespawnObject(this, destroy);
20592045
}
20602046

2047+
internal void SetupOnSpawn(ulong networkId, bool isPlayerObject, ulong ownerClientId, bool destroyWithScene)
2048+
{
2049+
NetworkObjectId = networkId;
2050+
IsPlayerObject = isPlayerObject;
2051+
OwnerClientId = ownerClientId;
2052+
// When spawned, previous owner is always the first assigned owner
2053+
PreviousOwnerId = ownerClientId;
2054+
m_HasAuthority = NetworkManagerOwner.DistributedAuthorityMode ? OwnerClientId == NetworkManagerOwner.LocalClientId : NetworkManagerOwner.IsServer;
2055+
IsSpawned = true;
2056+
2057+
// If this is the player, and the client is the owner, then lock ownership by default
2058+
if (NetworkManagerOwner.DistributedAuthorityMode && NetworkManagerOwner.LocalClientId == ownerClientId && isPlayerObject)
2059+
{
2060+
AddOwnershipExtended(OwnershipStatusExtended.Locked);
2061+
}
2062+
2063+
if (IsSpawnAuthority)
2064+
{
2065+
SetupObservers();
2066+
}
2067+
2068+
/*
2069+
* Setup scene related settings
2070+
*/
2071+
DestroyWithScene = InScenePlaced || destroyWithScene;
2072+
if (InScenePlaced)
2073+
{
2074+
// Always check to make sure our scene of origin is properly set for in-scene placed NetworkObjects
2075+
// Note: Always check SceneOriginHandle directly at this specific location.
2076+
if (SceneOriginHandle.IsEmpty())
2077+
{
2078+
SceneOrigin = gameObject.scene;
2079+
}
2080+
2081+
// If we are an in-scene placed NetworkObject and our InScenePlacedSourceGlobalObjectIdHash is set
2082+
// then assign this to the PrefabGlobalObjectIdHash
2083+
if (InScenePlacedSourceGlobalObjectIdHash != 0)
2084+
{
2085+
PrefabGlobalObjectIdHash = InScenePlacedSourceGlobalObjectIdHash;
2086+
}
2087+
}
2088+
else if (ActiveSceneSynchronization)
2089+
{
2090+
// Just in case it is a recycled NetworkObject, unsubscribe first
2091+
SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged;
2092+
SceneManager.activeSceneChanged += CurrentlyActiveSceneChanged;
2093+
}
2094+
}
2095+
20612096
internal void ResetOnDespawn()
20622097
{
20632098
// Always clear out the observers list when despawned
20642099
Observers.Clear();
2100+
m_HasAuthority = false;
20652101
IsSpawnAuthority = false;
20662102
IsSpawned = false;
20672103
DeferredDespawnTick = 0;
20682104
m_LatestParent = null;
20692105
RemoveOwnershipExtended(OwnershipStatusExtended.Locked | OwnershipStatusExtended.Requested);
20702106
}
20712107

2108+
internal void SetupObservers()
2109+
{
2110+
NetworkLog.InternalAssert(IsSpawnAuthority, "This function should only be called on the authority.");
2111+
2112+
if (!SpawnWithObservers)
2113+
{
2114+
if (NetworkManagerOwner.DistributedAuthorityMode)
2115+
{
2116+
// Always add the owner/authority in DA mode even if SpawnWithObservers is false
2117+
// (authority should not take into consideration networkObject.CheckObjectVisibility when SpawnWithObservers is false)
2118+
AddObserver(OwnerClientId);
2119+
}
2120+
2121+
return;
2122+
}
2123+
2124+
// If running as a server only, then make sure to always add the server's client identifier
2125+
if (NetworkManagerOwner.IsServer && !NetworkManagerOwner.IsHost)
2126+
{
2127+
AddObserver(NetworkManager.ServerClientId);
2128+
}
2129+
2130+
// If SpawnWithObservers is set,
2131+
// then add all connected clients as observers
2132+
foreach (var clientId in NetworkManagerOwner.ConnectedClientsIds)
2133+
{
2134+
// If CheckObjectVisibility has a callback, then allow that method determine who the observers are.
2135+
if (CheckObjectVisibility != null && !CheckObjectVisibility(clientId))
2136+
{
2137+
continue;
2138+
}
2139+
AddObserver(clientId);
2140+
}
2141+
2142+
// Intentionally checking as opposed to just assigning in order to generate notification.
2143+
if (!Observers.Contains(OwnerClientId))
2144+
{
2145+
// The owner only needs to always be included in DA mode.
2146+
if (NetworkManagerOwner.DistributedAuthorityMode)
2147+
{
2148+
if (NetworkManager.LogLevel <= LogLevel.Error)
2149+
{
2150+
NetworkLog.LogError($"Client-{OwnerClientId} is the owner of {name} but is not an observer! Adding owner as an observer!");
2151+
}
2152+
AddObserver(OwnerClientId);
2153+
}
2154+
else
2155+
{
2156+
if (NetworkManager.LogLevel <= LogLevel.Developer)
2157+
{
2158+
NetworkLog.LogWarning($"Client-{OwnerClientId} is the owner of {name} but is not an observer! This may cause issues");
2159+
}
2160+
}
2161+
}
2162+
}
2163+
20722164
/// <summary>
20732165
/// Removes all ownership of an object from any client. Can only be called from server
20742166
/// </summary>
@@ -2099,7 +2191,7 @@ public void ChangeOwnership(ulong newOwnerClientId)
20992191
}
21002192
return;
21012193
}
2102-
NetworkManagerOwner.SpawnManager.ChangeOwnership(this, newOwnerClientId, HasAuthority);
2194+
NetworkManagerOwner.SpawnManager.ChangeOwnership(this, newOwnerClientId, m_HasAuthority);
21032195
}
21042196

21052197
/// <summary>
@@ -2108,20 +2200,18 @@ public void ChangeOwnership(ulong newOwnerClientId)
21082200
/// </summary>
21092201
internal void InvokeBehaviourOnOwnershipChanged(ulong originalOwnerClientId, ulong newOwnerClientId)
21102202
{
2111-
if (!IsSpawned)
2112-
{
2113-
if (NetworkManager.LogLevel <= LogLevel.Error)
2114-
{
2115-
NetworkLog.LogErrorServer($"[{name}][Attempted behavior invoke on ownership changed before {nameof(NetworkObject)} was spawned]");
2116-
}
2117-
return;
2118-
}
2203+
NetworkLog.InternalAssert(IsSpawned, "[{name}][Attempted behavior invoke on ownership changed before {nameof(NetworkObject)} was spawned]");
21192204

21202205
var distributedAuthorityMode = NetworkManagerOwner.DistributedAuthorityMode;
21212206
var isServer = NetworkManagerOwner.IsServer;
21222207
var isPreviousOwner = originalOwnerClientId == NetworkManagerOwner.LocalClientId;
21232208
var isNewOwner = newOwnerClientId == NetworkManagerOwner.LocalClientId;
21242209

2210+
if (distributedAuthorityMode)
2211+
{
2212+
m_HasAuthority = isNewOwner;
2213+
}
2214+
21252215
if (distributedAuthorityMode || isPreviousOwner)
21262216
{
21272217
NetworkManagerOwner.SpawnManager.UpdateOwnershipTable(this, originalOwnerClientId, true);
@@ -2334,7 +2424,7 @@ public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true)
23342424

23352425
// DANGO-TODO: Do we want to worry about ownership permissions here?
23362426
// It wouldn't make sense to not allow parenting, but keeping this note here as a reminder.
2337-
var isAuthority = HasAuthority || (AllowOwnerToParent && IsOwner);
2427+
var isAuthority = m_HasAuthority || (AllowOwnerToParent && IsOwner);
23382428

23392429
// If we don't have authority and we are not shutting down, then don't allow any parenting.
23402430
// If we are shutting down and don't have authority then allow it.
@@ -2409,7 +2499,7 @@ private void OnTransformParentChanged()
24092499

24102500
// With distributed authority, we need to track "valid authoritative" parenting changes.
24112501
// So, either the authority or AuthorityAppliedParenting is considered a "valid parenting change".
2412-
var isParentingAuthority = HasAuthority || AuthorityAppliedParenting || (AllowOwnerToParent && IsOwner);
2502+
var isParentingAuthority = m_HasAuthority || AuthorityAppliedParenting || (AllowOwnerToParent && IsOwner);
24132503
// If we are spawned and don't have authority; reset the parent back to the cached parent and exit
24142504
if (!isParentingAuthority)
24152505
{
@@ -2663,8 +2753,6 @@ internal void InvokeBehaviourNetworkPreSpawn()
26632753

26642754
internal void InvokeBehaviourNetworkSpawn()
26652755
{
2666-
NetworkManagerOwner.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);
2667-
26682756
// Always invoke all InternalOnNetworkSpawn methods on each child NetworkBehaviour
26692757
// ** before ** invoking OnNetworkSpawn.
26702758
// This assures all NetworkVariables and RPC related tables have been initialized
@@ -3465,29 +3553,13 @@ internal static NetworkObject Deserialize(in SerializedObject serializedObject,
34653553
return networkObject;
34663554
}
34673555

3468-
/// <summary>
3469-
/// Subscribes to changes in the currently active scene
3470-
/// </summary>
3471-
/// <remarks>
3472-
/// Only for dynamically spawned NetworkObjects
3473-
/// </remarks>
3474-
internal void SubscribeToActiveSceneForSynch()
3475-
{
3476-
if (ActiveSceneSynchronization)
3477-
{
3478-
if (!InScenePlaced)
3479-
{
3480-
// Just in case it is a recycled NetworkObject, unsubscribe first
3481-
SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged;
3482-
SceneManager.activeSceneChanged += CurrentlyActiveSceneChanged;
3483-
}
3484-
}
3485-
}
3486-
34873556
/// <summary>
34883557
/// If AutoSynchActiveScene is enabled, then this is the callback that handles updating
34893558
/// a NetworkObject's scene information.
34903559
/// </summary>
3560+
/// <remarks>
3561+
/// Should only be used for dynamically spawned NetworkObjects
3562+
/// </remarks>
34913563
private void CurrentlyActiveSceneChanged(Scene current, Scene next)
34923564
{
34933565
// Early exit if the NetworkObject is not spawned, is an in-scene placed NetworkObject,
@@ -3526,15 +3598,14 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false)
35263598
return;
35273599
}
35283600

3529-
var isAuthority = HasAuthority;
35303601
SceneOriginHandle = scene.handle;
35313602

35323603
// non-authority needs to update the NetworkSceneHandle
3533-
if (!isAuthority && NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle.ContainsKey(SceneOriginHandle))
3604+
if (!m_HasAuthority && NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle.ContainsKey(SceneOriginHandle))
35343605
{
35353606
NetworkSceneHandle = NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle[SceneOriginHandle];
35363607
}
3537-
else if (isAuthority)
3608+
else if (m_HasAuthority)
35383609
{
35393610
// Since the authority is the source of truth for the NetworkSceneHandle,
35403611
// the NetworkSceneHandle is the same as the SceneOriginHandle.
@@ -3561,7 +3632,7 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false)
35613632
OnMigratedToNewScene?.Invoke();
35623633

35633634
// Only the authority side will notify clients of non-parented NetworkObject scene changes
3564-
if (isAuthority && notify && !transform.parent)
3635+
if (m_HasAuthority && notify && !transform.parent)
35653636
{
35663637
NetworkManagerOwner.SceneManager.NotifyNetworkObjectSceneChanged(this);
35673638
}

com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
using System.Diagnostics;
12
using System.Diagnostics.CodeAnalysis;
23
using System.Runtime.CompilerServices;
34
using System.Text.RegularExpressions;
45
using Unity.Netcode.Logging;
56
using UnityEngine;
7+
using UnityEngine.Assertions;
68

79
namespace Unity.Netcode
810
{
@@ -158,5 +160,11 @@ private static bool TryGetNetworkObjectName([NotNull] NetworkManager networkMana
158160
return true;
159161
}
160162

163+
[HideInCallstack]
164+
[Conditional("NETCODE_INTERNAL")]
165+
internal static void InternalAssert(bool condition, string message)
166+
{
167+
Assert.IsTrue(condition, message);
168+
}
161169
}
162170
}

0 commit comments

Comments
 (0)