Skip to content
Open
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
14 changes: 14 additions & 0 deletions com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public void OnProcessScene(Scene scene, BuildReport report)
log.AddInfo(scene.name, scene.handle);
foreach (var networkObject in FindObjects.FromSceneByType<NetworkObject>(scene, true))
{
// Trap for users just creating things during runtime where this will be zero.
if (networkObject.GlobalObjectIdHash == 0)
{
log.Warning(new Context(LogLevel.Developer, $"{nameof(NetworkObject)}'s GlobalObjectIdHash value is zero! Runtime creating of {nameof(NetworkObject)}s is not supported. Skipping processing.").AddNetworkObject(networkObject));
continue;
}
if (networkObject.SceneOrigin.IsValid() && networkObject.SceneOrigin.handle != scene.handle)
{
log.Warning(new Context(LogLevel.Developer, $"{nameof(NetworkObject)}'s SceneOrigin doesn't match current scene being processed! Skipping processing.").AddInfo("SceneOrigin", networkObject.SceneOriginHandle).AddNetworkObject(networkObject));
Expand All @@ -36,7 +42,15 @@ public void OnProcessScene(Scene scene, BuildReport report)
continue;
}

// If already marked, the do nothing.
if (networkObject.InScenePlaced)
{
continue;
}

networkObject.InScenePlaced = true;
// Will not be true when making a build and the values are serialized.
networkObject.InScenePlacedPostProcessorMarkedDuringRuntime = Application.isPlaying;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,10 @@ protected virtual void Awake()
if (!m_Animator)
{
#if !UNITY_EDITOR
Debug.LogError($"{nameof(NetworkAnimator)} {name} does not have an {nameof(UnityEngine.Animator)} assigned to it. The {nameof(NetworkAnimator)} will not initialize properly.");
if (!m_Animator)
{
Debug.LogWarning($"{nameof(NetworkAnimator)} {name} does not have an {nameof(UnityEngine.Animator)} assigned to it. The {nameof(NetworkAnimator)} will not initialize properly.");
}
#endif
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,83 @@ public class NetworkPrefabs
[NonSerialized]
private List<NetworkPrefab> m_Prefabs = new List<NetworkPrefab>();

internal List<NetworkPrefab> InternalPrefabs => m_Prefabs;

[NonSerialized]
private Dictionary<uint, NetworkPrefab> m_PrefabHashIds = new Dictionary<uint, NetworkPrefab>();

[NonSerialized]
private List<NetworkPrefab> m_RuntimeAddedPrefabs = new List<NetworkPrefab>();

private void AddTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
private bool InternalAddPrefab(NetworkPrefab networkPrefab)
{
if (AddPrefabRegistration(networkPrefab))
{
// Don't add this to m_RuntimeAddedPrefabs
// This prefab is now in the PrefabList, so if we shutdown and initialize again, we'll pick it up from there.
m_Prefabs.Add(networkPrefab);

// We are not getting all potential overrides but just determining if the prefab has been registered.
if (!m_PrefabHashIds.ContainsKey(networkPrefab.SourcePrefabGlobalObjectIdHash))
{
m_PrefabHashIds.Add(networkPrefab.SourcePrefabGlobalObjectIdHash, networkPrefab);
}
if (!m_PrefabHashIds.ContainsKey(networkPrefab.TargetPrefabGlobalObjectIdHash))
{
m_PrefabHashIds.Add(networkPrefab.TargetPrefabGlobalObjectIdHash, networkPrefab);
}
return true;
}
return false;
}

private void RemoveTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
private void InternalRemovePrefab(NetworkPrefab networkPrefab)
{
m_Prefabs.Remove(networkPrefab);
m_PrefabHashIds.Remove(networkPrefab.SourcePrefabGlobalObjectIdHash);
}

internal bool IsBasedOnRegisteredPrefab(NetworkObject networkObject)
{


return m_PrefabHashIds.ContainsKey(networkObject.GlobalObjectIdHash);
}

internal bool IsActualPrefabAsset(NetworkObject networkObject)
{
var isActualPrefabAsset = false;
if (m_PrefabHashIds.TryGetValue(networkObject.GlobalObjectIdHash, out NetworkPrefab networkPrefab))
{
switch (networkPrefab.Override)
{
case NetworkPrefabOverride.Prefab:
case NetworkPrefabOverride.None:
{
isActualPrefabAsset = networkPrefab.Prefab != null && networkObject.gameObject == networkPrefab.Prefab;
break;
}
case NetworkPrefabOverride.Hash:
{
isActualPrefabAsset = networkPrefab.SourceHashToOverride == networkObject.GlobalObjectIdHash;
break;
}
}
}
return isActualPrefabAsset;
}

private void AddTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
{
// Don't add this to m_RuntimeAddedPrefabs
// This prefab is now in the PrefabList, so if we shutdown and initialize again, we'll pick it up from there.
InternalAddPrefab(networkPrefab);
// Log warning if this returns false?
}

private void RemoveTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
{
InternalRemovePrefab(networkPrefab);
}

/// <summary>
Expand Down Expand Up @@ -93,6 +154,7 @@ internal void Shutdown()
/// <param name="warnInvalid">When true, logs warnings about invalid prefabs that are removed during initialization</param>
public void Initialize(bool warnInvalid = true)
{
m_PrefabHashIds.Clear();
m_Prefabs.Clear();
NetworkPrefabsLists.RemoveAll(x => x == null);
foreach (var list in NetworkPrefabsLists)
Expand All @@ -113,7 +175,7 @@ public void Initialize(bool warnInvalid = true)
prefabs.AddRange(list.PrefabList);
}
}

m_PrefabHashIds = new Dictionary<uint, NetworkPrefab>();
m_Prefabs = new List<NetworkPrefab>();

List<NetworkPrefab> removeList = null;
Expand All @@ -124,23 +186,15 @@ public void Initialize(bool warnInvalid = true)

foreach (var networkPrefab in prefabs)
{
if (AddPrefabRegistration(networkPrefab))
{
m_Prefabs.Add(networkPrefab);
}
else
if (!InternalAddPrefab(networkPrefab))
{
removeList?.Add(networkPrefab);
}
}

foreach (var networkPrefab in m_RuntimeAddedPrefabs)
{
if (AddPrefabRegistration(networkPrefab))
{
m_Prefabs.Add(networkPrefab);
}
else
if (!InternalAddPrefab(networkPrefab))
{
removeList?.Add(networkPrefab);
}
Expand Down Expand Up @@ -171,14 +225,12 @@ public void Initialize(bool warnInvalid = true)
/// </remarks>
public bool Add(NetworkPrefab networkPrefab)
{
if (AddPrefabRegistration(networkPrefab))
var added = InternalAddPrefab(networkPrefab);
if (added)
{
m_Prefabs.Add(networkPrefab);
m_RuntimeAddedPrefabs.Add(networkPrefab);
return true;
}

return false;
return added;
}

/// <summary>
Expand All @@ -197,8 +249,7 @@ public void Remove(NetworkPrefab prefab)
{
throw new ArgumentNullException(nameof(prefab));
}

m_Prefabs.Remove(prefab);
InternalRemovePrefab(prefab);
m_RuntimeAddedPrefabs.Remove(prefab);
OverrideToNetworkPrefab.Remove(prefab.TargetPrefabGlobalObjectIdHash);
NetworkPrefabOverrideLinks.Remove(prefab.SourcePrefabGlobalObjectIdHash);
Expand Down Expand Up @@ -294,14 +345,12 @@ private bool AddPrefabRegistration(NetworkPrefab networkPrefab)

uint source = networkPrefab.SourcePrefabGlobalObjectIdHash;
uint target = networkPrefab.TargetPrefabGlobalObjectIdHash;

// Make sure the prefab isn't already registered.
if (NetworkPrefabOverrideLinks.ContainsKey(source))
{
var networkObject = networkPrefab.Prefab.GetComponent<NetworkObject>();

var nameOrHashOverride = networkPrefab.Override == NetworkPrefabOverride.Hash ? $"Hash: {networkPrefab.SourcePrefabGlobalObjectIdHash}" : networkPrefab.Prefab?.name;
// This should never happen, but in the case it somehow does log an error and remove the duplicate entry
Debug.LogError($"{nameof(NetworkPrefab)} ({networkObject.name}) has a duplicate {nameof(NetworkObject.GlobalObjectIdHash)} source entry value of: {source}!");
Debug.LogError($"{nameof(NetworkPrefab)} ({nameOrHashOverride}) has a duplicate {nameof(NetworkObject.GlobalObjectIdHash)} source entry value of: {source}!");
return false;
}

Expand Down
29 changes: 24 additions & 5 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,13 @@ internal set
}
}

/// <summary>
/// This provides a means to determine if the post processing had applied
/// the in-scene placed status or if it was already serialized. This is used
/// when determining if the thing being spawned is a valid thing to spawn.
/// </summary>
internal bool InScenePlacedPostProcessorMarkedDuringRuntime;

/// <summary>
/// Sets whether this NetworkObject was instantiated as part of a scene
/// </summary>
Expand Down Expand Up @@ -1877,18 +1884,27 @@ private void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool play
}
}

// Trap for runtime generated instances as this is not valid
if (GlobalObjectIdHash == 0)
{
NetworkManager.Log.ErrorServer(new Context(LogLevel.Error, $"{name} has a {nameof(GlobalObjectIdHash)} value of {GlobalObjectIdHash}(zero)!" +
$"This is typically a sign of runtime generated {nameof(NetworkObject)}s which is not supported."));
return;
}

// Calculate the legacy IsSceneObject value as the public field is obsolete with warning
// We can't break the public behavior of the field.
#pragma warning disable CS0618 // Type or member is obsolete
var legacyIsSceneObject = IsSceneObject.HasValue && IsSceneObject.Value;
#pragma warning restore CS0618 // Type or member is obsolete

// If SpawnInternal is being called on an object that is marked as InScenePlaced,
// The scene object was never automatically spawned when the scene was loaded.
// Count this object as a dynamically spawned object.
// TODO-[MTT-15388]: Actually support disabled/not spawned InScenePlaced NetworkObjects
if (InScenePlaced && !HasBeenSpawned)
// If the initial state of the GameObject was disabled and InScenePlaced is marked,
// then spawn it as in-scene placed.[MTT-15388]
// Otherwise:
// If we are marked as in-scene place, have never been spawned, and the root GameObject
// was not disabled upon being instantiated, then treat this as a dynamically spawned
// instance.
if (InScenePlaced && !m_GameObjectWasDisabledWhenInstantiated && !HasBeenSpawned)
{
if (NetworkManagerOwner.NetworkConfig.EnableSceneManagement && NetworkManagerOwner.LogLevel <= LogLevel.Developer)
{
Expand Down Expand Up @@ -3701,10 +3717,13 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false)
}
}

private bool m_GameObjectWasDisabledWhenInstantiated;

private void Awake()
{
SetCachedParent(transform.parent);
SceneOrigin = gameObject.scene;
m_GameObjectWasDisabledWhenInstantiated = gameObject.activeInHierarchy;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,9 @@ internal bool AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n
NetworkLog.LogError(new Context(LogLevel.Developer, "Player prefab is marked as belonging to a scene. This may cause issues.").AddNetworkObject(networkObject).AddInfo("SceneName", networkObject.SceneOrigin.name));
networkObject.InScenePlaced = false;
}
NetworkLog.InternalAssert(sceneObject == networkObject.InScenePlaced, "Legacy sceneObject value should match calculated InScenePlaced value.");
// This logic is no longer true with the adjustments to spawn pre-disabled in-scene placed NetworkObjects.
// Leaving this for reference purposes.
//NetworkLog.InternalAssert(sceneObject == networkObject.InScenePlaced, "Legacy sceneObject value should match calculated InScenePlaced value.");

if (!networkObject.InScenePlaced && NetworkManager.LogLevel <= LogLevel.Error)
{
Expand Down Expand Up @@ -1580,10 +1582,52 @@ internal void ServerSpawnSceneObjectsOnStartSweep()
continue;
}

// This used to be two loops.
// The first added all NetworkObjects to a list and the second spawned all NetworkObjects in the list.
// Now, a parent will set its children's IsSceneObject value when spawned, so we check for null or for true.
if (networkObject.InScenePlaced)
// Do not attempt to spawn if it is the actual prefab asset itself:
// - This is not supported by NGO.
// - This will lead to other issues if it gets destroyed, when de-spawned, but the prefab is still registered.
// - This also prevents from spawning integration test prefabs.
if (NetworkManager.NetworkConfig.Prefabs.IsActualPrefabAsset(networkObject))
{
NetworkManager.Log.Warning(new Context(LogLevel.Developer, $"Skipping {networkObject.name} as it is the actual prefab asset itself!"));
continue;
}

// Determine if this is even a valid thing to spawn:
// - If it is not based on a registered prefab, it is invalid.
// - If the GlobalObjectIdHash is zero, it is invalid.
var isInvalidInstanceToSpawn = !NetworkManager.NetworkConfig.Prefabs.IsBasedOnRegisteredPrefab(networkObject) || networkObject.GlobalObjectIdHash == 0;

// If we are a valid prefab asset, marked as in-scene placed, but this was marked during runtime by the post processor.
if (!isInvalidInstanceToSpawn && networkObject.InScenePlaced && networkObject.InScenePlacedPostProcessorMarkedDuringRuntime)
{
// Then it is not in-scene placed and was pre-instantiated. Spawn dynamically.
networkObject.InScenePlaced = false;
}
else if (networkObject.InScenePlaced && !networkObject.InScenePlacedPostProcessorMarkedDuringRuntime)
{
// If this was marked as in-scene placed within the editor, then it is valid.
isInvalidInstanceToSpawn = false;
}

var wasPreInstantiated = !networkObject.IsSpawned && !networkObject.InScenePlaced;

// Dynamically created NetworkObjects instances are not supported and will not be spawned during the sweep.
if (wasPreInstantiated && isInvalidInstanceToSpawn)
{
// If this isn't the original prefab asset being skipped over (integration test would be a good example), then log the error.
if (!NetworkManager.NetworkConfig.Prefabs.IsActualPrefabAsset(networkObject))
{
NetworkManager.Log.Error(new Context(LogLevel.Error, $"{networkObject.name} appears to be a pre-instantiated {nameof(GameObject)} " +
$"with a {nameof(NetworkObject)} component instance that is not a registered prefab nor is it an in-scene placed {nameof(NetworkObject)}." +
$" Dynamically creating unregistered {nameof(NetworkObject)}s is not supported! {networkObject.name} will not be spawned."));
}
continue;
}

// The only valid things to spawn during the sweep are:
// - In-scene placed NetworkObjects.
// - Pre-instantiated NetworkObjects that are registered with the NetworkManager's network prefab list(s).
if (networkObject.InScenePlaced || wasPreInstantiated)
{
var ownerId = networkObject.OwnerClientId;
if (NetworkManager.DistributedAuthorityMode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ internal class BufferDataValidationComponent : NetworkBehaviour
private List<byte> m_SendBuffer;
private List<byte> m_PreCalculatedBufferValues;

// Start is called before the first frame update
private void Start()
protected override void OnNetworkPreSpawn(ref NetworkManager networkManager)
{
m_WaitForValidation = false;
m_CurrentBufferSize = BufferSizeStart;
Expand All @@ -49,6 +48,7 @@ private void Start()
{
m_PreCalculatedBufferValues.Add((byte)Random.Range(0, 255));
}
base.OnNetworkPreSpawn(ref networkManager);
}

/// <summary>
Expand All @@ -67,7 +67,12 @@ public bool IsTestComplete()
// Update is called once per frame
private void Update()
{
if (NetworkManager.Singleton.IsListening && EnableTesting && !IsTestComplete() && !m_WaitForValidation)
if (!EnableTesting || !IsSpawned)
{
return;
}

if (!m_WaitForValidation && !IsTestComplete())
{
m_SendBuffer.Clear();
//Keep the current contents of the bufffer and fill the buffer with the delta difference of the buffer's current size and new size from the m_PreCalculatedBufferValues
Expand All @@ -77,16 +82,16 @@ private void Update()
m_WaitForValidation = true;

//Send the buffer
SendBufferServerRpc(m_SendBuffer.ToArray());
SendBufferRpc(m_SendBuffer.ToArray());
}

}

/// <summary>
/// Server side RPC for testing
/// Sends to self for buffer queue testing
/// </summary>
/// <param name="parameters">server rpc parameters</param>
[ServerRpc]
private void SendBufferServerRpc(byte[] buffer)
[Rpc(SendTo.Me)]
private void SendBufferRpc(byte[] buffer)
{
TestFailed = !NetworkManagerHelper.BuffersMatch(0, buffer.Length, buffer, m_SendBuffer.ToArray());
if (!TestFailed)
Expand Down
Loading
Loading