This repository has been archived on 2025-04-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
com.unity.netcode.gameobjects/Tests/Runtime/DeferredMessagingTests.cs
Unity Technologies 07f206ff9e com.unity.netcode.gameobjects@1.8.0
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [1.8.0] - 2023-12-12

### Added

- Added a new RPC attribute, which is simply `Rpc`. (#2762)
  - This is a generic attribute that can perform the functions of both Server and Client RPCs, as well as enabling client-to-client RPCs. Includes several default targets: `Server`, `NotServer`, `Owner`, `NotOwner`, `Me`, `NotMe`, `ClientsAndHost`, and `Everyone`. Runtime overrides are available for any of these targets, as well as for sending to a specific ID or groups of IDs.
  - This attribute also includes the ability to defer RPCs that are sent to the local process to the start of the next frame instead of executing them immediately, treating them as if they had gone across the network. The default behavior is to execute immediately.
  - This attribute effectively replaces `ServerRpc` and `ClientRpc`. `ServerRpc` and `ClientRpc` remain in their existing forms for backward compatibility, but `Rpc` will be the recommended and most supported option.
- Added `NetworkManager.OnConnectionEvent` as a unified connection event callback to notify clients and servers of all client connections and disconnections within the session (#2762)
- Added `NetworkManager.ServerIsHost` and `NetworkBehaviour.ServerIsHost` to allow a client to tell if it is connected to a host or to a dedicated server (#2762)
- Added `SceneEventProgress.SceneManagementNotEnabled` return status to be returned when a `NetworkSceneManager` method is invoked and scene management is not enabled. (#2735)
- Added `SceneEventProgress.ServerOnlyAction` return status to be returned when a `NetworkSceneManager` method is invoked by a client. (#2735)
- Added `NetworkObject.InstantiateAndSpawn` and `NetworkSpawnManager.InstantiateAndSpawn` methods to simplify prefab spawning by assuring that the prefab is valid and applies any override prior to instantiating the `GameObject` and spawning the `NetworkObject` instance. (#2710)

### Fixed

- Fixed issue where a client disconnected by a server-host would not receive a local notification. (#2789)
- Fixed issue where a server-host could shutdown during a relay connection but periodically the transport disconnect message sent to any connected clients could be dropped. (#2789)
- Fixed issue where a host could disconnect its local client but remain running as a server. (#2789)
- Fixed issue where `OnClientDisconnectedCallback` was not being invoked under certain conditions. (#2789)
- Fixed issue where `OnClientDisconnectedCallback` was always returning 0 as the client identifier. (#2789)
- Fixed issue where if a host or server shutdown while a client owned NetworkObjects (other than the player) it would throw an exception. (#2789)
- Fixed issue where setting values on a `NetworkVariable` or `NetworkList` within `OnNetworkDespawn` during a shutdown sequence would throw an exception. (#2789)
- Fixed issue where a teleport state could potentially be overridden by a previous unreliable delta state. (#2777)
- Fixed issue where `NetworkTransform` was using the `NetworkManager.ServerTime.Tick` as opposed to `NetworkManager.NetworkTickSystem.ServerTime.Tick` during the authoritative side's tick update where it performed a delta state check. (#2777)
- Fixed issue where a parented in-scene placed NetworkObject would be destroyed upon a client or server exiting a network session but not unloading the original scene in which the NetworkObject was placed. (#2737)
- Fixed issue where during client synchronization and scene loading, when client synchronization or the scene loading mode are set to `LoadSceneMode.Single`, a `CreateObjectMessage` could be received, processed, and the resultant spawned `NetworkObject` could be instantiated in the client's currently active scene that could, towards the end of the client synchronization or loading process, be unloaded and cause the newly created `NetworkObject` to be destroyed (and throw and exception). (#2735)
- Fixed issue where a `NetworkTransform` instance with interpolation enabled would result in wide visual motion gaps (stuttering) under above normal latency conditions and a 1-5% or higher packet are drop rate. (#2713)
- Fixed issue where  you could not have multiple source network prefab overrides targeting the same network prefab as their override. (#2710)

### Changed
- Changed the server or host shutdown so it will now perform a "soft shutdown" when `NetworkManager.Shutdown` is invoked. This will send a disconnect notification to all connected clients and the server-host will wait for all connected clients to disconnect or timeout after a 5 second period before completing the shutdown process. (#2789)
- Changed `OnClientDisconnectedCallback` will now return the assigned client identifier on the local client side if the client was approved and assigned one prior to being disconnected. (#2789)
- Changed `NetworkTransform.SetState` (and related methods) now are cumulative during a fractional tick period and sent on the next pending tick. (#2777)
- `NetworkManager.ConnectedClientsIds` is now accessible on the client side and will contain the list of all clients in the session, including the host client if the server is operating in host mode (#2762)
- Changed `NetworkSceneManager` to return a `SceneEventProgress` status and not throw exceptions for methods invoked when scene management is disabled and when a client attempts to access a `NetworkSceneManager` method by a client. (#2735)
- Changed `NetworkTransform` authoritative instance tick registration so a single `NetworkTransform` specific tick event update will update all authoritative instances to improve perofmance. (#2713)
- Changed `NetworkPrefabs.OverrideToNetworkPrefab` dictionary is no longer used/populated due to it ending up being related to a regression bug and not allowing more than one override to be assigned to a network prefab asset. (#2710)
- Changed in-scene placed `NetworkObject`s now store their source network prefab asset's `GlobalObjectIdHash` internally that is used, when scene management is disabled, by clients to spawn the correct prefab even if the `NetworkPrefab` entry has an override. This does not impact dynamically spawning the same prefab which will yield the override on both host and client. (#2710)
- Changed in-scene placed `NetworkObject`s no longer require a `NetworkPrefab` entry with `GlobalObjectIdHash` override in order for clients to properly synchronize. (#2710)
- Changed in-scene placed `NetworkObject`s now set their `IsSceneObject` value when generating their `GlobalObjectIdHash` value. (#2710)
- Changed the default `NetworkConfig.SpawnTimeout` value from 1.0s to 10.0s. (#2710)
2023-12-12 00:00:00 +00:00

1335 lines
60 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace Unity.Netcode.RuntimeTests
{
internal class TestDeferredMessageManager : DeferredMessageManager
{
public bool DeferMessageCalled;
public bool ProcessTriggersCalled;
public delegate void BeforePurgeDelegate(TestDeferredMessageManager manager, ulong key);
public delegate void BeforeDeferDelegate(TestDeferredMessageManager manager, ulong key);
public BeforePurgeDelegate OnBeforePurge;
public BeforeDeferDelegate OnBeforeDefer;
// The way that OnBeforePurge gets used in these tests involves counting things that are still in the
// deferred message queue. This is something the base class doesn't support so the way it purges individual
// messages does not make it safe to access structures during purging. This set contains a list of
// keys that are unsafe to access because they've been purged/disposed, but not removed from the dictionary
// yet, so that we can avoid accessing those and record them instead as 0 (which is what they will be once
// the purge completes)
private HashSet<ulong> m_PurgedKeys = new HashSet<ulong>();
public TestDeferredMessageManager(NetworkManager networkManager) :
base(networkManager)
{
}
public void ClearCallFlags()
{
DeferMessageCalled = false;
ProcessTriggersCalled = false;
}
public int DeferredMessageCountTotal()
{
int ret = 0;
foreach (var kvp in m_Triggers)
{
ret += DeferredMessageCountForType(kvp.Key);
}
return ret;
}
public int DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType trigger)
{
var count = 0;
if (m_Triggers.TryGetValue(trigger, out var dict))
{
foreach (var kvp in dict)
{
if (m_PurgedKeys.Contains(kvp.Key))
{
continue;
}
count += kvp.Value.TriggerData.Length;
}
}
return count;
}
public int DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType trigger, ulong key)
{
if (m_PurgedKeys.Contains(key))
{
return 0;
}
if (m_Triggers.TryGetValue(trigger, out var dict))
{
if (dict.TryGetValue(key, out var subdict))
{
return subdict.TriggerData.Length;
}
}
return 0;
}
public override void DeferMessage(IDeferredNetworkMessageManager.TriggerType trigger, ulong key, FastBufferReader reader, ref NetworkContext context)
{
OnBeforeDefer?.Invoke(this, key);
DeferMessageCalled = true;
base.DeferMessage(trigger, key, reader, ref context);
}
public override void ProcessTriggers(IDeferredNetworkMessageManager.TriggerType trigger, ulong key)
{
if (trigger == IDeferredNetworkMessageManager.TriggerType.OnNextFrame)
{
return;
}
ProcessTriggersCalled = true;
base.ProcessTriggers(trigger, key);
}
protected override void PurgeTrigger(IDeferredNetworkMessageManager.TriggerType triggerType, ulong key, TriggerInfo triggerInfo)
{
OnBeforePurge?.Invoke(this, key);
base.PurgeTrigger(triggerType, key, triggerInfo);
m_PurgedKeys.Add(key);
}
public override void CleanupStaleTriggers()
{
base.CleanupStaleTriggers();
m_PurgedKeys.Clear();
}
}
public class DeferredMessageTestRpcComponent : NetworkBehaviour
{
public bool ClientRpcCalled;
[ClientRpc]
public void SendTestClientRpc()
{
ClientRpcCalled = true;
}
public static readonly List<ulong> ClientInstances = new List<ulong>();
public override void OnNetworkSpawn()
{
if (!IsServer)
{
ClientInstances.Add(NetworkManager.LocalClientId);
}
base.OnNetworkSpawn();
}
}
public class DeferredMessageTestNetworkVariableComponent : NetworkBehaviour
{
public static readonly List<ulong> ClientInstances = new List<ulong>();
public NetworkVariable<int> TestNetworkVariable;
public void Awake()
{
TestNetworkVariable = new NetworkVariable<int>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
}
public override void OnNetworkSpawn()
{
if (!IsServer)
{
ClientInstances.Add(NetworkManager.LocalClientId);
}
base.OnNetworkSpawn();
}
}
public class DeferredMessageTestRpcAndNetworkVariableComponent : NetworkBehaviour
{
public static readonly List<ulong> ClientInstances = new List<ulong>();
public bool ClientRpcCalled;
public NetworkVariable<int> TestNetworkVariable;
public void Awake()
{
TestNetworkVariable = new NetworkVariable<int>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
}
public override void OnNetworkSpawn()
{
if (!IsServer)
{
ClientInstances.Add(NetworkManager.LocalClientId);
}
base.OnNetworkSpawn();
}
[ClientRpc]
public void SendTestClientRpc()
{
ClientRpcCalled = true;
}
}
public class DeferredMessagingTest : NetcodeIntegrationTest
{
protected override int NumberOfClients => 0;
private List<MessageCatcher<CreateObjectMessage>> m_ClientSpawnCatchers = new List<MessageCatcher<CreateObjectMessage>>();
private GameObject m_RpcPrefab;
private GameObject m_NetworkVariablePrefab;
private GameObject m_RpcAndNetworkVariablePrefab;
private int m_NumberOfClientsToLateJoin = 2;
protected override bool m_EnableTimeTravel => true;
protected override bool m_SetupIsACoroutine => false;
protected override bool m_TearDownIsACoroutine => false;
protected override void OnInlineSetup()
{
DeferredMessageTestRpcAndNetworkVariableComponent.ClientInstances.Clear();
DeferredMessageTestRpcComponent.ClientInstances.Clear();
DeferredMessageTestNetworkVariableComponent.ClientInstances.Clear();
m_SkipAddingPrefabsToClient = false;
// Host is irrelevant, messages don't get sent to the host "client"
m_UseHost = false;
// Replace the IDeferredMessageManager component with our test one in the component factory
ComponentFactory.Register<IDeferredNetworkMessageManager>(networkManager => new TestDeferredMessageManager(networkManager));
}
protected override void OnInlineTearDown()
{
// Revert the IDeferredMessageManager component to its default (DeferredMessageManager)
ComponentFactory.Deregister<IDeferredNetworkMessageManager>();
m_ClientSpawnCatchers.Clear();
}
protected override void OnServerAndClientsCreated()
{
// Note: This is where prefabs should be created
m_RpcPrefab = CreateNetworkObjectPrefab("Object With RPC");
var networkObject = m_RpcPrefab.GetComponent<NetworkObject>();
m_RpcPrefab.AddComponent<DeferredMessageTestRpcComponent>();
m_NetworkVariablePrefab = CreateNetworkObjectPrefab("Object With NetworkVariable");
networkObject = m_NetworkVariablePrefab.GetComponent<NetworkObject>();
m_NetworkVariablePrefab.AddComponent<DeferredMessageTestNetworkVariableComponent>();
m_RpcAndNetworkVariablePrefab = CreateNetworkObjectPrefab("Object With NetworkVariable And RPC");
networkObject = m_RpcAndNetworkVariablePrefab.GetComponent<NetworkObject>();
m_RpcAndNetworkVariablePrefab.AddComponent<DeferredMessageTestRpcAndNetworkVariableComponent>();
m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false;
}
private bool m_SkipAddingPrefabsToClient = false;
private void AddPrefabsToClient(NetworkManager networkManager)
{
networkManager.AddNetworkPrefab(m_RpcPrefab);
networkManager.AddNetworkPrefab(m_NetworkVariablePrefab);
networkManager.AddNetworkPrefab(m_RpcAndNetworkVariablePrefab);
}
protected override void OnNewClientCreated(NetworkManager networkManager)
{
networkManager.NetworkConfig.ForceSamePrefabs = false;
if (!m_SkipAddingPrefabsToClient)
{
AddPrefabsToClient(networkManager);
}
base.OnNewClientCreated(networkManager);
}
private void SpawnClients(bool clearTestDeferredMessageManagerCallFlags = true)
{
for (int i = 0; i < m_NumberOfClientsToLateJoin; i++)
{
// Create and join client
CreateAndStartNewClientWithTimeTravel();
}
if (clearTestDeferredMessageManagerCallFlags)
{
ClearTestDeferredMessageManagerCallFlags();
}
}
private T GetComponentForClient<T>(ulong clientId) where T : NetworkBehaviour
{
#if UNITY_2023_1_OR_NEWER
var componentsToFind = Object.FindObjectsByType<T>(FindObjectsSortMode.InstanceID);
#else
var componentsToFind = Object.FindObjectsOfType<T>();
#endif
foreach (var component in componentsToFind)
{
if (component.IsSpawned && component.NetworkManager.LocalClientId == clientId)
{
return component;
}
}
return null;
}
private void CatchSpawns()
{
foreach (var client in m_ClientNetworkManagers)
{
var catcher = new MessageCatcher<CreateObjectMessage>(client);
m_ClientSpawnCatchers.Add(catcher);
client.ConnectionManager.MessageManager.Hook(catcher);
}
}
private void ReleaseSpawns()
{
for (var i = 0; i < m_ClientNetworkManagers.Length; ++i)
{
// Unhook first so the spawn catcher stops catching spawns
m_ClientNetworkManagers[i].ConnectionManager.MessageManager.Unhook(m_ClientSpawnCatchers[i]);
m_ClientSpawnCatchers[i].ReleaseMessages();
}
m_ClientSpawnCatchers.Clear();
}
protected override void OnTimeTravelServerAndClientsConnected()
{
// Clear out these values from whatever might have set them during the initial startup.
ClearTestDeferredMessageManagerCallFlags();
}
private void WaitForClientsToCatchSpawns(int count = 1)
{
Assert.IsTrue(WaitForConditionOrTimeOutWithTimeTravel(() =>
{
foreach (var catcher in m_ClientSpawnCatchers)
{
if (catcher.CaughtMessageCount != count)
{
return false;
}
}
return true;
}));
}
private void ClearTestDeferredMessageManagerCallFlags()
{
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.ClearCallFlags();
}
}
private void AssertSpawnTriggerCountForObject(TestDeferredMessageManager manager, GameObject serverObject, int expectedCount = 1)
{
Assert.AreEqual(expectedCount, manager.DeferredMessageCountTotal());
Assert.AreEqual(expectedCount, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(expectedCount, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab));
}
private void WaitForAllClientsToReceive<T>() where T : INetworkMessage
{
WaitForMessageReceivedWithTimeTravel<T>(m_ClientNetworkManagers.ToList(), ReceiptType.Received);
}
private void WaitForAllClientsToReceive<TFirstMessage, TSecondMessage>()
where TFirstMessage : INetworkMessage
where TSecondMessage : INetworkMessage
{
WaitForMessagesReceivedWithTimeTravel(new List<Type>
{
typeof(TFirstMessage),
typeof(TSecondMessage)
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
}
private void WaitForAllClientsToReceive<TFirstMessage, TSecondMessage, TThirdMessage>()
where TFirstMessage : INetworkMessage
where TSecondMessage : INetworkMessage
where TThirdMessage : INetworkMessage
{
WaitForMessagesReceivedWithTimeTravel(new List<Type>
{
typeof(TFirstMessage),
typeof(TSecondMessage),
typeof(TThirdMessage),
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
}
private void WaitForAllClientsToReceive<TFirstMessage, TSecondMessage, TThirdMessage, TFourthMessage>()
where TFirstMessage : INetworkMessage
where TSecondMessage : INetworkMessage
where TThirdMessage : INetworkMessage
where TFourthMessage : INetworkMessage
{
WaitForMessagesReceivedWithTimeTravel(new List<Type>
{
typeof(TFirstMessage),
typeof(TSecondMessage),
typeof(TThirdMessage),
typeof(TFourthMessage),
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
}
[Test]
public void WhenAnRpcArrivesBeforeASpawnArrives_ItIsDeferred()
{
SpawnClients();
CatchSpawns();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns();
serverObject.GetComponent<DeferredMessageTestRpcComponent>().SendTestClientRpc();
WaitForAllClientsToReceive<ClientRpcMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
AssertSpawnTriggerCountForObject(manager, serverObject);
}
}
[Test]
public void WhenADespawnArrivesBeforeASpawnArrives_ItIsDeferred()
{
SpawnClients();
CatchSpawns();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns();
serverObject.GetComponent<NetworkObject>().Despawn(false);
WaitForAllClientsToReceive<DestroyObjectMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
AssertSpawnTriggerCountForObject(manager, serverObject);
}
}
[Test]
public void WhenAChangeOwnershipMessageArrivesBeforeASpawnArrives_ItIsDeferred()
{
SpawnClients();
CatchSpawns();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns();
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
AssertSpawnTriggerCountForObject(manager, serverObject);
}
}
[Test]
public void WhenANetworkVariableDeltaMessageArrivesBeforeASpawnArrives_ItIsDeferred()
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
CatchSpawns();
var serverObject = Object.Instantiate(m_NetworkVariablePrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns();
serverObject.GetComponent<DeferredMessageTestNetworkVariableComponent>().TestNetworkVariable.Value = 1;
WaitForAllClientsToReceive<NetworkVariableDeltaMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
// TODO: Network Variables generate an extra message immediately at spawn for some reason...
// Seems like a bug since the network variable data is in the spawn message already.
AssertSpawnTriggerCountForObject(manager, serverObject, 1);
}
}
[Test]
//[Ignore("Disabling this temporarily until it is migrated into new integration test.")]
public void WhenASpawnMessageArrivesBeforeThePrefabIsAvailable_ItIsDeferred()
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForAllClientsToReceive<CreateObjectMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
Assert.AreEqual(1, manager.DeferredMessageCountTotal());
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab, serverObject.GetComponent<NetworkObject>().GlobalObjectIdHash));
var component = GetComponentForClient<DeferredMessageTestRpcComponent>(client.LocalClientId);
Assert.IsNull(component);
}
}
[Test]
public void WhenAnRpcIsDeferred_ItIsProcessedOnSpawn()
{
WhenAnRpcArrivesBeforeASpawnArrives_ItIsDeferred();
ReleaseSpawns();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
Assert.IsTrue(manager.ProcessTriggersCalled);
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
var component = GetComponentForClient<DeferredMessageTestRpcComponent>(client.LocalClientId);
Assert.IsTrue(component.ClientRpcCalled);
}
}
[Test]
public void WhenADespawnIsDeferred_ItIsProcessedOnSpawn()
{
WhenADespawnArrivesBeforeASpawnArrives_ItIsDeferred();
ReleaseSpawns();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
Assert.IsTrue(manager.ProcessTriggersCalled);
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
// Should have been immediately despawned.
var component = GetComponentForClient<DeferredMessageTestRpcComponent>(client.LocalClientId);
Assert.IsTrue(!component);
}
}
[Test]
public void WhenAChangeOwnershipMessageIsDeferred_ItIsProcessedOnSpawn()
{
WhenAChangeOwnershipMessageArrivesBeforeASpawnArrives_ItIsDeferred();
ReleaseSpawns();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.ProcessTriggersCalled);
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
var component = GetComponentForClient<DeferredMessageTestRpcComponent>(client.LocalClientId);
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, component.OwnerClientId);
}
}
[Test]
public void WhenANetworkVariableDeltaMessageIsDeferred_ItIsProcessedOnSpawn()
{
WhenANetworkVariableDeltaMessageArrivesBeforeASpawnArrives_ItIsDeferred();
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
ReleaseSpawns();
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestNetworkVariableComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
WaitForConditionOrTimeOutWithTimeTravel(HaveAllClientsSpawned);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.ProcessTriggersCalled);
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
var component = GetComponentForClient<DeferredMessageTestNetworkVariableComponent>(client.LocalClientId);
Assert.AreEqual(1, component.TestNetworkVariable.Value);
}
}
[Test]
public void WhenASpawnMessageIsDeferred_ItIsProcessedOnAddPrefab()
{
// This will prevent spawned clients from adding prefabs
m_SkipAddingPrefabsToClient = true;
WhenASpawnMessageArrivesBeforeThePrefabIsAvailable_ItIsDeferred();
// Now add the prefabs
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestRpcComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
WaitForConditionOrTimeOutWithTimeTravel(HaveAllClientsSpawned);
// Validate this test
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.ProcessTriggersCalled);
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
var component = GetComponentForClient<DeferredMessageTestRpcComponent>(client.LocalClientId);
Assert.IsNotNull(component);
}
}
[Test]
public void WhenMultipleSpawnTriggeredMessagesAreDeferred_TheyAreAllProcessedOnSpawn()
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
CatchSpawns();
var serverObject = Object.Instantiate(m_RpcAndNetworkVariablePrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns();
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().SendTestClientRpc();
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
WaitForAllClientsToReceive<ClientRpcMessage, NetworkVariableDeltaMessage>();
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage, NetworkVariableDeltaMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
Assert.AreEqual(4, manager.DeferredMessageCountTotal());
Assert.AreEqual(4, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(4, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab));
AddPrefabsToClient(client);
}
ReleaseSpawns();
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestRpcAndNetworkVariableComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
WaitForConditionOrTimeOutWithTimeTravel(HaveAllClientsSpawned);
TimeTravel(0.1, 1);
// Validate the spawned objects
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.ProcessTriggersCalled, "Process triggers were not called!");
Assert.AreEqual(0, manager.DeferredMessageCountTotal(), $"Deferred message count ({manager.DeferredMessageCountTotal()}) is not zero!");
var component = GetComponentForClient<DeferredMessageTestRpcAndNetworkVariableComponent>(client.LocalClientId);
Assert.IsTrue(component.ClientRpcCalled, "Client RPC was not called!");
Assert.AreEqual(1, component.TestNetworkVariable.Value, $"Test {nameof(NetworkVariable<int>)} ({component.TestNetworkVariable.Value}) does not equal 1!");
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, component.OwnerClientId, $"{component.name} owner id ({component.OwnerClientId}) does not equal first client id ({m_ClientNetworkManagers[0].LocalClientId})");
}
}
[Test]
public void WhenMultipleAddPrefabTriggeredMessagesAreDeferred_TheyAreAllProcessedOnAddNetworkPrefab()
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
var serverObject2 = Object.Instantiate(m_RpcPrefab);
serverObject2.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject2.GetComponent<NetworkObject>().Spawn();
WaitForAllClientsToReceive<CreateObjectMessage, CreateObjectMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
Assert.AreEqual(2, manager.DeferredMessageCountTotal());
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(2, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab));
Assert.AreEqual(2, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab, serverObject.GetComponent<NetworkObject>().GlobalObjectIdHash));
AddPrefabsToClient(client);
}
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestRpcComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
WaitForConditionOrTimeOutWithTimeTravel(HaveAllClientsSpawned);
foreach (var client in m_ClientNetworkManagers)
{
var found1 = false;
var found2 = false;
#if UNITY_2023_1_OR_NEWER
var deferredMessageTestRpcComponents = Object.FindObjectsByType<DeferredMessageTestRpcComponent>(FindObjectsSortMode.None);
#else
var deferredMessageTestRpcComponents = Object.FindObjectsOfType<DeferredMessageTestRpcComponent>();
#endif
foreach (var component in deferredMessageTestRpcComponents)
{
if (component.IsSpawned && component.NetworkManager.LocalClientId == client.LocalClientId)
{
if (component.NetworkObjectId == serverObject.GetComponent<NetworkObject>().NetworkObjectId)
{
found1 = true;
}
else if (component.NetworkObjectId == serverObject2.GetComponent<NetworkObject>().NetworkObjectId)
{
found2 = true;
}
}
}
Assert.IsTrue(found1);
Assert.IsTrue(found2);
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.ProcessTriggersCalled);
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
}
}
[Test]
public void WhenSpawnTriggeredMessagesAreDeferredBeforeThePrefabIsAdded_AddingThePrefabCausesThemToBeProcessed()
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
var serverObject = Object.Instantiate(m_RpcAndNetworkVariablePrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().SendTestClientRpc();
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
// TODO: Remove this if we figure out how to work around the NetworkVariableDeltaMessage.Serialized issue at line 59
// Otherwise, we have to wait for at least 1 tick for the NetworkVariableDeltaMessage to be generated before changing ownership
WaitForAllClientsToReceive<CreateObjectMessage, ClientRpcMessage, NetworkVariableDeltaMessage>();
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage, NetworkVariableDeltaMessage>();
// Validate messages are deferred and pending
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
Assert.AreEqual(5, manager.DeferredMessageCountTotal());
Assert.AreEqual(4, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(4, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab, serverObject.GetComponent<NetworkObject>().GlobalObjectIdHash));
AddPrefabsToClient(client);
}
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestRpcAndNetworkVariableComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
WaitForConditionOrTimeOutWithTimeTravel(HaveAllClientsSpawned);
TimeTravel(0.1, 1);
// Validate the test
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.ProcessTriggersCalled);
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
var component = GetComponentForClient<DeferredMessageTestRpcAndNetworkVariableComponent>(client.LocalClientId);
Assert.NotNull(component);
Assert.IsTrue(component.ClientRpcCalled);
Assert.AreEqual(1, component.TestNetworkVariable.Value);
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, component.OwnerClientId);
}
}
[Test]
public void WhenAMessageIsDeferredForMoreThanTheConfiguredTime_ItIsRemoved([Values(1, 2, 3)] int timeout)
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
CatchSpawns();
foreach (var client in m_ClientNetworkManagers)
{
client.NetworkConfig.SpawnTimeout = timeout;
}
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns();
var start = 0f;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforeDeferDelegate beforeDefer = (manager, key) =>
{
if (start == 0)
{
start = client.RealTimeProvider.RealTimeSinceStartup;
}
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforeDefer = beforeDefer;
}
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
}
int purgeCount = 0;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforePurgeDelegate beforePurge = (manager, key) =>
{
++purgeCount;
var elapsed = client.RealTimeProvider.RealTimeSinceStartup - start;
Debug.Log(client.RealTimeProvider.GetType().FullName);
Assert.GreaterOrEqual(elapsed, timeout);
Assert.AreEqual(1, manager.DeferredMessageCountTotal());
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, key));
Assert.AreEqual(serverObject.GetComponent<NetworkObject>().NetworkObjectId, key);
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforePurge = beforePurge;
}
TimeTravel(timeout - 0.01, 1);
bool HaveAnyClientsPurged()
{
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
if (manager.DeferredMessageCountTotal() == 0)
{
return true;
}
}
return false;
}
bool HaveAllClientsPurged()
{
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
if (manager.DeferredMessageCountTotal() != 0)
{
return false;
}
}
return true;
}
Assert.IsFalse(HaveAnyClientsPurged());
TimeTravel(0.02, 1);
Assert.IsTrue(HaveAllClientsPurged());
}
[Test]
public void WhenMultipleMessagesForTheSameObjectAreDeferredForMoreThanTheConfiguredTime_TheyAreAllRemoved([Values(1, 2, 3)] int timeout)
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
CatchSpawns();
foreach (var client in m_ClientNetworkManagers)
{
client.NetworkConfig.SpawnTimeout = timeout;
}
var serverObject = Object.Instantiate(m_RpcAndNetworkVariablePrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns();
var start = 0f;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforeDeferDelegate beforeDefer = (manager, key) =>
{
if (start == 0)
{
start = client.RealTimeProvider.RealTimeSinceStartup;
}
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforeDefer = beforeDefer;
}
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().SendTestClientRpc();
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
WaitForMessagesReceivedWithTimeTravel(
new List<Type> {typeof(ClientRpcMessage), typeof(NetworkVariableDeltaMessage), typeof(ChangeOwnershipMessage),
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
}
int purgeCount = 0;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforePurgeDelegate beforePurge = (manager, key) =>
{
++purgeCount;
var elapsed = client.RealTimeProvider.RealTimeSinceStartup - start;
Assert.GreaterOrEqual(elapsed, timeout);
Assert.AreEqual(3, manager.DeferredMessageCountTotal());
Assert.AreEqual(3, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(3, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, key));
Assert.AreEqual(serverObject.GetComponent<NetworkObject>().NetworkObjectId, key);
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforePurge = beforePurge;
}
var timePassedSinceFirstStart = MockTimeProvider.StaticRealTimeSinceStartup - start;
TimeTravel(timeout - 0.01 - timePassedSinceFirstStart, 1);
bool HaveAnyClientsPurged()
{
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
if (manager.DeferredMessageCountTotal() == 0)
{
return true;
}
}
return false;
}
bool HaveAllClientsPurged()
{
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
if (manager.DeferredMessageCountTotal() != 0)
{
return false;
}
}
return true;
}
Assert.IsFalse(HaveAnyClientsPurged());
TimeTravel(0.02 + timePassedSinceFirstStart, 1);
Assert.IsTrue(HaveAllClientsPurged());
}
[Test]
public void WhenMultipleMessagesForDifferentObjectsAreDeferredForMoreThanTheConfiguredTime_TheyAreAllRemoved([Values(1, 2, 3)] int timeout)
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
CatchSpawns();
foreach (var client in m_ClientNetworkManagers)
{
client.NetworkConfig.SpawnTimeout = timeout;
}
var serverObject = Object.Instantiate(m_RpcAndNetworkVariablePrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
var serverObject2 = Object.Instantiate(m_RpcAndNetworkVariablePrefab);
serverObject2.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject2.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns(2);
var start = 0f;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforeDeferDelegate beforeDefer = (manager, key) =>
{
if (start == 0)
{
start = client.RealTimeProvider.RealTimeSinceStartup;
}
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforeDefer = beforeDefer;
}
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().SendTestClientRpc();
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
serverObject2.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().SendTestClientRpc();
serverObject2.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
serverObject2.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
WaitForMessagesReceivedWithTimeTravel(
new List<Type> {typeof(ClientRpcMessage), typeof(NetworkVariableDeltaMessage), typeof(ChangeOwnershipMessage),typeof(ClientRpcMessage), typeof(NetworkVariableDeltaMessage), typeof(ChangeOwnershipMessage),
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject2.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
}
int purgeCount = 0;
foreach (var client in m_ClientNetworkManagers)
{
var remainingMessagesTotalThisClient = 6;
TestDeferredMessageManager.BeforePurgeDelegate beforePurge = (manager, key) =>
{
++purgeCount;
var elapsed = client.RealTimeProvider.RealTimeSinceStartup - start;
Assert.GreaterOrEqual(elapsed, timeout - 0.25f);
Assert.AreEqual(remainingMessagesTotalThisClient, manager.DeferredMessageCountTotal());
Assert.AreEqual(remainingMessagesTotalThisClient, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(3, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, key));
remainingMessagesTotalThisClient -= 3;
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforePurge = beforePurge;
}
TimeTravel(timeout + 0.1f, 1);
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
Assert.AreEqual(m_NumberOfClientsToLateJoin * 2, purgeCount);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
}
}
[Test]
public void WhenADeferredMessageIsRemoved_OtherMessagesForSameObjectAreRemoved([Values(1, 2, 3)] int timeout)
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
CatchSpawns();
foreach (var client in m_ClientNetworkManagers)
{
client.NetworkConfig.SpawnTimeout = timeout;
}
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns();
var start = 0f;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforeDeferDelegate beforeDefer = (manager, key) =>
{
if (start == 0)
{
start = client.RealTimeProvider.RealTimeSinceStartup;
}
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforeDefer = beforeDefer;
}
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage>();
TimeTravel(timeout - 0.5f, 1);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(1, manager.DeferredMessageCountTotal());
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
}
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ServerNetworkManager.LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(2, manager.DeferredMessageCountTotal());
Assert.AreEqual(2, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(2, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
}
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
}
int purgeCount = 0;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforePurgeDelegate beforePurge = (manager, key) =>
{
++purgeCount;
var elapsed = client.RealTimeProvider.RealTimeSinceStartup - start;
Assert.GreaterOrEqual(elapsed, timeout - 0.05f);
Assert.AreEqual(2, manager.DeferredMessageCountTotal());
Assert.AreEqual(2, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(2, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, key));
Assert.AreEqual(serverObject.GetComponent<NetworkObject>().NetworkObjectId, key);
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforePurge = beforePurge;
}
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
TimeTravel(0.6f, 1);
Assert.AreEqual(m_NumberOfClientsToLateJoin, purgeCount);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
}
}
[Test]
public void WhenADeferredMessageIsRemoved_OtherMessagesForDifferentObjectsAreNotRemoved([Values(1, 2, 3)] int timeout)
{
m_SkipAddingPrefabsToClient = true;
SpawnClients();
CatchSpawns();
foreach (var client in m_ClientNetworkManagers)
{
client.NetworkConfig.SpawnTimeout = timeout;
}
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
var serverObject2 = Object.Instantiate(m_RpcPrefab);
serverObject2.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject2.GetComponent<NetworkObject>().Spawn();
WaitForClientsToCatchSpawns(2);
var start = 0f;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforeDeferDelegate beforeDefer = (manager, key) =>
{
if (start == 0)
{
start = client.RealTimeProvider.RealTimeSinceStartup;
}
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforeDefer = beforeDefer;
}
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage>();
TimeTravel(timeout - 0.5f, 1);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(1, manager.DeferredMessageCountTotal());
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(0, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject2.GetComponent<NetworkObject>().NetworkObjectId));
}
serverObject2.GetComponent<NetworkObject>().ChangeOwnership(m_ServerNetworkManager.LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(2, manager.DeferredMessageCountTotal());
Assert.AreEqual(2, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject2.GetComponent<NetworkObject>().NetworkObjectId));
}
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
}
int purgeCount = 0;
foreach (var client in m_ClientNetworkManagers)
{
TestDeferredMessageManager.BeforePurgeDelegate beforePurge = (manager, key) =>
{
++purgeCount;
var elapsed = client.RealTimeProvider.RealTimeSinceStartup - start;
Assert.GreaterOrEqual(elapsed, timeout - 0.05f);
Assert.AreEqual(2, manager.DeferredMessageCountTotal());
Assert.AreEqual(2, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject2.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(serverObject.GetComponent<NetworkObject>().NetworkObjectId, key);
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforePurge = beforePurge;
}
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
TimeTravel(0.6f, 1);
Assert.AreEqual(m_NumberOfClientsToLateJoin, purgeCount);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(1, manager.DeferredMessageCountTotal());
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(0, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject2.GetComponent<NetworkObject>().NetworkObjectId));
}
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforePurge = null;
}
}
}
}