From 88eaa08d380bdbaa7dca03062193158ddab325d3 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Mon, 9 Dec 2024 17:29:58 -0600 Subject: [PATCH] fix: distributed authority spawning prefab overrides (#3160) * fix All clients in distributed authority should invoke CheckForGlobalObjectIdHashOverride when spawning a NetworkPrefab since each client has the authority to spawn (i.e. each should be treated like a host/server in regards to spawning and prefab overrides). * update adding changelog entry. * update adding changelog PR number. * test updating existing test to run through with distributed authority and client-server network topologies. (still need one more integration test to validate the final spawned object on the non-authority sides) * style removing whitespace * fix Fixed issue with server only NetworkManager instance spawning the network prefab itself as opposed to creating an instance. Extracted the instantiation portion of a network prefab into an internal method that can be used for integration testing prefab instantiation and potentially other edge case scenarios. * test Migrated all runtime network prefab related tests into a Prefabs subfolder. Added new NetworkPrefabOverrideTests to validate the actual spawned instances on all clients using both client-server and distributed authority network topologies. Added helper method `NettcodeIntegrationTestHelpers.CreateNetworkObject`. * style correcting some typos in comments --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 + .../Runtime/Core/NetworkObject.cs | 5 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 45 ++- .../Runtime/NetcodeIntegrationTestHelpers.cs | 31 +- .../Tests/Runtime/Prefabs.meta | 8 + .../{ => Prefabs}/AddNetworkPrefabTests.cs | 0 .../AddNetworkPrefabTests.cs.meta | 0 .../NetworkPrefabHandlerTests.cs | 9 +- .../NetworkPrefabHandlerTests.cs.meta | 0 .../Prefabs/NetworkPrefabOverrideTests.cs | 268 ++++++++++++++++++ .../NetworkPrefabOverrideTests.cs.meta | 2 + 11 files changed, 351 insertions(+), 19 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/Prefabs.meta rename com.unity.netcode.gameobjects/Tests/Runtime/{ => Prefabs}/AddNetworkPrefabTests.cs (100%) rename com.unity.netcode.gameobjects/Tests/Runtime/{ => Prefabs}/AddNetworkPrefabTests.cs.meta (100%) rename com.unity.netcode.gameobjects/Tests/Runtime/{ => Prefabs}/NetworkPrefabHandlerTests.cs (97%) rename com.unity.netcode.gameobjects/Tests/Runtime/{ => Prefabs}/NetworkPrefabHandlerTests.cs.meta (100%) create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs.meta 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