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/DistributedAuthority/DistributedAuthorityCodecTests.cs
Unity Technologies a813ba0dd6 com.unity.netcode.gameobjects@2.0.0-pre.3
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).

## [2.0.0-pre.3] - 2024-07-23

### Added
- Added: `UnityTransport.GetNetworkDriver` and `UnityTransport.GetLocalEndpoint` methods to expose the driver and local endpoint being used. (#2978)

### Fixed

- Fixed issue where deferred despawn was causing GC allocations when converting an `IEnumerable` to a list. (#2983)
- Fixed issue where the realtime network stats monitor was not able to display RPC traffic in release builds due to those stats being only available in development builds or the editor. (#2979)
- Fixed issue where `NetworkManager.ScenesLoaded` was not being updated if `PostSynchronizationSceneUnloading` was set and any loaded scenes not used during synchronization were unloaded. (#2971)
- Fixed issue where `Rigidbody2d` under Unity 6000.0.11f1 has breaking changes where `velocity` is now `linearVelocity` and `isKinematic` is replaced by `bodyType`. (#2971)
- Fixed issue where `NetworkSpawnManager.InstantiateAndSpawn` and `NetworkObject.InstantiateAndSpawn` were not honoring the ownerClientId parameter when using a client-server network topology. (#2968)
- Fixed issue where internal delta serialization could not have a byte serializer defined when serializing deltas for other types. Added `[GenerateSerializationForType(typeof(byte))]` to both the `NetworkVariable` and `AnticipatedNetworkVariable` classes to assure a byte serializer is defined.(#2962)
- Fixed issue when scene management was disabled and the session owner would still try to synchronize a late joining client. (#2962)
- Fixed issue when using a distributed authority network topology where it would allow a session owner to spawn a `NetworkObject` prior to being approved. Now, an error message is logged and the `NetworkObject` will not be spawned prior to the client being approved.  (#2962)
- Fixed issue where attempting to spawn during `NetworkBehaviour.OnInSceneObjectsSpawned` and `NetworkBehaviour.OnNetworkSessionSynchronized` notifications would throw a collection modified exception.  (#2962)

### Changed

- Changed logic where clients can now set the `NetworkSceneManager` client synchronization mode when using a distributed authority network topology. (#2985)
2024-07-23 00:00:00 +00:00

764 lines
27 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using NUnit.Framework;
using Unity.Collections;
using Unity.Netcode.TestHelpers.Runtime;
using Unity.Netcode.Transports.UTP;
#if UTP_TRANSPORT_2_0_ABOVE
using Unity.Networking.Transport;
#endif
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
internal class DistributedAuthorityCodecTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
// Use the CMB Service for all tests
protected override bool UseCMBService() => true;
// Set the network topology to distributed authority for all tests
protected override NetworkTopologyTypes OnGetNetworkTopologyType() => NetworkTopologyTypes.DistributedAuthority;
private CodecTestHooks m_ClientCodecHook;
private NetworkManager Client => m_ClientNetworkManagers[0];
private string m_TransportHost = Environment.GetEnvironmentVariable("NGO_HOST") ?? "127.0.0.1";
private const int k_TransportPort = 7777;
private const int k_ClientId = 0;
private GameObject m_SpawnObject;
internal class TestNetworkComponent : NetworkBehaviour
{
public NetworkList<int> MyNetworkList = new NetworkList<int>(new List<int> { 1, 2, 3 });
public NetworkVariable<int> MyNetworkVar = new NetworkVariable<int>(3);
[Rpc(SendTo.NotAuthority)]
public void TestNotAuthorityRpc(byte[] _)
{
}
[Rpc(SendTo.Authority)]
public void TestAuthorityRpc(byte[] _)
{
}
}
protected override void OnOneTimeSetup()
{
// Prevents the tests from running if no CMB Service is detected
#if !UTP_TRANSPORT_2_0_ABOVE
Assert.Ignore("ignoring DA codec tests because UTP transport must be 2.0");
#else
if (!CanConnectToServer(m_TransportHost, k_TransportPort))
{
Assert.Ignore("ignoring DA codec tests because UTP transport cannot connect to the runtime");
}
#endif
base.OnOneTimeSetup();
}
/// <summary>
/// Add any additional components to default player prefab
/// </summary>
protected override void OnCreatePlayerPrefab()
{
m_PlayerPrefab.AddComponent<TestNetworkComponent>();
base.OnCreatePlayerPrefab();
}
/// <summary>
/// Modify NetworkManager instances for settings specific to tests
/// </summary>
protected override void OnServerAndClientsCreated()
{
var utpTransport = Client.gameObject.AddComponent<UnityTransport>();
Client.NetworkConfig.NetworkTransport = utpTransport;
Client.NetworkConfig.EnableSceneManagement = false;
Client.NetworkConfig.AutoSpawnPlayerPrefabClientSide = true;
utpTransport.ConnectionData.Address = Dns.GetHostAddresses(m_TransportHost).First().ToString();
utpTransport.ConnectionData.Port = k_TransportPort;
Client.LogLevel = LogLevel.Developer;
// Validate we are in distributed authority mode with client side spawning and using CMB Service
Assert.True(Client.NetworkConfig.NetworkTopology == NetworkTopologyTypes.DistributedAuthority, "Distributed authority topology is not set!");
Assert.True(Client.AutoSpawnPlayerPrefabClientSide, "Client side spawning is not set!");
Assert.True(Client.CMBServiceConnection, "CMBServiceConnection is not set!");
// Create a prefab for creating and destroying tests (auto-registers with NetworkManagers)
m_SpawnObject = CreateNetworkObjectPrefab("TestObject");
m_SpawnObject.AddComponent<TestNetworkComponent>();
// Ignore the client connection timeout after starting the client
m_BypassConnectionTimeout = true;
}
protected override IEnumerator OnStartedServerAndClients()
{
// Validate the NetworkManager are in distributed authority mode
Assert.True(Client.DistributedAuthorityMode, "Distributed authority is not set!");
// Register hooks after starting clients and server (in this case just the one client)
// We do this at this point in time because the MessageManager exists (happens within the same call stack when starting NetworkManagers)
m_ClientCodecHook = new CodecTestHooks();
Client.MessageManager.Hook(m_ClientCodecHook);
yield return base.OnStartedServerAndClients();
// wait for client to connect since m_BypassConnectionTimeout
yield return WaitForConditionOrTimeOut(() => Client.LocalClient.PlayerObject != null);
AssertOnTimeout($"Timed out waiting for the client's player to be spanwed!");
}
[UnityTest]
public IEnumerator AuthorityRpc()
{
var player = Client.LocalClient.PlayerObject;
player.OwnerClientId = Client.LocalClientId + 1;
var networkComponent = player.GetComponent<TestNetworkComponent>();
networkComponent.UpdateNetworkProperties();
networkComponent.TestAuthorityRpc(new byte[] { 1, 2, 3, 4 });
// Universal Rpcs are sent as a ProxyMessage (which contains an RpcMessage)
yield return m_ClientCodecHook.WaitForMessageReceived<ProxyMessage>();
}
[UnityTest]
public IEnumerator ChangeOwnership()
{
var message = new ChangeOwnershipMessage
{
DistributedAuthorityMode = true,
NetworkObjectId = 100,
OwnerClientId = 2,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator ClientConnected()
{
var message = new ClientConnectedMessage()
{
ClientId = 2,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator ClientDisconnected()
{
var message = new ClientDisconnectedMessage()
{
ClientId = 2,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator CreateObject()
{
SpawnObject(m_SpawnObject, Client);
yield return m_ClientCodecHook.WaitForMessageReceived<CreateObjectMessage>();
}
[UnityTest]
public IEnumerator DestroyObject()
{
var spawnedObject = SpawnObject(m_SpawnObject, Client);
yield return m_ClientCodecHook.WaitForMessageReceived<CreateObjectMessage>();
spawnedObject.GetComponent<NetworkObject>().Despawn();
yield return m_ClientCodecHook.WaitForMessageReceived<DestroyObjectMessage>();
}
[UnityTest]
public IEnumerator Disconnect()
{
var message = new DisconnectReasonMessage
{
Reason = "test"
};
return SendMessage(ref message);
}
[UnityTest]
public IEnumerator NamedMessage()
{
var writeBuffer = new FastBufferWriter(sizeof(int), Allocator.Temp);
writeBuffer.WriteValueSafe(5);
var message = new NamedMessage
{
Hash = 3,
SendData = writeBuffer,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator NetworkVariableDelta()
{
var message = new NetworkVariableDeltaMessage
{
NetworkObjectId = 0,
NetworkBehaviourIndex = 1,
DeliveryMappedNetworkVariableIndex = new HashSet<int> { 2, 3, 4 },
TargetClientId = 5,
NetworkBehaviour = Client.LocalClient.PlayerObject.GetComponent<TestNetworkComponent>(),
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator NetworkVariableDelta_WithValueUpdate()
{
var networkObj = CreateNetworkObjectPrefab("TestObject");
networkObj.AddComponent<TestNetworkComponent>();
var instance = SpawnObject(networkObj, Client);
yield return m_ClientCodecHook.WaitForMessageReceived<CreateObjectMessage>();
var component = instance.GetComponent<TestNetworkComponent>();
var newValue = 5;
component.MyNetworkVar.Value = newValue;
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
Assert.AreEqual(newValue, component.MyNetworkVar.Value);
}
[UnityTest]
public IEnumerator NetworkListDelta_WithValueUpdate()
{
var networkObj = CreateNetworkObjectPrefab("TestObject");
networkObj.AddComponent<TestNetworkComponent>();
var instance = SpawnObject(networkObj, Client);
yield return m_ClientCodecHook.WaitForMessageReceived<CreateObjectMessage>();
var component = instance.GetComponent<TestNetworkComponent>();
component.MyNetworkList.Add(5);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Add(6);
component.MyNetworkList.Add(7);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Insert(1, 8);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Insert(8, 11);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Remove(6);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.RemoveAt(2);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Clear();
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
}
[UnityTest]
public IEnumerator NotAuthorityRpc()
{
Client.LocalClient.PlayerObject.GetComponent<TestNetworkComponent>().TestNotAuthorityRpc(new byte[] { 1, 2, 3, 4 });
// Universal Rpcs are sent as a ProxyMessage (which contains an RpcMessage)
yield return m_ClientCodecHook.WaitForMessageReceived<ProxyMessage>();
}
[UnityTest]
public IEnumerator ParentSync()
{
var message = new ParentSyncMessage
{
NetworkObjectId = 0,
WorldPositionStays = true,
IsLatestParentSet = false,
Position = new Vector3(1, 2, 3),
Rotation = new Quaternion(4, 5, 6, 7),
Scale = new Vector3(8, 9, 10),
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SessionOwner()
{
var message = new SessionOwnerMessage()
{
SessionOwner = 2,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator ServerLog()
{
var message = new ServerLogMessage()
{
LogType = NetworkLog.LogType.Info,
Message = "test",
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator UnnamedMessage()
{
var writeBuffer = new FastBufferWriter(sizeof(int), Allocator.Temp);
writeBuffer.WriteValueSafe(5);
var message = new UnnamedMessage
{
SendData = writeBuffer,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageLoad()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.Load,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageLoadWithObjects()
{
Client.SceneManager.SkipSceneHandling = true;
var prefabNetworkObject = m_SpawnObject.GetComponent<NetworkObject>();
Client.SceneManager.ScenePlacedObjects.Add(0, new Dictionary<int, NetworkObject>()
{
{ 1, prefabNetworkObject }
});
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.Load,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageUnload()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.Unload,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageLoadComplete()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.LoadComplete,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageUnloadComplete()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.UnloadComplete,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageLoadCompleted()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.LoadEventCompleted,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
ClientsCompleted = new List<ulong>() { k_ClientId },
ClientsTimedOut = new List<ulong>() { 123456789 },
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageUnloadLoadCompleted()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.UnloadEventCompleted,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
ClientsCompleted = new List<ulong>() { k_ClientId },
ClientsTimedOut = new List<ulong>() { 123456789 },
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageSynchronize()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.Synchronize,
LoadSceneMode = LoadSceneMode.Single,
ClientSynchronizationMode = LoadSceneMode.Single,
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
ScenesToSynchronize = new Queue<uint>()
};
eventData.ScenesToSynchronize.Enqueue(101);
eventData.SceneHandlesToSynchronize = new Queue<uint>();
eventData.SceneHandlesToSynchronize.Enqueue(202);
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageReSynchronize()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.ReSynchronize,
LoadSceneMode = LoadSceneMode.Single,
ClientSynchronizationMode = LoadSceneMode.Single,
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageSynchronizeComplete()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.ReSynchronize,
LoadSceneMode = LoadSceneMode.Single,
ClientSynchronizationMode = LoadSceneMode.Single,
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageActiveSceneChanged()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.ActiveSceneChanged,
ActiveSceneHash = XXHash.Hash32("ActiveScene")
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest, Ignore("Serializing twice causes data to disappear in the SceneManager for this event")]
public IEnumerator SceneEventMessageObjectSceneChanged()
{
Client.SceneManager.SkipSceneHandling = true;
var prefabNetworkObject = m_SpawnObject.GetComponent<NetworkObject>();
Client.SceneManager.ObjectsMigratedIntoNewScene = new Dictionary<int, Dictionary<ulong, List<NetworkObject>>>
{
{ 0, new Dictionary<ulong, List<NetworkObject>>()}
};
Client.SceneManager.ObjectsMigratedIntoNewScene[0].Add(Client.LocalClientId, new List<NetworkObject>() { prefabNetworkObject });
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.ObjectSceneChanged,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
private IEnumerator SendMessage<T>(ref T message) where T : INetworkMessage
{
Client.MessageManager.SetVersion(k_ClientId, XXHash.Hash32(typeof(T).FullName), 0);
var clientIds = new NativeArray<ulong>(1, Allocator.Temp);
clientIds[0] = k_ClientId;
Client.MessageManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientIds);
Client.MessageManager.ProcessSendQueues();
return m_ClientCodecHook.WaitForMessageReceived(message);
}
#if UTP_TRANSPORT_2_0_ABOVE
private static bool CanConnectToServer(string host, ushort port, double timeoutMs = 100)
{
var address = Dns.GetHostAddresses(host).First();
var endpoint = NetworkEndpoint.Parse(address.ToString(), port);
var driver = NetworkDriver.Create();
var connection = driver.Connect(endpoint);
var start = DateTime.Now;
var ev = Networking.Transport.NetworkEvent.Type.Empty;
while (ev != Networking.Transport.NetworkEvent.Type.Connect)
{
driver.ScheduleUpdate().Complete();
ev = driver.PopEventForConnection(connection, out _, out _);
if (DateTime.Now - start > TimeSpan.FromMilliseconds(timeoutMs))
{
return false;
}
}
driver.Disconnect(connection);
return true;
}
#endif
}
internal class CodecTestHooks : INetworkHooks
{
private Dictionary<string, Queue<TestMessage>> m_ExpectedMessages = new Dictionary<string, Queue<TestMessage>>();
private Dictionary<string, HashSet<string>> m_ReceivedMessages = new Dictionary<string, HashSet<string>>();
private struct TestMessage
{
public string Name;
public byte[] Data;
}
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
{
if (message is ConnectionRequestMessage)
{
return;
}
var writer = new FastBufferWriter(1024, Allocator.Temp);
message.Serialize(writer, 0);
var testName = TestContext.CurrentContext.Test.Name;
if (!m_ExpectedMessages.ContainsKey(testName))
{
m_ExpectedMessages[testName] = new Queue<TestMessage>();
}
m_ExpectedMessages[testName].Enqueue(new TestMessage
{
Name = typeof(T).ToString(),
Data = writer.ToArray(),
});
writer.Dispose();
}
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
{
}
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
{
return true;
}
public bool OnVerifyCanReceive(ulong senderId, Type messageType, FastBufferReader messageContent, ref NetworkContext context)
{
if (messageType == typeof(ConnectionApprovedMessage))
{
return true;
}
var testName = TestContext.CurrentContext.Test.Name;
Assert.True(m_ExpectedMessages.ContainsKey(testName));
Assert.IsNotEmpty(m_ExpectedMessages[testName]);
var nextMessage = m_ExpectedMessages[testName].Dequeue();
Assert.AreEqual(messageType.ToString(), nextMessage.Name, $"received unexpected message type: {messageType}");
if (!m_ReceivedMessages.ContainsKey(testName))
{
m_ReceivedMessages[testName] = new HashSet<string>();
}
m_ReceivedMessages[testName].Add(messageType.ToString());
// ServerLogMessage is an exception - it gets decoded correctly, but the bytes from the runtime do not directly match those sent by the SDK.
if (messageType == typeof(ServerLogMessage))
{
return true;
}
var expectedBytes = nextMessage.Data;
var receivedBytes = messageContent.ToArray();
Assert.AreEqual(expectedBytes, receivedBytes);
return true;
}
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
public IEnumerator WaitForMessageReceived<T>(float timeout = 5) where T : INetworkMessage
{
var testName = TestContext.CurrentContext.Test.Name;
var messageType = typeof(T).FullName;
var startTime = Time.realtimeSinceStartup;
while ((!m_ReceivedMessages.ContainsKey(testName) || !m_ReceivedMessages[testName].Contains(messageType)) && Time.realtimeSinceStartup - startTime < timeout)
{
yield return null;
}
Assert.True(m_ReceivedMessages.ContainsKey(testName), "failed to receive any messages");
Assert.True(m_ReceivedMessages[testName].Contains(messageType), $"failed to receive {messageType} message, received: {string.Join(", ", m_ReceivedMessages[testName])}");
// Reset received messages
m_ReceivedMessages[testName] = new HashSet<string>();
}
public IEnumerator WaitForMessageReceived<T>(T _, float timeout = 5) where T : INetworkMessage
{
return WaitForMessageReceived<T>(timeout: timeout);
}
}
}