using System; using System.Collections; using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Unity.Collections; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; using UnityEngine.TestTools; using Random = UnityEngine.Random; namespace Unity.Netcode.RuntimeTests { [TestFixtureSource(nameof(TestDataSource))] public class NetworkVariablePermissionTests : NetcodeIntegrationTest { public static IEnumerable TestDataSource() { foreach (HostOrServer hostOrServer in Enum.GetValues(typeof(HostOrServer))) { yield return new TestFixtureData(hostOrServer); } } protected override int NumberOfClients => 3; public NetworkVariablePermissionTests(HostOrServer hostOrServer) : base(hostOrServer) { } private GameObject m_TestObjPrefab; private ulong m_TestObjId = 0; protected override void OnServerAndClientsCreated() { m_TestObjPrefab = CreateNetworkObjectPrefab($"[{nameof(NetworkVariablePermissionTests)}.{nameof(m_TestObjPrefab)}]"); var testComp = m_TestObjPrefab.AddComponent(); } protected override IEnumerator OnServerAndClientsConnected() { m_TestObjId = SpawnObject(m_TestObjPrefab, m_ServerNetworkManager).GetComponent().NetworkObjectId; yield return null; } private IEnumerator WaitForPositionsAreEqual(NetworkVariable netvar, Vector3 expected) { yield return WaitForConditionOrTimeOut(() => netvar.Value == expected); Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut); } private IEnumerator WaitForOwnerWritableAreEqualOnAll() { yield return WaitForConditionOrTimeOut(CheckOwnerWritableAreEqualOnAll); Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut); } private bool CheckOwnerWritableAreEqualOnAll() { var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompServer = testObjServer.GetComponent(); foreach (var clientNetworkManager in m_ClientNetworkManagers) { var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompClient = testObjClient.GetComponent(); if (testObjServer.OwnerClientId != testObjClient.OwnerClientId || testCompServer.OwnerWritable_Position.Value != testCompClient.OwnerWritable_Position.Value || testCompServer.OwnerWritable_Position.ReadPerm != testCompClient.OwnerWritable_Position.ReadPerm || testCompServer.OwnerWritable_Position.WritePerm != testCompClient.OwnerWritable_Position.WritePerm) { return false; } } return true; } private IEnumerator WaitForServerWritableAreEqualOnAll() { yield return WaitForConditionOrTimeOut(CheckServerWritableAreEqualOnAll); Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut); } private bool CheckServerWritableAreEqualOnAll() { var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompServer = testObjServer.GetComponent(); foreach (var clientNetworkManager in m_ClientNetworkManagers) { var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompClient = testObjClient.GetComponent(); if (testCompServer.ServerWritable_Position.Value != testCompClient.ServerWritable_Position.Value || testCompServer.ServerWritable_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm || testCompServer.ServerWritable_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm) { return false; } } return true; } private bool CheckOwnerReadWriteAreEqualOnOwnerAndServer() { var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompServer = testObjServer.GetComponent(); foreach (var clientNetworkManager in m_ClientNetworkManagers) { var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompClient = testObjClient.GetComponent(); if (testObjServer.OwnerClientId == testObjClient.OwnerClientId && testCompServer.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value && testCompServer.OwnerReadWrite_Position.ReadPerm == testCompClient.ServerWritable_Position.ReadPerm && testCompServer.OwnerReadWrite_Position.WritePerm == testCompClient.ServerWritable_Position.WritePerm) { return true; } } return false; } private bool CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(NetVarPermTestComp ownerReadWriteObject) { foreach (var clientNetworkManager in m_ClientNetworkManagers) { var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompClient = testObjClient.GetComponent(); if (testObjClient.OwnerClientId != ownerReadWriteObject.OwnerClientId || ownerReadWriteObject.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value || ownerReadWriteObject.OwnerReadWrite_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm || ownerReadWriteObject.OwnerReadWrite_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm) { return false; } } return true; } [UnityTest] public IEnumerator ServerChangesOwnerWritableNetVar() { yield return WaitForOwnerWritableAreEqualOnAll(); var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompServer = testObjServer.GetComponent(); var oldValue = testCompServer.OwnerWritable_Position.Value; var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f)); testCompServer.OwnerWritable_Position.Value = newValue; yield return WaitForPositionsAreEqual(testCompServer.OwnerWritable_Position, newValue); yield return WaitForOwnerWritableAreEqualOnAll(); } [UnityTest] public IEnumerator ServerChangesServerWritableNetVar() { yield return WaitForServerWritableAreEqualOnAll(); var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompServer = testObjServer.GetComponent(); var oldValue = testCompServer.ServerWritable_Position.Value; var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f)); testCompServer.ServerWritable_Position.Value = newValue; yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, newValue); yield return WaitForServerWritableAreEqualOnAll(); } [UnityTest] public IEnumerator ClientChangesOwnerWritableNetVar() { yield return WaitForOwnerWritableAreEqualOnAll(); var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; int clientManagerIndex = m_ClientNetworkManagers.Length - 1; var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId; testObjServer.ChangeOwnership(newOwnerClientId); yield return WaitForTicks(m_ServerNetworkManager, 2); yield return WaitForOwnerWritableAreEqualOnAll(); var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId]; var testCompClient = testObjClient.GetComponent(); var oldValue = testCompClient.OwnerWritable_Position.Value; var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f)); testCompClient.OwnerWritable_Position.Value = newValue; yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue); yield return WaitForOwnerWritableAreEqualOnAll(); } /// /// This tests the scenario where a client owner has both read and write /// permissions set. The server should be the only instance that can read /// the NetworkVariable. ServerCannotChangeOwnerWritableNetVar performs /// the same check to make sure the server cannot write to a client owner /// NetworkVariable with owner write permissions. /// [UnityTest] public IEnumerator ClientOwnerWithReadWriteChangesNetVar() { yield return WaitForOwnerWritableAreEqualOnAll(); var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; int clientManagerIndex = m_ClientNetworkManagers.Length - 1; var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId; testObjServer.ChangeOwnership(newOwnerClientId); yield return WaitForTicks(m_ServerNetworkManager, 2); yield return WaitForOwnerWritableAreEqualOnAll(); var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId]; var testCompClient = testObjClient.GetComponent(); var oldValue = testCompClient.OwnerReadWrite_Position.Value; var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f)); testCompClient.OwnerWritable_Position.Value = newValue; yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue); // Verify the client owner and server match yield return CheckOwnerReadWriteAreEqualOnOwnerAndServer(); // Verify the non-owner clients do not have the same Value but do have the same permissions yield return CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(testCompClient); } [UnityTest] public IEnumerator ClientCannotChangeServerWritableNetVar() { yield return WaitForServerWritableAreEqualOnAll(); var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompServer = testObjServer.GetComponent(); int clientManagerIndex = m_ClientNetworkManagers.Length - 1; var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId; testObjServer.ChangeOwnership(newOwnerClientId); yield return WaitForTicks(m_ServerNetworkManager, 2); yield return WaitForServerWritableAreEqualOnAll(); var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId]; var testCompClient = testObjClient.GetComponent(); var oldValue = testCompClient.ServerWritable_Position.Value; var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f)); Assert.That(() => testCompClient.ServerWritable_Position.Value = newValue, Throws.TypeOf()); yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, oldValue); yield return WaitForServerWritableAreEqualOnAll(); testCompServer.ServerWritable_Position.Value = newValue; yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, newValue); yield return WaitForServerWritableAreEqualOnAll(); } [UnityTest] public IEnumerator ServerCannotChangeOwnerWritableNetVar() { yield return WaitForOwnerWritableAreEqualOnAll(); var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId]; var testCompServer = testObjServer.GetComponent(); int clientManagerIndex = m_ClientNetworkManagers.Length - 1; var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId; testObjServer.ChangeOwnership(newOwnerClientId); yield return WaitForTicks(m_ServerNetworkManager, 2); yield return WaitForOwnerWritableAreEqualOnAll(); var oldValue = testCompServer.OwnerWritable_Position.Value; var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f)); Assert.That(() => testCompServer.OwnerWritable_Position.Value = newValue, Throws.TypeOf()); yield return WaitForPositionsAreEqual(testCompServer.OwnerWritable_Position, oldValue); yield return WaitForOwnerWritableAreEqualOnAll(); var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId]; var testCompClient = testObjClient.GetComponent(); testCompClient.OwnerWritable_Position.Value = newValue; yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue); yield return WaitForOwnerWritableAreEqualOnAll(); } } public struct TestStruct : INetworkSerializable, IEquatable { public uint SomeInt; public bool SomeBool; public static bool NetworkSerializeCalledOnWrite; public static bool NetworkSerializeCalledOnRead; public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { if (serializer.IsReader) { NetworkSerializeCalledOnRead = true; } else { NetworkSerializeCalledOnWrite = true; } serializer.SerializeValue(ref SomeInt); serializer.SerializeValue(ref SomeBool); } public bool Equals(TestStruct other) { return SomeInt == other.SomeInt && SomeBool == other.SomeBool; } public override bool Equals(object obj) { return obj is TestStruct other && Equals(other); } public override int GetHashCode() { unchecked { return ((int)SomeInt * 397) ^ SomeBool.GetHashCode(); } } } public class TestClass : INetworkSerializable, IEquatable { public uint SomeInt; public bool SomeBool; public static bool NetworkSerializeCalledOnWrite; public static bool NetworkSerializeCalledOnRead; public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { if (serializer.IsReader) { NetworkSerializeCalledOnRead = true; } else { NetworkSerializeCalledOnWrite = true; } serializer.SerializeValue(ref SomeInt); serializer.SerializeValue(ref SomeBool); } public bool Equals(TestClass other) { return SomeInt == other.SomeInt && SomeBool == other.SomeBool; } public override bool Equals(object obj) { return obj is TestClass other && Equals(other); } public override int GetHashCode() { unchecked { return ((int)SomeInt * 397) ^ SomeBool.GetHashCode(); } } } // Used just to create a NetworkVariable in the templated NetworkBehaviour type that isn't referenced anywhere else // Please do not reference this class anywhere else! public class TestClass_ReferencedOnlyByTemplateNetworkBehavourType : TestClass, IEquatable { public bool Equals(TestClass_ReferencedOnlyByTemplateNetworkBehavourType other) { return Equals((TestClass)other); } } public class NetworkVariableTest : NetworkBehaviour { public enum SomeEnum { A, B, C } public readonly NetworkVariable TheScalar = new NetworkVariable(); public readonly NetworkVariable TheEnum = new NetworkVariable(); public readonly NetworkList TheList = new NetworkList(); public readonly NetworkList TheStructList = new NetworkList(); public readonly NetworkList TheLargeList = new NetworkList(); public readonly NetworkVariable FixedString32 = new NetworkVariable(); private void ListChanged(NetworkListEvent e) { ListDelegateTriggered = true; } public void Awake() { TheList.OnListChanged += ListChanged; } public readonly NetworkVariable TheStruct = new NetworkVariable(); public readonly NetworkVariable TheClass = new NetworkVariable(); public NetworkVariable> TheTemplateStruct = new NetworkVariable>(); public NetworkVariable> TheTemplateClass = new NetworkVariable>(); public bool ListDelegateTriggered; public override void OnNetworkSpawn() { if (!IsServer) { NetworkVariableTests.ClientNetworkVariableTestSpawned(this); } base.OnNetworkSpawn(); } } [TestFixture(true)] [TestFixture(false)] public class NetworkVariableTests : NetcodeIntegrationTest { private const string k_StringTestValue = "abcdefghijklmnopqrstuvwxyz"; private static readonly FixedString32Bytes k_FixedStringTestValue = k_StringTestValue; protected override int NumberOfClients => 2; private const uint k_TestUInt = 0x12345678; private const int k_TestVal1 = 111; private const int k_TestVal2 = 222; private const int k_TestVal3 = 333; protected override bool m_EnableTimeTravel => true; protected override bool m_SetupIsACoroutine => false; protected override bool m_TearDownIsACoroutine => false; private static List s_ClientNetworkVariableTestInstances = new List(); public static void ClientNetworkVariableTestSpawned(NetworkVariableTest networkVariableTest) { s_ClientNetworkVariableTestInstances.Add(networkVariableTest); } // Player1 component on the server private NetworkVariableTest m_Player1OnServer; // Player1 component on client1 private NetworkVariableTest m_Player1OnClient1; private NetworkListTestPredicate m_NetworkListPredicateHandler; private readonly bool m_EnsureLengthSafety; public NetworkVariableTests(bool ensureLengthSafety) { m_EnsureLengthSafety = ensureLengthSafety; } protected override bool CanStartServerAndClients() { return false; } /// /// This is an adjustment to how the server and clients are started in order /// to avoid timing issues when running in a stand alone test runner build. /// private void InitializeServerAndClients(HostOrServer useHost) { s_ClientNetworkVariableTestInstances.Clear(); m_PlayerPrefab.AddComponent(); m_PlayerPrefab.AddComponent(); m_PlayerPrefab.AddComponent(); m_PlayerPrefab.AddComponent(); m_ServerNetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_EnsureLengthSafety; m_ServerNetworkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab; foreach (var client in m_ClientNetworkManagers) { client.NetworkConfig.EnsureNetworkVariableLengthSafety = m_EnsureLengthSafety; client.NetworkConfig.PlayerPrefab = m_PlayerPrefab; } Assert.True(NetcodeIntegrationTestHelpers.Start(useHost == HostOrServer.Host, m_ServerNetworkManager, m_ClientNetworkManagers), "Failed to start server and client instances"); RegisterSceneManagerHandler(); // Wait for connection on client and server side var success = WaitForClientsConnectedOrTimeOutWithTimeTravel(); Assert.True(success, $"Timed-out waiting for all clients to connect!"); // These are the *SERVER VERSIONS* of the *CLIENT PLAYER 1 & 2* var result = new NetcodeIntegrationTestHelpers.ResultWrapper(); NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentationWithTimeTravel( x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId, m_ServerNetworkManager, result); // Assign server-side client's player m_Player1OnServer = result.Result.GetComponent(); // This is client1's view of itself NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentationWithTimeTravel( x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId, m_ClientNetworkManagers[0], result); // Assign client-side local player m_Player1OnClient1 = result.Result.GetComponent(); m_Player1OnServer.TheList.Clear(); if (m_Player1OnServer.TheList.Count > 0) { throw new Exception("at least one server network container not empty at start"); } if (m_Player1OnClient1.TheList.Count > 0) { throw new Exception("at least one client network container not empty at start"); } var instanceCount = useHost == HostOrServer.Host ? NumberOfClients * 3 : NumberOfClients * 2; // Wait for the client-side to notify it is finished initializing and spawning. success = WaitForConditionOrTimeOutWithTimeTravel(() => s_ClientNetworkVariableTestInstances.Count == instanceCount); Assert.True(success, "Timed out waiting for all client NetworkVariableTest instances to register they have spawned!"); TimeTravelToNextTick(); } /// /// Runs generalized tests on all predefined NetworkVariable types /// [Test] public void AllNetworkVariableTypes([Values] HostOrServer useHost) { // Create, instantiate, and host // This would normally go in Setup, but since every other test but this one // uses NetworkManagerHelper, and it does its own NetworkManager setup / teardown, // for now we put this within this one test until we migrate it to MIH Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out NetworkManager server, useHost == HostOrServer.Host ? NetworkManagerHelper.NetworkManagerOperatingMode.Host : NetworkManagerHelper.NetworkManagerOperatingMode.Server)); Assert.IsTrue(server.IsHost == (useHost == HostOrServer.Host), $"{nameof(useHost)} does not match the server.IsHost value!"); Guid gameObjectId = NetworkManagerHelper.AddGameNetworkObject("NetworkVariableTestComponent"); var networkVariableTestComponent = NetworkManagerHelper.AddComponentToObject(gameObjectId); NetworkManagerHelper.SpawnNetworkObject(gameObjectId); // Start Testing networkVariableTestComponent.EnableTesting = true; var success = WaitForConditionOrTimeOutWithTimeTravel(() => true == networkVariableTestComponent.IsTestComplete()); Assert.True(success, "Timed out waiting for the test to complete!"); // Stop Testing networkVariableTestComponent.EnableTesting = false; Assert.IsTrue(networkVariableTestComponent.DidAllValuesChange()); networkVariableTestComponent.AssertAllValuesAreCorrect(); // Disable this once we are done. networkVariableTestComponent.gameObject.SetActive(false); // This would normally go in Teardown, but since every other test but this one // uses NetworkManagerHelper, and it does its own NetworkManager setup / teardown, // for now we put this within this one test until we migrate it to MIH NetworkManagerHelper.ShutdownNetworkManager(); } [Test] public void ClientWritePermissionTest([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); // client must not be allowed to write to a server auth variable Assert.Throws(() => m_Player1OnClient1.TheScalar.Value = k_TestVal1); } /// /// Runs tests that network variables sync on client whatever the local value of . /// [Test] public void NetworkVariableSync_WithDifferentTimeScale([Values] HostOrServer useHost, [Values(0.0f, 1.0f, 2.0f)] float timeScale) { Time.timeScale = timeScale; InitializeServerAndClients(useHost); m_Player1OnServer.TheScalar.Value = k_TestVal1; // Now wait for the client side version to be updated to k_TestVal1 var success = WaitForConditionOrTimeOutWithTimeTravel(() => m_Player1OnClient1.TheScalar.Value == k_TestVal1); Assert.True(success, "Timed out waiting for client-side NetworkVariable to update!"); } [Test] public void FixedString32Test([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); m_Player1OnServer.FixedString32.Value = k_FixedStringTestValue; // Now wait for the client side version to be updated to k_FixedStringTestValue var success = WaitForConditionOrTimeOutWithTimeTravel(() => m_Player1OnClient1.FixedString32.Value == k_FixedStringTestValue); Assert.True(success, "Timed out waiting for client-side NetworkVariable to update!"); } [Test] public void NetworkListAdd([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); m_NetworkListPredicateHandler = new NetworkListTestPredicate(m_Player1OnServer, m_Player1OnClient1, NetworkListTestPredicate.NetworkListTestStates.Add, 10); Assert.True(WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler)); } [Test] public void WhenListContainsManyLargeValues_OverflowExceptionIsNotThrown([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); m_NetworkListPredicateHandler = new NetworkListTestPredicate(m_Player1OnServer, m_Player1OnClient1, NetworkListTestPredicate.NetworkListTestStates.ContainsLarge, 20); Assert.True(WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler)); } [Test] public void NetworkListContains([Values] HostOrServer useHost) { // Re-use the NetworkListAdd to initialize the server and client as well as make sure the list is populated NetworkListAdd(useHost); // Now test the NetworkList.Contains method m_NetworkListPredicateHandler.SetNetworkListTestState(NetworkListTestPredicate.NetworkListTestStates.Contains); Assert.True(WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler)); } [Test] public void NetworkListInsert([Values] HostOrServer useHost) { // Re-use the NetworkListAdd to initialize the server and client as well as make sure the list is populated NetworkListAdd(useHost); // Now randomly insert a random value entry m_Player1OnServer.TheList.Insert(Random.Range(0, 9), Random.Range(1, 99)); // Verify the element count and values on the client matches the server m_NetworkListPredicateHandler.SetNetworkListTestState(NetworkListTestPredicate.NetworkListTestStates.VerifyData); Assert.True(WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler)); } [Test] public void NetworkListIndexOf([Values] HostOrServer useHost) { // Re-use the NetworkListAdd to initialize the server and client as well as make sure the list is populated NetworkListAdd(useHost); m_NetworkListPredicateHandler.SetNetworkListTestState(NetworkListTestPredicate.NetworkListTestStates.IndexOf); Assert.True(WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler)); } [Test] public void NetworkListValueUpdate([Values] HostOrServer useHost) { var testSucceeded = false; InitializeServerAndClients(useHost); // Add 1 element value and verify it is the same on the client m_NetworkListPredicateHandler = new NetworkListTestPredicate(m_Player1OnServer, m_Player1OnClient1, NetworkListTestPredicate.NetworkListTestStates.Add, 1); Assert.True(WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler)); // Setup our original and var previousValue = m_Player1OnServer.TheList[0]; var updatedValue = previousValue + 10; // Callback that verifies the changed event occurred and that the original and new values are correct void TestValueUpdatedCallback(NetworkListEvent changedEvent) { testSucceeded = changedEvent.PreviousValue == previousValue && changedEvent.Value == updatedValue; } // Subscribe to the OnListChanged event on the client side and m_Player1OnClient1.TheList.OnListChanged += TestValueUpdatedCallback; m_Player1OnServer.TheList[0] = updatedValue; // Wait until we know the client side matches the server side before checking if the callback was a success m_NetworkListPredicateHandler.SetNetworkListTestState(NetworkListTestPredicate.NetworkListTestStates.VerifyData); WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler); Assert.That(testSucceeded); m_Player1OnClient1.TheList.OnListChanged -= TestValueUpdatedCallback; } private List m_ExpectedValuesServer = new List(); private List m_ExpectedValuesClient = new List(); public enum ListRemoveTypes { Remove, RemoveAt } [Test] public void NetworkListRemoveTests([Values] HostOrServer useHost, [Values] ListRemoveTypes listRemoveType) { m_ExpectedValuesServer.Clear(); m_ExpectedValuesClient.Clear(); // Re-use the NetworkListAdd to initialize the server and client as well as make sure the list is populated NetworkListAdd(useHost); // Randomly remove a few entries m_Player1OnServer.TheList.OnListChanged += Server_OnListChanged; m_Player1OnClient1.TheList.OnListChanged += Client_OnListChanged; // Remove half of the elements for (int i = 0; i < (int)(m_Player1OnServer.TheList.Count * 0.5f); i++) { var index = Random.Range(0, m_Player1OnServer.TheList.Count - 1); var value = m_Player1OnServer.TheList[index]; m_ExpectedValuesServer.Add(value); m_ExpectedValuesClient.Add(value); if (listRemoveType == ListRemoveTypes.RemoveAt) { m_Player1OnServer.TheList.RemoveAt(index); } else { m_Player1OnServer.TheList.Remove(value); } } // Verify the element count and values on the client matches the server m_NetworkListPredicateHandler.SetNetworkListTestState(NetworkListTestPredicate.NetworkListTestStates.VerifyData); Assert.True(WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler)); Assert.True(m_ExpectedValuesServer.Count == 0, $"Server was not notified of all elements removed and still has {m_ExpectedValuesServer.Count} elements left!"); Assert.True(m_ExpectedValuesClient.Count == 0, $"Client was not notified of all elements removed and still has {m_ExpectedValuesClient.Count} elements left!"); } private void Server_OnListChanged(NetworkListEvent changeEvent) { Assert.True(m_ExpectedValuesServer.Contains(changeEvent.Value)); m_ExpectedValuesServer.Remove(changeEvent.Value); } private void Client_OnListChanged(NetworkListEvent changeEvent) { Assert.True(m_ExpectedValuesClient.Contains(changeEvent.Value)); m_ExpectedValuesClient.Remove(changeEvent.Value); } [Test] public void NetworkListClear([Values] HostOrServer useHost) { // Re-use the NetworkListAdd to initialize the server and client as well as make sure the list is populated NetworkListAdd(useHost); m_Player1OnServer.TheList.Clear(); // Verify the element count and values on the client matches the server m_NetworkListPredicateHandler.SetNetworkListTestState(NetworkListTestPredicate.NetworkListTestStates.VerifyData); Assert.True(WaitForConditionOrTimeOutWithTimeTravel(m_NetworkListPredicateHandler)); } [Test] public void TestNetworkVariableClass([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyClass() { return m_Player1OnClient1.TheClass.Value != null && m_Player1OnClient1.TheClass.Value.SomeBool == m_Player1OnServer.TheClass.Value.SomeBool && m_Player1OnClient1.TheClass.Value.SomeInt == m_Player1OnServer.TheClass.Value.SomeInt; } m_Player1OnServer.TheClass.Value = new TestClass { SomeInt = k_TestUInt, SomeBool = false }; m_Player1OnServer.TheClass.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyClass)); } [Test] public void TestNetworkVariableTemplateClass([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyClass() { return m_Player1OnClient1.TheTemplateClass.Value.Value != null && m_Player1OnClient1.TheTemplateClass.Value.Value.SomeBool == m_Player1OnServer.TheTemplateClass.Value.Value.SomeBool && m_Player1OnClient1.TheTemplateClass.Value.Value.SomeInt == m_Player1OnServer.TheTemplateClass.Value.Value.SomeInt; } m_Player1OnServer.TheTemplateClass.Value = new ManagedTemplateNetworkSerializableType { Value = new TestClass { SomeInt = k_TestUInt, SomeBool = false } }; m_Player1OnServer.TheTemplateClass.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyClass)); } [Test] public void TestNetworkListStruct([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyList() { return m_Player1OnClient1.TheStructList.Count == m_Player1OnServer.TheStructList.Count && m_Player1OnClient1.TheStructList[0].Value == m_Player1OnServer.TheStructList[0].Value && m_Player1OnClient1.TheStructList[1].Value == m_Player1OnServer.TheStructList[1].Value; } m_Player1OnServer.TheStructList.Add(new StructUsedOnlyInNetworkList { Value = 1 }); m_Player1OnServer.TheStructList.Add(new StructUsedOnlyInNetworkList { Value = 2 }); m_Player1OnServer.TheStructList.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyList)); } [Test] public void TestNetworkVariableStruct([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyStructure() { return m_Player1OnClient1.TheStruct.Value.SomeBool == m_Player1OnServer.TheStruct.Value.SomeBool && m_Player1OnClient1.TheStruct.Value.SomeInt == m_Player1OnServer.TheStruct.Value.SomeInt; } m_Player1OnServer.TheStruct.Value = new TestStruct { SomeInt = k_TestUInt, SomeBool = false }; m_Player1OnServer.TheStruct.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyStructure)); } [Test] public void TestNetworkVariableTemplateStruct([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyStructure() { return m_Player1OnClient1.TheTemplateStruct.Value.Value.SomeBool == m_Player1OnServer.TheTemplateStruct.Value.Value.SomeBool && m_Player1OnClient1.TheTemplateStruct.Value.Value.SomeInt == m_Player1OnServer.TheTemplateStruct.Value.Value.SomeInt; } m_Player1OnServer.TheTemplateStruct.Value = new UnmanagedTemplateNetworkSerializableType { Value = new TestStruct { SomeInt = k_TestUInt, SomeBool = false } }; m_Player1OnServer.TheTemplateStruct.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyStructure)); } [Test] public void TestNetworkVariableTemplateBehaviourClass([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyClass() { return (m_Player1OnClient1.GetComponent().TheVar.Value != null && m_Player1OnClient1.GetComponent().TheVar.Value.SomeBool == m_Player1OnServer.GetComponent().TheVar.Value.SomeBool && m_Player1OnClient1.GetComponent().TheVar.Value.SomeInt == m_Player1OnServer.GetComponent().TheVar.Value.SomeInt) && (m_Player1OnClient1.GetComponent().TheVar2.Value != null && m_Player1OnClient1.GetComponent().TheVar2.Value.SomeBool == m_Player1OnServer.GetComponent().TheVar2.Value.SomeBool && m_Player1OnClient1.GetComponent().TheVar2.Value.SomeInt == m_Player1OnServer.GetComponent().TheVar2.Value.SomeInt); } m_Player1OnServer.GetComponent().TheVar.Value = new TestClass { SomeInt = k_TestUInt, SomeBool = false }; m_Player1OnServer.GetComponent().TheVar2.Value = new TestClass { SomeInt = k_TestUInt, SomeBool = false }; m_Player1OnServer.GetComponent().TheVar.SetDirty(true); m_Player1OnServer.GetComponent().TheVar2.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyClass)); } [Test] public void TestNetworkVariableTemplateBehaviourClassNotReferencedElsewhere([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyClass() { return m_Player1OnClient1.GetComponent().TheVar.Value != null && m_Player1OnClient1.GetComponent().TheVar.Value.SomeBool == m_Player1OnServer.GetComponent().TheVar.Value.SomeBool && m_Player1OnClient1.GetComponent().TheVar.Value.SomeInt == m_Player1OnServer.GetComponent().TheVar.Value.SomeInt; } m_Player1OnServer.GetComponent().TheVar.Value = new TestClass_ReferencedOnlyByTemplateNetworkBehavourType { SomeInt = k_TestUInt, SomeBool = false }; m_Player1OnServer.GetComponent().TheVar.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyClass)); } [Test] public void TestNetworkVariableTemplateBehaviourStruct([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyClass() { return m_Player1OnClient1.GetComponent().TheVar.Value.SomeBool == m_Player1OnServer.GetComponent().TheVar.Value.SomeBool && m_Player1OnClient1.GetComponent().TheVar.Value.SomeInt == m_Player1OnServer.GetComponent().TheVar.Value.SomeInt; } m_Player1OnServer.GetComponent().TheVar.Value = new TestStruct { SomeInt = k_TestUInt, SomeBool = false }; m_Player1OnServer.GetComponent().TheVar.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyClass)); } [Test] public void TestNetworkVariableEnum([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); bool VerifyStructure() { return m_Player1OnClient1.TheEnum.Value == NetworkVariableTest.SomeEnum.C; } m_Player1OnServer.TheEnum.Value = NetworkVariableTest.SomeEnum.C; m_Player1OnServer.TheEnum.SetDirty(true); // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyStructure)); } [Test] public void TestINetworkSerializableClassCallsNetworkSerialize([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); TestClass.NetworkSerializeCalledOnWrite = false; TestClass.NetworkSerializeCalledOnRead = false; m_Player1OnServer.TheClass.Value = new TestClass { SomeBool = true, SomeInt = 32 }; static bool VerifyCallback() => TestClass.NetworkSerializeCalledOnWrite && TestClass.NetworkSerializeCalledOnRead; // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyCallback)); } [Test] public void TestINetworkSerializableStructCallsNetworkSerialize([Values] HostOrServer useHost) { InitializeServerAndClients(useHost); TestStruct.NetworkSerializeCalledOnWrite = false; TestStruct.NetworkSerializeCalledOnRead = false; m_Player1OnServer.TheStruct.Value = new TestStruct() { SomeInt = k_TestUInt, SomeBool = false }; static bool VerifyCallback() => TestStruct.NetworkSerializeCalledOnWrite && TestStruct.NetworkSerializeCalledOnRead; // Wait for the client-side to notify it is finished initializing and spawning. Assert.True(WaitForConditionOrTimeOutWithTimeTravel(VerifyCallback)); } [Test] public void TestCustomGenericSerialization() { // Just verifies that the ILPP codegen initialized these values for this type. Assert.AreEqual(typeof(UnmanagedTypeSerializer), NetworkVariableSerialization.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedTypeSerializer), NetworkVariableSerialization.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedTypeSerializer), NetworkVariableSerialization.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedTypeSerializer), NetworkVariableSerialization.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedTypeSerializer), NetworkVariableSerialization.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedTypeSerializer), NetworkVariableSerialization.Serializer.GetType()); Assert.IsNotNull(NetworkVariableSerialization.AreEqual); Assert.IsNotNull(NetworkVariableSerialization.AreEqual); Assert.IsNotNull(NetworkVariableSerialization.AreEqual); Assert.IsNotNull(NetworkVariableSerialization.AreEqual); Assert.IsNotNull(NetworkVariableSerialization.AreEqual); Assert.IsNotNull(NetworkVariableSerialization.AreEqual); // Verify no issues with generic values... Assert.AreEqual(typeof(UnmanagedArraySerializer), NetworkVariableSerialization>.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedArraySerializer), NetworkVariableSerialization>.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedArraySerializer), NetworkVariableSerialization>.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedArraySerializer), NetworkVariableSerialization>.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedArraySerializer), NetworkVariableSerialization>.Serializer.GetType()); Assert.AreEqual(typeof(UnmanagedArraySerializer), NetworkVariableSerialization>.Serializer.GetType()); Assert.IsNotNull(NetworkVariableSerialization>.AreEqual); Assert.IsNotNull(NetworkVariableSerialization>.AreEqual); Assert.IsNotNull(NetworkVariableSerialization>.AreEqual); Assert.IsNotNull(NetworkVariableSerialization>.AreEqual); Assert.IsNotNull(NetworkVariableSerialization>.AreEqual); Assert.IsNotNull(NetworkVariableSerialization>.AreEqual); } [Test] public void TestUnsupportedManagedTypesThrowExceptions() { var variable = new NetworkVariable(); using var writer = new FastBufferWriter(1024, Allocator.Temp); using var reader = new FastBufferReader(writer, Allocator.None); // Just making sure these are null, just in case. UserNetworkVariableSerialization.ReadValue = null; UserNetworkVariableSerialization.WriteValue = null; UserNetworkVariableSerialization.DuplicateValue = null; Assert.Throws(() => { variable.WriteField(writer); }); Assert.Throws(() => { variable.ReadField(reader); }); } [Test] public void TestUnsupportedManagedTypesWithUserSerializationDoNotThrowExceptions() { var variable = new NetworkVariable(); UserNetworkVariableSerialization.ReadValue = (FastBufferReader reader, out string value) => { reader.ReadValueSafe(out value); }; UserNetworkVariableSerialization.WriteValue = (FastBufferWriter writer, in string value) => { writer.WriteValueSafe(value); }; UserNetworkVariableSerialization.DuplicateValue = (in string a, ref string b) => { b = string.Copy(a); }; try { using var writer = new FastBufferWriter(1024, Allocator.Temp); variable.Value = "012345"; variable.WriteField(writer); variable.Value = ""; using var reader = new FastBufferReader(writer, Allocator.None); variable.ReadField(reader); Assert.AreEqual("012345", variable.Value); } finally { UserNetworkVariableSerialization.ReadValue = null; UserNetworkVariableSerialization.WriteValue = null; UserNetworkVariableSerialization.DuplicateValue = null; } } [Test] public void TestUnsupportedUnmanagedTypesThrowExceptions() { var variable = new NetworkVariable(); using var writer = new FastBufferWriter(1024, Allocator.Temp); using var reader = new FastBufferReader(writer, Allocator.None); // Just making sure these are null, just in case. UserNetworkVariableSerialization.ReadValue = null; UserNetworkVariableSerialization.WriteValue = null; UserNetworkVariableSerialization.DuplicateValue = null; Assert.Throws(() => { variable.WriteField(writer); }); Assert.Throws(() => { variable.ReadField(reader); }); } [Test] public void TestTypesReferencedInSubclassSerializeSuccessfully() { var variable = new NetworkVariableSubclass>(); using var writer = new FastBufferWriter(1024, Allocator.Temp); var value = new TemplatedValueOnlyReferencedByNetworkVariableSubclass { Value = 12345 }; variable.Value = value; variable.WriteField(writer); variable.Value = new TemplatedValueOnlyReferencedByNetworkVariableSubclass { Value = 54321 }; using var reader = new FastBufferReader(writer, Allocator.None); variable.ReadField(reader); Assert.AreEqual(value.Value, variable.Value.Value); } [Test] public void TestUnsupportedUnmanagedTypesWithUserSerializationDoNotThrowExceptions() { var variable = new NetworkVariable(); UserNetworkVariableSerialization.ReadValue = (FastBufferReader reader, out Guid value) => { var tmpValue = new ForceNetworkSerializeByMemcpy(); reader.ReadValueSafe(out tmpValue); value = tmpValue.Value; }; UserNetworkVariableSerialization.WriteValue = (FastBufferWriter writer, in Guid value) => { var tmpValue = new ForceNetworkSerializeByMemcpy(value); writer.WriteValueSafe(tmpValue); }; UserNetworkVariableSerialization.DuplicateValue = (in Guid a, ref Guid b) => { b = a; }; try { using var writer = new FastBufferWriter(1024, Allocator.Temp); var guid = Guid.NewGuid(); variable.Value = guid; variable.WriteField(writer); variable.Value = Guid.Empty; using var reader = new FastBufferReader(writer, Allocator.None); variable.ReadField(reader); Assert.AreEqual(guid, variable.Value); } finally { UserNetworkVariableSerialization.ReadValue = null; UserNetworkVariableSerialization.WriteValue = null; UserNetworkVariableSerialization.DuplicateValue = null; } } [Test] public void WhenCreatingAnArrayOfNetVars_InitializingVariablesDoesNotThrowAnException() { var testObjPrefab = CreateNetworkObjectPrefab($"NetVarArrayPrefab"); var testComp = testObjPrefab.AddComponent(); testComp.InitializeVariables(); // Verify all variables were initialized Assert.AreEqual(testComp.InitializedFieldCount, 5); Assert.NotNull(testComp.Int0.GetBehaviour()); Assert.NotNull(testComp.Int1.GetBehaviour()); Assert.NotNull(testComp.Int2.GetBehaviour()); Assert.NotNull(testComp.Int3.GetBehaviour()); Assert.NotNull(testComp.Int4.GetBehaviour()); Assert.NotNull(testComp.Int0.Name); Assert.NotNull(testComp.Int1.Name); Assert.NotNull(testComp.Int2.Name); Assert.NotNull(testComp.Int3.Name); Assert.NotNull(testComp.Int4.Name); Assert.AreNotEqual("", testComp.Int0.Name); Assert.AreNotEqual("", testComp.Int1.Name); Assert.AreNotEqual("", testComp.Int2.Name); Assert.AreNotEqual("", testComp.Int3.Name); Assert.AreNotEqual("", testComp.Int4.Name); Assert.AreSame(testComp.AllInts[0], testComp.Int0); Assert.AreSame(testComp.AllInts[1], testComp.Int1); Assert.AreSame(testComp.AllInts[2], testComp.Int2); Assert.AreSame(testComp.AllInts[3], testComp.Int3); Assert.AreSame(testComp.AllInts[4], testComp.Int4); } private void TestValueType(T testValue, T changedValue) where T : unmanaged { var serverVariable = new NetworkVariable(testValue); var clientVariable = new NetworkVariable(); using var writer = new FastBufferWriter(1024, Allocator.Temp); serverVariable.WriteField(writer); Assert.IsFalse(NetworkVariableSerialization.AreEqual(ref serverVariable.RefValue(), ref clientVariable.RefValue())); using var reader = new FastBufferReader(writer, Allocator.None); clientVariable.ReadField(reader); Assert.IsTrue(NetworkVariableSerialization.AreEqual(ref serverVariable.RefValue(), ref clientVariable.RefValue())); serverVariable.Value = changedValue; Assert.IsFalse(NetworkVariableSerialization.AreEqual(ref serverVariable.RefValue(), ref clientVariable.RefValue())); writer.Seek(0); serverVariable.WriteDelta(writer); Assert.IsFalse(NetworkVariableSerialization.AreEqual(ref serverVariable.RefValue(), ref clientVariable.RefValue())); using var reader2 = new FastBufferReader(writer, Allocator.None); clientVariable.ReadDelta(reader2, false); Assert.IsTrue(NetworkVariableSerialization.AreEqual(ref serverVariable.RefValue(), ref clientVariable.RefValue())); } public void AssertArraysMatch(ref NativeArray a, ref NativeArray b) where T : unmanaged { Assert.IsTrue(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Lists do not match: {ArrayStr(a)} != {ArrayStr(b)}"); } public void AssertArraysDoNotMatch(ref NativeArray a, ref NativeArray b) where T : unmanaged { Assert.IsFalse(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Lists match when they should not: {ArrayStr(a)} == {ArrayStr(b)}"); } private void TestValueTypeNativeArray(NativeArray testValue, NativeArray changedValue) where T : unmanaged { VerboseDebug($"Changing {ArrayStr(testValue)} to {ArrayStr(changedValue)}"); var serverVariable = new NetworkVariable>(testValue); var clientVariable = new NetworkVariable>(new NativeArray(1, Allocator.Persistent)); using var writer = new FastBufferWriter(1024, Allocator.Temp, int.MaxValue); serverVariable.WriteField(writer); AssertArraysDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); using var reader = new FastBufferReader(writer, Allocator.None); clientVariable.ReadField(reader); AssertArraysMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); serverVariable.ResetDirty(); serverVariable.Value.Dispose(); serverVariable.Value = changedValue; AssertArraysDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); writer.Seek(0); serverVariable.WriteDelta(writer); AssertArraysDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); using var reader2 = new FastBufferReader(writer, Allocator.None); clientVariable.ReadDelta(reader2, false); AssertArraysMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); var cachedValue = changedValue[0]; var differentValue = changedValue[0]; foreach (var checkValue in testValue) { var checkValueRef = checkValue; if (!NetworkVariableSerialization.AreEqual(ref checkValueRef, ref differentValue)) { differentValue = checkValue; break; } } changedValue[0] = differentValue; Assert.IsTrue(serverVariable.IsDirty()); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); changedValue[0] = cachedValue; Assert.IsTrue(serverVariable.IsDirty()); serverVariable.Dispose(); clientVariable.Dispose(); } public void AssertListsMatch(ref List a, ref List b) { Assert.IsTrue(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Lists do not match: {ListStr(a)} != {ListStr(b)}"); } public void AssertListsDoNotMatch(ref List a, ref List b) { Assert.IsFalse(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Lists match when they should not: {ListStr(a)} == {ListStr(b)}"); } private void TestList(List testValue, List changedValue) { VerboseDebug($"Changing {ListStr(testValue)} to {ListStr(changedValue)}"); var serverVariable = new NetworkVariable>(testValue); var inPlaceList = new List(); var clientVariable = new NetworkVariable>(inPlaceList); using var writer = new FastBufferWriter(1024, Allocator.Temp, int.MaxValue); serverVariable.WriteField(writer); AssertListsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader = new FastBufferReader(writer, Allocator.None); clientVariable.ReadField(reader); AssertListsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); serverVariable.Value = changedValue; AssertListsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); writer.Seek(0); serverVariable.WriteDelta(writer); AssertListsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader2 = new FastBufferReader(writer, Allocator.None); clientVariable.ReadDelta(reader2, false); AssertListsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Clear(); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Add(default); Assert.IsTrue(serverVariable.IsDirty()); } public void AssertSetsMatch(ref HashSet a, ref HashSet b) where T : IEquatable { Assert.IsTrue(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Sets do not match: {HashSetStr(a)} != {HashSetStr(b)}"); } public void AssertSetsDoNotMatch(ref HashSet a, ref HashSet b) where T : IEquatable { Assert.IsFalse(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Sets match when they should not: {HashSetStr(a)} == {HashSetStr(b)}"); } private void TestHashSet(HashSet testValue, HashSet changedValue) where T : IEquatable { VerboseDebug($"Changing {HashSetStr(testValue)} to {HashSetStr(changedValue)}"); var serverVariable = new NetworkVariable>(testValue); var inPlaceList = new HashSet(); var clientVariable = new NetworkVariable>(inPlaceList); using var writer = new FastBufferWriter(1024, Allocator.Temp, int.MaxValue); serverVariable.WriteField(writer); AssertSetsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader = new FastBufferReader(writer, Allocator.None); clientVariable.ReadField(reader); AssertSetsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); serverVariable.Value = changedValue; AssertSetsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); writer.Seek(0); serverVariable.WriteDelta(writer); AssertSetsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader2 = new FastBufferReader(writer, Allocator.None); clientVariable.ReadDelta(reader2, false); AssertSetsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Clear(); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Add(default); Assert.IsTrue(serverVariable.IsDirty()); } public void AssertMapsMatch(ref Dictionary a, ref Dictionary b) where TKey : IEquatable { Assert.IsTrue(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Maps do not match: {DictionaryStr(a)} != {DictionaryStr(b)}"); } public void AssertMapsDoNotMatch(ref Dictionary a, ref Dictionary b) where TKey : IEquatable { Assert.IsFalse(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Maps match when they should not: {DictionaryStr(a)} != {DictionaryStr(b)}"); } private void TestDictionary(Dictionary testValue, Dictionary changedValue) where TKey : IEquatable { VerboseDebug($"Changing {DictionaryStr(testValue)} to {DictionaryStr(changedValue)}"); var serverVariable = new NetworkVariable>(testValue); var inPlaceList = new Dictionary(); var clientVariable = new NetworkVariable>(inPlaceList); using var writer = new FastBufferWriter(1024, Allocator.Temp, int.MaxValue); serverVariable.WriteField(writer); AssertMapsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader = new FastBufferReader(writer, Allocator.None); clientVariable.ReadField(reader); AssertMapsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); serverVariable.Value = changedValue; AssertMapsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); writer.Seek(0); serverVariable.WriteDelta(writer); AssertMapsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader2 = new FastBufferReader(writer, Allocator.None); clientVariable.ReadDelta(reader2, false); AssertMapsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Clear(); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); foreach (var kvp in testValue) { if (!serverVariable.Value.ContainsKey(kvp.Key)) { serverVariable.Value.Add(kvp.Key, kvp.Value); } } Assert.IsTrue(serverVariable.IsDirty()); } #if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT public void AssertListsMatch(ref NativeList a, ref NativeList b) where T : unmanaged { Assert.IsTrue(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Lists do not match: {NativeListStr(a)} != {NativeListStr(b)}"); } public void AssertListsDoNotMatch(ref NativeList a, ref NativeList b) where T : unmanaged { Assert.IsFalse(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Lists match when they should not: {NativeListStr(a)} == {NativeListStr(b)}"); } private void TestValueTypeNativeList(NativeList testValue, NativeList changedValue) where T : unmanaged { VerboseDebug($"Changing {NativeListStr(testValue)} to {NativeListStr(changedValue)}"); var serverVariable = new NetworkVariable>(testValue); var inPlaceList = new NativeList(1, Allocator.Temp); var clientVariable = new NetworkVariable>(inPlaceList); using var writer = new FastBufferWriter(1024, Allocator.Temp, int.MaxValue); serverVariable.WriteField(writer); AssertListsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader = new FastBufferReader(writer, Allocator.None); clientVariable.ReadField(reader); AssertListsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); serverVariable.Value.Dispose(); serverVariable.Value = changedValue; AssertListsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); writer.Seek(0); serverVariable.WriteDelta(writer); AssertListsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader2 = new FastBufferReader(writer, Allocator.None); clientVariable.ReadDelta(reader2, false); AssertListsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertListsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Clear(); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Add(default); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.Dispose(); clientVariable.Dispose(); } public void AssertSetsMatch(ref NativeHashSet a, ref NativeHashSet b) where T : unmanaged, IEquatable { Assert.IsTrue(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Sets do not match: {NativeHashSetStr(a)} != {NativeHashSetStr(b)}"); } public void AssertSetsDoNotMatch(ref NativeHashSet a, ref NativeHashSet b) where T : unmanaged, IEquatable { Assert.IsFalse(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Sets match when they should not: {NativeHashSetStr(a)} == {NativeHashSetStr(b)}"); } private void TestValueTypeNativeHashSet(NativeHashSet testValue, NativeHashSet changedValue) where T : unmanaged, IEquatable { VerboseDebug($"Changing {NativeHashSetStr(testValue)} to {NativeHashSetStr(changedValue)}"); var serverVariable = new NetworkVariable>(testValue); var inPlaceList = new NativeHashSet(1, Allocator.Temp); var clientVariable = new NetworkVariable>(inPlaceList); using var writer = new FastBufferWriter(1024, Allocator.Temp, int.MaxValue); serverVariable.WriteField(writer); AssertSetsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader = new FastBufferReader(writer, Allocator.None); clientVariable.ReadField(reader); AssertSetsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); serverVariable.Value.Dispose(); serverVariable.Value = changedValue; AssertSetsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); writer.Seek(0); serverVariable.WriteDelta(writer); AssertSetsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader2 = new FastBufferReader(writer, Allocator.None); clientVariable.ReadDelta(reader2, false); AssertSetsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertSetsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Clear(); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Add(default); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.Dispose(); clientVariable.Dispose(); } public void AssertMapsMatch(ref NativeHashMap a, ref NativeHashMap b) where TKey : unmanaged, IEquatable where TVal : unmanaged { Assert.IsTrue(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Maps do not match: {NativeHashMapStr(a)} != {NativeHashMapStr(b)}"); } public void AssertMapsDoNotMatch(ref NativeHashMap a, ref NativeHashMap b) where TKey : unmanaged, IEquatable where TVal : unmanaged { Assert.IsFalse(NetworkVariableSerialization>.AreEqual(ref a, ref b), $"Maps match when they should not: {NativeHashMapStr(a)} != {NativeHashMapStr(b)}"); } private void TestValueTypeNativeHashMap(NativeHashMap testValue, NativeHashMap changedValue) where TKey : unmanaged, IEquatable where TVal : unmanaged { VerboseDebug($"Changing {NativeHashMapStr(testValue)} to {NativeHashMapStr(changedValue)}"); var serverVariable = new NetworkVariable>(testValue); var inPlaceList = new NativeHashMap(1, Allocator.Temp); var clientVariable = new NetworkVariable>(inPlaceList); using var writer = new FastBufferWriter(1024, Allocator.Temp, int.MaxValue); serverVariable.WriteField(writer); AssertMapsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader = new FastBufferReader(writer, Allocator.None); clientVariable.ReadField(reader); AssertMapsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); serverVariable.Value.Dispose(); serverVariable.Value = changedValue; AssertMapsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); writer.Seek(0); serverVariable.WriteDelta(writer); AssertMapsDoNotMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); using var reader2 = new FastBufferReader(writer, Allocator.None); clientVariable.ReadDelta(reader2, false); AssertMapsMatch(ref serverVariable.RefValue(), ref clientVariable.RefValue()); // Lists are deserialized in place so this should ALWAYS be true. Checking it every time to make sure! AssertMapsMatch(ref clientVariable.RefValue(), ref inPlaceList); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Clear(); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.ResetDirty(); Assert.IsFalse(serverVariable.IsDirty()); serverVariable.Value.Add(default, default); Assert.IsTrue(serverVariable.IsDirty()); serverVariable.Dispose(); clientVariable.Dispose(); } #endif [Test] public void WhenSerializingAndDeserializingValueTypeNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(NetworkVariableTestStruct), typeof(FixedString32Bytes))] Type testType) { if (testType == typeof(byte)) { TestValueType(byte.MinValue + 5, byte.MaxValue); } else if (testType == typeof(sbyte)) { TestValueType(sbyte.MinValue + 5, sbyte.MaxValue); } else if (testType == typeof(short)) { TestValueType(short.MinValue + 5, short.MaxValue); } else if (testType == typeof(ushort)) { TestValueType(ushort.MinValue + 5, ushort.MaxValue); } else if (testType == typeof(int)) { TestValueType(int.MinValue + 5, int.MaxValue); } else if (testType == typeof(uint)) { TestValueType(uint.MinValue + 5, uint.MaxValue); } else if (testType == typeof(long)) { TestValueType(long.MinValue + 5, long.MaxValue); } else if (testType == typeof(ulong)) { TestValueType(ulong.MinValue + 5, ulong.MaxValue); } else if (testType == typeof(bool)) { TestValueType(true, false); } else if (testType == typeof(char)) { TestValueType('z', ' '); } else if (testType == typeof(float)) { TestValueType(float.MinValue + 5.12345678f, float.MaxValue); } else if (testType == typeof(double)) { TestValueType(double.MinValue + 5.12345678, double.MaxValue); } else if (testType == typeof(ByteEnum)) { TestValueType(ByteEnum.B, ByteEnum.C); } else if (testType == typeof(SByteEnum)) { TestValueType(SByteEnum.B, SByteEnum.C); } else if (testType == typeof(ShortEnum)) { TestValueType(ShortEnum.B, ShortEnum.C); } else if (testType == typeof(UShortEnum)) { TestValueType(UShortEnum.B, UShortEnum.C); } else if (testType == typeof(IntEnum)) { TestValueType(IntEnum.B, IntEnum.C); } else if (testType == typeof(UIntEnum)) { TestValueType(UIntEnum.B, UIntEnum.C); } else if (testType == typeof(LongEnum)) { TestValueType(LongEnum.B, LongEnum.C); } else if (testType == typeof(ULongEnum)) { TestValueType(ULongEnum.B, ULongEnum.C); } else if (testType == typeof(Vector2)) { TestValueType( new Vector2(5, 10), new Vector2(15, 20)); } else if (testType == typeof(Vector3)) { TestValueType( new Vector3(5, 10, 15), new Vector3(20, 25, 30)); } else if (testType == typeof(Vector2Int)) { TestValueType( new Vector2Int(5, 10), new Vector2Int(15, 20)); } else if (testType == typeof(Vector3Int)) { TestValueType( new Vector3Int(5, 10, 15), new Vector3Int(20, 25, 30)); } else if (testType == typeof(Vector4)) { TestValueType( new Vector4(5, 10, 15, 20), new Vector4(25, 30, 35, 40)); } else if (testType == typeof(Quaternion)) { TestValueType( new Quaternion(5, 10, 15, 20), new Quaternion(25, 30, 35, 40)); } else if (testType == typeof(Color)) { TestValueType( new Color(1, 0, 0), new Color(0, 1, 1)); } else if (testType == typeof(Color32)) { TestValueType( new Color32(255, 0, 0, 128), new Color32(0, 255, 255, 255)); } else if (testType == typeof(Ray)) { TestValueType( new Ray(new Vector3(0, 1, 2), new Vector3(3, 4, 5)), new Ray(new Vector3(6, 7, 8), new Vector3(9, 10, 11))); } else if (testType == typeof(Ray2D)) { TestValueType( new Ray2D(new Vector2(0, 1), new Vector2(2, 3)), new Ray2D(new Vector2(4, 5), new Vector2(6, 7))); } else if (testType == typeof(NetworkVariableTestStruct)) { TestValueType(NetworkVariableTestStruct.GetTestStruct(), NetworkVariableTestStruct.GetTestStruct()); } else if (testType == typeof(FixedString32Bytes)) { TestValueType(new FixedString32Bytes("foobar"), new FixedString32Bytes("12345678901234567890123456789")); } } [Test] public void WhenSerializingAndDeserializingValueTypeNativeArrayNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(NetworkVariableTestStruct), typeof(FixedString32Bytes))] Type testType) { if (testType == typeof(byte)) { TestValueTypeNativeArray( new NativeArray(new byte[] { byte.MinValue + 5, byte.MaxValue }, Allocator.Temp), new NativeArray(new byte[] { 0, byte.MinValue + 10, byte.MaxValue - 10 }, Allocator.Temp)); } else if (testType == typeof(sbyte)) { TestValueTypeNativeArray( new NativeArray(new sbyte[] { sbyte.MinValue + 5, sbyte.MaxValue }, Allocator.Temp), new NativeArray(new sbyte[] { 0, sbyte.MinValue + 10, sbyte.MaxValue - 10 }, Allocator.Temp)); } else if (testType == typeof(short)) { TestValueTypeNativeArray( new NativeArray(new short[] { short.MinValue + 5, short.MaxValue }, Allocator.Temp), new NativeArray(new short[] { 0, short.MinValue + 10, short.MaxValue - 10 }, Allocator.Temp)); } else if (testType == typeof(ushort)) { TestValueTypeNativeArray( new NativeArray(new ushort[] { ushort.MinValue + 5, ushort.MaxValue }, Allocator.Temp), new NativeArray(new ushort[] { 0, ushort.MinValue + 10, ushort.MaxValue - 10 }, Allocator.Temp)); } else if (testType == typeof(int)) { TestValueTypeNativeArray( new NativeArray(new int[] { int.MinValue + 5, int.MaxValue }, Allocator.Temp), new NativeArray(new int[] { 0, int.MinValue + 10, int.MaxValue - 10 }, Allocator.Temp)); } else if (testType == typeof(uint)) { TestValueTypeNativeArray( new NativeArray(new uint[] { uint.MinValue + 5, uint.MaxValue }, Allocator.Temp), new NativeArray(new uint[] { 0, uint.MinValue + 10, uint.MaxValue - 10 }, Allocator.Temp)); } else if (testType == typeof(long)) { TestValueTypeNativeArray( new NativeArray(new long[] { long.MinValue + 5, long.MaxValue }, Allocator.Temp), new NativeArray(new long[] { 0, long.MinValue + 10, long.MaxValue - 10 }, Allocator.Temp)); } else if (testType == typeof(ulong)) { TestValueTypeNativeArray( new NativeArray(new ulong[] { ulong.MinValue + 5, ulong.MaxValue }, Allocator.Temp), new NativeArray(new ulong[] { 0, ulong.MinValue + 10, ulong.MaxValue - 10 }, Allocator.Temp)); } else if (testType == typeof(bool)) { TestValueTypeNativeArray( new NativeArray(new bool[] { true, false, true }, Allocator.Temp), new NativeArray(new bool[] { false, true, false, true, false }, Allocator.Temp)); } else if (testType == typeof(char)) { TestValueTypeNativeArray( new NativeArray(new char[] { 'z', ' ', '?' }, Allocator.Temp), new NativeArray(new char[] { 'n', 'e', 'w', ' ', 'v', 'a', 'l', 'u', 'e' }, Allocator.Temp)); } else if (testType == typeof(float)) { TestValueTypeNativeArray( new NativeArray(new float[] { float.MinValue + 5.12345678f, float.MaxValue }, Allocator.Temp), new NativeArray(new float[] { 0, float.MinValue + 10.987654321f, float.MaxValue - 10.135792468f }, Allocator.Temp)); } else if (testType == typeof(double)) { TestValueTypeNativeArray( new NativeArray(new double[] { double.MinValue + 5.12345678, double.MaxValue }, Allocator.Temp), new NativeArray(new double[] { 0, double.MinValue + 10.987654321, double.MaxValue - 10.135792468 }, Allocator.Temp)); } else if (testType == typeof(ByteEnum)) { TestValueTypeNativeArray( new NativeArray(new ByteEnum[] { ByteEnum.C, ByteEnum.B, ByteEnum.A }, Allocator.Temp), new NativeArray(new ByteEnum[] { ByteEnum.B, ByteEnum.C, ByteEnum.B, ByteEnum.A, ByteEnum.C }, Allocator.Temp)); } else if (testType == typeof(SByteEnum)) { TestValueTypeNativeArray( new NativeArray(new SByteEnum[] { SByteEnum.C, SByteEnum.B, SByteEnum.A }, Allocator.Temp), new NativeArray(new SByteEnum[] { SByteEnum.B, SByteEnum.C, SByteEnum.B, SByteEnum.A, SByteEnum.C }, Allocator.Temp)); } else if (testType == typeof(ShortEnum)) { TestValueTypeNativeArray( new NativeArray(new ShortEnum[] { ShortEnum.C, ShortEnum.B, ShortEnum.A }, Allocator.Temp), new NativeArray(new ShortEnum[] { ShortEnum.B, ShortEnum.C, ShortEnum.B, ShortEnum.A, ShortEnum.C }, Allocator.Temp)); } else if (testType == typeof(UShortEnum)) { TestValueTypeNativeArray( new NativeArray(new UShortEnum[] { UShortEnum.C, UShortEnum.B, UShortEnum.A }, Allocator.Temp), new NativeArray(new UShortEnum[] { UShortEnum.B, UShortEnum.C, UShortEnum.B, UShortEnum.A, UShortEnum.C }, Allocator.Temp)); } else if (testType == typeof(IntEnum)) { TestValueTypeNativeArray( new NativeArray(new IntEnum[] { IntEnum.C, IntEnum.B, IntEnum.A }, Allocator.Temp), new NativeArray(new IntEnum[] { IntEnum.B, IntEnum.C, IntEnum.B, IntEnum.A, IntEnum.C }, Allocator.Temp)); } else if (testType == typeof(UIntEnum)) { TestValueTypeNativeArray( new NativeArray(new UIntEnum[] { UIntEnum.C, UIntEnum.B, UIntEnum.A }, Allocator.Temp), new NativeArray(new UIntEnum[] { UIntEnum.B, UIntEnum.C, UIntEnum.B, UIntEnum.A, UIntEnum.C }, Allocator.Temp)); } else if (testType == typeof(LongEnum)) { TestValueTypeNativeArray( new NativeArray(new LongEnum[] { LongEnum.C, LongEnum.B, LongEnum.A }, Allocator.Temp), new NativeArray(new LongEnum[] { LongEnum.B, LongEnum.C, LongEnum.B, LongEnum.A, LongEnum.C }, Allocator.Temp)); } else if (testType == typeof(ULongEnum)) { TestValueTypeNativeArray( new NativeArray(new ULongEnum[] { ULongEnum.C, ULongEnum.B, ULongEnum.A }, Allocator.Temp), new NativeArray(new ULongEnum[] { ULongEnum.B, ULongEnum.C, ULongEnum.B, ULongEnum.A, ULongEnum.C }, Allocator.Temp)); } else if (testType == typeof(Vector2)) { TestValueTypeNativeArray( new NativeArray(new Vector2[] { new Vector2(5, 10), new Vector2(15, 20) }, Allocator.Temp), new NativeArray(new Vector2[] { new Vector2(25, 30), new Vector2(35, 40), new Vector2(45, 50) }, Allocator.Temp)); } else if (testType == typeof(Vector3)) { TestValueTypeNativeArray( new NativeArray(new Vector3[] { new Vector3(5, 10, 15), new Vector3(20, 25, 30) }, Allocator.Temp), new NativeArray(new Vector3[] { new Vector3(35, 40, 45), new Vector3(50, 55, 60), new Vector3(65, 70, 75) }, Allocator.Temp)); } else if (testType == typeof(Vector2Int)) { TestValueTypeNativeArray( new NativeArray(new Vector2Int[] { new Vector2Int(5, 10), new Vector2Int(15, 20) }, Allocator.Temp), new NativeArray(new Vector2Int[] { new Vector2Int(25, 30), new Vector2Int(35, 40), new Vector2Int(45, 50) }, Allocator.Temp)); } else if (testType == typeof(Vector3Int)) { TestValueTypeNativeArray( new NativeArray(new Vector3Int[] { new Vector3Int(5, 10, 15), new Vector3Int(20, 25, 30) }, Allocator.Temp), new NativeArray(new Vector3Int[] { new Vector3Int(35, 40, 45), new Vector3Int(50, 55, 60), new Vector3Int(65, 70, 75) }, Allocator.Temp)); } else if (testType == typeof(Vector4)) { TestValueTypeNativeArray( new NativeArray(new Vector4[] { new Vector4(5, 10, 15, 20), new Vector4(25, 30, 35, 40) }, Allocator.Temp), new NativeArray(new Vector4[] { new Vector4(45, 50, 55, 60), new Vector4(65, 70, 75, 80), new Vector4(85, 90, 95, 100) }, Allocator.Temp)); } else if (testType == typeof(Quaternion)) { TestValueTypeNativeArray( new NativeArray(new Quaternion[] { new Quaternion(5, 10, 15, 20), new Quaternion(25, 30, 35, 40) }, Allocator.Temp), new NativeArray(new Quaternion[] { new Quaternion(45, 50, 55, 60), new Quaternion(65, 70, 75, 80), new Quaternion(85, 90, 95, 100) }, Allocator.Temp)); } else if (testType == typeof(Color)) { TestValueTypeNativeArray( new NativeArray(new Color[] { new Color(.5f, .10f, .15f), new Color(.20f, .25f, .30f) }, Allocator.Temp), new NativeArray(new Color[] { new Color(.35f, .40f, .45f), new Color(.50f, .55f, .60f), new Color(.65f, .70f, .75f) }, Allocator.Temp)); } else if (testType == typeof(Color32)) { TestValueTypeNativeArray( new NativeArray(new Color32[] { new Color32(5, 10, 15, 20), new Color32(25, 30, 35, 40) }, Allocator.Temp), new NativeArray(new Color32[] { new Color32(45, 50, 55, 60), new Color32(65, 70, 75, 80), new Color32(85, 90, 95, 100) }, Allocator.Temp)); } else if (testType == typeof(Ray)) { TestValueTypeNativeArray( new NativeArray(new Ray[] { new Ray(new Vector3(0, 1, 2), new Vector3(3, 4, 5)), new Ray(new Vector3(6, 7, 8), new Vector3(9, 10, 11)), }, Allocator.Temp), new NativeArray(new Ray[] { new Ray(new Vector3(12, 13, 14), new Vector3(15, 16, 17)), new Ray(new Vector3(18, 19, 20), new Vector3(21, 22, 23)), new Ray(new Vector3(24, 25, 26), new Vector3(27, 28, 29)), }, Allocator.Temp)); } else if (testType == typeof(Ray2D)) { TestValueTypeNativeArray( new NativeArray(new Ray2D[] { new Ray2D(new Vector2(0, 1), new Vector2(3, 4)), new Ray2D(new Vector2(6, 7), new Vector2(9, 10)), }, Allocator.Temp), new NativeArray(new Ray2D[] { new Ray2D(new Vector2(12, 13), new Vector2(15, 16)), new Ray2D(new Vector2(18, 19), new Vector2(21, 22)), new Ray2D(new Vector2(24, 25), new Vector2(27, 28)), }, Allocator.Temp)); } else if (testType == typeof(NetworkVariableTestStruct)) { TestValueTypeNativeArray( new NativeArray(new NetworkVariableTestStruct[] { NetworkVariableTestStruct.GetTestStruct(), NetworkVariableTestStruct.GetTestStruct() }, Allocator.Temp), new NativeArray(new NetworkVariableTestStruct[] { NetworkVariableTestStruct.GetTestStruct(), NetworkVariableTestStruct.GetTestStruct(), NetworkVariableTestStruct.GetTestStruct() }, Allocator.Temp)); } else if (testType == typeof(FixedString32Bytes)) { TestValueTypeNativeArray( new NativeArray(new FixedString32Bytes[] { new FixedString32Bytes("foobar"), new FixedString32Bytes("12345678901234567890123456789") }, Allocator.Temp), new NativeArray(new FixedString32Bytes[] { new FixedString32Bytes("BazQux"), new FixedString32Bytes("98765432109876543210987654321"), new FixedString32Bytes("FixedString32Bytes") }, Allocator.Temp)); } } public delegate T GetRandomElement(System.Random rand); public unsafe T RandGenBytes(System.Random rand) where T : unmanaged { var t = new T(); T* tPtr = &t; var s = new Span(tPtr, sizeof(T)); rand.NextBytes(s); return t; } public FixedString32Bytes RandGenFixedString32(System.Random rand) { var s = new FixedString32Bytes(); var len = rand.Next(s.Capacity); s.Length = len; for (var i = 0; i < len; ++i) { // Ascii visible character range s[i] = (byte)rand.Next(32, 126); } return s; } public string ArrayStr(NativeArray arr) where T : unmanaged { var str = "["; var comma = false; foreach (var item in arr) { if (comma) { str += ", "; } comma = true; str += $"{item}"; } str += "]"; return str; } public (NativeArray original, NativeArray original2, NativeArray changed, NativeArray changed2) GetArarys(GetRandomElement generator) where T : unmanaged { var rand = new System.Random(); var originalSize = rand.Next(32, 64); var changedSize = rand.Next(32, 64); var changed2Changes = rand.Next(12, 16); var changed2Adds = rand.Next(-16, 16); var original = new NativeArray(originalSize, Allocator.Temp); var changed = new NativeArray(changedSize, Allocator.Temp); var original2 = new NativeArray(originalSize, Allocator.Temp); var changed2 = new NativeArray(originalSize + changed2Adds, Allocator.Temp); for (var i = 0; i < originalSize; ++i) { var item = generator(rand); original[i] = item; original2[i] = item; if (i < changed2.Length) { changed2[i] = item; } } for (var i = 0; i < changedSize; ++i) { var item = generator(rand); changed[i] = item; } for (var i = 0; i < changed2Changes; ++i) { var idx = rand.Next(changed2.Length - 1); var item = generator(rand); changed2[idx] = item; } for (var i = 0; i < changed2Adds; ++i) { var item = generator(rand); changed2[originalSize + i] = item; } VerboseDebug($"Original: {ArrayStr(original)}"); VerboseDebug($"Changed: {ArrayStr(changed)}"); VerboseDebug($"Original2: {ArrayStr(original2)}"); VerboseDebug($"Changed2: {ArrayStr(changed2)}"); return (original, original2, changed, changed2); } [Test] [Repeat(5)] public void WhenSerializingAndDeserializingVeryLargeValueTypeNativeArrayNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(NetworkVariableTestStruct), typeof(FixedString32Bytes))] Type testType) { if (testType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(sbyte)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(short)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(ushort)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(int)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(uint)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(long)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(bool)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(char)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(float)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(double)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Vector3)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Vector2Int)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Vector2Int(rand.Next(), rand.Next()) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Vector3Int)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next()) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Vector4)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Quaternion)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Color)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Color32)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Color32((byte)rand.Next(), (byte)rand.Next(), (byte)rand.Next(), (byte)rand.Next()) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Ray)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Ray( new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()), new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(Ray2D)) { (var original, var original2, var changed, var changed2) = GetArarys( (rand) => new Ray2D( new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()) ) ); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(NetworkVariableTestStruct)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenBytes); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } else if (testType == typeof(FixedString32Bytes)) { (var original, var original2, var changed, var changed2) = GetArarys(RandGenFixedString32); TestValueTypeNativeArray(original, changed); TestValueTypeNativeArray(original2, changed2); } } public string ListStr(List list) { var str = "["; var comma = false; foreach (var item in list) { if (comma) { str += ", "; } comma = true; str += $"{item}"; } str += "]"; return str; } public string HashSetStr(HashSet list) where T : IEquatable { var str = "{"; var comma = false; foreach (var item in list) { if (comma) { str += ", "; } comma = true; str += $"{item}"; } str += "}"; return str; } public string DictionaryStr(Dictionary list) where TKey : IEquatable { var str = "{"; var comma = false; foreach (var item in list) { if (comma) { str += ", "; } comma = true; str += $"{item.Key}: {item.Value}"; } str += "}"; return str; } public (List original, List original2, List changed, List changed2) GetLists(GetRandomElement generator) { var original = new List(); var changed = new List(); var original2 = new List(); var changed2 = new List(); var rand = new System.Random(); var originalSize = rand.Next(32, 64); var changedSize = rand.Next(32, 64); var changed2Changes = rand.Next(12, 16); var changed2Adds = rand.Next(-16, 16); for (var i = 0; i < originalSize; ++i) { var item = generator(rand); original.Add(item); original2.Add(item); changed2.Add(item); } for (var i = 0; i < changedSize; ++i) { var item = generator(rand); changed.Add(item); } for (var i = 0; i < changed2Changes; ++i) { var idx = rand.Next(changed2.Count - 1); var item = generator(rand); changed2[idx] = item; } if (changed2Adds < 0) { changed2.RemoveRange(changed2.Count + changed2Adds, -changed2Adds); } else { for (var i = 0; i < changed2Adds; ++i) { var item = generator(rand); changed2.Add(item); } } VerboseDebug($"Original: {ListStr(original)}"); VerboseDebug($"Changed: {ListStr(changed)}"); VerboseDebug($"Original2: {ListStr(original2)}"); VerboseDebug($"Changed2: {ListStr(changed2)}"); return (original, original2, changed, changed2); } public (HashSet original, HashSet original2, HashSet changed, HashSet changed2) GetHashSets(GetRandomElement generator) where T : IEquatable { var original = new HashSet(); var changed = new HashSet(); var original2 = new HashSet(); var changed2 = new HashSet(); var rand = new System.Random(); var originalSize = rand.Next(32, 64); var changedSize = rand.Next(32, 64); var changed2Removes = rand.Next(12, 16); var changed2Adds = rand.Next(12, 16); for (var i = 0; i < originalSize; ++i) { var item = generator(rand); while (original.Contains(item)) { item = generator(rand); } original.Add(item); original2.Add(item); changed2.Add(item); } for (var i = 0; i < changedSize; ++i) { var item = generator(rand); while (changed.Contains(item)) { item = generator(rand); } changed.Add(item); } for (var i = 0; i < changed2Removes; ++i) { var which = rand.Next(changed2.Count()); T toRemove = default; foreach (var check in changed2) { if (which == 0) { toRemove = check; break; } --which; } changed2.Remove(toRemove); } for (var i = 0; i < changed2Adds; ++i) { var item = generator(rand); while (changed2.Contains(item)) { item = generator(rand); } changed2.Add(item); } VerboseDebug($"Original: {HashSetStr(original)}"); VerboseDebug($"Changed: {HashSetStr(changed)}"); VerboseDebug($"Original2: {HashSetStr(original2)}"); VerboseDebug($"Changed2: {HashSetStr(changed2)}"); return (original, original2, changed, changed2); } public (Dictionary original, Dictionary original2, Dictionary changed, Dictionary changed2) GetDictionaries(GetRandomElement keyGenerator, GetRandomElement valGenerator) where TKey : IEquatable { var original = new Dictionary(); var changed = new Dictionary(); var original2 = new Dictionary(); var changed2 = new Dictionary(); var rand = new System.Random(); var originalSize = rand.Next(32, 64); var changedSize = rand.Next(32, 64); var changed2Removes = rand.Next(12, 16); var changed2Adds = rand.Next(12, 16); var changed2Changes = rand.Next(12, 16); for (var i = 0; i < originalSize; ++i) { var key = keyGenerator(rand); while (original.ContainsKey(key)) { key = keyGenerator(rand); } var val = valGenerator(rand); original.Add(key, val); original2.Add(key, val); changed2.Add(key, val); } for (var i = 0; i < changedSize; ++i) { var key = keyGenerator(rand); while (changed.ContainsKey(key)) { key = keyGenerator(rand); } var val = valGenerator(rand); changed.Add(key, val); } for (var i = 0; i < changed2Removes; ++i) { var which = rand.Next(changed2.Count()); TKey toRemove = default; foreach (var check in changed2) { if (which == 0) { toRemove = check.Key; break; } --which; } changed2.Remove(toRemove); } for (var i = 0; i < changed2Changes; ++i) { var which = rand.Next(changed2.Count()); TKey key = default; foreach (var check in changed2) { if (which == 0) { key = check.Key; break; } --which; } var val = valGenerator(rand); changed2[key] = val; } for (var i = 0; i < changed2Adds; ++i) { var key = keyGenerator(rand); while (changed2.ContainsKey(key)) { key = keyGenerator(rand); } var val = valGenerator(rand); changed2.Add(key, val); } VerboseDebug($"Original: {DictionaryStr(original)}"); VerboseDebug($"Changed: {DictionaryStr(changed)}"); VerboseDebug($"Original2: {DictionaryStr(original2)}"); VerboseDebug($"Changed2: {DictionaryStr(changed2)}"); return (original, original2, changed, changed2); } [Test] [Repeat(5)] public void WhenSerializingAndDeserializingVeryLargeListNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(NetworkVariableTestClass), typeof(FixedString32Bytes))] Type testType) { if (testType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(sbyte)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(short)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(ushort)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(int)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(uint)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(long)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(bool)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(char)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(float)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(double)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenBytes); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Vector3)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Vector2Int)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Vector2Int(rand.Next(), rand.Next()) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Vector3Int)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next()) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Vector4)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Quaternion)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Color)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Color32)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Color32((byte)rand.Next(), (byte)rand.Next(), (byte)rand.Next(), (byte)rand.Next()) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Ray)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Ray( new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()), new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(Ray2D)) { (var original, var original2, var changed, var changed2) = GetLists( (rand) => new Ray2D( new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()) ) ); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(NetworkVariableTestClass)) { (var original, var original2, var changed, var changed2) = GetLists((rand) => { return new NetworkVariableTestClass { Data = RandGenBytes(rand) }; }); TestList(original, changed); TestList(original2, changed2); } else if (testType == typeof(FixedString32Bytes)) { (var original, var original2, var changed, var changed2) = GetLists(RandGenFixedString32); TestList(original, changed); TestList(original2, changed2); } } [Test] [Repeat(5)] public void WhenSerializingAndDeserializingVeryLargeHashSetNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(HashableNetworkVariableTestClass), typeof(FixedString32Bytes))] Type testType) { if (testType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(sbyte)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(short)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(ushort)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(int)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(uint)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(long)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(bool)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(char)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(float)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(double)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenBytes); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetHashSets( (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()) ); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(Vector3)) { (var original, var original2, var changed, var changed2) = GetHashSets( (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(Vector2Int)) { (var original, var original2, var changed, var changed2) = GetHashSets( (rand) => new Vector2Int(rand.Next(), rand.Next()) ); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(Vector3Int)) { (var original, var original2, var changed, var changed2) = GetHashSets( (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next()) ); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(Vector4)) { (var original, var original2, var changed, var changed2) = GetHashSets( (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(Quaternion)) { (var original, var original2, var changed, var changed2) = GetHashSets( (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(Color)) { (var original, var original2, var changed, var changed2) = GetHashSets( (rand) => new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(HashableNetworkVariableTestClass)) { (var original, var original2, var changed, var changed2) = GetHashSets((rand) => { return new HashableNetworkVariableTestClass { Data = RandGenBytes(rand) }; }); TestHashSet(original, changed); TestHashSet(original2, changed2); } else if (testType == typeof(FixedString32Bytes)) { (var original, var original2, var changed, var changed2) = GetHashSets(RandGenFixedString32); TestHashSet(original, changed); TestHashSet(original2, changed2); } } [Test] [Repeat(5)] public void WhenSerializingAndDeserializingVeryLargeDictionaryNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(ulong), typeof(Vector2), typeof(HashMapKeyClass))] Type keyType, [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(HashMapValClass), typeof(FixedString32Bytes))] Type valType) { if (valType == typeof(byte)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(sbyte)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(short)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(ushort)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(int)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(uint)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(long)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(ulong)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(bool)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(char)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(float)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(double)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenBytes); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(Vector2)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(Vector3)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(Vector2Int)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector2Int(rand.Next(), rand.Next())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector2Int(rand.Next(), rand.Next())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector2Int(rand.Next(), rand.Next())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, (rand) => new Vector2Int(rand.Next(), rand.Next())); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(Vector3Int)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next())); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(Vector4)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(Quaternion)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(HashMapValClass)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new HashMapValClass { Data = RandGenBytes(rand) }); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, (rand) => new HashMapValClass { Data = RandGenBytes(rand) }); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new HashMapValClass { Data = RandGenBytes(rand) }); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, (rand) => new HashMapValClass { Data = RandGenBytes(rand) }); TestDictionary(original, changed); TestDictionary(original2, changed2); } } else if (valType == typeof(FixedString32Bytes)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenFixedString32); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetDictionaries(RandGenBytes, RandGenFixedString32); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenFixedString32); TestDictionary(original, changed); TestDictionary(original2, changed2); } else if (keyType == typeof(HashMapKeyClass)) { (var original, var original2, var changed, var changed2) = GetDictionaries((rand) => new HashMapKeyClass { Data = RandGenBytes(rand) }, RandGenFixedString32); TestDictionary(original, changed); TestDictionary(original2, changed2); } } } #if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT [Test] public void WhenSerializingAndDeserializingValueTypeNativeListNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(NetworkVariableTestStruct), typeof(FixedString32Bytes))] Type testType) { if (testType == typeof(byte)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { byte.MinValue + 5, byte.MaxValue }, new NativeList(Allocator.Temp) { 0, byte.MinValue + 10, byte.MaxValue - 10 }); } else if (testType == typeof(sbyte)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { sbyte.MinValue + 5, sbyte.MaxValue }, new NativeList(Allocator.Temp) { 0, sbyte.MinValue + 10, sbyte.MaxValue - 10 }); } else if (testType == typeof(short)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { short.MinValue + 5, short.MaxValue }, new NativeList(Allocator.Temp) { 0, short.MinValue + 10, short.MaxValue - 10 }); } else if (testType == typeof(ushort)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { ushort.MinValue + 5, ushort.MaxValue }, new NativeList(Allocator.Temp) { 0, ushort.MinValue + 10, ushort.MaxValue - 10 }); } else if (testType == typeof(int)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { int.MinValue + 5, int.MaxValue }, new NativeList(Allocator.Temp) { 0, int.MinValue + 10, int.MaxValue - 10 }); } else if (testType == typeof(uint)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { uint.MinValue + 5, uint.MaxValue }, new NativeList(Allocator.Temp) { 0, uint.MinValue + 10, uint.MaxValue - 10 }); } else if (testType == typeof(long)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { long.MinValue + 5, long.MaxValue }, new NativeList(Allocator.Temp) { 0, long.MinValue + 10, long.MaxValue - 10 }); } else if (testType == typeof(ulong)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { ulong.MinValue + 5, ulong.MaxValue }, new NativeList(Allocator.Temp) { 0, ulong.MinValue + 10, ulong.MaxValue - 10 }); } else if (testType == typeof(bool)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { true, false, true }, new NativeList(Allocator.Temp) { false, true, false, true, false }); } else if (testType == typeof(char)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { 'z', ' ', '?' }, new NativeList(Allocator.Temp) { 'n', 'e', 'w', ' ', 'v', 'a', 'l', 'u', 'e' }); } else if (testType == typeof(float)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { float.MinValue + 5.12345678f, float.MaxValue }, new NativeList(Allocator.Temp) { 0, float.MinValue + 10.987654321f, float.MaxValue - 10.135792468f }); } else if (testType == typeof(double)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { double.MinValue + 5.12345678, double.MaxValue }, new NativeList(Allocator.Temp) { 0, double.MinValue + 10.987654321, double.MaxValue - 10.135792468 }); } else if (testType == typeof(ByteEnum)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { ByteEnum.C, ByteEnum.B, ByteEnum.A }, new NativeList(Allocator.Temp) { ByteEnum.B, ByteEnum.C, ByteEnum.B, ByteEnum.A, ByteEnum.C }); } else if (testType == typeof(SByteEnum)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { SByteEnum.C, SByteEnum.B, SByteEnum.A }, new NativeList(Allocator.Temp) { SByteEnum.B, SByteEnum.C, SByteEnum.B, SByteEnum.A, SByteEnum.C }); } else if (testType == typeof(ShortEnum)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { ShortEnum.C, ShortEnum.B, ShortEnum.A }, new NativeList(Allocator.Temp) { ShortEnum.B, ShortEnum.C, ShortEnum.B, ShortEnum.A, ShortEnum.C }); } else if (testType == typeof(UShortEnum)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { UShortEnum.C, UShortEnum.B, UShortEnum.A }, new NativeList(Allocator.Temp) { UShortEnum.B, UShortEnum.C, UShortEnum.B, UShortEnum.A, UShortEnum.C }); } else if (testType == typeof(IntEnum)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { IntEnum.C, IntEnum.B, IntEnum.A }, new NativeList(Allocator.Temp) { IntEnum.B, IntEnum.C, IntEnum.B, IntEnum.A, IntEnum.C }); } else if (testType == typeof(UIntEnum)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { UIntEnum.C, UIntEnum.B, UIntEnum.A }, new NativeList(Allocator.Temp) { UIntEnum.B, UIntEnum.C, UIntEnum.B, UIntEnum.A, UIntEnum.C }); } else if (testType == typeof(LongEnum)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { LongEnum.C, LongEnum.B, LongEnum.A }, new NativeList(Allocator.Temp) { LongEnum.B, LongEnum.C, LongEnum.B, LongEnum.A, LongEnum.C }); } else if (testType == typeof(ULongEnum)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { ULongEnum.C, ULongEnum.B, ULongEnum.A }, new NativeList(Allocator.Temp) { ULongEnum.B, ULongEnum.C, ULongEnum.B, ULongEnum.A, ULongEnum.C }); } else if (testType == typeof(Vector2)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Vector2(5, 10), new Vector2(15, 20) }, new NativeList(Allocator.Temp) { new Vector2(25, 30), new Vector2(35, 40), new Vector2(45, 50) }); } else if (testType == typeof(Vector3)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Vector3(5, 10, 15), new Vector3(20, 25, 30) }, new NativeList(Allocator.Temp) { new Vector3(35, 40, 45), new Vector3(50, 55, 60), new Vector3(65, 70, 75) }); } else if (testType == typeof(Vector2Int)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Vector2Int(5, 10), new Vector2Int(15, 20) }, new NativeList(Allocator.Temp) { new Vector2Int(25, 30), new Vector2Int(35, 40), new Vector2Int(45, 50) }); } else if (testType == typeof(Vector3Int)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Vector3Int(5, 10, 15), new Vector3Int(20, 25, 30) }, new NativeList(Allocator.Temp) { new Vector3Int(35, 40, 45), new Vector3Int(50, 55, 60), new Vector3Int(65, 70, 75) }); } else if (testType == typeof(Vector4)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Vector4(5, 10, 15, 20), new Vector4(25, 30, 35, 40) }, new NativeList(Allocator.Temp) { new Vector4(45, 50, 55, 60), new Vector4(65, 70, 75, 80), new Vector4(85, 90, 95, 100) }); } else if (testType == typeof(Quaternion)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Quaternion(5, 10, 15, 20), new Quaternion(25, 30, 35, 40) }, new NativeList(Allocator.Temp) { new Quaternion(45, 50, 55, 60), new Quaternion(65, 70, 75, 80), new Quaternion(85, 90, 95, 100) }); } else if (testType == typeof(Color)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Color(.5f, .10f, .15f), new Color(.20f, .25f, .30f) }, new NativeList(Allocator.Temp) { new Color(.35f, .40f, .45f), new Color(.50f, .55f, .60f), new Color(.65f, .70f, .75f) }); } else if (testType == typeof(Color32)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Color32(5, 10, 15, 20), new Color32(25, 30, 35, 40) }, new NativeList(Allocator.Temp) { new Color32(45, 50, 55, 60), new Color32(65, 70, 75, 80), new Color32(85, 90, 95, 100) }); } else if (testType == typeof(Ray)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Ray(new Vector3(0, 1, 2), new Vector3(3, 4, 5)), new Ray(new Vector3(6, 7, 8), new Vector3(9, 10, 11)), }, new NativeList(Allocator.Temp) { new Ray(new Vector3(12, 13, 14), new Vector3(15, 16, 17)), new Ray(new Vector3(18, 19, 20), new Vector3(21, 22, 23)), new Ray(new Vector3(24, 25, 26), new Vector3(27, 28, 29)), }); } else if (testType == typeof(Ray2D)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new Ray2D(new Vector2(0, 1), new Vector2(3, 4)), new Ray2D(new Vector2(6, 7), new Vector2(9, 10)), }, new NativeList(Allocator.Temp) { new Ray2D(new Vector2(12, 13), new Vector2(15, 16)), new Ray2D(new Vector2(18, 19), new Vector2(21, 22)), new Ray2D(new Vector2(24, 25), new Vector2(27, 28)), }); } else if (testType == typeof(NetworkVariableTestStruct)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { NetworkVariableTestStruct.GetTestStruct(), NetworkVariableTestStruct.GetTestStruct() }, new NativeList(Allocator.Temp) { NetworkVariableTestStruct.GetTestStruct(), NetworkVariableTestStruct.GetTestStruct(), NetworkVariableTestStruct.GetTestStruct() }); } else if (testType == typeof(FixedString32Bytes)) { TestValueTypeNativeList( new NativeList(Allocator.Temp) { new FixedString32Bytes("foobar"), new FixedString32Bytes("12345678901234567890123456789") }, new NativeList(Allocator.Temp) { new FixedString32Bytes("BazQux"), new FixedString32Bytes("98765432109876543210987654321"), new FixedString32Bytes("FixedString32Bytes") }); } } public string NativeListStr(NativeList list) where T : unmanaged { var str = "["; var comma = false; foreach (var item in list) { if (comma) { str += ", "; } comma = true; str += $"{item}"; } str += "]"; return str; } public string NativeHashSetStr(NativeHashSet list) where T : unmanaged, IEquatable { var str = "{"; var comma = false; foreach (var item in list) { if (comma) { str += ", "; } comma = true; str += $"{item}"; } str += "}"; return str; } public string NativeHashMapStr(NativeHashMap list) where TKey : unmanaged, IEquatable where TVal : unmanaged { var str = "{"; var comma = false; foreach (var item in list) { if (comma) { str += ", "; } comma = true; str += $"{item.Key}: {item.Value}"; } str += "}"; return str; } public (NativeList original, NativeList original2, NativeList changed, NativeList changed2) GetNativeLists(GetRandomElement generator) where T : unmanaged { var original = new NativeList(Allocator.Temp); var changed = new NativeList(Allocator.Temp); var original2 = new NativeList(Allocator.Temp); var changed2 = new NativeList(Allocator.Temp); var rand = new System.Random(); var originalSize = rand.Next(32, 64); var changedSize = rand.Next(32, 64); var changed2Changes = rand.Next(12, 16); var changed2Adds = rand.Next(-16, 16); for (var i = 0; i < originalSize; ++i) { var item = generator(rand); original.Add(item); original2.Add(item); changed2.Add(item); } for (var i = 0; i < changedSize; ++i) { var item = generator(rand); changed.Add(item); } for (var i = 0; i < changed2Changes; ++i) { var idx = rand.Next(changed2.Length - 1); var item = generator(rand); changed2[idx] = item; } if (changed2Adds < 0) { changed2.Resize(changed2.Length + changed2Adds, NativeArrayOptions.UninitializedMemory); } else { for (var i = 0; i < changed2Adds; ++i) { var item = generator(rand); changed2.Add(item); } } VerboseDebug($"Original: {NativeListStr(original)}"); VerboseDebug($"Changed: {NativeListStr(changed)}"); VerboseDebug($"Original2: {NativeListStr(original2)}"); VerboseDebug($"Changed2: {NativeListStr(changed2)}"); return (original, original2, changed, changed2); } public (NativeHashSet original, NativeHashSet original2, NativeHashSet changed, NativeHashSet changed2) GetNativeHashSets(GetRandomElement generator) where T : unmanaged, IEquatable { var original = new NativeHashSet(16, Allocator.Temp); var changed = new NativeHashSet(16, Allocator.Temp); var original2 = new NativeHashSet(16, Allocator.Temp); var changed2 = new NativeHashSet(16, Allocator.Temp); var rand = new System.Random(); var originalSize = rand.Next(32, 64); var changedSize = rand.Next(32, 64); var changed2Removes = rand.Next(12, 16); var changed2Adds = rand.Next(12, 16); for (var i = 0; i < originalSize; ++i) { var item = generator(rand); while (original.Contains(item)) { item = generator(rand); } original.Add(item); original2.Add(item); changed2.Add(item); } for (var i = 0; i < changedSize; ++i) { var item = generator(rand); while (changed.Contains(item)) { item = generator(rand); } changed.Add(item); } for (var i = 0; i < changed2Removes; ++i) { var which = rand.Next(changed2.Count()); T toRemove = default; foreach (var check in changed2) { if (which == 0) { toRemove = check; break; } --which; } changed2.Remove(toRemove); } for (var i = 0; i < changed2Adds; ++i) { var item = generator(rand); while (changed2.Contains(item)) { item = generator(rand); } changed2.Add(item); } VerboseDebug($"Original: {NativeHashSetStr(original)}"); VerboseDebug($"Changed: {NativeHashSetStr(changed)}"); VerboseDebug($"Original2: {NativeHashSetStr(original2)}"); VerboseDebug($"Changed2: {NativeHashSetStr(changed2)}"); return (original, original2, changed, changed2); } public (NativeHashMap original, NativeHashMap original2, NativeHashMap changed, NativeHashMap changed2) GetMaps(GetRandomElement keyGenerator, GetRandomElement valGenerator) where TKey : unmanaged, IEquatable where TVal : unmanaged { var original = new NativeHashMap(16, Allocator.Temp); var changed = new NativeHashMap(16, Allocator.Temp); var original2 = new NativeHashMap(16, Allocator.Temp); var changed2 = new NativeHashMap(16, Allocator.Temp); var rand = new System.Random(); var originalSize = rand.Next(32, 64); var changedSize = rand.Next(32, 64); var changed2Removes = rand.Next(12, 16); var changed2Adds = rand.Next(12, 16); var changed2Changes = rand.Next(12, 16); for (var i = 0; i < originalSize; ++i) { var key = keyGenerator(rand); while (original.ContainsKey(key)) { key = keyGenerator(rand); } var val = valGenerator(rand); original.Add(key, val); original2.Add(key, val); changed2.Add(key, val); } for (var i = 0; i < changedSize; ++i) { var key = keyGenerator(rand); while (changed.ContainsKey(key)) { key = keyGenerator(rand); } var val = valGenerator(rand); changed.Add(key, val); } for (var i = 0; i < changed2Removes; ++i) { var which = rand.Next(changed2.Count()); TKey toRemove = default; foreach (var check in changed2) { if (which == 0) { toRemove = check.Key; break; } --which; } changed2.Remove(toRemove); } for (var i = 0; i < changed2Changes; ++i) { var which = rand.Next(changed2.Count()); TKey key = default; foreach (var check in changed2) { if (which == 0) { key = check.Key; break; } --which; } var val = valGenerator(rand); changed2[key] = val; } for (var i = 0; i < changed2Adds; ++i) { var key = keyGenerator(rand); while (changed2.ContainsKey(key)) { key = keyGenerator(rand); } var val = valGenerator(rand); changed2.Add(key, val); } VerboseDebug($"Original: {NativeHashMapStr(original)}"); VerboseDebug($"Changed: {NativeHashMapStr(changed)}"); VerboseDebug($"Original2: {NativeHashMapStr(original2)}"); VerboseDebug($"Changed2: {NativeHashMapStr(changed2)}"); return (original, original2, changed, changed2); } [Test] [Repeat(5)] public void WhenSerializingAndDeserializingVeryLargeValueTypeNativeListNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(NetworkVariableTestStruct), typeof(FixedString32Bytes))] Type testType) { if (testType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(sbyte)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(short)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(ushort)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(int)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(uint)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(long)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(bool)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(char)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(float)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(double)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Vector3)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Vector2Int)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Vector2Int(rand.Next(), rand.Next()) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Vector3Int)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next()) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Vector4)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Quaternion)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Color)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Color32)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Color32((byte)rand.Next(), (byte)rand.Next(), (byte)rand.Next(), (byte)rand.Next()) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Ray)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Ray( new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()), new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(Ray2D)) { (var original, var original2, var changed, var changed2) = GetNativeLists( (rand) => new Ray2D( new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()) ) ); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(NetworkVariableTestStruct)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenBytes); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } else if (testType == typeof(FixedString32Bytes)) { (var original, var original2, var changed, var changed2) = GetNativeLists(RandGenFixedString32); TestValueTypeNativeList(original, changed); TestValueTypeNativeList(original2, changed2); } } [Test] [Repeat(5)] public void WhenSerializingAndDeserializingVeryLargeValueTypeNativeHashSetNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(HashableNetworkVariableTestStruct), typeof(FixedString32Bytes))] Type testType) { if (testType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(sbyte)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(short)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(ushort)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(int)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(uint)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(long)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(bool)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(char)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(float)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(double)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets( (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(Vector3)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets( (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(Vector2Int)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets( (rand) => new Vector2Int(rand.Next(), rand.Next()) ); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(Vector3Int)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets( (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next()) ); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(Vector4)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets( (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(Quaternion)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets( (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(Color)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets( (rand) => new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) ); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(HashableNetworkVariableTestStruct)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenBytes); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } else if (testType == typeof(FixedString32Bytes)) { (var original, var original2, var changed, var changed2) = GetNativeHashSets(RandGenFixedString32); TestValueTypeNativeHashSet(original, changed); TestValueTypeNativeHashSet(original2, changed2); } } [Test] [Repeat(5)] public void WhenSerializingAndDeserializingVeryLargeValueTypeNativeHashMapNetworkVariables_ValuesAreSerializedCorrectly( [Values(typeof(byte), typeof(ulong), typeof(Vector2), typeof(HashMapKeyStruct))] Type keyType, [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(Vector2), typeof(Vector3), typeof(Vector2Int), typeof(Vector3Int), typeof(Vector4), typeof(Quaternion), typeof(HashMapValStruct), typeof(FixedString32Bytes))] Type valType) { if (valType == typeof(byte)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(sbyte)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(short)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(ushort)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(int)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(uint)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(long)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(ulong)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(bool)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(char)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(float)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(double)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(Vector2)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(Vector3)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(Vector2Int)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector2Int(rand.Next(), rand.Next())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector2Int(rand.Next(), rand.Next())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector2Int(rand.Next(), rand.Next())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector2Int(rand.Next(), rand.Next())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(Vector3Int)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector3Int(rand.Next(), rand.Next(), rand.Next())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(Vector4)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Vector4((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(Quaternion)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, (rand) => new Quaternion((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble())); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(HashMapValStruct)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenBytes); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } else if (valType == typeof(FixedString32Bytes)) { if (keyType == typeof(byte)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenFixedString32); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(ulong)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenFixedString32); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(Vector2)) { (var original, var original2, var changed, var changed2) = GetMaps((rand) => new Vector2((float)rand.NextDouble(), (float)rand.NextDouble()), RandGenFixedString32); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } else if (keyType == typeof(HashMapKeyStruct)) { (var original, var original2, var changed, var changed2) = GetMaps(RandGenBytes, RandGenFixedString32); TestValueTypeNativeHashMap(original, changed); TestValueTypeNativeHashMap(original2, changed2); } } } #endif [Test] public void TestManagedINetworkSerializableNetworkVariablesDeserializeInPlace() { var variable = new NetworkVariable { Value = new ManagedNetworkSerializableType { InMemoryValue = 1, Ints = new[] { 2, 3, 4 }, Str = "five", Embedded = new EmbeddedManagedNetworkSerializableType { Int = 6 } } }; using var writer = new FastBufferWriter(1024, Allocator.Temp); variable.WriteField(writer); Assert.AreEqual(1, variable.Value.InMemoryValue); Assert.AreEqual(new[] { 2, 3, 4 }, variable.Value.Ints); Assert.AreEqual("five", variable.Value.Str); Assert.AreEqual(6, variable.Value.Embedded.Int); variable.Value = new ManagedNetworkSerializableType { InMemoryValue = 10, Ints = new[] { 20, 30, 40, 50 }, Str = "sixty", Embedded = new EmbeddedManagedNetworkSerializableType { Int = 60 } }; using var reader = new FastBufferReader(writer, Allocator.None); variable.ReadField(reader); Assert.AreEqual(10, variable.Value.InMemoryValue, "In-memory value was not the same - in-place deserialization should not change this"); Assert.AreEqual(new[] { 2, 3, 4 }, variable.Value.Ints, "Ints were not correctly deserialized"); Assert.AreEqual("five", variable.Value.Str, "Str was not correctly deserialized"); Assert.AreEqual(6, variable.Value.Embedded.Int, "Embedded int was not correctly deserialized"); } [Test] public void TestUnmnagedINetworkSerializableNetworkVariablesDeserializeInPlace() { var variable = new NetworkVariable { Value = new UnmanagedNetworkSerializableType { InMemoryValue = 1, Int = 2, Str = "three" } }; using var writer = new FastBufferWriter(1024, Allocator.Temp); variable.WriteField(writer); Assert.AreEqual(1, variable.Value.InMemoryValue); Assert.AreEqual(2, variable.Value.Int); Assert.AreEqual("three", variable.Value.Str); variable.Value = new UnmanagedNetworkSerializableType { InMemoryValue = 10, Int = 20, Str = "thirty" }; using var reader = new FastBufferReader(writer, Allocator.None); variable.ReadField(reader); Assert.AreEqual(10, variable.Value.InMemoryValue, "In-memory value was not the same - in-place deserialization should not change this"); Assert.AreEqual(2, variable.Value.Int, "Int was not correctly deserialized"); Assert.AreEqual("three", variable.Value.Str, "Str was not correctly deserialized"); } private float m_OriginalTimeScale = 1.0f; protected override IEnumerator OnSetup() { m_OriginalTimeScale = Time.timeScale; yield return null; } protected override IEnumerator OnTearDown() { Time.timeScale = m_OriginalTimeScale; m_NetworkListPredicateHandler = null; yield return base.OnTearDown(); } } /// /// Handles the more generic conditional logic for NetworkList tests /// which can be used with the /// that accepts anything derived from the class /// as a parameter. /// public class NetworkListTestPredicate : ConditionalPredicateBase { private const int k_MaxRandomValue = 1000; private Dictionary> m_StateFunctions; // Player1 component on the Server private NetworkVariableTest m_Player1OnServer; // Player1 component on client1 private NetworkVariableTest m_Player1OnClient1; private string m_TestStageFailedMessage; public enum NetworkListTestStates { Add, ContainsLarge, Contains, VerifyData, IndexOf, } private NetworkListTestStates m_NetworkListTestState; public void SetNetworkListTestState(NetworkListTestStates networkListTestState) { m_NetworkListTestState = networkListTestState; } /// /// Determines if the condition has been reached for the current NetworkListTestState /// protected override bool OnHasConditionBeenReached() { var isStateRegistered = m_StateFunctions.ContainsKey(m_NetworkListTestState); Assert.IsTrue(isStateRegistered); return m_StateFunctions[m_NetworkListTestState].Invoke(); } /// /// Provides all information about the players for both sides for simplicity and informative sake. /// /// private string ConditionFailedInfo() { return $"{m_NetworkListTestState} condition test failed:\n Server List Count: {m_Player1OnServer.TheList.Count} vs Client List Count: {m_Player1OnClient1.TheList.Count}\n" + $"Server List Count: {m_Player1OnServer.TheLargeList.Count} vs Client List Count: {m_Player1OnClient1.TheLargeList.Count}\n" + $"Server Delegate Triggered: {m_Player1OnServer.ListDelegateTriggered} | Client Delegate Triggered: {m_Player1OnClient1.ListDelegateTriggered}\n"; } /// /// When finished, check if a time out occurred and if so assert and provide meaningful information to troubleshoot why /// protected override void OnFinished() { Assert.IsFalse(TimedOut, $"{nameof(NetworkListTestPredicate)} timed out waiting for the {m_NetworkListTestState} condition to be reached! \n" + ConditionFailedInfo()); } // Uses the ArrayOperator and validates that on both sides the count and values are the same private bool OnVerifyData() { // Wait until both sides have the same number of elements if (m_Player1OnServer.TheList.Count != m_Player1OnClient1.TheList.Count) { return false; } // Check the client values against the server values to make sure they match for (int i = 0; i < m_Player1OnServer.TheList.Count; i++) { if (m_Player1OnServer.TheList[i] != m_Player1OnClient1.TheList[i]) { return false; } } return true; } /// /// Verifies the data count, values, and that the ListDelegate on both sides was triggered /// private bool OnAdd() { bool wasTriggerred = m_Player1OnServer.ListDelegateTriggered && m_Player1OnClient1.ListDelegateTriggered; return wasTriggerred && OnVerifyData(); } /// /// The current version of this test only verified the count of the large list, so that is what this does /// private bool OnContainsLarge() { return m_Player1OnServer.TheLargeList.Count == m_Player1OnClient1.TheLargeList.Count; } /// /// Tests NetworkList.Contains which also verifies all values are the same on both sides /// private bool OnContains() { // Wait until both sides have the same number of elements if (m_Player1OnServer.TheList.Count != m_Player1OnClient1.TheList.Count) { return false; } // Parse through all server values and use the NetworkList.Contains method to check if the value is in the list on the client side foreach (var serverValue in m_Player1OnServer.TheList) { if (!m_Player1OnClient1.TheList.Contains(serverValue)) { return false; } } return true; } /// /// Tests NetworkList.IndexOf and verifies that all values are aligned on both sides /// private bool OnIndexOf() { foreach (var serverSideValue in m_Player1OnServer.TheList) { var indexToTest = m_Player1OnServer.TheList.IndexOf(serverSideValue); if (indexToTest != m_Player1OnServer.TheList.IndexOf(serverSideValue)) { return false; } } return true; } public NetworkListTestPredicate(NetworkVariableTest player1OnServer, NetworkVariableTest player1OnClient1, NetworkListTestStates networkListTestState, int elementCount) { m_NetworkListTestState = networkListTestState; m_Player1OnServer = player1OnServer; m_Player1OnClient1 = player1OnClient1; m_StateFunctions = new Dictionary> { { NetworkListTestStates.Add, OnAdd }, { NetworkListTestStates.ContainsLarge, OnContainsLarge }, { NetworkListTestStates.Contains, OnContains }, { NetworkListTestStates.VerifyData, OnVerifyData }, { NetworkListTestStates.IndexOf, OnIndexOf } }; if (networkListTestState == NetworkListTestStates.ContainsLarge) { for (var i = 0; i < elementCount; ++i) { m_Player1OnServer.TheLargeList.Add(new FixedString128Bytes()); } } else { for (int i = 0; i < elementCount; i++) { m_Player1OnServer.TheList.Add(Random.Range(0, k_MaxRandomValue)); } } } } [TestFixtureSource(nameof(TestDataSource))] public class NetworkVariableInheritanceTests : NetcodeIntegrationTest { public NetworkVariableInheritanceTests(HostOrServer hostOrServer) : base(hostOrServer) { } protected override int NumberOfClients => 2; public static IEnumerable TestDataSource() => Enum.GetValues(typeof(HostOrServer)).OfType().Select(x => new TestFixtureData(x)); public class ComponentA : NetworkBehaviour { public NetworkVariable PublicFieldA = new NetworkVariable(1); protected NetworkVariable m_ProtectedFieldA = new NetworkVariable(2); private NetworkVariable m_PrivateFieldA = new NetworkVariable(3); public void ChangeValuesA(int pub, int pro, int pri) { PublicFieldA.Value = pub; m_ProtectedFieldA.Value = pro; m_PrivateFieldA.Value = pri; } public bool CompareValuesA(ComponentA other) { return PublicFieldA.Value == other.PublicFieldA.Value && m_ProtectedFieldA.Value == other.m_ProtectedFieldA.Value && m_PrivateFieldA.Value == other.m_PrivateFieldA.Value; } } public class ComponentB : ComponentA { public NetworkVariable PublicFieldB = new NetworkVariable(11); protected NetworkVariable m_ProtectedFieldB = new NetworkVariable(22); private NetworkVariable m_PrivateFieldB = new NetworkVariable(33); public void ChangeValuesB(int pub, int pro, int pri) { PublicFieldB.Value = pub; m_ProtectedFieldB.Value = pro; m_PrivateFieldB.Value = pri; } public bool CompareValuesB(ComponentB other) { return PublicFieldB.Value == other.PublicFieldB.Value && m_ProtectedFieldB.Value == other.m_ProtectedFieldB.Value && m_PrivateFieldB.Value == other.m_PrivateFieldB.Value; } } public class ComponentC : ComponentB { public NetworkVariable PublicFieldC = new NetworkVariable(111); protected NetworkVariable m_ProtectedFieldC = new NetworkVariable(222); private NetworkVariable m_PrivateFieldC = new NetworkVariable(333); public void ChangeValuesC(int pub, int pro, int pri) { PublicFieldC.Value = pub; m_ProtectedFieldA.Value = pro; m_PrivateFieldC.Value = pri; } public bool CompareValuesC(ComponentC other) { return PublicFieldC.Value == other.PublicFieldC.Value && m_ProtectedFieldC.Value == other.m_ProtectedFieldC.Value && m_PrivateFieldC.Value == other.m_PrivateFieldC.Value; } } private GameObject m_TestObjectPrefab; private ulong m_TestObjectId = 0; protected override void OnServerAndClientsCreated() { m_TestObjectPrefab = CreateNetworkObjectPrefab($"[{nameof(NetworkVariableInheritanceTests)}.{nameof(m_TestObjectPrefab)}]"); m_TestObjectPrefab.AddComponent(); m_TestObjectPrefab.AddComponent(); m_TestObjectPrefab.AddComponent(); } protected override IEnumerator OnServerAndClientsConnected() { var serverTestObject = SpawnObject(m_TestObjectPrefab, m_ServerNetworkManager).GetComponent(); m_TestObjectId = serverTestObject.NetworkObjectId; var serverTestComponentA = serverTestObject.GetComponent(); var serverTestComponentB = serverTestObject.GetComponent(); var serverTestComponentC = serverTestObject.GetComponent(); serverTestComponentA.ChangeValuesA(1000, 2000, 3000); serverTestComponentB.ChangeValuesA(1000, 2000, 3000); serverTestComponentB.ChangeValuesB(1100, 2200, 3300); serverTestComponentC.ChangeValuesA(1000, 2000, 3000); serverTestComponentC.ChangeValuesB(1100, 2200, 3300); serverTestComponentC.ChangeValuesC(1110, 2220, 3330); yield return WaitForTicks(m_ServerNetworkManager, 2); } private bool CheckTestObjectComponentValuesOnAll() { var serverTestObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjectId]; var serverTestComponentA = serverTestObject.GetComponent(); var serverTestComponentB = serverTestObject.GetComponent(); var serverTestComponentC = serverTestObject.GetComponent(); foreach (var clientNetworkManager in m_ClientNetworkManagers) { var clientTestObject = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjectId]; var clientTestComponentA = clientTestObject.GetComponent(); var clientTestComponentB = clientTestObject.GetComponent(); var clientTestComponentC = clientTestObject.GetComponent(); if (!serverTestComponentA.CompareValuesA(clientTestComponentA) || !serverTestComponentB.CompareValuesA(clientTestComponentB) || !serverTestComponentB.CompareValuesB(clientTestComponentB) || !serverTestComponentC.CompareValuesA(clientTestComponentC) || !serverTestComponentC.CompareValuesB(clientTestComponentC) || !serverTestComponentC.CompareValuesC(clientTestComponentC)) { return false; } } return true; } [UnityTest] public IEnumerator TestInheritedFields() { yield return WaitForConditionOrTimeOut(CheckTestObjectComponentValuesOnAll); Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, nameof(CheckTestObjectComponentValuesOnAll)); } } public class NetvarDespawnShutdown : NetworkBehaviour { private NetworkVariable m_IntNetworkVariable = new NetworkVariable(); private NetworkList m_IntList; private void Awake() { m_IntList = new NetworkList(); } public override void OnNetworkDespawn() { if (IsServer) { m_IntNetworkVariable.Value = 5; for (int i = 0; i < 10; i++) { m_IntList.Add(i); } } base.OnNetworkDespawn(); } } /// /// Validates that setting values for NetworkVariable or NetworkList during the /// OnNetworkDespawn method will not cause an exception to occur. /// public class NetworkVariableModifyOnNetworkDespawn : NetcodeIntegrationTest { protected override int NumberOfClients => 1; private GameObject m_TestPrefab; protected override void OnServerAndClientsCreated() { m_TestPrefab = CreateNetworkObjectPrefab("NetVarDespawn"); m_TestPrefab.AddComponent(); base.OnServerAndClientsCreated(); } private bool OnClientSpawnedTestPrefab(ulong networkObjectId) { var clientId = m_ClientNetworkManagers[0].LocalClientId; if (!s_GlobalNetworkObjects.ContainsKey(clientId)) { return false; } if (!s_GlobalNetworkObjects[clientId].ContainsKey(networkObjectId)) { return false; } return true; } [UnityTest] public IEnumerator ModifyNetworkVariableOrListOnNetworkDespawn() { var instance = SpawnObject(m_TestPrefab, m_ServerNetworkManager); yield return WaitForConditionOrTimeOut(() => OnClientSpawnedTestPrefab(instance.GetComponent().NetworkObjectId)); m_ServerNetworkManager.Shutdown(); // As long as no excetptions occur, the test passes. } } }