diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md
index 49ad3bba83..228a4217ea 100644
--- a/com.unity.netcode.gameobjects/CHANGELOG.md
+++ b/com.unity.netcode.gameobjects/CHANGELOG.md
@@ -15,6 +15,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Fixed
+- Fixed issue where a sever only `NetworkManager` instance would spawn the actual `NetworkPrefab`'s `GameObject` as opposed to creating an instance of it. (#3160)
+- Fixed issue where only the session owner (as opposed to all clients) would handle spawning prefab overrides properly when using a distributed authority network topology. (#3160)
- Fixed issue where an exception was thrown when calling `NetworkManager.Shutdown` after calling `UnityTransport.Shutdown`. (#3118)
- Fixed issue where `NetworkList` properties on in-scene placed `NetworkObject`s could cause small memory leaks when entering playmode. (#3147)
- Fixed in-scene `NertworkObject` synchronization issue when loading a scene with currently connected clients connected to a session created by a `NetworkManager` started as a server (i.e. not as a host). (#3133)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
index 661c1ce41b..501cda138d 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
@@ -3246,14 +3246,15 @@ internal void UpdateForSceneChanges()
}
///
- /// Only applies to Hosts or session owners (for now)
+ /// Client-Server: Only applies to spawn authority (i.e. Server)
+ /// Distributed Authority: Applies to all clients since they all have spawn authority.
/// Will return the registered source NetworkPrefab's GlobalObjectIdHash if one exists.
/// Server and Clients will always return the NetworkObject's GlobalObjectIdHash.
///
/// appropriate hash value
internal uint CheckForGlobalObjectIdHashOverride()
{
- if (NetworkManager.IsServer || (NetworkManager.DistributedAuthorityMode && NetworkManager.LocalClient.IsSessionOwner))
+ if (NetworkManager.IsServer || NetworkManager.DistributedAuthorityMode)
{
if (NetworkManager.PrefabHandler.ContainsHandler(this))
{
diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
index 2df9f1ffb6..9ce6595c5f 100644
--- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
@@ -736,12 +736,20 @@ public NetworkObject InstantiateAndSpawn(NetworkObject networkPrefab, ulong owne
internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default)
{
var networkObject = networkPrefab;
- // Host spawns the ovveride and server spawns the original prefab unless forceOverride is set to true where both server or host will spawn the override.
- // In distributed authority mode, we alaways get the override
- if (forceOverride || NetworkManager.IsHost || NetworkManager.DistributedAuthorityMode)
+ // - Host and clients always instantiate the override if one exists.
+ // - Server instantiates the original prefab unless:
+ // -- forceOverride is set to true =or=
+ // -- The prefab has a registered prefab handler, then we let user code determine what to spawn.
+ // - Distributed authority mode always spawns the override if one exists.
+ if (forceOverride || NetworkManager.IsClient || NetworkManager.DistributedAuthorityMode || NetworkManager.PrefabHandler.ContainsHandler(networkPrefab.GlobalObjectIdHash))
{
networkObject = GetNetworkObjectToSpawn(networkPrefab.GlobalObjectIdHash, ownerClientId, position, rotation);
}
+ else // Under this case, server instantiate the prefab passed in.
+ {
+ networkObject = InstantiateNetworkPrefab(networkPrefab.gameObject, networkPrefab.GlobalObjectIdHash, position, rotation);
+ }
+
if (networkObject == null)
{
Debug.LogError($"Failed to instantiate and spawn {networkPrefab.name}!");
@@ -824,16 +832,37 @@ internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ow
else
{
// Create prefab instance while applying any pre-assigned position and rotation values
- networkObject = UnityEngine.Object.Instantiate(networkPrefabReference).GetComponent();
- networkObject.transform.position = position ?? networkObject.transform.position;
- networkObject.transform.rotation = rotation ?? networkObject.transform.rotation;
- networkObject.NetworkManagerOwner = NetworkManager;
- networkObject.PrefabGlobalObjectIdHash = globalObjectIdHash;
+ networkObject = InstantiateNetworkPrefab(networkPrefabReference, globalObjectIdHash, position, rotation);
}
}
return networkObject;
}
+ ///
+ /// Instantiates a network prefab instance, assigns the base prefab , positions, and orients
+ /// the instance.
+ /// !!! Should only be invoked by unless used by an integration test !!!
+ ///
+ ///
+ /// should be the base prefab value and not the
+ /// overrided value.
+ /// (Can be used for integration testing)
+ ///
+ /// prefab to instantiate
+ /// of the base prefab instance
+ /// conditional position in place of the network prefab's default position
+ /// conditional rotation in place of the network prefab's default rotation
+ /// the instance of the
+ internal NetworkObject InstantiateNetworkPrefab(GameObject networkPrefab, uint prefabGlobalObjectIdHash, Vector3? position, Quaternion? rotation)
+ {
+ var networkObject = UnityEngine.Object.Instantiate(networkPrefab).GetComponent();
+ networkObject.transform.position = position ?? networkObject.transform.position;
+ networkObject.transform.rotation = rotation ?? networkObject.transform.rotation;
+ networkObject.NetworkManagerOwner = NetworkManager;
+ networkObject.PrefabGlobalObjectIdHash = prefabGlobalObjectIdHash;
+ return networkObject;
+ }
+
///
/// Creates a local NetowrkObject to be spawned.
///
diff --git a/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTestHelpers.cs b/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTestHelpers.cs
index 8758db8629..1b4e09426a 100644
--- a/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTestHelpers.cs
+++ b/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTestHelpers.cs
@@ -559,6 +559,29 @@ public static void MakeNetworkObjectTestPrefab(NetworkObject networkObject, uint
}
}
+ ///
+ /// Creates a to be used with integration testing
+ ///
+ /// namr of the object
+ /// owner of the object
+ /// when true, the instance is automatically migrated into the DDOL
+ ///
+ internal static GameObject CreateNetworkObject(string baseName, NetworkManager owner, bool moveToDDOL = false)
+ {
+ var gameObject = new GameObject
+ {
+ name = baseName
+ };
+ var networkObject = gameObject.AddComponent();
+ networkObject.NetworkManagerOwner = owner;
+ MakeNetworkObjectTestPrefab(networkObject);
+ if (moveToDDOL)
+ {
+ Object.DontDestroyOnLoad(gameObject);
+ }
+ return gameObject;
+ }
+
public static GameObject CreateNetworkObjectPrefab(string baseName, NetworkManager server, params NetworkManager[] clients)
{
void AddNetworkPrefab(NetworkConfig config, NetworkPrefab prefab)
@@ -570,13 +593,7 @@ void AddNetworkPrefab(NetworkConfig config, NetworkPrefab prefab)
Assert.IsNotNull(server, prefabCreateAssertError);
Assert.IsFalse(server.IsListening, prefabCreateAssertError);
- var gameObject = new GameObject
- {
- name = baseName
- };
- var networkObject = gameObject.AddComponent();
- networkObject.NetworkManagerOwner = server;
- MakeNetworkObjectTestPrefab(networkObject);
+ var gameObject = CreateNetworkObject(baseName, server);
var networkPrefab = new NetworkPrefab() { Prefab = gameObject };
// We could refactor this test framework to share a NetworkPrefabList instance, but at this point it's
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs.meta
new file mode 100644
index 0000000000..588c42fb5b
--- /dev/null
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 83133d71f2112db45ba626157c46e7f8
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/AddNetworkPrefabTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/AddNetworkPrefabTests.cs
similarity index 100%
rename from com.unity.netcode.gameobjects/Tests/Runtime/AddNetworkPrefabTests.cs
rename to com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/AddNetworkPrefabTests.cs
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/AddNetworkPrefabTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/AddNetworkPrefabTests.cs.meta
similarity index 100%
rename from com.unity.netcode.gameobjects/Tests/Runtime/AddNetworkPrefabTests.cs.meta
rename to com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/AddNetworkPrefabTests.cs.meta
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs
similarity index 97%
rename from com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs
rename to com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs
index 2dc5f6001d..c66c843abf 100644
--- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs
@@ -85,9 +85,14 @@ public void NetworkConfigInvalidNetworkPrefabTest()
private const string k_PrefabObjectName = "NetworkPrefabHandlerTestObject";
[Test]
- public void NetworkPrefabHandlerClass()
+ public void NetworkPrefabHandlerClass([Values] bool distributedAuthority)
{
- Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _));
+ var networkConfig = new NetworkConfig()
+ {
+ NetworkTopology = distributedAuthority ? NetworkTopologyTypes.DistributedAuthority : NetworkTopologyTypes.ClientServer,
+ };
+
+ Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _, networkConfig: networkConfig));
var testPrefabObjectName = k_PrefabObjectName;
Guid baseObjectID = NetworkManagerHelper.AddGameNetworkObject(testPrefabObjectName);
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs.meta
similarity index 100%
rename from com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs.meta
rename to com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs.meta
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs
new file mode 100644
index 0000000000..b7d86e3356
--- /dev/null
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs
@@ -0,0 +1,268 @@
+using System.Collections;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using Unity.Netcode.TestHelpers.Runtime;
+using UnityEngine;
+using UnityEngine.TestTools;
+
+namespace Unity.Netcode.RuntimeTests
+{
+ ///
+ /// Integration test that validates spawning instances of s with overrides and
+ /// registered overrides.
+ ///
+ [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Server)]
+ [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Host)]
+ [TestFixture(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost)]
+ internal class NetworkPrefabOverrideTests : NetcodeIntegrationTest
+ {
+ private const string k_PrefabRootName = "PrefabObj";
+ protected override int NumberOfClients => 2;
+
+ private NetworkPrefab m_ClientSidePlayerPrefab;
+ private NetworkPrefab m_PrefabOverride;
+
+ public NetworkPrefabOverrideTests(NetworkTopologyTypes networkTopologyType, HostOrServer hostOrServer) : base(networkTopologyType, hostOrServer) { }
+
+ ///
+ /// Prefab override handler that will instantiate the ServerSideInstance (m_PlayerPrefab) only on server instances
+ /// and will spawn the ClientSideInstance (m_ClientSidePlayerPrefab.Prefab) only on clients and/or a host.
+ ///
+ public class TestPrefabOverrideHandler : MonoBehaviour, INetworkPrefabInstanceHandler
+ {
+ public GameObject ServerSideInstance;
+ public GameObject ClientSideInstance;
+ private NetworkManager m_NetworkManager;
+
+ private void Start()
+ {
+ m_NetworkManager = GetComponent();
+ m_NetworkManager.PrefabHandler.AddHandler(ServerSideInstance, this);
+ }
+
+ private void OnDestroy()
+ {
+ if (m_NetworkManager != null && m_NetworkManager.PrefabHandler != null)
+ {
+ m_NetworkManager.PrefabHandler.RemoveHandler(ServerSideInstance);
+ }
+ }
+
+ public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
+ {
+ var instance = m_NetworkManager.IsClient ? Instantiate(ClientSideInstance) : Instantiate(ServerSideInstance);
+ return instance.GetComponent();
+ }
+
+ public void Destroy(NetworkObject networkObject)
+ {
+ Object.Destroy(networkObject);
+ }
+ }
+
+ ///
+ /// Mock component for testing that the client-side player is using the right
+ /// network prefab.
+ ///
+ public class ClientSideOnlyComponent : MonoBehaviour
+ {
+
+ }
+
+ ///
+ /// When we create the player prefab, make a modified instance that will be used
+ /// with the .
+ ///
+ protected override void OnCreatePlayerPrefab()
+ {
+ var clientPlayer = Object.Instantiate(m_PlayerPrefab);
+ clientPlayer.AddComponent();
+ Object.DontDestroyOnLoad(clientPlayer);
+ m_ClientSidePlayerPrefab = new NetworkPrefab()
+ {
+ Prefab = clientPlayer,
+ };
+
+ base.OnCreatePlayerPrefab();
+ }
+
+ ///
+ /// Add the additional s and s to
+ /// all instances.
+ ///
+ protected override void OnServerAndClientsCreated()
+ {
+ // Create a NetworkPrefab with an override
+ var basePrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-base", m_ServerNetworkManager, true);
+ var targetPrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-over", m_ServerNetworkManager, true);
+ m_PrefabOverride = new NetworkPrefab()
+ {
+ Prefab = basePrefab,
+ Override = NetworkPrefabOverride.Prefab,
+ SourcePrefabToOverride = basePrefab,
+ OverridingTargetPrefab = targetPrefab,
+ };
+
+ // Add the prefab override handler for instance specific player prefabs to the server side
+ var playerPrefabOverrideHandler = m_ServerNetworkManager.gameObject.AddComponent();
+ playerPrefabOverrideHandler.ServerSideInstance = m_PlayerPrefab;
+ playerPrefabOverrideHandler.ClientSideInstance = m_ClientSidePlayerPrefab.Prefab;
+
+ // Add the NetworkPrefab with override
+ m_ServerNetworkManager.NetworkConfig.Prefabs.Add(m_PrefabOverride);
+ // Add the client player prefab that will be used on clients (and the host)
+ m_ServerNetworkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab);
+
+ foreach (var networkManager in m_ClientNetworkManagers)
+ {
+ // Add the prefab override handler for instance specific player prefabs to the client side
+ playerPrefabOverrideHandler = networkManager.gameObject.AddComponent();
+ playerPrefabOverrideHandler.ServerSideInstance = m_PlayerPrefab;
+ playerPrefabOverrideHandler.ClientSideInstance = m_ClientSidePlayerPrefab.Prefab;
+
+ // Add the NetworkPrefab with override
+ networkManager.NetworkConfig.Prefabs.Add(m_PrefabOverride);
+ // Add the client player prefab that will be used on clients (and the host)
+ networkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab);
+ }
+
+ m_PrefabOverride.Prefab.GetComponent().IsSceneObject = false;
+ m_PrefabOverride.SourcePrefabToOverride.GetComponent().IsSceneObject = false;
+ m_PrefabOverride.OverridingTargetPrefab.GetComponent().IsSceneObject = false;
+ m_ClientSidePlayerPrefab.Prefab.GetComponent().IsSceneObject = false;
+
+ base.OnServerAndClientsCreated();
+ }
+
+ protected override IEnumerator OnTearDown()
+ {
+ if (m_PrefabOverride != null)
+ {
+ if (m_PrefabOverride.SourcePrefabToOverride)
+ {
+ Object.Destroy(m_PrefabOverride.SourcePrefabToOverride);
+ }
+
+ if (m_PrefabOverride.OverridingTargetPrefab)
+ {
+ Object.Destroy(m_PrefabOverride.OverridingTargetPrefab);
+ }
+ }
+
+ if (m_ClientSidePlayerPrefab != null)
+ {
+ if (m_ClientSidePlayerPrefab.Prefab)
+ {
+ Object.Destroy(m_ClientSidePlayerPrefab.Prefab);
+ }
+ }
+ m_ClientSidePlayerPrefab = null;
+ m_PrefabOverride = null;
+
+ yield return base.OnTearDown();
+ }
+
+
+ private GameObject GetPlayerNetworkPrefabObject(NetworkManager networkManager)
+ {
+ return networkManager.IsClient ? m_ClientSidePlayerPrefab.Prefab : m_PlayerPrefab;
+ }
+
+ [UnityTest]
+ public IEnumerator PrefabOverrideTests()
+ {
+ var prefabNetworkObject = (NetworkObject)null;
+ var spawnedGlobalObjectId = (uint)0;
+
+ var networkManagers = m_ClientNetworkManagers.ToList();
+ if (m_UseHost)
+ {
+ networkManagers.Insert(0, m_ServerNetworkManager);
+ }
+ else
+ {
+ // If running as just a server, validate that all player prefab clone instances are the server side version
+ prefabNetworkObject = GetPlayerNetworkPrefabObject(m_ServerNetworkManager).GetComponent();
+ foreach (var playerEntry in m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId])
+ {
+ spawnedGlobalObjectId = playerEntry.Value.GlobalObjectIdHash;
+ Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"Server-Side {playerEntry.Value.name} was spawned as prefab ({spawnedGlobalObjectId}) but we expected ({prefabNetworkObject.GlobalObjectIdHash})!");
+ }
+ }
+
+ // Validates prefab overrides via the NetworkPrefabHandler.
+ // Validate the player prefab instance clones relative to all NetworkManagers.
+ foreach (var networkManager in networkManagers)
+ {
+ // Get the expected player prefab to be spawned based on the NetworkManager
+ prefabNetworkObject = GetPlayerNetworkPrefabObject(networkManager).GetComponent();
+ if (networkManager.IsClient)
+ {
+ spawnedGlobalObjectId = networkManager.LocalClient.PlayerObject.GlobalObjectIdHash;
+ Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"{networkManager.name} spawned player prefab ({spawnedGlobalObjectId}) did not match the expected one ({prefabNetworkObject.GlobalObjectIdHash})!");
+ }
+
+ foreach (var playerEntry in m_PlayerNetworkObjects[networkManager.LocalClientId])
+ {
+ // We already checked our locally spawned player prefab above
+ if (playerEntry.Key == networkManager.LocalClientId)
+ {
+ continue;
+ }
+ spawnedGlobalObjectId = playerEntry.Value.GlobalObjectIdHash;
+ Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"Client-{networkManager.LocalClientId} clone of {playerEntry.Value.name} was spawned as prefab ({spawnedGlobalObjectId}) but we expected ({prefabNetworkObject.GlobalObjectIdHash})!");
+ }
+ }
+
+ // Validates prefab overrides via NetworkPrefab configuration.
+ var spawnedInstance = (NetworkObject)null;
+ var networkManagerOwner = m_ServerNetworkManager;
+
+ if (m_DistributedAuthority)
+ {
+ networkManagerOwner = m_ClientNetworkManagers[0];
+ }
+ // Clients and Host will spawn the OverridingTargetPrefab while a dedicated server will spawn the SourcePrefabToOverride
+ var expectedServerGlobalObjectIdHash = networkManagerOwner.IsClient ? m_PrefabOverride.OverridingTargetPrefab.GetComponent().GlobalObjectIdHash : m_PrefabOverride.SourcePrefabToOverride.GetComponent().GlobalObjectIdHash;
+ var expectedClientGlobalObjectIdHash = m_PrefabOverride.OverridingTargetPrefab.GetComponent().GlobalObjectIdHash;
+
+ spawnedInstance = NetworkObject.InstantiateAndSpawn(m_PrefabOverride.SourcePrefabToOverride, networkManagerOwner, networkManagerOwner.LocalClientId);
+ var builder = new StringBuilder();
+ bool ObjectSpawnedOnAllNetworkMangers()
+ {
+ builder.Clear();
+ if (!m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(spawnedInstance.NetworkObjectId))
+ {
+ builder.AppendLine($"Client-{m_ServerNetworkManager.LocalClientId} failed to spawn {spawnedInstance.name}-{spawnedInstance.NetworkObjectId}!");
+ return false;
+ }
+ var instanceGID = m_ServerNetworkManager.SpawnManager.SpawnedObjects[spawnedInstance.NetworkObjectId].GlobalObjectIdHash;
+ if (instanceGID != expectedServerGlobalObjectIdHash)
+ {
+ builder.AppendLine($"Client-{m_ServerNetworkManager.LocalClientId} instance {spawnedInstance.name}-{spawnedInstance.NetworkObjectId} GID is {instanceGID} but was expected to be {expectedServerGlobalObjectIdHash}!");
+ return false;
+ }
+
+ foreach (var networkManger in m_ClientNetworkManagers)
+ {
+ if (!networkManger.SpawnManager.SpawnedObjects.ContainsKey(spawnedInstance.NetworkObjectId))
+ {
+ builder.AppendLine($"Client-{networkManger.LocalClientId} failed to spawn {spawnedInstance.name}-{spawnedInstance.NetworkObjectId}!");
+ return false;
+ }
+ instanceGID = networkManger.SpawnManager.SpawnedObjects[spawnedInstance.NetworkObjectId].GlobalObjectIdHash;
+ if (instanceGID != expectedClientGlobalObjectIdHash)
+ {
+ builder.AppendLine($"Client-{networkManger.LocalClientId} instance {spawnedInstance.name}-{spawnedInstance.NetworkObjectId} GID is {instanceGID} but was expected to be {expectedClientGlobalObjectIdHash}!");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ yield return WaitForConditionOrTimeOut(ObjectSpawnedOnAllNetworkMangers);
+ AssertOnTimeout($"The spawned prefab override validation failed!\n {builder}");
+ }
+ }
+}
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs.meta
new file mode 100644
index 0000000000..e0120dbf5c
--- /dev/null
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: c5e27e24bc5f783458d6a45585308f87
\ No newline at end of file