Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 133 additions & 62 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ public void DeferDespawn(int tickOffset, bool destroy = true)
return;
}

if (!HasAuthority)
if (!m_HasAuthority)
{
if (NetworkManagerOwner.LogLevel <= LogLevel.Error)
{
Expand Down Expand Up @@ -633,7 +633,7 @@ public bool SetOwnershipLock(bool lockOwnership = true)
}

// If we don't have authority exit early
if (!HasAuthority)
if (!m_HasAuthority)
{
if (NetworkManager.LogLevel <= LogLevel.Error)
{
Expand Down Expand Up @@ -909,7 +909,7 @@ internal void OwnershipRequest(ulong clientRequestingOwnership)

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool InternalHasAuthority()
{
var networkManager = NetworkManager;
return networkManager.DistributedAuthorityMode ? OwnerClientId == networkManager.LocalClientId : networkManager.IsServer;
}
private bool m_HasAuthority;

/// <summary>
/// The NetworkManager that owns this NetworkObject.
Expand Down Expand Up @@ -1448,10 +1443,7 @@ public bool IsNetworkVisibleTo(ulong clientId)
/// </summary>
internal Scene SceneOrigin
{
get
{
return m_SceneOrigin;
}
get => m_SceneOrigin;

set
{
Expand All @@ -1471,13 +1463,8 @@ internal Scene SceneOrigin
/// </summary>
internal NetworkSceneHandle GetSceneOriginHandle()
{
if (SceneOriginHandle.IsEmpty() && IsSpawned && InScenePlaced)
{
if (NetworkManager.LogLevel <= LogLevel.Error)
{
NetworkLog.LogErrorServer($"{nameof(GetSceneOriginHandle)} called when {nameof(SceneOriginHandle)} is still zero but the {nameof(NetworkObject)} is already spawned!");
}
}
NetworkLog.InternalAssert(!(IsSpawned && InScenePlaced && SceneOriginHandle.IsEmpty()), $"Spawned in scene placed NetworkObject {name} should always have a valid SceneOriginHandle");

return !SceneOriginHandle.IsEmpty() ? SceneOriginHandle : gameObject.scene.handle;
}

Expand Down Expand Up @@ -1506,7 +1493,7 @@ public void NetworkShow(ulong clientId)
return;
}

if (!HasAuthority)
if (!m_HasAuthority)
{
if (NetworkManagerOwner.DistributedAuthorityMode)
{
Expand Down Expand Up @@ -1601,7 +1588,7 @@ public void NetworkHide(ulong clientId)
return;
}

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

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

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

internal void SetupOnSpawn(ulong networkId, bool isPlayerObject, ulong ownerClientId, bool destroyWithScene)
{
NetworkObjectId = networkId;
IsPlayerObject = isPlayerObject;
OwnerClientId = ownerClientId;
// When spawned, previous owner is always the first assigned owner
PreviousOwnerId = ownerClientId;
m_HasAuthority = NetworkManagerOwner.DistributedAuthorityMode ? OwnerClientId == NetworkManagerOwner.LocalClientId : NetworkManagerOwner.IsServer;
IsSpawned = true;

// If this is the player, and the client is the owner, then lock ownership by default
if (NetworkManagerOwner.DistributedAuthorityMode && NetworkManagerOwner.LocalClientId == ownerClientId && isPlayerObject)
{
AddOwnershipExtended(OwnershipStatusExtended.Locked);
}

if (IsSpawnAuthority)
{
SetupObservers();
}

/*
* Setup scene related settings
*/
DestroyWithScene = InScenePlaced || destroyWithScene;
if (InScenePlaced)
{
// Always check to make sure our scene of origin is properly set for in-scene placed NetworkObjects
// Note: Always check SceneOriginHandle directly at this specific location.
if (SceneOriginHandle.IsEmpty())
{
SceneOrigin = gameObject.scene;
}

// If we are an in-scene placed NetworkObject and our InScenePlacedSourceGlobalObjectIdHash is set
// then assign this to the PrefabGlobalObjectIdHash
if (InScenePlacedSourceGlobalObjectIdHash != 0)
{
PrefabGlobalObjectIdHash = InScenePlacedSourceGlobalObjectIdHash;
}
}
else if (ActiveSceneSynchronization)
{
// Just in case it is a recycled NetworkObject, unsubscribe first
SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged;
SceneManager.activeSceneChanged += CurrentlyActiveSceneChanged;
}
}

internal void ResetOnDespawn()
{
// Always clear out the observers list when despawned
Observers.Clear();
m_HasAuthority = false;
IsSpawnAuthority = false;
IsSpawned = false;
DeferredDespawnTick = 0;
m_LatestParent = null;
RemoveOwnershipExtended(OwnershipStatusExtended.Locked | OwnershipStatusExtended.Requested);
}

internal void SetupObservers()
{
NetworkLog.InternalAssert(IsSpawnAuthority, "This function should only be called on the authority.");

if (!SpawnWithObservers)
{
if (NetworkManagerOwner.DistributedAuthorityMode)
{
// Always add the owner/authority in DA mode even if SpawnWithObservers is false
// (authority should not take into consideration networkObject.CheckObjectVisibility when SpawnWithObservers is false)
AddObserver(OwnerClientId);
}

return;
}

// If running as a server only, then make sure to always add the server's client identifier
if (NetworkManagerOwner.IsServer && !NetworkManagerOwner.IsHost)
{
AddObserver(NetworkManager.ServerClientId);
}

// If SpawnWithObservers is set,
// then add all connected clients as observers
foreach (var clientId in NetworkManagerOwner.ConnectedClientsIds)
{
// If CheckObjectVisibility has a callback, then allow that method determine who the observers are.
if (CheckObjectVisibility != null && !CheckObjectVisibility(clientId))
{
continue;
}
AddObserver(clientId);
}

// Intentionally checking as opposed to just assigning in order to generate notification.
if (!Observers.Contains(OwnerClientId))
{
// The owner only needs to always be included in DA mode.
if (NetworkManagerOwner.DistributedAuthorityMode)
{
if (NetworkManager.LogLevel <= LogLevel.Error)
{
NetworkLog.LogError($"Client-{OwnerClientId} is the owner of {name} but is not an observer! Adding owner as an observer!");
}
AddObserver(OwnerClientId);
}
else
{
if (NetworkManager.LogLevel <= LogLevel.Developer)
{
NetworkLog.LogWarning($"Client-{OwnerClientId} is the owner of {name} but is not an observer! This may cause issues");
}
}
}
}

/// <summary>
/// Removes all ownership of an object from any client. Can only be called from server
/// </summary>
Expand Down Expand Up @@ -2099,7 +2191,7 @@ public void ChangeOwnership(ulong newOwnerClientId)
}
return;
}
NetworkManagerOwner.SpawnManager.ChangeOwnership(this, newOwnerClientId, HasAuthority);
NetworkManagerOwner.SpawnManager.ChangeOwnership(this, newOwnerClientId, m_HasAuthority);
}

/// <summary>
Expand All @@ -2108,20 +2200,18 @@ public void ChangeOwnership(ulong newOwnerClientId)
/// </summary>
internal void InvokeBehaviourOnOwnershipChanged(ulong originalOwnerClientId, ulong newOwnerClientId)
{
if (!IsSpawned)
{
if (NetworkManager.LogLevel <= LogLevel.Error)
{
NetworkLog.LogErrorServer($"[{name}][Attempted behavior invoke on ownership changed before {nameof(NetworkObject)} was spawned]");
}
return;
}
NetworkLog.InternalAssert(IsSpawned, "[{name}][Attempted behavior invoke on ownership changed before {nameof(NetworkObject)} was spawned]");

var distributedAuthorityMode = NetworkManagerOwner.DistributedAuthorityMode;
var isServer = NetworkManagerOwner.IsServer;
var isPreviousOwner = originalOwnerClientId == NetworkManagerOwner.LocalClientId;
var isNewOwner = newOwnerClientId == NetworkManagerOwner.LocalClientId;

if (distributedAuthorityMode)
{
m_HasAuthority = isNewOwner;
}

if (distributedAuthorityMode || isPreviousOwner)
{
NetworkManagerOwner.SpawnManager.UpdateOwnershipTable(this, originalOwnerClientId, true);
Expand Down Expand Up @@ -2334,7 +2424,7 @@ public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true)

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

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

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

internal void InvokeBehaviourNetworkSpawn()
{
NetworkManagerOwner.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);

// Always invoke all InternalOnNetworkSpawn methods on each child NetworkBehaviour
// ** before ** invoking OnNetworkSpawn.
// This assures all NetworkVariables and RPC related tables have been initialized
Expand Down Expand Up @@ -3465,29 +3553,13 @@ internal static NetworkObject Deserialize(in SerializedObject serializedObject,
return networkObject;
}

/// <summary>
/// Subscribes to changes in the currently active scene
/// </summary>
/// <remarks>
/// Only for dynamically spawned NetworkObjects
/// </remarks>
internal void SubscribeToActiveSceneForSynch()
{
if (ActiveSceneSynchronization)
{
if (!InScenePlaced)
{
// Just in case it is a recycled NetworkObject, unsubscribe first
SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged;
SceneManager.activeSceneChanged += CurrentlyActiveSceneChanged;
}
}
}

/// <summary>
/// If AutoSynchActiveScene is enabled, then this is the callback that handles updating
/// a NetworkObject's scene information.
/// </summary>
/// <remarks>
/// Should only be used for dynamically spawned NetworkObjects
/// </remarks>
private void CurrentlyActiveSceneChanged(Scene current, Scene next)
{
// Early exit if the NetworkObject is not spawned, is an in-scene placed NetworkObject,
Expand Down Expand Up @@ -3526,15 +3598,14 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false)
return;
}

var isAuthority = HasAuthority;
SceneOriginHandle = scene.handle;

// non-authority needs to update the NetworkSceneHandle
if (!isAuthority && NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle.ContainsKey(SceneOriginHandle))
if (!m_HasAuthority && NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle.ContainsKey(SceneOriginHandle))
{
NetworkSceneHandle = NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle[SceneOriginHandle];
}
else if (isAuthority)
else if (m_HasAuthority)
{
// Since the authority is the source of truth for the NetworkSceneHandle,
// the NetworkSceneHandle is the same as the SceneOriginHandle.
Expand All @@ -3561,7 +3632,7 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false)
OnMigratedToNewScene?.Invoke();

// Only the authority side will notify clients of non-parented NetworkObject scene changes
if (isAuthority && notify && !transform.parent)
if (m_HasAuthority && notify && !transform.parent)
{
NetworkManagerOwner.SceneManager.NotifyNetworkObjectSceneChanged(this);
}
Expand Down
8 changes: 8 additions & 0 deletions com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using Unity.Netcode.Logging;
using UnityEngine;
using UnityEngine.Assertions;

namespace Unity.Netcode
{
Expand Down Expand Up @@ -158,5 +160,11 @@ private static bool TryGetNetworkObjectName([NotNull] NetworkManager networkMana
return true;
}

[HideInCallstack]
[Conditional("NETCODE_INTERNAL")]
internal static void InternalAssert(bool condition, string message)
{
Assert.IsTrue(condition, message);
}
}
}
Loading