com.unity.netcode.gameobjects@1.1.0

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

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

## [1.1.0] - 2022-10-21

### Added

- Added `NetworkManager.IsApproved` flag that is set to `true` a client has been approved.(#2261)
- `UnityTransport` now provides a way to set the Relay server data directly from the `RelayServerData` structure (provided by the Unity Transport package) throuh its `SetRelayServerData` method. This allows making use of the new APIs in UTP 1.3 that simplify integration of the Relay SDK. (#2235)
- IPv6 is now supported for direct connections when using `UnityTransport`. (#2232)
- Added WebSocket support when using UTP 2.0 with `UseWebSockets` property in the `UnityTransport` component of the `NetworkManager` allowing to pick WebSockets for communication. When building for WebGL, this selection happens automatically. (#2201)
- Added position, rotation, and scale to the `ParentSyncMessage` which provides users the ability to specify the final values on the server-side when `OnNetworkObjectParentChanged` is invoked just before the message is created (when the `Transform` values are applied to the message). (#2146)
- Added `NetworkObject.TryRemoveParent` method for convenience purposes opposed to having to cast null to either `GameObject` or `NetworkObject`. (#2146)

### Changed

- Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.0. (#2231)
- The send queues of `UnityTransport` are now dynamically-sized. This means that there shouldn't be any need anymore to tweak the 'Max Send Queue Size' value. In fact, this field is now removed from the inspector and will not be serialized anymore. It is still possible to set it manually using the `MaxSendQueueSize` property, but it is not recommended to do so aside from some specific needs (e.g. limiting the amount of memory used by the send queues in very constrained environments). (#2212)
- As a consequence of the above change, the `UnityTransport.InitialMaxSendQueueSize` field is now deprecated. There is no default value anymore since send queues are dynamically-sized. (#2212)
- The debug simulator in `UnityTransport` is now non-deterministic. Its random number generator used to be seeded with a constant value, leading to the same pattern of packet drops, delays, and jitter in every run. (#2196)
- `NetworkVariable<>` now supports managed `INetworkSerializable` types, as well as other managed types with serialization/deserialization delegates registered to `UserNetworkVariableSerialization<T>.WriteValue` and `UserNetworkVariableSerialization<T>.ReadValue` (#2219)
- `NetworkVariable<>` and `BufferSerializer<BufferSerializerReader>` now deserialize `INetworkSerializable` types in-place, rather than constructing new ones. (#2219)

### Fixed

- Fixed `NetworkManager.ApprovalTimeout` will not timeout due to slower client synchronization times as it now uses the added `NetworkManager.IsApproved` flag to determined if the client has been approved or not.(#2261)
- Fixed issue caused when changing ownership of objects hidden to some clients (#2242)
- Fixed issue where an in-scene placed NetworkObject would not invoke NetworkBehaviour.OnNetworkSpawn if the GameObject was disabled when it was despawned. (#2239)
- Fixed issue where clients were not rebuilding the `NetworkConfig` hash value for each unique connection request. (#2226)
- Fixed the issue where player objects were not taking the `DontDestroyWithOwner` property into consideration when a client disconnected. (#2225)
- Fixed issue where `SceneEventProgress` would not complete if a client late joins while it is still in progress. (#2222)
- Fixed issue where `SceneEventProgress` would not complete if a client disconnects. (#2222)
- Fixed issues with detecting if a `SceneEventProgress` has timed out. (#2222)
- Fixed issue #1924 where `UnityTransport` would fail to restart after a first failure (even if what caused the initial failure was addressed). (#2220)
- Fixed issue where `NetworkTransform.SetStateServerRpc` and `NetworkTransform.SetStateClientRpc` were not honoring local vs world space settings when applying the position and rotation. (#2203)
- Fixed ILPP `TypeLoadException` on WebGL on MacOS Editor and potentially other platforms. (#2199)
- Implicit conversion of NetworkObjectReference to GameObject will now return null instead of throwing an exception if the referenced object could not be found (i.e., was already despawned) (#2158)
- Fixed warning resulting from a stray NetworkAnimator.meta file (#2153)
- Fixed Connection Approval Timeout not working client side. (#2164)
- Fixed issue where the `WorldPositionStays` parenting parameter was not being synchronized with clients. (#2146)
- Fixed issue where parented in-scene placed `NetworkObject`s would fail for late joining clients. (#2146)
- Fixed issue where scale was not being synchronized which caused issues with nested parenting and scale when `WorldPositionStays` was true. (#2146)
- Fixed issue with `NetworkTransform.ApplyTransformToNetworkStateWithInfo` where it was not honoring axis sync settings when `NetworkTransformState.IsTeleportingNextFrame` was true. (#2146)
- Fixed issue with `NetworkTransform.TryCommitTransformToServer` where it was not honoring the `InLocalSpace` setting. (#2146)
- Fixed ClientRpcs always reporting in the profiler view as going to all clients, even when limited to a subset of clients by `ClientRpcParams`. (#2144)
- Fixed RPC codegen failing to choose the correct extension methods for `FastBufferReader` and `FastBufferWriter` when the parameters were a generic type (i.e., List<int>) and extensions for multiple instantiations of that type have been defined (i.e., List<int> and List<string>) (#2142)
- Fixed the issue where running a server (i.e. not host) the second player would not receive updates (unless a third player joined). (#2127)
- Fixed issue where late-joining client transition synchronization could fail when more than one transition was occurring.(#2127)
- Fixed throwing an exception in `OnNetworkUpdate` causing other `OnNetworkUpdate` calls to not be executed. (#1739)
- Fixed synchronization when Time.timeScale is set to 0. This changes timing update to use unscaled deltatime. Now network updates rate are independent from the local time scale. (#2171)
- Fixed not sending all NetworkVariables to all clients when a client connects to a server. (#1987)
- Fixed IsOwner/IsOwnedByServer being wrong on the server after calling RemoveOwnership (#2211)
This commit is contained in:
Unity Technologies
2022-10-21 00:00:00 +00:00
parent a6969670f5
commit 1e7078c160
97 changed files with 6175 additions and 1643 deletions

View File

@@ -137,9 +137,9 @@ namespace Unity.Netcode.EditorTests
{
var sender = new NopMessageSender();
var systemOne = new MessagingSystem(sender, null, new TestMessageProviderOne());
var systemTwo = new MessagingSystem(sender, null, new TestMessageProviderTwo());
var systemThree = new MessagingSystem(sender, null, new TestMessageProviderThree());
using var systemOne = new MessagingSystem(sender, null, new TestMessageProviderOne());
using var systemTwo = new MessagingSystem(sender, null, new TestMessageProviderTwo());
using var systemThree = new MessagingSystem(sender, null, new TestMessageProviderThree());
using (systemOne)
using (systemTwo)
@@ -161,9 +161,9 @@ namespace Unity.Netcode.EditorTests
{
var sender = new NopMessageSender();
var systemOne = new MessagingSystem(sender, null, new TestMessageProviderOne());
var systemTwo = new MessagingSystem(sender, null, new TestMessageProviderTwo());
var systemThree = new MessagingSystem(sender, null, new TestMessageProviderThree());
using var systemOne = new MessagingSystem(sender, null, new TestMessageProviderOne());
using var systemTwo = new MessagingSystem(sender, null, new TestMessageProviderTwo());
using var systemThree = new MessagingSystem(sender, null, new TestMessageProviderThree());
using (systemOne)
using (systemTwo)
@@ -235,7 +235,7 @@ namespace Unity.Netcode.EditorTests
{
var sender = new NopMessageSender();
var provider = new OrderingMessageProvider();
var messagingSystem = new MessagingSystem(sender, null, provider);
using var messagingSystem = new MessagingSystem(sender, null, provider);
// the 3 priority messages should appear first, in lexicographic order
Assert.AreEqual(messagingSystem.MessageTypes[0], typeof(ConnectionApprovedMessage));

View File

@@ -10,12 +10,13 @@ namespace Unity.Netcode.EditorTests.Metrics
{
public class NetworkMetricsRegistrationTests
{
static Type[] s_MetricTypes = AppDomain.CurrentDomain.GetAssemblies()
private static Type[] s_MetricTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => x.GetInterfaces().Contains(typeof(INetworkMetricEvent)))
.ToArray();
[TestCaseSource(nameof(s_MetricTypes))][Ignore("Disable test while we reevaluate the assumption that INetworkMetricEvent interfaces must be reported from MLAPI.")]
[TestCaseSource(nameof(s_MetricTypes))]
[Ignore("Disable test while we reevaluate the assumption that INetworkMetricEvent interfaces must be reported from MLAPI.")]
public void ValidateThatAllMetricTypesAreRegistered(Type metricType)
{
var dispatcher = new NetworkMetrics().Dispatcher as MetricDispatcher;

View File

@@ -8,8 +8,9 @@ namespace Unity.Netcode.EditorTests
{
public class BatchedSendQueueTests
{
private const int k_TestQueueCapacity = 1024;
private const int k_TestMessageSize = 42;
private const int k_TestQueueCapacity = 16 * 1024;
private const int k_TestMessageSize = 1020;
private const int k_NumMessagesToFillQueue = k_TestQueueCapacity / (k_TestMessageSize + BatchedSendQueue.PerMessageOverhead);
private ArraySegment<byte> m_TestMessage;
@@ -52,7 +53,14 @@ namespace Unity.Netcode.EditorTests
}
[Test]
public void BatchedSendQueue_PushMessageReturnValue()
public void BatchedSendQueue_InitialCapacityLessThanMaximum()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
Assert.AreEqual(q.Capacity, BatchedSendQueue.MinimumMinimumCapacity);
}
[Test]
public void BatchedSendQueue_PushMessage_ReturnValue()
{
// Will fit a single test message, but not two (with overhead included).
var queueCapacity = (k_TestMessageSize * 2) + BatchedSendQueue.PerMessageOverhead;
@@ -64,7 +72,7 @@ namespace Unity.Netcode.EditorTests
}
[Test]
public void BatchedSendQueue_LengthIncreasedAfterPush()
public void BatchedSendQueue_PushMessage_IncreasesLength()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
@@ -73,13 +81,12 @@ namespace Unity.Netcode.EditorTests
}
[Test]
public void BatchedSendQueue_PushedMessageGeneratesCopy()
public void BatchedSendQueue_PushMessage_SucceedsAfterConsume()
{
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
var queueCapacity = messageLength * 2;
using var q = new BatchedSendQueue(queueCapacity);
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
q.PushMessage(m_TestMessage);
q.PushMessage(m_TestMessage);
@@ -89,6 +96,60 @@ namespace Unity.Netcode.EditorTests
Assert.AreEqual(queueCapacity, q.Length);
}
[Test]
public void BatchedSendQueue_PushMessage_GrowsDataIfNeeded()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
Assert.AreEqual(q.Capacity, BatchedSendQueue.MinimumMinimumCapacity);
var numMessagesToFillMinimum = BatchedSendQueue.MinimumMinimumCapacity / messageLength;
for (int i = 0; i < numMessagesToFillMinimum; i++)
{
q.PushMessage(m_TestMessage);
}
Assert.AreEqual(q.Capacity, BatchedSendQueue.MinimumMinimumCapacity);
q.PushMessage(m_TestMessage);
Assert.AreEqual(q.Capacity, BatchedSendQueue.MinimumMinimumCapacity * 2);
}
[Test]
public void BatchedSendQueue_PushMessage_DoesNotGrowDataPastMaximum()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
for (int i = 0; i < k_NumMessagesToFillQueue; i++)
{
Assert.IsTrue(q.PushMessage(m_TestMessage));
}
Assert.AreEqual(q.Capacity, k_TestQueueCapacity);
Assert.IsFalse(q.PushMessage(m_TestMessage));
Assert.AreEqual(q.Capacity, k_TestQueueCapacity);
}
[Test]
public void BatchedSendQueue_PushMessage_TrimsDataAfterGrowing()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
for (int i = 0; i < k_NumMessagesToFillQueue; i++)
{
Assert.IsTrue(q.PushMessage(m_TestMessage));
}
Assert.AreEqual(q.Capacity, k_TestQueueCapacity);
q.Consume(messageLength * (k_NumMessagesToFillQueue - 1));
Assert.IsTrue(q.PushMessage(m_TestMessage));
Assert.AreEqual(messageLength * 2, q.Length);
Assert.AreEqual(q.Capacity, BatchedSendQueue.MinimumMinimumCapacity * 2);
}
[Test]
public void BatchedSendQueue_FillWriterWithMessages_ReturnValue()
{
@@ -227,7 +288,7 @@ namespace Unity.Netcode.EditorTests
}
[Test]
public void BatchedSendQueue_ConsumeLessThanLength()
public void BatchedSendQueue_Consume_LessThanLength()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
@@ -240,7 +301,7 @@ namespace Unity.Netcode.EditorTests
}
[Test]
public void BatchedSendQueue_ConsumeExactLength()
public void BatchedSendQueue_Consume_ExactLength()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
@@ -252,7 +313,7 @@ namespace Unity.Netcode.EditorTests
}
[Test]
public void BatchedSendQueue_ConsumeMoreThanLength()
public void BatchedSendQueue_Consume_MoreThanLength()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
@@ -262,5 +323,20 @@ namespace Unity.Netcode.EditorTests
Assert.AreEqual(0, q.Length);
Assert.True(q.IsEmpty);
}
[Test]
public void BatchedSendQueue_Consume_TrimsDataOnEmpty()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
for (int i = 0; i < k_NumMessagesToFillQueue; i++)
{
q.PushMessage(m_TestMessage);
}
Assert.AreEqual(q.Capacity, k_TestQueueCapacity);
q.Consume(k_TestQueueCapacity);
Assert.AreEqual(q.Capacity, BatchedSendQueue.MinimumMinimumCapacity);
}
}
}

View File

@@ -1,14 +1,15 @@
using NUnit.Framework;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.EditorTests
{
public class UnityTransportTests
{
// Check that starting a server doesn't immediately result in faulted tasks.
// Check that starting an IPv4 server succeeds.
[Test]
public void BasicInitServer()
public void UnityTransport_BasicInitServer_IPv4()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
@@ -18,9 +19,9 @@ namespace Unity.Netcode.EditorTests
transport.Shutdown();
}
// Check that starting a client doesn't immediately result in faulted tasks.
// Check that starting an IPv4 client succeeds.
[Test]
public void BasicInitClient()
public void UnityTransport_BasicInitClient_IPv4()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
@@ -30,9 +31,35 @@ namespace Unity.Netcode.EditorTests
transport.Shutdown();
}
// Check that starting an IPv6 server succeeds.
[Test]
public void UnityTransport_BasicInitServer_IPv6()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
transport.SetConnectionData("::1", 7777);
Assert.True(transport.StartServer());
transport.Shutdown();
}
// Check that starting an IPv6 client succeeds.
[Test]
public void UnityTransport_BasicInitClient_IPv6()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
transport.SetConnectionData("::1", 7777);
Assert.True(transport.StartClient());
transport.Shutdown();
}
// Check that we can't restart a server.
[Test]
public void NoRestartServer()
public void UnityTransport_NoRestartServer()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
@@ -45,7 +72,7 @@ namespace Unity.Netcode.EditorTests
// Check that we can't restart a client.
[Test]
public void NoRestartClient()
public void UnityTransport_NoRestartClient()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
@@ -58,7 +85,7 @@ namespace Unity.Netcode.EditorTests
// Check that we can't start both a server and client on the same transport.
[Test]
public void NotBothServerAndClient()
public void UnityTransport_NotBothServerAndClient()
{
UnityTransport transport;
@@ -80,5 +107,24 @@ namespace Unity.Netcode.EditorTests
transport.Shutdown();
}
// Check that restarting after failure succeeds.
[Test]
public void UnityTransport_RestartSucceedsAfterFailure()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
transport.SetConnectionData("127.0.0.", 4242);
Assert.False(transport.StartServer());
LogAssert.Expect(LogType.Error, "Invalid network endpoint: 127.0.0.:4242.");
LogAssert.Expect(LogType.Error, "Server failed to bind");
transport.SetConnectionData("127.0.0.1", 4242);
Assert.True(transport.StartServer());
transport.Shutdown();
}
}
}

View File

@@ -22,17 +22,18 @@ namespace Unity.Netcode.RuntimeTests
// Host is irrelevant, messages don't get sent to the host "client"
m_UseHost = false;
yield return null;
}
protected override void OnServerAndClientsCreated()
{
m_Prefab = new GameObject("Object");
var networkObject = m_Prefab.AddComponent<NetworkObject>();
m_Prefab.AddComponent<EmptyComponent>();
// Make it a prefab
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
yield return null;
}
protected override void OnServerAndClientsCreated()
{
m_ServerNetworkManager.NetworkConfig.SpawnTimeout = 0;
m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false;
foreach (var client in m_ClientNetworkManagers)

View File

@@ -1,8 +1,161 @@
using System;
using NUnit.Framework;
using Unity.Collections;
using UnityEngine;
using Unity.Netcode.TestHelpers.Runtime;
namespace Unity.Netcode.RuntimeTests
{
public class ManagedNetworkSerializableType : INetworkSerializable, IEquatable<ManagedNetworkSerializableType>
{
public string Str = "";
public int[] Ints = Array.Empty<int>();
public int InMemoryValue;
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref Str, true);
var length = Ints.Length;
serializer.SerializeValue(ref length);
if (serializer.IsReader)
{
Ints = new int[length];
}
for (var i = 0; i < length; ++i)
{
var val = Ints[i];
serializer.SerializeValue(ref val);
Ints[i] = val;
}
}
public bool Equals(ManagedNetworkSerializableType other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
if (Str != other.Str)
{
return false;
}
if (Ints.Length != other.Ints.Length)
{
return false;
}
for (var i = 0; i < Ints.Length; ++i)
{
if (Ints[i] != other.Ints[i])
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((ManagedNetworkSerializableType)obj);
}
public override int GetHashCode()
{
return 0;
}
}
public struct UnmanagedNetworkSerializableType : INetworkSerializable, IEquatable<UnmanagedNetworkSerializableType>
{
public FixedString32Bytes Str;
public int Int;
public int InMemoryValue;
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref Str);
serializer.SerializeValue(ref Int);
}
public bool Equals(UnmanagedNetworkSerializableType other)
{
return Str.Equals(other.Str) && Int == other.Int;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((ManagedNetworkSerializableType)obj);
}
public override int GetHashCode()
{
return Str.GetHashCode() ^ Int.GetHashCode() ^ InMemoryValue.GetHashCode();
}
}
public struct UnmanagedTemplateNetworkSerializableType<T> : INetworkSerializable where T : unmanaged, INetworkSerializable
{
public T Value;
public void NetworkSerialize<TReaderWriterType>(BufferSerializer<TReaderWriterType> serializer) where TReaderWriterType : IReaderWriter
{
serializer.SerializeValue(ref Value);
}
}
public struct ManagedTemplateNetworkSerializableType<T> : INetworkSerializable where T : class, INetworkSerializable, new()
{
public T Value;
public void NetworkSerialize<TReaderWriterType>(BufferSerializer<TReaderWriterType> serializer) where TReaderWriterType : IReaderWriter
{
bool isNull = Value == null;
serializer.SerializeValue(ref isNull);
if (!isNull)
{
if (Value == null)
{
Value = new T();
}
serializer.SerializeValue(ref Value);
}
}
}
/// <summary>
/// This provides coverage for all of the predefined NetworkVariable types
/// The initial goal is for generalized full coverage of NetworkVariables:
@@ -30,6 +183,12 @@ namespace Unity.Netcode.RuntimeTests
private NetworkVariable<ulong> m_NetworkVariableULong = new NetworkVariable<ulong>();
private NetworkVariable<uint> m_NetworkVariableUInt = new NetworkVariable<uint>();
private NetworkVariable<ushort> m_NetworkVariableUShort = new NetworkVariable<ushort>();
private NetworkVariable<FixedString32Bytes> m_NetworkVariableFixedString32 = new NetworkVariable<FixedString32Bytes>();
private NetworkVariable<FixedString64Bytes> m_NetworkVariableFixedString64 = new NetworkVariable<FixedString64Bytes>();
private NetworkVariable<FixedString128Bytes> m_NetworkVariableFixedString128 = new NetworkVariable<FixedString128Bytes>();
private NetworkVariable<FixedString512Bytes> m_NetworkVariableFixedString512 = new NetworkVariable<FixedString512Bytes>();
private NetworkVariable<FixedString4096Bytes> m_NetworkVariableFixedString4096 = new NetworkVariable<FixedString4096Bytes>();
private NetworkVariable<ManagedNetworkSerializableType> m_NetworkVariableManaged = new NetworkVariable<ManagedNetworkSerializableType>();
public NetworkVariableHelper<bool> Bool_Var;
@@ -50,6 +209,12 @@ namespace Unity.Netcode.RuntimeTests
public NetworkVariableHelper<ulong> Ulong_Var;
public NetworkVariableHelper<uint> Uint_Var;
public NetworkVariableHelper<ushort> Ushort_Var;
public NetworkVariableHelper<FixedString32Bytes> FixedString32_Var;
public NetworkVariableHelper<FixedString64Bytes> FixedString64_Var;
public NetworkVariableHelper<FixedString128Bytes> FixedString128_Var;
public NetworkVariableHelper<FixedString512Bytes> FixedString512_Var;
public NetworkVariableHelper<FixedString4096Bytes> FixedString4096_Var;
public NetworkVariableHelper<ManagedNetworkSerializableType> Managed_Var;
public bool EnableTesting;
@@ -80,6 +245,12 @@ namespace Unity.Netcode.RuntimeTests
m_NetworkVariableULong = new NetworkVariable<ulong>();
m_NetworkVariableUInt = new NetworkVariable<uint>();
m_NetworkVariableUShort = new NetworkVariable<ushort>();
m_NetworkVariableFixedString32 = new NetworkVariable<FixedString32Bytes>();
m_NetworkVariableFixedString64 = new NetworkVariable<FixedString64Bytes>();
m_NetworkVariableFixedString128 = new NetworkVariable<FixedString128Bytes>();
m_NetworkVariableFixedString512 = new NetworkVariable<FixedString512Bytes>();
m_NetworkVariableFixedString4096 = new NetworkVariable<FixedString4096Bytes>();
m_NetworkVariableManaged = new NetworkVariable<ManagedNetworkSerializableType>();
// NetworkVariable Value Type Constructor Test Coverage
@@ -101,6 +272,16 @@ namespace Unity.Netcode.RuntimeTests
m_NetworkVariableULong = new NetworkVariable<ulong>(1);
m_NetworkVariableUInt = new NetworkVariable<uint>(1);
m_NetworkVariableUShort = new NetworkVariable<ushort>(1);
m_NetworkVariableFixedString32 = new NetworkVariable<FixedString32Bytes>("1234567890");
m_NetworkVariableFixedString64 = new NetworkVariable<FixedString64Bytes>("1234567890");
m_NetworkVariableFixedString128 = new NetworkVariable<FixedString128Bytes>("1234567890");
m_NetworkVariableFixedString512 = new NetworkVariable<FixedString512Bytes>("1234567890");
m_NetworkVariableFixedString4096 = new NetworkVariable<FixedString4096Bytes>("1234567890");
m_NetworkVariableManaged = new NetworkVariable<ManagedNetworkSerializableType>(new ManagedNetworkSerializableType
{
Str = "1234567890",
Ints = new[] { 1, 2, 3, 4, 5 }
});
// Use this nifty class: NetworkVariableHelper
// Tracks if NetworkVariable changed invokes the OnValueChanged callback for the given instance type
@@ -122,6 +303,12 @@ namespace Unity.Netcode.RuntimeTests
Ulong_Var = new NetworkVariableHelper<ulong>(m_NetworkVariableULong);
Uint_Var = new NetworkVariableHelper<uint>(m_NetworkVariableUInt);
Ushort_Var = new NetworkVariableHelper<ushort>(m_NetworkVariableUShort);
FixedString32_Var = new NetworkVariableHelper<FixedString32Bytes>(m_NetworkVariableFixedString32);
FixedString64_Var = new NetworkVariableHelper<FixedString64Bytes>(m_NetworkVariableFixedString64);
FixedString128_Var = new NetworkVariableHelper<FixedString128Bytes>(m_NetworkVariableFixedString128);
FixedString512_Var = new NetworkVariableHelper<FixedString512Bytes>(m_NetworkVariableFixedString512);
FixedString4096_Var = new NetworkVariableHelper<FixedString4096Bytes>(m_NetworkVariableFixedString4096);
Managed_Var = new NetworkVariableHelper<ManagedNetworkSerializableType>(m_NetworkVariableManaged);
}
/// <summary>
@@ -152,6 +339,57 @@ namespace Unity.Netcode.RuntimeTests
InitializeTest();
}
public void AssertAllValuesAreCorrect()
{
Assert.AreEqual(false, m_NetworkVariableBool.Value);
Assert.AreEqual(255, m_NetworkVariableByte.Value);
Assert.AreEqual(100, m_NetworkVariableColor.Value.r);
Assert.AreEqual(100, m_NetworkVariableColor.Value.g);
Assert.AreEqual(100, m_NetworkVariableColor.Value.b);
Assert.AreEqual(100, m_NetworkVariableColor32.Value.r);
Assert.AreEqual(100, m_NetworkVariableColor32.Value.g);
Assert.AreEqual(100, m_NetworkVariableColor32.Value.b);
Assert.AreEqual(100, m_NetworkVariableColor32.Value.a);
Assert.AreEqual(1000, m_NetworkVariableDouble.Value);
Assert.AreEqual(1000.0f, m_NetworkVariableFloat.Value);
Assert.AreEqual(1000, m_NetworkVariableInt.Value);
Assert.AreEqual(100000, m_NetworkVariableLong.Value);
Assert.AreEqual(-127, m_NetworkVariableSByte.Value);
Assert.AreEqual(100, m_NetworkVariableQuaternion.Value.w);
Assert.AreEqual(100, m_NetworkVariableQuaternion.Value.x);
Assert.AreEqual(100, m_NetworkVariableQuaternion.Value.y);
Assert.AreEqual(100, m_NetworkVariableQuaternion.Value.z);
Assert.AreEqual(short.MaxValue, m_NetworkVariableShort.Value);
Assert.AreEqual(1000, m_NetworkVariableVector4.Value.w);
Assert.AreEqual(1000, m_NetworkVariableVector4.Value.x);
Assert.AreEqual(1000, m_NetworkVariableVector4.Value.y);
Assert.AreEqual(1000, m_NetworkVariableVector4.Value.z);
Assert.AreEqual(1000, m_NetworkVariableVector3.Value.x);
Assert.AreEqual(1000, m_NetworkVariableVector3.Value.y);
Assert.AreEqual(1000, m_NetworkVariableVector3.Value.z);
Assert.AreEqual(1000, m_NetworkVariableVector2.Value.x);
Assert.AreEqual(1000, m_NetworkVariableVector2.Value.y);
Assert.AreEqual(Vector3.one.x, m_NetworkVariableRay.Value.origin.x);
Assert.AreEqual(Vector3.one.y, m_NetworkVariableRay.Value.origin.y);
Assert.AreEqual(Vector3.one.z, m_NetworkVariableRay.Value.origin.z);
Assert.AreEqual(Vector3.right.x, m_NetworkVariableRay.Value.direction.x);
Assert.AreEqual(Vector3.right.y, m_NetworkVariableRay.Value.direction.y);
Assert.AreEqual(Vector3.right.z, m_NetworkVariableRay.Value.direction.z);
Assert.AreEqual(ulong.MaxValue, m_NetworkVariableULong.Value);
Assert.AreEqual(uint.MaxValue, m_NetworkVariableUInt.Value);
Assert.AreEqual(ushort.MaxValue, m_NetworkVariableUShort.Value);
Assert.IsTrue(m_NetworkVariableFixedString32.Value.Equals("FixedString32Bytes"));
Assert.IsTrue(m_NetworkVariableFixedString64.Value.Equals("FixedString64Bytes"));
Assert.IsTrue(m_NetworkVariableFixedString128.Value.Equals("FixedString128Bytes"));
Assert.IsTrue(m_NetworkVariableFixedString512.Value.Equals("FixedString512Bytes"));
Assert.IsTrue(m_NetworkVariableFixedString4096.Value.Equals("FixedString4096Bytes"));
Assert.IsTrue(m_NetworkVariableManaged.Value.Equals(new ManagedNetworkSerializableType
{
Str = "ManagedNetworkSerializableType",
Ints = new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }
}));
}
// Update is called once per frame
private void Update()
{
@@ -187,6 +425,16 @@ namespace Unity.Netcode.RuntimeTests
m_NetworkVariableULong.Value = ulong.MaxValue;
m_NetworkVariableUInt.Value = uint.MaxValue;
m_NetworkVariableUShort.Value = ushort.MaxValue;
m_NetworkVariableFixedString32.Value = new FixedString32Bytes("FixedString32Bytes");
m_NetworkVariableFixedString64.Value = new FixedString64Bytes("FixedString64Bytes");
m_NetworkVariableFixedString128.Value = new FixedString128Bytes("FixedString128Bytes");
m_NetworkVariableFixedString512.Value = new FixedString512Bytes("FixedString512Bytes");
m_NetworkVariableFixedString4096.Value = new FixedString4096Bytes("FixedString4096Bytes");
m_NetworkVariableManaged.Value = new ManagedNetworkSerializableType
{
Str = "ManagedNetworkSerializableType",
Ints = new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }
};
//Set the timeout (i.e. how long we will wait for all NetworkVariables to have registered their changes)
m_WaitForChangesTimeout = Time.realtimeSinceStartup + 0.50f;

View File

@@ -62,11 +62,27 @@ namespace Unity.Netcode.RuntimeTests
response.PlayerPrefabHash = null;
}
[Test]
public void VerifyUniqueNetworkConfigPerRequest()
{
var networkConfig = new NetworkConfig();
networkConfig.EnableSceneManagement = true;
networkConfig.TickRate = 30;
var currentHash = networkConfig.GetConfig();
networkConfig.EnableSceneManagement = false;
networkConfig.TickRate = 60;
var newHash = networkConfig.GetConfig(false);
Assert.True(currentHash != newHash, $"Hashed {nameof(NetworkConfig)} values {currentHash} and {newHash} should not be the same!");
}
[TearDown]
public void TearDown()
{
// Stop, shutdown, and destroy
NetworkManagerHelper.ShutdownNetworkManager();
}
}
}

View File

@@ -0,0 +1,101 @@
using System.Collections;
using System.Text.RegularExpressions;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(ApprovalTimedOutTypes.ServerDoesNotRespond)]
[TestFixture(ApprovalTimedOutTypes.ClientDoesNotRequest)]
public class ConnectionApprovalTimeoutTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
public enum ApprovalTimedOutTypes
{
ClientDoesNotRequest,
ServerDoesNotRespond
}
private ApprovalTimedOutTypes m_ApprovalFailureType;
public ConnectionApprovalTimeoutTests(ApprovalTimedOutTypes approvalFailureType)
{
m_ApprovalFailureType = approvalFailureType;
}
// Must be >= 2 since this is an int value and the test waits for timeout - 1 to try to verify it doesn't
// time out early
private const int k_TestTimeoutPeriod = 1;
private Regex m_ExpectedLogMessage;
private LogType m_LogType;
protected override IEnumerator OnSetup()
{
m_BypassConnectionTimeout = true;
return base.OnSetup();
}
protected override IEnumerator OnTearDown()
{
m_BypassConnectionTimeout = false;
return base.OnTearDown();
}
protected override void OnServerAndClientsCreated()
{
m_ServerNetworkManager.NetworkConfig.ClientConnectionBufferTimeout = k_TestTimeoutPeriod;
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
m_ClientNetworkManagers[0].NetworkConfig.ClientConnectionBufferTimeout = k_TestTimeoutPeriod;
m_ClientNetworkManagers[0].LogLevel = LogLevel.Developer;
base.OnServerAndClientsCreated();
}
protected override IEnumerator OnStartedServerAndClients()
{
if (m_ApprovalFailureType == ApprovalTimedOutTypes.ServerDoesNotRespond)
{
// We catch (don't process) the incoming approval message to simulate the server not sending the approved message in time
m_ClientNetworkManagers[0].MessagingSystem.Hook(new MessageCatcher<ConnectionApprovedMessage>(m_ClientNetworkManagers[0]));
m_ExpectedLogMessage = new Regex("Timed out waiting for the server to approve the connection request.");
m_LogType = LogType.Log;
}
else
{
// We catch (don't process) the incoming connection request message to simulate a transport connection but the client never
// sends (or takes too long to send) the connection request.
m_ServerNetworkManager.MessagingSystem.Hook(new MessageCatcher<ConnectionRequestMessage>(m_ServerNetworkManager));
// For this test, we know the timed out client will be Client-1
m_ExpectedLogMessage = new Regex("Server detected a transport connection from Client-1, but timed out waiting for the connection request message.");
m_LogType = LogType.Warning;
}
yield return null;
}
[UnityTest]
public IEnumerator ValidateApprovalTimeout()
{
// Delay for half of the wait period
yield return new WaitForSeconds(k_TestTimeoutPeriod * 0.5f);
// Verify we haven't received the time out message yet
NetcodeLogAssert.LogWasNotReceived(LogType.Log, m_ExpectedLogMessage);
// Wait for 3/4s of the time out period to pass (totaling 1.25x the wait period)
yield return new WaitForSeconds(k_TestTimeoutPeriod * 0.75f);
// We should have the test relative log message by this time.
NetcodeLogAssert.LogWasReceived(m_LogType, m_ExpectedLogMessage);
// It should only have the host client connected
Assert.AreEqual(1, m_ServerNetworkManager.ConnectedClients.Count, $"Expected only one client when there were {m_ServerNetworkManager.ConnectedClients.Count} clients connected!");
Assert.AreEqual(0, m_ServerNetworkManager.PendingClients.Count, $"Expected no pending clients when there were {m_ServerNetworkManager.PendingClients.Count} pending clients!");
Assert.True(!m_ClientNetworkManagers[0].IsApproved, $"Expected the client to not have been approved, but it was!");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b0c4159ea234415fa9497860e6ef4fc2
timeCreated: 1661796642

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Unity.Collections;
using UnityEngine;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
@@ -113,82 +113,6 @@ namespace Unity.Netcode.RuntimeTests
}
}
internal class SpawnCatcher : INetworkHooks
{
public struct TriggerData
{
public FastBufferReader Reader;
public MessageHeader Header;
public ulong SenderId;
public float Timestamp;
public int SerializedHeaderSize;
}
public readonly List<TriggerData> CaughtMessages = new List<TriggerData>();
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
{
}
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
{
}
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
{
return true;
}
public bool OnVerifyCanReceive(ulong senderId, Type messageType, FastBufferReader messageContent, ref NetworkContext context)
{
if (messageType == typeof(CreateObjectMessage))
{
CaughtMessages.Add(new TriggerData
{
Reader = new FastBufferReader(messageContent, Allocator.Persistent),
Header = context.Header,
Timestamp = context.Timestamp,
SenderId = context.SenderId,
SerializedHeaderSize = context.SerializedHeaderSize
});
return false;
}
return true;
}
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
}
public class DeferredMessageTestRpcComponent : NetworkBehaviour
{
public bool ClientRpcCalled;
@@ -198,62 +122,87 @@ namespace Unity.Netcode.RuntimeTests
{
ClientRpcCalled = true;
}
public static readonly List<ulong> ClientInstances = new List<ulong>();
public override void OnNetworkSpawn()
{
if (!IsServer)
{
ClientInstances.Add(NetworkManager.LocalClientId);
}
base.OnNetworkSpawn();
}
}
public class DeferredMessageTestNetworkVariableComponent : NetworkBehaviour
{
public NetworkVariable<int> TestNetworkVariable = new NetworkVariable<int>();
public static readonly List<ulong> ClientInstances = new List<ulong>();
public NetworkVariable<int> TestNetworkVariable;
public void Awake()
{
TestNetworkVariable = new NetworkVariable<int>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
}
public override void OnNetworkSpawn()
{
if (!IsServer)
{
ClientInstances.Add(NetworkManager.LocalClientId);
}
base.OnNetworkSpawn();
}
}
public class DeferredMessageTestRpcAndNetworkVariableComponent : NetworkBehaviour
{
public static readonly List<ulong> ClientInstances = new List<ulong>();
public bool ClientRpcCalled;
public NetworkVariable<int> TestNetworkVariable;
public void Awake()
{
TestNetworkVariable = new NetworkVariable<int>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
}
public override void OnNetworkSpawn()
{
if (!IsServer)
{
ClientInstances.Add(NetworkManager.LocalClientId);
}
base.OnNetworkSpawn();
}
[ClientRpc]
public void SendTestClientRpc()
{
ClientRpcCalled = true;
}
public NetworkVariable<int> TestNetworkVariable = new NetworkVariable<int>();
}
public class DeferredMessagingTest : NetcodeIntegrationTest
{
protected override int NumberOfClients => 2;
protected override int NumberOfClients => 0;
private List<SpawnCatcher> m_ClientSpawnCatchers = new List<SpawnCatcher>();
private List<MessageCatcher<CreateObjectMessage>> m_ClientSpawnCatchers = new List<MessageCatcher<CreateObjectMessage>>();
private GameObject m_RpcPrefab;
private GameObject m_NetworkVariablePrefab;
private GameObject m_RpcAndNetworkVariablePrefab;
private int m_NumberOfClientsToLateJoin = 2;
protected override IEnumerator OnSetup()
{
DeferredMessageTestRpcAndNetworkVariableComponent.ClientInstances.Clear();
DeferredMessageTestRpcComponent.ClientInstances.Clear();
DeferredMessageTestNetworkVariableComponent.ClientInstances.Clear();
m_SkipAddingPrefabsToClient = false;
// Host is irrelevant, messages don't get sent to the host "client"
m_UseHost = false;
m_RpcPrefab = new GameObject("Object With RPC");
var networkObject = m_RpcPrefab.AddComponent<NetworkObject>();
m_RpcPrefab.AddComponent<DeferredMessageTestRpcComponent>();
// Make it a prefab
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
m_NetworkVariablePrefab = new GameObject("Object With NetworkVariable");
networkObject = m_NetworkVariablePrefab.AddComponent<NetworkObject>();
m_NetworkVariablePrefab.AddComponent<DeferredMessageTestNetworkVariableComponent>();
// Make it a prefab
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
m_RpcAndNetworkVariablePrefab = new GameObject("Object With NetworkVariable And RPC");
networkObject = m_RpcAndNetworkVariablePrefab.AddComponent<NetworkObject>();
m_RpcAndNetworkVariablePrefab.AddComponent<DeferredMessageTestRpcAndNetworkVariableComponent>();
// Make it a prefab
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
// Replace the IDeferredMessageManager component with our test one in the component factory
ComponentFactory.Register<IDeferredMessageManager>(networkManager => new TestDeferredMessageManager(networkManager));
yield return null;
@@ -269,13 +218,54 @@ namespace Unity.Netcode.RuntimeTests
protected override void OnServerAndClientsCreated()
{
m_ServerNetworkManager.AddNetworkPrefab(m_RpcPrefab);
m_ServerNetworkManager.AddNetworkPrefab(m_NetworkVariablePrefab);
m_ServerNetworkManager.AddNetworkPrefab(m_RpcAndNetworkVariablePrefab);
// Note: This is where prefabs should be created
m_RpcPrefab = CreateNetworkObjectPrefab("Object With RPC");
var networkObject = m_RpcPrefab.GetComponent<NetworkObject>();
m_RpcPrefab.AddComponent<DeferredMessageTestRpcComponent>();
m_NetworkVariablePrefab = CreateNetworkObjectPrefab("Object With NetworkVariable");
networkObject = m_NetworkVariablePrefab.GetComponent<NetworkObject>();
m_NetworkVariablePrefab.AddComponent<DeferredMessageTestNetworkVariableComponent>();
m_RpcAndNetworkVariablePrefab = CreateNetworkObjectPrefab("Object With NetworkVariable And RPC");
networkObject = m_RpcAndNetworkVariablePrefab.GetComponent<NetworkObject>();
m_RpcAndNetworkVariablePrefab.AddComponent<DeferredMessageTestRpcAndNetworkVariableComponent>();
m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false;
foreach (var client in m_ClientNetworkManagers)
}
private bool m_SkipAddingPrefabsToClient = false;
private void AddPrefabsToClient(NetworkManager networkManager)
{
networkManager.AddNetworkPrefab(m_RpcPrefab);
networkManager.AddNetworkPrefab(m_NetworkVariablePrefab);
networkManager.AddNetworkPrefab(m_RpcAndNetworkVariablePrefab);
}
protected override void OnNewClientCreated(NetworkManager networkManager)
{
networkManager.NetworkConfig.ForceSamePrefabs = false;
if (!m_SkipAddingPrefabsToClient)
{
client.NetworkConfig.ForceSamePrefabs = false;
AddPrefabsToClient(networkManager);
}
base.OnNewClientCreated(networkManager);
}
private IEnumerator SpawnClients(bool clearTestDeferredMessageManagerCallFlags = true)
{
for (int i = 0; i < m_NumberOfClientsToLateJoin; i++)
{
// Create and join client
yield return CreateAndStartNewClient();
}
if (clearTestDeferredMessageManagerCallFlags)
{
ClearTestDeferredMessageManagerCallFlags();
}
}
@@ -296,38 +286,19 @@ namespace Unity.Netcode.RuntimeTests
{
foreach (var client in m_ClientNetworkManagers)
{
var catcher = new SpawnCatcher();
var catcher = new MessageCatcher<CreateObjectMessage>(client);
m_ClientSpawnCatchers.Add(catcher);
client.MessagingSystem.Hook(catcher);
}
}
private void RegisterClientPrefabs(bool clearTestDeferredMessageManagerCallFlags = true)
{
foreach (var client in m_ClientNetworkManagers)
{
client.AddNetworkPrefab(m_RpcPrefab);
client.AddNetworkPrefab(m_NetworkVariablePrefab);
client.AddNetworkPrefab(m_RpcAndNetworkVariablePrefab);
}
if (clearTestDeferredMessageManagerCallFlags)
{
ClearTestDeferredMessageManagerCallFlags();
}
}
private void ReleaseSpawns()
{
for (var i = 0; i < m_ClientNetworkManagers.Length; ++i)
{
// Unhook first so the spawn catcher stops catching spawns
m_ClientNetworkManagers[i].MessagingSystem.Unhook(m_ClientSpawnCatchers[i]);
foreach (var caughtSpawn in m_ClientSpawnCatchers[i].CaughtMessages)
{
// Reader will be disposed within HandleMessage
m_ClientNetworkManagers[i].MessagingSystem.HandleMessage(caughtSpawn.Header, caughtSpawn.Reader, caughtSpawn.SenderId, caughtSpawn.Timestamp, caughtSpawn.SerializedHeaderSize);
}
m_ClientSpawnCatchers[i].ReleaseMessages();
}
m_ClientSpawnCatchers.Clear();
}
@@ -345,7 +316,7 @@ namespace Unity.Netcode.RuntimeTests
{
foreach (var catcher in m_ClientSpawnCatchers)
{
if (catcher.CaughtMessages.Count != count)
if (catcher.CaughtMessageCount != count)
{
return false;
}
@@ -372,89 +343,54 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnAddPrefab));
}
private static CoroutineRunner s_CoroutineRunner;
private Coroutine Run(IEnumerator enumerator)
private IEnumerator WaitForAllClientsToReceive<T>() where T : INetworkMessage
{
if (s_CoroutineRunner == null)
{
s_CoroutineRunner = new GameObject(nameof(CoroutineRunner)).AddComponent<CoroutineRunner>();
}
return s_CoroutineRunner.StartCoroutine(enumerator);
yield return WaitForMessageReceived<T>(m_ClientNetworkManagers.ToList(), ReceiptType.Received);
}
private IEnumerator RunMultiple(List<IEnumerator> waitFor)
{
yield return WaitMultiple(StartMultiple(waitFor));
}
private List<Coroutine> StartMultiple(List<IEnumerator> waitFor)
{
var runningCoroutines = new List<Coroutine>();
foreach (var enumerator in waitFor)
{
runningCoroutines.Add(Run(enumerator));
}
return runningCoroutines;
}
private IEnumerator WaitMultiple(List<Coroutine> runningCoroutines)
{
foreach (var coroutine in runningCoroutines)
{
yield return coroutine;
}
}
private List<IEnumerator> WaitForAllClientsToReceive<T>() where T : INetworkMessage
{
var waiters = new List<IEnumerator>();
foreach (var client in m_ClientNetworkManagers)
{
waiters.Add(NetcodeIntegrationTestHelpers.WaitForMessageOfTypeReceived<T>(client));
}
return waiters;
}
private List<IEnumerator> WaitForAllClientsToReceive<TFirstMessage, TSecondMessage>()
private IEnumerator WaitForAllClientsToReceive<TFirstMessage, TSecondMessage>()
where TFirstMessage : INetworkMessage
where TSecondMessage : INetworkMessage
{
var waiters = new List<IEnumerator>();
foreach (var client in m_ClientNetworkManagers)
yield return WaitForMessagesReceived(new List<Type>
{
waiters.Add(NetcodeIntegrationTestHelpers.WaitForMessageOfTypeReceived<TFirstMessage>(client));
waiters.Add(NetcodeIntegrationTestHelpers.WaitForMessageOfTypeReceived<TSecondMessage>(client));
}
return waiters;
typeof(TFirstMessage),
typeof(TSecondMessage)
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
}
private List<IEnumerator> WaitForAllClientsToReceive<TFirstMessage, TSecondMessage, TThirdMessage, TFourthMessage>()
private IEnumerator WaitForAllClientsToReceive<TFirstMessage, TSecondMessage, TThirdMessage>()
where TFirstMessage : INetworkMessage
where TSecondMessage : INetworkMessage
where TThirdMessage : INetworkMessage
{
yield return WaitForMessagesReceived(new List<Type>
{
typeof(TFirstMessage),
typeof(TSecondMessage),
typeof(TThirdMessage),
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
}
private IEnumerator WaitForAllClientsToReceive<TFirstMessage, TSecondMessage, TThirdMessage, TFourthMessage>()
where TFirstMessage : INetworkMessage
where TSecondMessage : INetworkMessage
where TThirdMessage : INetworkMessage
where TFourthMessage : INetworkMessage
{
var waiters = new List<IEnumerator>();
foreach (var client in m_ClientNetworkManagers)
yield return WaitForMessagesReceived(new List<Type>
{
waiters.Add(NetcodeIntegrationTestHelpers.WaitForMessageOfTypeReceived<TFirstMessage>(client));
waiters.Add(NetcodeIntegrationTestHelpers.WaitForMessageOfTypeReceived<TSecondMessage>(client));
waiters.Add(NetcodeIntegrationTestHelpers.WaitForMessageOfTypeReceived<TThirdMessage>(client));
waiters.Add(NetcodeIntegrationTestHelpers.WaitForMessageOfTypeReceived<TFourthMessage>(client));
}
return waiters;
typeof(TFirstMessage),
typeof(TSecondMessage),
typeof(TThirdMessage),
typeof(TFourthMessage),
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
}
[UnityTest]
public IEnumerator WhenAnRpcArrivesBeforeASpawnArrives_ItIsDeferred()
{
RegisterClientPrefabs();
yield return SpawnClients();
CatchSpawns();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
@@ -463,7 +399,7 @@ namespace Unity.Netcode.RuntimeTests
serverObject.GetComponent<DeferredMessageTestRpcComponent>().SendTestClientRpc();
yield return RunMultiple(WaitForAllClientsToReceive<ClientRpcMessage>());
yield return WaitForAllClientsToReceive<ClientRpcMessage>();
foreach (var client in m_ClientNetworkManagers)
{
@@ -477,7 +413,7 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator WhenADespawnArrivesBeforeASpawnArrives_ItIsDeferred()
{
RegisterClientPrefabs();
yield return SpawnClients();
CatchSpawns();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
@@ -486,7 +422,7 @@ namespace Unity.Netcode.RuntimeTests
serverObject.GetComponent<NetworkObject>().Despawn(false);
yield return RunMultiple(WaitForAllClientsToReceive<DestroyObjectMessage>());
yield return WaitForAllClientsToReceive<DestroyObjectMessage>();
foreach (var client in m_ClientNetworkManagers)
{
@@ -500,7 +436,7 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator WhenAChangeOwnershipMessageArrivesBeforeASpawnArrives_ItIsDeferred()
{
RegisterClientPrefabs();
yield return SpawnClients();
CatchSpawns();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
@@ -508,7 +444,7 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForClientsToCatchSpawns();
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
yield return RunMultiple(WaitForAllClientsToReceive<ChangeOwnershipMessage>());
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
@@ -521,13 +457,10 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator WhenANetworkVariableDeltaMessageArrivesBeforeASpawnArrives_ItIsDeferred()
{
RegisterClientPrefabs();
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
CatchSpawns();
// Have to start these before spawning here because spawning sends a NetworkVariableDeltaMessage, too
// Depending on timing, if we start this after spawning, we may end up missing the first one.
var waiters = WaitForAllClientsToReceive<NetworkVariableDeltaMessage, NetworkVariableDeltaMessage>();
var coroutines = StartMultiple(waiters);
var serverObject = Object.Instantiate(m_NetworkVariablePrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
@@ -536,7 +469,7 @@ namespace Unity.Netcode.RuntimeTests
serverObject.GetComponent<DeferredMessageTestNetworkVariableComponent>().TestNetworkVariable.Value = 1;
yield return WaitMultiple(coroutines);
yield return WaitForAllClientsToReceive<NetworkVariableDeltaMessage>();
foreach (var client in m_ClientNetworkManagers)
{
@@ -545,18 +478,21 @@ namespace Unity.Netcode.RuntimeTests
Assert.IsFalse(manager.ProcessTriggersCalled);
// TODO: Network Variables generate an extra message immediately at spawn for some reason...
// Seems like a bug since the network variable data is in the spawn message already.
AssertSpawnTriggerCountForObject(manager, serverObject, 2);
AssertSpawnTriggerCountForObject(manager, serverObject, 1);
}
}
[UnityTest]
//[Ignore("Disabling this temporarily until it is migrated into new integration test.")]
public IEnumerator WhenASpawnMessageArrivesBeforeThePrefabIsAvailable_ItIsDeferred()
{
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
yield return RunMultiple(WaitForAllClientsToReceive<CreateObjectMessage>());
yield return WaitForAllClientsToReceive<CreateObjectMessage>();
foreach (var client in m_ClientNetworkManagers)
{
@@ -631,8 +567,28 @@ namespace Unity.Netcode.RuntimeTests
public IEnumerator WhenANetworkVariableDeltaMessageIsDeferred_ItIsProcessedOnSpawn()
{
yield return WhenANetworkVariableDeltaMessageArrivesBeforeASpawnArrives_ItIsDeferred();
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
ReleaseSpawns();
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestNetworkVariableComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
yield return WaitForConditionOrTimeOut(HaveAllClientsSpawned);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
@@ -647,9 +603,31 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator WhenASpawnMessageIsDeferred_ItIsProcessedOnAddPrefab()
{
// This will prevent spawned clients from adding prefabs
m_SkipAddingPrefabsToClient = true;
yield return WhenASpawnMessageArrivesBeforeThePrefabIsAvailable_ItIsDeferred();
RegisterClientPrefabs(false);
// Now add the prefabs
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestRpcComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
yield return WaitForConditionOrTimeOut(HaveAllClientsSpawned);
// Validate this test
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
@@ -664,14 +642,10 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator WhenMultipleSpawnTriggeredMessagesAreDeferred_TheyAreAllProcessedOnSpawn()
{
RegisterClientPrefabs();
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
CatchSpawns();
// Have to start these before spawning here because spawning sends a NetworkVariableDeltaMessage, too
// Depending on timing, if we start this after spawning, we may end up missing the first one.
var waiters = WaitForAllClientsToReceive<ClientRpcMessage, NetworkVariableDeltaMessage, NetworkVariableDeltaMessage, ChangeOwnershipMessage>();
var coroutines = StartMultiple(waiters);
var serverObject = Object.Instantiate(m_RpcAndNetworkVariablePrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
@@ -679,10 +653,12 @@ namespace Unity.Netcode.RuntimeTests
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().SendTestClientRpc();
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
yield return WaitForAllClientsToReceive<ClientRpcMessage, NetworkVariableDeltaMessage>();
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
// Should be received in order so we'll wait for the last one.
yield return WaitMultiple(coroutines);
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var client in m_ClientNetworkManagers)
{
@@ -690,29 +666,49 @@ namespace Unity.Netcode.RuntimeTests
Assert.IsTrue(manager.DeferMessageCalled);
Assert.IsFalse(manager.ProcessTriggersCalled);
Assert.AreEqual(4, manager.DeferredMessageCountTotal());
Assert.AreEqual(4, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(4, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(3, manager.DeferredMessageCountTotal());
Assert.AreEqual(3, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(3, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnAddPrefab));
AddPrefabsToClient(client);
}
ReleaseSpawns();
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestRpcAndNetworkVariableComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
yield return WaitForConditionOrTimeOut(HaveAllClientsSpawned);
yield return new WaitForSeconds(0.1f);
// Validate the spawned objects
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.IsTrue(manager.ProcessTriggersCalled);
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
Assert.IsTrue(manager.ProcessTriggersCalled, "Process triggers were not called!");
Assert.AreEqual(0, manager.DeferredMessageCountTotal(), $"Deferred message count ({manager.DeferredMessageCountTotal()}) is not zero!");
var component = GetComponentForClient<DeferredMessageTestRpcAndNetworkVariableComponent>(client.LocalClientId);
Assert.IsTrue(component.ClientRpcCalled);
Assert.AreEqual(1, component.TestNetworkVariable.Value);
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, component.OwnerClientId);
Assert.IsTrue(component.ClientRpcCalled, "Client RPC was not called!");
Assert.AreEqual(1, component.TestNetworkVariable.Value, $"Test {nameof(NetworkVariable<int>)} ({component.TestNetworkVariable.Value}) does not equal 1!");
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, component.OwnerClientId, $"{component.name} owner id ({component.OwnerClientId}) does not equal first client id ({m_ClientNetworkManagers[0].LocalClientId})");
}
}
[UnityTest]
public IEnumerator WhenMultipleAddPrefabTriggeredMessagesAreDeferred_TheyAreAllProcessedOnAddNetworkPrefab()
{
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
var serverObject = Object.Instantiate(m_RpcPrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject.GetComponent<NetworkObject>().Spawn();
@@ -721,7 +717,7 @@ namespace Unity.Netcode.RuntimeTests
serverObject2.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
serverObject2.GetComponent<NetworkObject>().Spawn();
yield return RunMultiple(WaitForAllClientsToReceive<CreateObjectMessage, CreateObjectMessage>());
yield return WaitForAllClientsToReceive<CreateObjectMessage, CreateObjectMessage>();
foreach (var client in m_ClientNetworkManagers)
{
@@ -733,9 +729,23 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(2, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnAddPrefab));
Assert.AreEqual(2, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnAddPrefab, serverObject.GetComponent<NetworkObject>().GlobalObjectIdHash));
AddPrefabsToClient(client);
}
RegisterClientPrefabs(false);
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestRpcComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
yield return WaitForConditionOrTimeOut(HaveAllClientsSpawned);
foreach (var client in m_ClientNetworkManagers)
{
@@ -769,10 +779,8 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator WhenSpawnTriggeredMessagesAreDeferredBeforeThePrefabIsAdded_AddingThePrefabCausesThemToBeProcessed()
{
// Because we're not waiting for the client to receive the spawn before we change the network variable value,
// there's only one NetworkVariableDeltaMessage this time.
var waiters = WaitForAllClientsToReceive<CreateObjectMessage, ClientRpcMessage, NetworkVariableDeltaMessage, ChangeOwnershipMessage>();
var coroutines = StartMultiple(waiters);
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
var serverObject = Object.Instantiate(m_RpcAndNetworkVariablePrefab);
serverObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
@@ -780,10 +788,15 @@ namespace Unity.Netcode.RuntimeTests
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().SendTestClientRpc();
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
// TODO: Remove this if we figure out how to work around the NetworkVariableDeltaMessage.Serialized issue at line 59
// Otherwise, we have to wait for at least 1 tick for the NetworkVariableDeltaMessage to be generated before changing ownership
yield return WaitForAllClientsToReceive<CreateObjectMessage, ClientRpcMessage, NetworkVariableDeltaMessage>();
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
yield return WaitMultiple(coroutines);
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
// Validate messages are deferred and pending
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
@@ -794,10 +807,26 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(3, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnAddPrefab));
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnAddPrefab, serverObject.GetComponent<NetworkObject>().GlobalObjectIdHash));
AddPrefabsToClient(client);
}
RegisterClientPrefabs(false);
// Wait for the clients to spawn the NetworkObjects
bool HaveAllClientsSpawned()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!DeferredMessageTestRpcAndNetworkVariableComponent.ClientInstances.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
yield return WaitForConditionOrTimeOut(HaveAllClientsSpawned);
yield return new WaitForSeconds(0.1f);
// Validate the test
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
@@ -813,10 +842,10 @@ namespace Unity.Netcode.RuntimeTests
}
[UnityTest]
[Ignore("This test is unstable (MTT-4146)")]
public IEnumerator WhenAMessageIsDeferredForMoreThanTheConfiguredTime_ItIsRemoved([Values(1, 2, 3)] int timeout)
{
RegisterClientPrefabs();
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
CatchSpawns();
foreach (var client in m_ClientNetworkManagers)
{
@@ -870,24 +899,29 @@ namespace Unity.Netcode.RuntimeTests
yield return new WaitForSeconds(timeout + 0.1f);
Assert.AreEqual(NumberOfClients, purgeCount);
foreach (var client in m_ClientNetworkManagers)
bool HaveAllClientsPurged()
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
if (manager.DeferredMessageCountTotal() != 0)
{
return false;
}
}
return true;
}
yield return WaitForConditionOrTimeOut(HaveAllClientsPurged);
AssertOnTimeout("Timed out waiting for all clients to purge their deferred messages!");
}
[UnityTest]
[Ignore("This test is unstable on standalones")]
public IEnumerator WhenMultipleMessagesForTheSameObjectAreDeferredForMoreThanTheConfiguredTime_TheyAreAllRemoved([Values(1, 2, 3)] int timeout)
{
RegisterClientPrefabs();
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
CatchSpawns();
// Have to start these before spawning here because spawning sends a NetworkVariableDeltaMessage, too
// Depending on timing, if we start this after spawning, we may end up missing the first one.
var waiters = WaitForAllClientsToReceive<ClientRpcMessage, NetworkVariableDeltaMessage, NetworkVariableDeltaMessage, ChangeOwnershipMessage>();
var coroutines = StartMultiple(waiters);
foreach (var client in m_ClientNetworkManagers)
{
@@ -917,7 +951,9 @@ namespace Unity.Netcode.RuntimeTests
serverObject.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
yield return WaitMultiple(coroutines);
yield return WaitForMessagesReceived(
new List<Type> {typeof(ClientRpcMessage), typeof(NetworkVariableDeltaMessage), typeof(ChangeOwnershipMessage),
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
foreach (var unused in m_ClientNetworkManagers)
{
@@ -931,38 +967,40 @@ namespace Unity.Netcode.RuntimeTests
{
++purgeCount;
var elapsed = Time.realtimeSinceStartup - start;
Assert.GreaterOrEqual(elapsed, timeout - 0.05f);
Assert.AreEqual(4, manager.DeferredMessageCountTotal());
Assert.AreEqual(4, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(4, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, key));
Assert.GreaterOrEqual(elapsed, timeout - 0.25f);
Assert.AreEqual(3, manager.DeferredMessageCountTotal());
Assert.AreEqual(3, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(3, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, key));
Assert.AreEqual(serverObject.GetComponent<NetworkObject>().NetworkObjectId, key);
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforePurge = beforePurge;
}
yield return new WaitForSeconds(timeout + 0.1f);
Assert.AreEqual(NumberOfClients, purgeCount);
foreach (var client in m_ClientNetworkManagers)
bool HaveAllClientsPurged()
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
Assert.AreEqual(0, manager.DeferredMessageCountTotal());
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
if (manager.DeferredMessageCountTotal() != 0)
{
return false;
}
}
return true;
}
yield return WaitForConditionOrTimeOut(HaveAllClientsPurged);
AssertOnTimeout("Timed out waiting for all clients to purge their deferred messages!");
}
[UnityTest]
public IEnumerator WhenMultipleMessagesForDifferentObjectsAreDeferredForMoreThanTheConfiguredTime_TheyAreAllRemoved([Values(1, 2, 3)] int timeout)
{
RegisterClientPrefabs();
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
CatchSpawns();
// Have to start these before spawning here because spawning sends a NetworkVariableDeltaMessage, too
// Depending on timing, if we start this after spawning, we may end up missing the first one.
var waiters = WaitForAllClientsToReceive<ClientRpcMessage, NetworkVariableDeltaMessage, NetworkVariableDeltaMessage, ChangeOwnershipMessage>();
waiters.AddRange(WaitForAllClientsToReceive<ClientRpcMessage, NetworkVariableDeltaMessage, NetworkVariableDeltaMessage, ChangeOwnershipMessage>());
var coroutines = StartMultiple(waiters);
foreach (var client in m_ClientNetworkManagers)
{
client.NetworkConfig.SpawnTimeout = timeout;
@@ -1000,10 +1038,13 @@ namespace Unity.Netcode.RuntimeTests
serverObject2.GetComponent<DeferredMessageTestRpcAndNetworkVariableComponent>().TestNetworkVariable.Value = 1;
serverObject2.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
yield return WaitMultiple(coroutines);
yield return WaitForMessagesReceived(
new List<Type> {typeof(ClientRpcMessage), typeof(NetworkVariableDeltaMessage), typeof(ChangeOwnershipMessage),typeof(ClientRpcMessage), typeof(NetworkVariableDeltaMessage), typeof(ChangeOwnershipMessage),
}, m_ClientNetworkManagers.ToList(), ReceiptType.Received);
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredMessageManager.TriggerType.OnSpawn} with key {serverObject2.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
}
@@ -1011,24 +1052,27 @@ namespace Unity.Netcode.RuntimeTests
int purgeCount = 0;
foreach (var client in m_ClientNetworkManagers)
{
var remainingMessagesTotalThisClient = 8;
var remainingMessagesTotalThisClient = 6;
TestDeferredMessageManager.BeforePurgeDelegate beforePurge = (manager, key) =>
{
++purgeCount;
var elapsed = Time.realtimeSinceStartup - start;
Assert.GreaterOrEqual(elapsed, timeout - 0.05f);
Assert.GreaterOrEqual(elapsed, timeout - 0.25f);
Assert.AreEqual(remainingMessagesTotalThisClient, manager.DeferredMessageCountTotal());
Assert.AreEqual(remainingMessagesTotalThisClient, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
Assert.AreEqual(4, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, key));
remainingMessagesTotalThisClient -= 4;
Assert.AreEqual(3, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, key));
remainingMessagesTotalThisClient -= 3;
};
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
manager.OnBeforePurge = beforePurge;
}
yield return new WaitForSeconds(timeout + 0.1f);
Assert.AreEqual(NumberOfClients * 2, purgeCount);
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
Assert.AreEqual(m_NumberOfClientsToLateJoin * 2, purgeCount);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
@@ -1039,7 +1083,8 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator WhenADeferredMessageIsRemoved_OtherMessagesForSameObjectAreRemoved([Values(1, 2, 3)] int timeout)
{
RegisterClientPrefabs();
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
CatchSpawns();
foreach (var client in m_ClientNetworkManagers)
{
@@ -1067,7 +1112,7 @@ namespace Unity.Netcode.RuntimeTests
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
yield return RunMultiple(WaitForAllClientsToReceive<ChangeOwnershipMessage>());
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
yield return new WaitForSeconds(timeout - 0.5f);
@@ -1080,7 +1125,7 @@ namespace Unity.Netcode.RuntimeTests
}
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ServerNetworkManager.LocalClientId);
yield return RunMultiple(WaitForAllClientsToReceive<ChangeOwnershipMessage>());
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var client in m_ClientNetworkManagers)
{
@@ -1112,9 +1157,14 @@ namespace Unity.Netcode.RuntimeTests
manager.OnBeforePurge = beforePurge;
}
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
yield return new WaitForSeconds(0.6f);
Assert.AreEqual(NumberOfClients, purgeCount);
Assert.AreEqual(m_NumberOfClientsToLateJoin, purgeCount);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
@@ -1125,7 +1175,8 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator WhenADeferredMessageIsRemoved_OtherMessagesForDifferentObjectsAreNotRemoved([Values(1, 2, 3)] int timeout)
{
RegisterClientPrefabs();
m_SkipAddingPrefabsToClient = true;
yield return SpawnClients();
CatchSpawns();
foreach (var client in m_ClientNetworkManagers)
{
@@ -1156,7 +1207,7 @@ namespace Unity.Netcode.RuntimeTests
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
yield return RunMultiple(WaitForAllClientsToReceive<ChangeOwnershipMessage>());
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
yield return new WaitForSeconds(timeout - 0.5f);
@@ -1170,7 +1221,7 @@ namespace Unity.Netcode.RuntimeTests
}
serverObject2.GetComponent<NetworkObject>().ChangeOwnership(m_ServerNetworkManager.LocalClientId);
yield return RunMultiple(WaitForAllClientsToReceive<ChangeOwnershipMessage>());
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var client in m_ClientNetworkManagers)
{
@@ -1206,9 +1257,14 @@ namespace Unity.Netcode.RuntimeTests
manager.OnBeforePurge = beforePurge;
}
foreach (var client in m_ClientNetworkManagers)
{
AddPrefabsToClient(client);
}
yield return new WaitForSeconds(0.6f);
Assert.AreEqual(NumberOfClients, purgeCount);
Assert.AreEqual(m_NumberOfClientsToLateJoin, purgeCount);
foreach (var client in m_ClientNetworkManagers)
{
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;

View File

@@ -60,5 +60,53 @@ namespace Unity.Netcode.RuntimeTests
{
m_ClientDisconnected = true;
}
[UnityTest]
public IEnumerator ClientDisconnectPlayerObjectCleanup()
{
// create server and client instances
NetcodeIntegrationTestHelpers.Create(1, out NetworkManager server, out NetworkManager[] clients);
// create prefab
var gameObject = new GameObject("PlayerObject");
var networkObject = gameObject.AddComponent<NetworkObject>();
networkObject.DontDestroyWithOwner = true;
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
server.NetworkConfig.PlayerPrefab = gameObject;
for (int i = 0; i < clients.Length; i++)
{
clients[i].NetworkConfig.PlayerPrefab = gameObject;
}
// start server and connect clients
NetcodeIntegrationTestHelpers.Start(false, server, clients);
// wait for connection on client side
yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(clients);
// wait for connection on server side
yield return NetcodeIntegrationTestHelpers.WaitForClientConnectedToServer(server);
// disconnect the remote client
m_ClientDisconnected = false;
server.OnClientDisconnectCallback += OnClientDisconnectCallback;
var serverSideClientPlayer = server.ConnectedClients[clients[0].LocalClientId].PlayerObject;
// Stopping the client is the same as the client disconnecting
NetcodeIntegrationTestHelpers.StopOneClient(clients[0]);
var timeoutHelper = new TimeoutHelper();
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => m_ClientDisconnected, timeoutHelper);
// ensure the object was destroyed
Assert.True(serverSideClientPlayer.IsOwnedByServer, $"The client's player object's ownership was not transferred back to the server!");
// cleanup
NetcodeIntegrationTestHelpers.Destroy();
}
}
}

View File

@@ -2,7 +2,6 @@
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using Unity.Multiplayer.Tools.MetricTypes;
using Unity.Netcode.TestHelpers.Runtime;

View File

@@ -5,7 +5,6 @@ using NUnit.Framework;
using Unity.Multiplayer.Tools.NetStats;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
using Unity.Netcode.TestHelpers.Runtime.Metrics;
namespace Unity.Netcode.RuntimeTests.Metrics
{

View File

@@ -1,5 +1,4 @@
#if MULTIPLAYER_TOOLS
using System;
using System.Collections;
using System.Linq;
using NUnit.Framework;
@@ -36,7 +35,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
private NetworkObject SpawnNetworkObject()
{
// Spawn another network object so we can hide multiple.
var gameObject = UnityEngine.Object.Instantiate(m_NewNetworkPrefab); // new GameObject(NewNetworkObjectName);
var gameObject = Object.Instantiate(m_NewNetworkPrefab); // new GameObject(NewNetworkObjectName);
var networkObject = gameObject.GetComponent<NetworkObject>();
networkObject.NetworkManagerOwner = Server;
networkObject.Spawn();
@@ -62,6 +61,13 @@ namespace Unity.Netcode.RuntimeTests.Metrics
var ownershipChangeSent = metricValues.First();
Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeSent.NetworkId.NetworkId);
Assert.AreEqual(Server.LocalClientId, ownershipChangeSent.Connection.Id);
Assert.AreEqual(0, ownershipChangeSent.BytesCount);
// The first metric is to the server(self), so its size is now correctly reported as 0.
// Let's check the last one instead, to have a valid value
ownershipChangeSent = metricValues.Last();
Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeSent.NetworkId.NetworkId);
Assert.AreEqual(Client.LocalClientId, ownershipChangeSent.Connection.Id);
Assert.AreEqual(FastBufferWriter.GetWriteSize<ChangeOwnershipMessage>() + k_MessageHeaderSize, ownershipChangeSent.BytesCount);
}

View File

@@ -1,7 +1,6 @@
#if MULTIPLAYER_TOOLS
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
using System;
using System.Collections;
using NUnit.Framework;
using Unity.Collections;
@@ -9,7 +8,9 @@ using Unity.Multiplayer.Tools.MetricTypes;
using Unity.Netcode.TestHelpers.Runtime;
using Unity.Netcode.TestHelpers.Runtime.Metrics;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
#if UTP_TRANSPORT_2_0_ABOVE
using Unity.Networking.Transport.Utilities;
#endif
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests.Metrics
@@ -18,16 +19,25 @@ namespace Unity.Netcode.RuntimeTests.Metrics
{
protected override int NumberOfClients => 1;
private readonly int m_PacketLossRate = 25;
private readonly int m_PacketLossRangeDelta = 5;
private readonly int m_PacketLossRangeDelta = 3;
private readonly int m_MessageSize = 200;
public PacketLossMetricsTests()
: base(HostOrServer.Server)
{}
{ }
protected override void OnServerAndClientsCreated()
{
var clientTransport = (UnityTransport)m_ClientNetworkManagers[0].NetworkConfig.NetworkTransport;
#if !UTP_TRANSPORT_2_0_ABOVE
clientTransport.SetDebugSimulatorParameters(0, 0, m_PacketLossRate);
#endif
// Determined through trial and error. With both UTP 1.2 and 2.0, this random seed
// results in an effective packet loss percentage between 22% and 28%. Future UTP
// updates may change the RNG call patterns and cause this test to fail, in which
// case the value should be modified again.
clientTransport.DebugSimulatorRandomSeed = 4;
base.OnServerAndClientsCreated();
}
@@ -41,11 +51,9 @@ namespace Unity.Netcode.RuntimeTests.Metrics
for (int i = 0; i < 1000; ++i)
{
using (var writer = new FastBufferWriter(sizeof(byte), Allocator.Persistent))
{
writer.WriteByteSafe(42);
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage("Test", m_ServerNetworkManager.ConnectedClientsIds, writer);
}
using var writer = new FastBufferWriter(m_MessageSize, Allocator.Persistent);
writer.WriteBytesSafe(new byte[m_MessageSize]);
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage("Test", m_ServerNetworkManager.ConnectedClientsIds, writer);
}
yield return waitForPacketLossMetric.WaitForMetricsReceived();
@@ -57,20 +65,26 @@ namespace Unity.Netcode.RuntimeTests.Metrics
[UnityTest]
public IEnumerator TrackPacketLossAsClient()
{
double packetLossRateMinRange = (m_PacketLossRate-m_PacketLossRangeDelta) / 100d;
double packetLossRateMinRange = (m_PacketLossRate - m_PacketLossRangeDelta) / 100d;
double packetLossRateMaxrange = (m_PacketLossRate + m_PacketLossRangeDelta) / 100d;
var clientNetworkManager = m_ClientNetworkManagers[0];
#if UTP_TRANSPORT_2_0_ABOVE
var clientTransport = (UnityTransport)clientNetworkManager.NetworkConfig.NetworkTransport;
clientTransport.NetworkDriver.CurrentSettings.TryGet<SimulatorUtility.Parameters>(out var parameters);
parameters.PacketDropPercentage = m_PacketLossRate;
clientTransport.NetworkDriver.ModifySimulatorStageParameters(parameters);
#endif
var waitForPacketLossMetric = new WaitForGaugeMetricValues((clientNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher,
NetworkMetricTypes.PacketLoss,
metric => packetLossRateMinRange <= metric && metric <= packetLossRateMaxrange);
for (int i = 0; i < 1000; ++i)
{
using (var writer = new FastBufferWriter(sizeof(byte), Allocator.Persistent))
{
writer.WriteByteSafe(42);
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage("Test", m_ServerNetworkManager.ConnectedClientsIds, writer);
}
using var writer = new FastBufferWriter(m_MessageSize, Allocator.Persistent);
writer.WriteBytesSafe(new byte[m_MessageSize]);
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage("Test", m_ServerNetworkManager.ConnectedClientsIds, writer);
}
yield return waitForPacketLossMetric.WaitForMetricsReceived();

View File

@@ -5,7 +5,6 @@ using NUnit.Framework;
using Unity.Collections;
using Unity.Multiplayer.Tools.MetricTypes;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
using Unity.Netcode.TestHelpers.Runtime.Metrics;
namespace Unity.Netcode.RuntimeTests.Metrics

View File

@@ -2,13 +2,14 @@
using System.Collections;
using System.Linq;
using NUnit.Framework;
using Unity.Collections;
using Unity.Multiplayer.Tools.MetricTypes;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime.Metrics;
namespace Unity.Netcode.RuntimeTests.Metrics
{
internal class RpcMetricsTests : SingleClientMetricTestBase
internal class RpcMetricsTests : DualClientMetricTestBase
{
protected override void OnCreatePlayerPrefab()
{
@@ -17,30 +18,79 @@ namespace Unity.Netcode.RuntimeTests.Metrics
}
[UnityTest]
public IEnumerator TrackRpcSentMetricOnServer()
public IEnumerator TrackRpcSentMetricOnServerToOnlyOneClientWithArray()
{
var waitForMetricValues = new WaitForEventMetricValues<RpcEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.RpcSent);
m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][Client.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc();
m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc(new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIds = new[] { FirstClient.LocalClientId }
}
});
yield return waitForMetricValues.WaitForMetricsReceived();
var serverRpcSentValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(2, serverRpcSentValues.Count); // Server will receive this, since it's host
Assert.AreEqual(1, serverRpcSentValues.Count);
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.Name == nameof(RpcTestComponent.MyClientRpc)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.NetworkBehaviourName == nameof(RpcTestComponent)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.BytesCount != 0));
Assert.AreEqual(FirstClient.LocalClientId, serverRpcSentValues.First().Connection.Id);
}
[UnityTest]
public IEnumerator TrackRpcSentMetricOnServerToOnlyOneClientWithNativeArray()
{
var waitForMetricValues = new WaitForEventMetricValues<RpcEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.RpcSent);
m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc(new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIdsNativeArray = new NativeArray<ulong>(new[] { FirstClient.LocalClientId }, Allocator.Temp)
}
});
yield return waitForMetricValues.WaitForMetricsReceived();
var serverRpcSentValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, serverRpcSentValues.Count);
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.Name == nameof(RpcTestComponent.MyClientRpc)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.NetworkBehaviourName == nameof(RpcTestComponent)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.BytesCount != 0));
Assert.AreEqual(FirstClient.LocalClientId, serverRpcSentValues.First().Connection.Id);
}
[UnityTest]
public IEnumerator TrackRpcSentMetricOnServerToAllClients()
{
var waitForMetricValues = new WaitForEventMetricValues<RpcEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.RpcSent);
m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc();
yield return waitForMetricValues.WaitForMetricsReceived();
var serverRpcSentValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(3, serverRpcSentValues.Count); // Server will receive this, since it's host
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.Name == nameof(RpcTestComponent.MyClientRpc)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.NetworkBehaviourName == nameof(RpcTestComponent)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.BytesCount != 0));
Assert.Contains(Server.LocalClientId, serverRpcSentValues.Select(x => x.Connection.Id).ToArray());
Assert.Contains(Client.LocalClientId, serverRpcSentValues.Select(x => x.Connection.Id).ToArray());
Assert.Contains(FirstClient.LocalClientId, serverRpcSentValues.Select(x => x.Connection.Id).ToArray());
Assert.Contains(SecondClient.LocalClientId, serverRpcSentValues.Select(x => x.Connection.Id).ToArray());
}
[UnityTest]
public IEnumerator TrackRpcSentMetricOnClient()
{
var waitForClientMetricsValues = new WaitForEventMetricValues<RpcEvent>(ClientMetrics.Dispatcher, NetworkMetricTypes.RpcSent);
var waitForClientMetricsValues = new WaitForEventMetricValues<RpcEvent>(FirstClientMetrics.Dispatcher, NetworkMetricTypes.RpcSent);
m_PlayerNetworkObjects[Client.LocalClientId][Client.LocalClientId].GetComponent<RpcTestComponent>().MyServerRpc();
m_PlayerNetworkObjects[FirstClient.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyServerRpc();
yield return waitForClientMetricsValues.WaitForMetricsReceived();
@@ -58,7 +108,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
public IEnumerator TrackRpcReceivedMetricOnServer()
{
var waitForServerMetricsValues = new WaitForEventMetricValues<RpcEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.RpcReceived);
m_PlayerNetworkObjects[Client.LocalClientId][Client.LocalClientId].GetComponent<RpcTestComponent>().MyServerRpc();
m_PlayerNetworkObjects[FirstClient.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyServerRpc();
yield return waitForServerMetricsValues.WaitForMetricsReceived();
@@ -66,7 +116,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
Assert.AreEqual(1, serverRpcReceivedValues.Count);
var rpcReceived = serverRpcReceivedValues.First();
Assert.AreEqual(Client.LocalClientId, rpcReceived.Connection.Id);
Assert.AreEqual(FirstClient.LocalClientId, rpcReceived.Connection.Id);
Assert.AreEqual(nameof(RpcTestComponent.MyServerRpc), rpcReceived.Name);
Assert.AreEqual(nameof(RpcTestComponent), rpcReceived.NetworkBehaviourName);
Assert.AreNotEqual(0, rpcReceived.BytesCount);
@@ -75,9 +125,9 @@ namespace Unity.Netcode.RuntimeTests.Metrics
[UnityTest]
public IEnumerator TrackRpcReceivedMetricOnClient()
{
var waitForClientMetricsValues = new WaitForEventMetricValues<RpcEvent>(ClientMetrics.Dispatcher, NetworkMetricTypes.RpcReceived);
var waitForClientMetricsValues = new WaitForEventMetricValues<RpcEvent>(FirstClientMetrics.Dispatcher, NetworkMetricTypes.RpcReceived);
m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][Client.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc();
m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc();
yield return waitForClientMetricsValues.WaitForMetricsReceived();

View File

@@ -1,7 +1,6 @@
#if MULTIPLAYER_TOOLS
using System;
using System.Collections;
using System.IO;
using NUnit.Framework;
using Unity.Collections;
using Unity.Multiplayer.Tools.MetricTypes;
@@ -15,7 +14,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
{
// Header is dynamically sized due to packing, will be 2 bytes for all test messages.
private const int k_MessageHeaderSize = 2;
static readonly long MessageOverhead = 8 + FastBufferWriter.GetWriteSize<BatchHeader>() + k_MessageHeaderSize;
private static readonly long k_MessageOverhead = 8 + FastBufferWriter.GetWriteSize<BatchHeader>() + k_MessageHeaderSize;
[UnityTest]
public IEnumerator TrackTotalNumberOfBytesSent()
@@ -42,7 +41,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
}
Assert.True(observer.Found);
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead, observer.Value);
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead, observer.Value);
}
[UnityTest]
@@ -72,7 +71,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
}
Assert.True(observer.Found);
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead, observer.Value);
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead, observer.Value);
}
private class TotalBytesObserver : IMetricObserver

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3acde7838205d4b09ae3a035554c51c5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -9,6 +9,11 @@ namespace Unity.Netcode.RuntimeTests
{
public class NetworkObjectNetworkClientOwnedObjectsTests : NetcodeIntegrationTest
{
private class DummyNetworkBehaviour : NetworkBehaviour
{
}
protected override int NumberOfClients => 1;
private NetworkPrefab m_NetworkPrefab;
protected override void OnServerAndClientsCreated()
@@ -16,6 +21,7 @@ namespace Unity.Netcode.RuntimeTests
// create prefab
var gameObject = new GameObject("ClientOwnedObject");
var networkObject = gameObject.AddComponent<NetworkObject>();
gameObject.AddComponent<DummyNetworkBehaviour>();
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
m_NetworkPrefab = (new NetworkPrefab()
@@ -39,7 +45,7 @@ namespace Unity.Netcode.RuntimeTests
serverObject.Spawn();
// Provide enough time for the client to receive and process the spawned message.
yield return s_DefaultWaitForTick;
yield return WaitForMessageReceived<CreateObjectMessage>(m_ClientNetworkManagers.ToList());
// The object is owned by server
Assert.False(m_ServerNetworkManager.SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId).Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
@@ -48,13 +54,71 @@ namespace Unity.Netcode.RuntimeTests
serverObject.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
// Provide enough time for the client to receive and process the change in ownership message.
yield return s_DefaultWaitForTick;
yield return WaitForMessageReceived<ChangeOwnershipMessage>(m_ClientNetworkManagers.ToList());
// Ensure it's now added to the list
yield return WaitForConditionOrTimeOut(() => m_ClientNetworkManagers[0].SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId).Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client to gain ownership!");
Assert.True(m_ClientNetworkManagers[0].SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId).Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
Assert.True(m_ServerNetworkManager.SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId).Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
}
[UnityTest]
public IEnumerator WhenOwnershipIsChanged_OwnershipValuesUpdateCorrectly()
{
NetworkObject serverObject = Object.Instantiate(m_NetworkPrefab.Prefab).GetComponent<NetworkObject>();
serverObject.NetworkManagerOwner = m_ServerNetworkManager;
serverObject.Spawn();
// Provide enough time for the client to receive and process the spawned message.
yield return WaitForMessageReceived<CreateObjectMessage>(m_ClientNetworkManagers.ToList());
// The object is owned by server
Assert.False(m_ServerNetworkManager.SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId).Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
// Change the ownership
serverObject.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
// Provide enough time for the client to receive and process the change in ownership message.
yield return WaitForMessageReceived<ChangeOwnershipMessage>(m_ClientNetworkManagers.ToList());
Assert.IsFalse(serverObject.IsOwner);
Assert.IsFalse(serverObject.IsOwnedByServer);
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, serverObject.OwnerClientId);
var serverBehaviour = serverObject.GetComponent<DummyNetworkBehaviour>();
Assert.IsFalse(serverBehaviour.IsOwner);
Assert.IsFalse(serverBehaviour.IsOwnedByServer);
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, serverBehaviour.OwnerClientId);
var clientObject = Object.FindObjectsOfType<NetworkObject>().Where((obj) => obj.NetworkManagerOwner == m_ClientNetworkManagers[0]).FirstOrDefault();
Assert.IsNotNull(clientObject);
Assert.IsTrue(clientObject.IsOwner);
Assert.IsFalse(clientObject.IsOwnedByServer);
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, clientObject.OwnerClientId);
var clientBehaviour = clientObject.GetComponent<DummyNetworkBehaviour>();
Assert.IsTrue(clientBehaviour.IsOwner);
Assert.IsFalse(clientBehaviour.IsOwnedByServer);
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, clientBehaviour.OwnerClientId);
serverObject.RemoveOwnership();
// Provide enough time for the client to receive and process the change in ownership message.
yield return WaitForMessageReceived<ChangeOwnershipMessage>(m_ClientNetworkManagers.ToList());
Assert.IsTrue(serverObject.IsOwner);
Assert.IsTrue(serverObject.IsOwnedByServer);
Assert.AreEqual(NetworkManager.ServerClientId, serverObject.OwnerClientId);
Assert.IsTrue(serverBehaviour.IsOwner);
Assert.IsTrue(serverBehaviour.IsOwnedByServer);
Assert.AreEqual(NetworkManager.ServerClientId, serverBehaviour.OwnerClientId);
Assert.IsFalse(clientObject.IsOwner);
Assert.IsTrue(clientObject.IsOwnedByServer);
Assert.AreEqual(NetworkManager.ServerClientId, clientObject.OwnerClientId);
Assert.IsFalse(clientBehaviour.IsOwner);
Assert.IsTrue(clientBehaviour.IsOwnedByServer);
Assert.AreEqual(NetworkManager.ServerClientId, clientBehaviour.OwnerClientId);
}
}
}

View File

@@ -17,6 +17,7 @@ namespace Unity.Netcode.RuntimeTests
{
public static List<ShowHideObject> ClientTargetedNetworkObjects = new List<ShowHideObject>();
public static ulong ClientIdToTarget;
public static bool Silent;
public static NetworkObject GetNetworkObjectById(ulong networkObjectId)
{
@@ -36,6 +37,17 @@ namespace Unity.Netcode.RuntimeTests
{
ClientTargetedNetworkObjects.Add(this);
}
if (IsServer)
{
MyListSetOnSpawn.Add(45);
}
else
{
Debug.Assert(MyListSetOnSpawn.Count == 1);
Debug.Assert(MyListSetOnSpawn[0] == 45);
}
base.OnNetworkSpawn();
}
@@ -49,16 +61,22 @@ namespace Unity.Netcode.RuntimeTests
}
public NetworkVariable<int> MyNetworkVariable;
public NetworkList<int> MyListSetOnSpawn;
private void Awake()
{
MyNetworkVariable = new NetworkVariable<int>();
MyNetworkVariable.OnValueChanged += Changed;
MyListSetOnSpawn = new NetworkList<int>();
}
public void Changed(int before, int after)
{
Debug.Log($"Value changed from {before} to {after}");
if (!Silent)
{
Debug.Log($"Value changed from {before} to {after}");
}
}
}
@@ -264,5 +282,61 @@ namespace Unity.Netcode.RuntimeTests
yield return CheckVisible(true);
}
}
[UnityTest]
public IEnumerator NetworkHideDespawnTest()
{
m_ClientId0 = m_ClientNetworkManagers[0].LocalClientId;
ShowHideObject.ClientTargetedNetworkObjects.Clear();
ShowHideObject.ClientIdToTarget = m_ClientId0;
ShowHideObject.Silent = true;
var spawnedObject1 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
var spawnedObject2 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
var spawnedObject3 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
m_NetSpawnedObject1 = spawnedObject1.GetComponent<NetworkObject>();
m_NetSpawnedObject2 = spawnedObject2.GetComponent<NetworkObject>();
m_NetSpawnedObject3 = spawnedObject3.GetComponent<NetworkObject>();
m_NetSpawnedObject1.GetComponent<ShowHideObject>().MyNetworkVariable.Value++;
m_NetSpawnedObject1.NetworkHide(m_ClientId0);
m_NetSpawnedObject1.Despawn();
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 5);
LogAssert.NoUnexpectedReceived();
}
[UnityTest]
public IEnumerator NetworkHideChangeOwnership()
{
ShowHideObject.ClientTargetedNetworkObjects.Clear();
ShowHideObject.ClientIdToTarget = m_ClientNetworkManagers[1].LocalClientId;
ShowHideObject.Silent = true;
var spawnedObject1 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
m_NetSpawnedObject1 = spawnedObject1.GetComponent<NetworkObject>();
m_NetSpawnedObject1.GetComponent<ShowHideObject>().MyNetworkVariable.Value++;
// Hide an object to a client
m_NetSpawnedObject1.NetworkHide(m_ClientNetworkManagers[1].LocalClientId);
yield return WaitForConditionOrTimeOut(() => ShowHideObject.ClientTargetedNetworkObjects.Count == 0);
// Change ownership while the object is hidden to some
m_NetSpawnedObject1.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
// The two-second wait is actually needed as there's a potential warning of unhandled message after 1 second
yield return new WaitForSeconds(1.25f);
LogAssert.NoUnexpectedReceived();
// Show the object again to check nothing unexpected happens
m_NetSpawnedObject1.NetworkShow(m_ClientNetworkManagers[1].LocalClientId);
yield return WaitForConditionOrTimeOut(() => ShowHideObject.ClientTargetedNetworkObjects.Count == 1);
Assert.True(ShowHideObject.ClientTargetedNetworkObjects[0].OwnerClientId == m_ClientNetworkManagers[0].LocalClientId);
}
}
}

View File

@@ -4,15 +4,87 @@ using UnityEngine;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(TransformSpace.World)]
[TestFixture(TransformSpace.Local)]
public class NetworkTransformStateTests
{
[Test]
public void TestSyncAxes(
[Values] bool inLocalSpace,
[Values] bool syncPosX, [Values] bool syncPosY, [Values] bool syncPosZ,
[Values] bool syncRotX, [Values] bool syncRotY, [Values] bool syncRotZ,
[Values] bool syncScaX, [Values] bool syncScaY, [Values] bool syncScaZ)
public enum SyncAxis
{
SyncPosX,
SyncPosY,
SyncPosZ,
SyncPosXY,
SyncPosXZ,
SyncPosYZ,
SyncPosXYZ,
SyncRotX,
SyncRotY,
SyncRotZ,
SyncRotXY,
SyncRotXZ,
SyncRotYZ,
SyncRotXYZ,
SyncScaleX,
SyncScaleY,
SyncScaleZ,
SyncScaleXY,
SyncScaleXZ,
SyncScaleYZ,
SyncScaleXYZ,
SyncAllX,
SyncAllY,
SyncAllZ,
SyncAllXY,
SyncAllXZ,
SyncAllYZ,
SyncAllXYZ
}
public enum TransformSpace
{
World,
Local
}
public enum SynchronizationType
{
Delta,
Teleport
}
private TransformSpace m_TransformSpace;
public NetworkTransformStateTests(TransformSpace transformSpace)
{
m_TransformSpace = transformSpace;
}
private bool WillAnAxisBeSynchronized(ref NetworkTransform networkTransform)
{
return networkTransform.SyncScaleX || networkTransform.SyncScaleY || networkTransform.SyncScaleZ ||
networkTransform.SyncRotAngleX || networkTransform.SyncRotAngleY || networkTransform.SyncRotAngleZ ||
networkTransform.SyncPositionX || networkTransform.SyncPositionY || networkTransform.SyncPositionZ;
}
[Test]
public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Values] SyncAxis syncAxis)
{
bool inLocalSpace = m_TransformSpace == TransformSpace.Local;
bool isTeleporting = synchronizationType == SynchronizationType.Teleport;
bool syncPosX = syncAxis == SyncAxis.SyncPosX || syncAxis == SyncAxis.SyncPosXY || syncAxis == SyncAxis.SyncPosXZ || syncAxis == SyncAxis.SyncPosXYZ || syncAxis == SyncAxis.SyncAllX || syncAxis == SyncAxis.SyncAllXY || syncAxis == SyncAxis.SyncAllXZ || syncAxis == SyncAxis.SyncAllXYZ;
bool syncPosY = syncAxis == SyncAxis.SyncPosY || syncAxis == SyncAxis.SyncPosXY || syncAxis == SyncAxis.SyncPosYZ || syncAxis == SyncAxis.SyncPosXYZ || syncAxis == SyncAxis.SyncAllY || syncAxis == SyncAxis.SyncAllXY || syncAxis == SyncAxis.SyncAllYZ || syncAxis == SyncAxis.SyncAllXYZ;
bool syncPosZ = syncAxis == SyncAxis.SyncPosZ || syncAxis == SyncAxis.SyncPosXZ || syncAxis == SyncAxis.SyncPosYZ || syncAxis == SyncAxis.SyncPosXYZ || syncAxis == SyncAxis.SyncAllZ || syncAxis == SyncAxis.SyncAllXZ || syncAxis == SyncAxis.SyncAllYZ || syncAxis == SyncAxis.SyncAllXYZ;
bool syncRotX = syncAxis == SyncAxis.SyncRotX || syncAxis == SyncAxis.SyncRotXY || syncAxis == SyncAxis.SyncRotXZ || syncAxis == SyncAxis.SyncRotXYZ || syncAxis == SyncAxis.SyncRotX || syncAxis == SyncAxis.SyncAllXY || syncAxis == SyncAxis.SyncAllXZ || syncAxis == SyncAxis.SyncAllXYZ;
bool syncRotY = syncAxis == SyncAxis.SyncRotY || syncAxis == SyncAxis.SyncRotXY || syncAxis == SyncAxis.SyncRotYZ || syncAxis == SyncAxis.SyncRotXYZ || syncAxis == SyncAxis.SyncRotY || syncAxis == SyncAxis.SyncAllXY || syncAxis == SyncAxis.SyncAllYZ || syncAxis == SyncAxis.SyncAllXYZ;
bool syncRotZ = syncAxis == SyncAxis.SyncRotZ || syncAxis == SyncAxis.SyncRotXZ || syncAxis == SyncAxis.SyncRotYZ || syncAxis == SyncAxis.SyncRotXYZ || syncAxis == SyncAxis.SyncRotZ || syncAxis == SyncAxis.SyncAllXZ || syncAxis == SyncAxis.SyncAllYZ || syncAxis == SyncAxis.SyncAllXYZ;
bool syncScaX = syncAxis == SyncAxis.SyncScaleX || syncAxis == SyncAxis.SyncScaleXY || syncAxis == SyncAxis.SyncScaleXZ || syncAxis == SyncAxis.SyncScaleXYZ || syncAxis == SyncAxis.SyncAllX || syncAxis == SyncAxis.SyncAllXY || syncAxis == SyncAxis.SyncAllXZ || syncAxis == SyncAxis.SyncAllXYZ;
bool syncScaY = syncAxis == SyncAxis.SyncScaleY || syncAxis == SyncAxis.SyncScaleXY || syncAxis == SyncAxis.SyncScaleYZ || syncAxis == SyncAxis.SyncScaleXYZ || syncAxis == SyncAxis.SyncAllY || syncAxis == SyncAxis.SyncAllXY || syncAxis == SyncAxis.SyncAllYZ || syncAxis == SyncAxis.SyncAllXYZ;
bool syncScaZ = syncAxis == SyncAxis.SyncScaleZ || syncAxis == SyncAxis.SyncScaleXZ || syncAxis == SyncAxis.SyncScaleYZ || syncAxis == SyncAxis.SyncScaleXYZ || syncAxis == SyncAxis.SyncAllZ || syncAxis == SyncAxis.SyncAllXZ || syncAxis == SyncAxis.SyncAllYZ || syncAxis == SyncAxis.SyncAllXYZ;
var gameObject = new GameObject($"Test-{nameof(NetworkTransformStateTests)}.{nameof(TestSyncAxes)}");
var networkObject = gameObject.AddComponent<NetworkObject>();
var networkTransform = gameObject.AddComponent<NetworkTransform>();
@@ -36,27 +108,13 @@ namespace Unity.Netcode.RuntimeTests
networkTransform.SyncScaleZ = syncScaZ;
networkTransform.InLocalSpace = inLocalSpace;
// We want a relatively clean networkTransform state before we try to apply the transform to it
// We only preserve InLocalSpace and IsTeleportingNextFrame properties as they are the only things
// needed when applying a transform to a NetworkTransformState
var networkTransformState = new NetworkTransform.NetworkTransformState
{
PositionX = initialPosition.x,
PositionY = initialPosition.y,
PositionZ = initialPosition.z,
RotAngleX = initialRotAngles.x,
RotAngleY = initialRotAngles.y,
RotAngleZ = initialRotAngles.z,
ScaleX = initialScale.x,
ScaleY = initialScale.y,
ScaleZ = initialScale.z,
HasPositionX = syncPosX,
HasPositionY = syncPosY,
HasPositionZ = syncPosZ,
HasRotAngleX = syncRotX,
HasRotAngleY = syncRotY,
HasRotAngleZ = syncRotZ,
HasScaleX = syncScaX,
HasScaleY = syncScaY,
HasScaleZ = syncScaZ,
InLocalSpace = inLocalSpace
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
// Step 1: change properties, expect state to be dirty
@@ -71,95 +129,382 @@ namespace Unity.Netcode.RuntimeTests
}
}
// Step 2: disable a particular sync flag, expect state to be not dirty
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
var position = networkTransform.transform.position;
var rotAngles = networkTransform.transform.eulerAngles;
var scale = networkTransform.transform.localScale;
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
var position = networkTransform.transform.position;
var rotAngles = networkTransform.transform.eulerAngles;
var scale = networkTransform.transform.localScale;
// Step 2: Verify the state changes in a tick are additive
// TODO: This will need to change if we update NetworkTransform to send all of the
// axis deltas that happened over a tick as a collection instead of collapsing them
// as the changes are detected.
{
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
// SyncPositionX
if (syncPosX)
{
position.x++;
networkTransform.transform.position = position;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX);
}
// SyncPositionY
if (syncPosY)
{
position = networkTransform.transform.position;
position.y++;
networkTransform.transform.position = position;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX || !syncPosX);
Assert.IsTrue(networkTransformState.HasPositionY);
}
// SyncPositionZ
if (syncPosZ)
{
position = networkTransform.transform.position;
position.z++;
networkTransform.transform.position = position;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX || !syncPosX);
Assert.IsTrue(networkTransformState.HasPositionY || !syncPosY);
Assert.IsTrue(networkTransformState.HasPositionZ);
}
// SyncRotAngleX
if (syncRotX)
{
rotAngles = networkTransform.transform.eulerAngles;
rotAngles.x++;
networkTransform.transform.eulerAngles = rotAngles;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX || !syncPosX);
Assert.IsTrue(networkTransformState.HasPositionY || !syncPosY);
Assert.IsTrue(networkTransformState.HasPositionZ || !syncPosZ);
Assert.IsTrue(networkTransformState.HasRotAngleX);
}
// SyncRotAngleY
if (syncRotY)
{
rotAngles = networkTransform.transform.eulerAngles;
rotAngles.y++;
networkTransform.transform.eulerAngles = rotAngles;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX || !syncPosX);
Assert.IsTrue(networkTransformState.HasPositionY || !syncPosY);
Assert.IsTrue(networkTransformState.HasPositionZ || !syncPosZ);
Assert.IsTrue(networkTransformState.HasRotAngleX || !syncRotX);
Assert.IsTrue(networkTransformState.HasRotAngleY);
}
// SyncRotAngleZ
if (syncRotZ)
{
rotAngles = networkTransform.transform.eulerAngles;
rotAngles.z++;
networkTransform.transform.eulerAngles = rotAngles;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX || !syncPosX);
Assert.IsTrue(networkTransformState.HasPositionY || !syncPosY);
Assert.IsTrue(networkTransformState.HasPositionZ || !syncPosZ);
Assert.IsTrue(networkTransformState.HasRotAngleX || !syncRotX);
Assert.IsTrue(networkTransformState.HasRotAngleY || !syncRotY);
Assert.IsTrue(networkTransformState.HasRotAngleZ);
}
// SyncScaleX
if (syncScaX)
{
scale = networkTransform.transform.localScale;
scale.x++;
networkTransform.transform.localScale = scale;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX || !syncPosX);
Assert.IsTrue(networkTransformState.HasPositionY || !syncPosY);
Assert.IsTrue(networkTransformState.HasPositionZ || !syncPosZ);
Assert.IsTrue(networkTransformState.HasRotAngleX || !syncRotX);
Assert.IsTrue(networkTransformState.HasRotAngleY || !syncRotY);
Assert.IsTrue(networkTransformState.HasRotAngleZ || !syncRotZ);
Assert.IsTrue(networkTransformState.HasScaleX);
}
// SyncScaleY
if (syncScaY)
{
scale = networkTransform.transform.localScale;
scale.y++;
networkTransform.transform.localScale = scale;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX || !syncPosX);
Assert.IsTrue(networkTransformState.HasPositionY || !syncPosY);
Assert.IsTrue(networkTransformState.HasPositionZ || !syncPosZ);
Assert.IsTrue(networkTransformState.HasRotAngleX || !syncRotX);
Assert.IsTrue(networkTransformState.HasRotAngleY || !syncRotY);
Assert.IsTrue(networkTransformState.HasRotAngleZ || !syncRotZ);
Assert.IsTrue(networkTransformState.HasScaleX || !syncScaX);
Assert.IsTrue(networkTransformState.HasScaleY);
}
// SyncScaleZ
if (syncScaZ)
{
scale = networkTransform.transform.localScale;
scale.z++;
networkTransform.transform.localScale = scale;
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsTrue(networkTransformState.HasPositionX || !syncPosX);
Assert.IsTrue(networkTransformState.HasPositionY || !syncPosY);
Assert.IsTrue(networkTransformState.HasPositionZ || !syncPosZ);
Assert.IsTrue(networkTransformState.HasRotAngleX || !syncRotX);
Assert.IsTrue(networkTransformState.HasRotAngleY || !syncRotY);
Assert.IsTrue(networkTransformState.HasRotAngleZ || !syncRotZ);
Assert.IsTrue(networkTransformState.HasScaleX || !syncScaX);
Assert.IsTrue(networkTransformState.HasScaleY || !syncScaY);
Assert.IsTrue(networkTransformState.HasScaleZ);
}
}
// Step 3: disable a particular sync flag, expect state to be not dirty
// We do this last because it changes which axis will be synchronized.
{
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
position = networkTransform.transform.position;
rotAngles = networkTransform.transform.eulerAngles;
scale = networkTransform.transform.localScale;
// SyncPositionX
if (syncPosX)
{
networkTransform.SyncPositionX = false;
position.x++;
networkTransform.transform.position = position;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
// If we are synchronizing more than 1 axis (teleporting impacts this too)
if (syncAxis != SyncAxis.SyncPosX && WillAnAxisBeSynchronized(ref networkTransform))
{
// For the x axis position value We should expect the state to still be considered dirty (more than one axis is being synchronized and we are teleporting)
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
// However, we expect it to not have applied the position x delta
Assert.IsFalse(networkTransformState.HasPositionX);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
// SyncPositionY
if (syncPosY)
{
networkTransform.SyncPositionY = false;
position.y++;
networkTransform.transform.position = position;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
if (syncAxis != SyncAxis.SyncPosY && WillAnAxisBeSynchronized(ref networkTransform))
{
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsFalse(networkTransformState.HasPositionY);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
// SyncPositionZ
if (syncPosZ)
{
networkTransform.SyncPositionZ = false;
position.z++;
networkTransform.transform.position = position;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
if (syncAxis != SyncAxis.SyncPosZ && WillAnAxisBeSynchronized(ref networkTransform))
{
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsFalse(networkTransformState.HasPositionZ);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
// SyncRotAngleX
if (syncRotX)
{
networkTransform.SyncRotAngleX = false;
rotAngles.x++;
networkTransform.transform.eulerAngles = rotAngles;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
if (syncAxis != SyncAxis.SyncRotX && WillAnAxisBeSynchronized(ref networkTransform))
{
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsFalse(networkTransformState.HasRotAngleX);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
// SyncRotAngleY
if (syncRotY)
{
networkTransform.SyncRotAngleY = false;
rotAngles.y++;
networkTransform.transform.eulerAngles = rotAngles;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
if (syncAxis != SyncAxis.SyncRotY && WillAnAxisBeSynchronized(ref networkTransform))
{
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsFalse(networkTransformState.HasRotAngleY);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
// SyncRotAngleZ
if (syncRotZ)
{
networkTransform.SyncRotAngleZ = false;
rotAngles.z++;
networkTransform.transform.eulerAngles = rotAngles;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
if (syncAxis != SyncAxis.SyncRotZ && WillAnAxisBeSynchronized(ref networkTransform))
{
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsFalse(networkTransformState.HasRotAngleZ);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
// SyncScaleX
if (syncScaX)
{
networkTransform.SyncScaleX = false;
scale.x++;
networkTransform.transform.localScale = scale;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
if (syncAxis != SyncAxis.SyncScaleX && WillAnAxisBeSynchronized(ref networkTransform))
{
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsFalse(networkTransformState.HasScaleX);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
// SyncScaleY
if (syncScaY)
{
networkTransform.SyncScaleY = false;
scale.y++;
networkTransform.transform.localScale = scale;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
if (syncAxis != SyncAxis.SyncScaleY && WillAnAxisBeSynchronized(ref networkTransform))
{
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsFalse(networkTransformState.HasScaleY);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
// SyncScaleZ
if (syncScaZ)
{
networkTransform.SyncScaleZ = false;
scale.z++;
networkTransform.transform.localScale = scale;
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
if (syncAxis != SyncAxis.SyncScaleZ && WillAnAxisBeSynchronized(ref networkTransform))
{
// We want to start with a fresh NetworkTransformState since it could have other state
// information from the last time we applied the transform
networkTransformState = new NetworkTransform.NetworkTransformState
{
InLocalSpace = inLocalSpace,
IsTeleportingNextFrame = isTeleporting,
};
Assert.IsTrue(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
Assert.IsFalse(networkTransformState.HasScaleZ);
}
else
{
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
}
}
}
Object.DestroyImmediate(gameObject);
@@ -168,11 +513,11 @@ namespace Unity.Netcode.RuntimeTests
[Test]
public void TestThresholds(
[Values] bool inLocalSpace,
[Values(NetworkTransform.PositionThresholdDefault, 1.0f)] float positionThreshold,
[Values(NetworkTransform.RotAngleThresholdDefault, 1.0f)] float rotAngleThreshold,
[Values(NetworkTransform.ScaleThresholdDefault, 0.5f)] float scaleThreshold)
{
var inLocalSpace = m_TransformSpace == TransformSpace.Local;
var gameObject = new GameObject($"Test-{nameof(NetworkTransformStateTests)}.{nameof(TestThresholds)}");
var networkTransform = gameObject.AddComponent<NetworkTransform>();
networkTransform.enabled = false; // do not tick `FixedUpdate()` or `Update()`

View File

@@ -189,7 +189,8 @@ namespace Unity.Netcode.RuntimeTests
public enum OverrideState
{
Update,
CommitToTransform
CommitToTransform,
SetState
}
/// <summary>
@@ -299,9 +300,8 @@ namespace Unity.Netcode.RuntimeTests
/// parented under another NetworkTransform
/// </summary>
[UnityTest]
public IEnumerator NetworkTransformParentedLocalSpaceTest([Values] Interpolation interpolation, [Values] OverrideState overideState)
public IEnumerator NetworkTransformParentedLocalSpaceTest([Values] Interpolation interpolation)
{
var overrideUpdate = overideState == OverrideState.CommitToTransform;
m_AuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
m_NonAuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
var authoritativeChildObject = SpawnObject(m_ChildObjectToBeParented.gameObject, m_AuthoritativeTransform.NetworkManager);
@@ -334,15 +334,28 @@ namespace Unity.Netcode.RuntimeTests
/// Validates that moving, rotating, and scaling the authority side with a single
/// tick will properly synchronize the non-authoritative side with the same values.
/// </summary>
private IEnumerator MoveRotateAndScaleAuthority(Vector3 position, Vector3 rotation, Vector3 scale)
private IEnumerator MoveRotateAndScaleAuthority(Vector3 position, Vector3 rotation, Vector3 scale, OverrideState overrideState)
{
m_AuthoritativeTransform.transform.position = position;
yield return null;
var authoritativeRotation = m_AuthoritativeTransform.transform.rotation;
authoritativeRotation.eulerAngles = rotation;
m_AuthoritativeTransform.transform.rotation = authoritativeRotation;
yield return null;
m_AuthoritativeTransform.transform.localScale = scale;
switch (overrideState)
{
case OverrideState.SetState:
{
m_AuthoritativeTransform.SetState(position, Quaternion.Euler(rotation), scale);
break;
}
case OverrideState.Update:
default:
{
m_AuthoritativeTransform.transform.position = position;
yield return null;
var authoritativeRotation = m_AuthoritativeTransform.transform.rotation;
authoritativeRotation.eulerAngles = rotation;
m_AuthoritativeTransform.transform.rotation = authoritativeRotation;
yield return null;
m_AuthoritativeTransform.transform.localScale = scale;
break;
}
}
}
/// <summary>
@@ -400,7 +413,6 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator NetworkTransformMultipleChangesOverTime([Values] TransformSpace testLocalTransform, [Values] OverrideState overideState)
{
var overrideUpdate = overideState == OverrideState.CommitToTransform;
m_AuthoritativeTransform.InLocalSpace = testLocalTransform == TransformSpace.Local;
var positionStart = new Vector3(1.0f, 0.5f, 2.0f);
@@ -422,7 +434,7 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForNextTick();
// Apply deltas
MoveRotateAndScaleAuthority(position, rotation, scale);
MoveRotateAndScaleAuthority(position, rotation, scale, overideState);
// Wait for deltas to synchronize on non-authoritative side
yield return WaitForPositionRotationAndScaleToMatch(4);
@@ -455,7 +467,7 @@ namespace Unity.Netcode.RuntimeTests
// to apply both deltas within the same tick period.
yield return WaitForNextTick();
MoveRotateAndScaleAuthority(position, rotation, scale);
MoveRotateAndScaleAuthority(position, rotation, scale, overideState);
yield return WaitForPositionRotationAndScaleToMatch(4);
}
@@ -471,7 +483,7 @@ namespace Unity.Netcode.RuntimeTests
rotation = rotationStart * i;
scale = scaleStart * i;
MoveRotateAndScaleAuthority(position, rotation, scale);
MoveRotateAndScaleAuthority(position, rotation, scale, overideState);
}
yield return WaitForPositionRotationAndScaleToMatch(1);
@@ -486,7 +498,7 @@ namespace Unity.Netcode.RuntimeTests
position = positionStart * i;
rotation = rotationStart * i;
scale = scaleStart * i;
MoveRotateAndScaleAuthority(position, rotation, scale);
MoveRotateAndScaleAuthority(position, rotation, scale, overideState);
}
yield return WaitForPositionRotationAndScaleToMatch(1);
}
@@ -513,11 +525,16 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(Vector3.zero, m_NonAuthoritativeTransform.transform.position, "server side pos should be zero at first"); // sanity check
authPlayerTransform.position = new Vector3(10, 20, 30);
if (overrideUpdate)
var nextPosition = new Vector3(10, 20, 30);
if (overideState != OverrideState.SetState)
{
authPlayerTransform.position = nextPosition;
m_OwnerTransform.CommitToTransform();
}
else
{
m_OwnerTransform.SetState(nextPosition, null, null, m_AuthoritativeTransform.Interpolate);
}
yield return WaitForConditionOrTimeOut(PositionsMatch);
AssertOnTimeout($"Timed out waiting for positions to match");
@@ -525,20 +542,30 @@ namespace Unity.Netcode.RuntimeTests
// test rotation
Assert.AreEqual(Quaternion.identity, m_NonAuthoritativeTransform.transform.rotation, "wrong initial value for rotation"); // sanity check
authPlayerTransform.rotation = Quaternion.Euler(45, 40, 35); // using euler angles instead of quaternions directly to really see issues users might encounter
if (overrideUpdate)
var nextRotation = Quaternion.Euler(45, 40, 35); // using euler angles instead of quaternions directly to really see issues users might encounter
if (overideState != OverrideState.SetState)
{
authPlayerTransform.rotation = nextRotation;
m_OwnerTransform.CommitToTransform();
}
else
{
m_OwnerTransform.SetState(null, nextRotation, null, m_AuthoritativeTransform.Interpolate);
}
yield return WaitForConditionOrTimeOut(RotationsMatch);
AssertOnTimeout($"Timed out waiting for rotations to match");
authPlayerTransform.localScale = new Vector3(2, 3, 4);
var nextScale = new Vector3(2, 3, 4);
if (overrideUpdate)
{
authPlayerTransform.localScale = nextScale;
m_OwnerTransform.CommitToTransform();
}
else
{
m_OwnerTransform.SetState(null, null, nextScale, m_AuthoritativeTransform.Interpolate);
}
yield return WaitForConditionOrTimeOut(ScaleValuesMatch);
AssertOnTimeout($"Timed out waiting for scale values to match");

View File

@@ -17,6 +17,57 @@ namespace Unity.Netcode.RuntimeTests
public NetworkVariable<Vector3> OwnerReadWrite_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Owner);
}
// The ILPP code for NetworkVariables to determine how to serialize them relies on them existing as fields of a NetworkBehaviour to find them.
// Some of the tests below create NetworkVariables on the stack, so this class is here just to make sure the relevant types are all accounted for.
public class NetVarILPPClassForTests : NetworkBehaviour
{
public NetworkVariable<UnmanagedNetworkSerializableType> UnmanagedNetworkSerializableTypeVar;
public NetworkVariable<ManagedNetworkSerializableType> ManagedNetworkSerializableTypeVar;
public NetworkVariable<string> StringVar;
public NetworkVariable<Guid> GuidVar;
}
public class TemplateNetworkBehaviourType<T> : NetworkBehaviour
{
public NetworkVariable<T> TheVar;
}
public class ClassHavingNetworkBehaviour : TemplateNetworkBehaviourType<TestClass>
{
}
// Please do not reference TestClass2 anywhere other than here!
public class ClassHavingNetworkBehaviour2 : TemplateNetworkBehaviourType<TestClass_ReferencedOnlyByTemplateNetworkBehavourType>
{
}
public class StructHavingNetworkBehaviour : TemplateNetworkBehaviourType<TestStruct>
{
}
public struct StructUsedOnlyInNetworkList : IEquatable<StructUsedOnlyInNetworkList>, INetworkSerializeByMemcpy
{
public int Value;
public bool Equals(StructUsedOnlyInNetworkList other)
{
return Value == other.Value;
}
public override bool Equals(object obj)
{
return obj is StructUsedOnlyInNetworkList other && Equals(other);
}
public override int GetHashCode()
{
return Value;
}
}
[TestFixtureSource(nameof(TestDataSource))]
public class NetworkVariablePermissionTests : NetcodeIntegrationTest
{
@@ -344,6 +395,53 @@ namespace Unity.Netcode.RuntimeTests
}
}
public class TestClass : INetworkSerializable, IEquatable<TestClass>
{
public uint SomeInt;
public bool SomeBool;
public static bool NetworkSerializeCalledOnWrite;
public static bool NetworkSerializeCalledOnRead;
public void NetworkSerialize<T>(BufferSerializer<T> 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
{
}
public class NetworkVariableTest : NetworkBehaviour
{
public enum SomeEnum
@@ -355,6 +453,7 @@ namespace Unity.Netcode.RuntimeTests
public readonly NetworkVariable<int> TheScalar = new NetworkVariable<int>();
public readonly NetworkVariable<SomeEnum> TheEnum = new NetworkVariable<SomeEnum>();
public readonly NetworkList<int> TheList = new NetworkList<int>();
public readonly NetworkList<StructUsedOnlyInNetworkList> TheStructList = new NetworkList<StructUsedOnlyInNetworkList>();
public readonly NetworkList<FixedString128Bytes> TheLargeList = new NetworkList<FixedString128Bytes>();
public readonly NetworkVariable<FixedString32Bytes> FixedString32 = new NetworkVariable<FixedString32Bytes>();
@@ -370,7 +469,10 @@ namespace Unity.Netcode.RuntimeTests
}
public readonly NetworkVariable<TestStruct> TheStruct = new NetworkVariable<TestStruct>();
public readonly NetworkList<TestStruct> TheListOfStructs = new NetworkList<TestStruct>();
public readonly NetworkVariable<TestClass> TheClass = new NetworkVariable<TestClass>();
public NetworkVariable<UnmanagedTemplateNetworkSerializableType<TestStruct>> TheTemplateStruct = new NetworkVariable<UnmanagedTemplateNetworkSerializableType<TestStruct>>();
public NetworkVariable<ManagedTemplateNetworkSerializableType<TestClass>> TheTemplateClass = new NetworkVariable<ManagedTemplateNetworkSerializableType<TestClass>>();
public bool ListDelegateTriggered;
@@ -433,6 +535,10 @@ namespace Unity.Netcode.RuntimeTests
s_ClientNetworkVariableTestInstances.Clear();
m_PlayerPrefab.AddComponent<NetworkVariableTest>();
m_PlayerPrefab.AddComponent<ClassHavingNetworkBehaviour>();
m_PlayerPrefab.AddComponent<ClassHavingNetworkBehaviour2>();
m_PlayerPrefab.AddComponent<StructHavingNetworkBehaviour>();
m_ServerNetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_EnsureLengthSafety;
m_ServerNetworkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
foreach (var client in m_ClientNetworkManagers)
@@ -517,6 +623,7 @@ namespace Unity.Netcode.RuntimeTests
networkVariableTestComponent.EnableTesting = false;
Assert.IsTrue(networkVariableTestComponent.DidAllValuesChange());
networkVariableTestComponent.AssertAllValuesAreCorrect();
// Disable this once we are done.
networkVariableTestComponent.gameObject.SetActive(false);
@@ -536,6 +643,23 @@ namespace Unity.Netcode.RuntimeTests
Assert.Throws<InvalidOperationException>(() => m_Player1OnClient1.TheScalar.Value = k_TestVal1);
}
/// <summary>
/// Runs tests that network variables sync on client whatever the local value of <see cref="Time.timeScale"/>.
/// </summary>
[UnityTest]
public IEnumerator NetworkVariableSync_WithDifferentTimeScale([Values(true, false)] bool useHost, [Values(0.0f, 1.0f, 2.0f)] float timeScale)
{
Time.timeScale = timeScale;
yield return InitializeServerAndClients(useHost);
m_Player1OnServer.TheScalar.Value = k_TestVal1;
// Now wait for the client side version to be updated to k_TestVal1
yield return WaitForConditionOrTimeOut(() => m_Player1OnClient1.TheScalar.Value == k_TestVal1);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client-side NetworkVariable to update!");
}
[UnityTest]
public IEnumerator FixedString32Test([Values(true, false)] bool useHost)
{
@@ -672,6 +796,63 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForConditionOrTimeOut(m_NetworkListPredicateHandler);
}
[UnityTest]
public IEnumerator TestNetworkVariableClass([Values(true, false)] bool useHost)
{
yield return 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.
yield return WaitForConditionOrTimeOut(VerifyClass);
}
[UnityTest]
public IEnumerator TestNetworkVariableTemplateClass([Values(true, false)] bool useHost)
{
yield return 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<TestClass> { 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.
yield return WaitForConditionOrTimeOut(VerifyClass);
}
[UnityTest]
public IEnumerator TestNetworkListStruct([Values(true, false)] bool useHost)
{
yield return 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.
yield return WaitForConditionOrTimeOut(VerifyList);
}
[UnityTest]
public IEnumerator TestNetworkVariableStruct([Values(true, false)] bool useHost)
{
@@ -683,13 +864,85 @@ namespace Unity.Netcode.RuntimeTests
m_Player1OnClient1.TheStruct.Value.SomeInt == m_Player1OnServer.TheStruct.Value.SomeInt;
}
m_Player1OnServer.TheStruct.Value = new TestStruct() { SomeInt = k_TestUInt, SomeBool = false };
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.
yield return WaitForConditionOrTimeOut(VerifyStructure);
}
[UnityTest]
public IEnumerator TestNetworkVariableTemplateStruct([Values(true, false)] bool useHost)
{
yield return 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<TestStruct> { 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.
yield return WaitForConditionOrTimeOut(VerifyStructure);
}
[UnityTest]
public IEnumerator TestNetworkVariableTemplateBehaviourClass([Values(true, false)] bool useHost)
{
yield return InitializeServerAndClients(useHost);
bool VerifyClass()
{
return m_Player1OnClient1.GetComponent<ClassHavingNetworkBehaviour>().TheVar.Value != null && m_Player1OnClient1.GetComponent<ClassHavingNetworkBehaviour>().TheVar.Value.SomeBool == m_Player1OnServer.GetComponent<ClassHavingNetworkBehaviour>().TheVar.Value.SomeBool &&
m_Player1OnClient1.GetComponent<ClassHavingNetworkBehaviour>().TheVar.Value.SomeInt == m_Player1OnServer.GetComponent<ClassHavingNetworkBehaviour>().TheVar.Value.SomeInt;
}
m_Player1OnServer.GetComponent<ClassHavingNetworkBehaviour>().TheVar.Value = new TestClass { SomeInt = k_TestUInt, SomeBool = false };
m_Player1OnServer.GetComponent<ClassHavingNetworkBehaviour>().TheVar.SetDirty(true);
// Wait for the client-side to notify it is finished initializing and spawning.
yield return WaitForConditionOrTimeOut(VerifyClass);
}
[UnityTest]
public IEnumerator TestNetworkVariableTemplateBehaviourClassNotReferencedElsewhere([Values(true, false)] bool useHost)
{
yield return InitializeServerAndClients(useHost);
bool VerifyClass()
{
return m_Player1OnClient1.GetComponent<ClassHavingNetworkBehaviour2>().TheVar.Value != null && m_Player1OnClient1.GetComponent<ClassHavingNetworkBehaviour2>().TheVar.Value.SomeBool == m_Player1OnServer.GetComponent<ClassHavingNetworkBehaviour2>().TheVar.Value.SomeBool &&
m_Player1OnClient1.GetComponent<ClassHavingNetworkBehaviour2>().TheVar.Value.SomeInt == m_Player1OnServer.GetComponent<ClassHavingNetworkBehaviour2>().TheVar.Value.SomeInt;
}
m_Player1OnServer.GetComponent<ClassHavingNetworkBehaviour2>().TheVar.Value = new TestClass_ReferencedOnlyByTemplateNetworkBehavourType { SomeInt = k_TestUInt, SomeBool = false };
m_Player1OnServer.GetComponent<ClassHavingNetworkBehaviour2>().TheVar.SetDirty(true);
// Wait for the client-side to notify it is finished initializing and spawning.
yield return WaitForConditionOrTimeOut(VerifyClass);
}
[UnityTest]
public IEnumerator TestNetworkVariableTemplateBehaviourStruct([Values(true, false)] bool useHost)
{
yield return InitializeServerAndClients(useHost);
bool VerifyClass()
{
return m_Player1OnClient1.GetComponent<StructHavingNetworkBehaviour>().TheVar.Value.SomeBool == m_Player1OnServer.GetComponent<StructHavingNetworkBehaviour>().TheVar.Value.SomeBool &&
m_Player1OnClient1.GetComponent<StructHavingNetworkBehaviour>().TheVar.Value.SomeInt == m_Player1OnServer.GetComponent<StructHavingNetworkBehaviour>().TheVar.Value.SomeInt;
}
m_Player1OnServer.GetComponent<StructHavingNetworkBehaviour>().TheVar.Value = new TestStruct { SomeInt = k_TestUInt, SomeBool = false };
m_Player1OnServer.GetComponent<StructHavingNetworkBehaviour>().TheVar.SetDirty(true);
// Wait for the client-side to notify it is finished initializing and spawning.
yield return WaitForConditionOrTimeOut(VerifyClass);
}
[UnityTest]
public IEnumerator TestNetworkVariableEnum([Values(true, false)] bool useHost)
{
@@ -708,7 +961,25 @@ namespace Unity.Netcode.RuntimeTests
}
[UnityTest]
public IEnumerator TestINetworkSerializableCallsNetworkSerialize([Values(true, false)] bool useHost)
public IEnumerator TestINetworkSerializableClassCallsNetworkSerialize([Values(true, false)] bool useHost)
{
yield return 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.
yield return WaitForConditionOrTimeOut(VerifyCallback);
}
[UnityTest]
public IEnumerator TestINetworkSerializableStructCallsNetworkSerialize([Values(true, false)] bool useHost)
{
yield return InitializeServerAndClients(useHost);
TestStruct.NetworkSerializeCalledOnWrite = false;
@@ -756,11 +1027,181 @@ namespace Unity.Netcode.RuntimeTests
}
}
}
[Test]
public void TestUnsupportedManagedTypesThrowExceptions()
{
var variable = new NetworkVariable<string>();
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<string>.ReadValue = null;
UserNetworkVariableSerialization<string>.WriteValue = null;
Assert.Throws<ArgumentException>(() =>
{
variable.WriteField(writer);
});
Assert.Throws<ArgumentException>(() =>
{
variable.ReadField(reader);
});
}
[Test]
public void TestUnsupportedManagedTypesWithUserSerializationDoNotThrowExceptions()
{
var variable = new NetworkVariable<string>();
UserNetworkVariableSerialization<string>.ReadValue = (FastBufferReader reader, out string value) =>
{
reader.ReadValueSafe(out value);
};
UserNetworkVariableSerialization<string>.WriteValue = (FastBufferWriter writer, in string value) =>
{
writer.WriteValueSafe(value);
};
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<string>.ReadValue = null;
UserNetworkVariableSerialization<string>.WriteValue = null;
}
}
[Test]
public void TestUnsupportedUnmanagedTypesThrowExceptions()
{
var variable = new NetworkVariable<Guid>();
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<Guid>.ReadValue = null;
UserNetworkVariableSerialization<Guid>.WriteValue = null;
Assert.Throws<ArgumentException>(() =>
{
variable.WriteField(writer);
});
Assert.Throws<ArgumentException>(() =>
{
variable.ReadField(reader);
});
}
[Test]
public void TestUnsupportedUnmanagedTypesWithUserSerializationDoNotThrowExceptions()
{
var variable = new NetworkVariable<Guid>();
UserNetworkVariableSerialization<Guid>.ReadValue = (FastBufferReader reader, out Guid value) =>
{
var tmpValue = new ForceNetworkSerializeByMemcpy<Guid>();
reader.ReadValueSafe(out tmpValue);
value = tmpValue.Value;
};
UserNetworkVariableSerialization<Guid>.WriteValue = (FastBufferWriter writer, in Guid value) =>
{
var tmpValue = new ForceNetworkSerializeByMemcpy<Guid>(value);
writer.WriteValueSafe(tmpValue);
};
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<Guid>.ReadValue = null;
UserNetworkVariableSerialization<Guid>.WriteValue = null;
}
}
[Test]
public void TestManagedINetworkSerializableNetworkVariablesDeserializeInPlace()
{
var variable = new NetworkVariable<ManagedNetworkSerializableType>();
variable.Value = new ManagedNetworkSerializableType
{
InMemoryValue = 1,
Ints = new[] { 2, 3, 4 },
Str = "five"
};
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);
variable.Value = new ManagedNetworkSerializableType
{
InMemoryValue = 10,
Ints = new[] { 20, 30, 40, 50 },
Str = "sixty"
};
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");
}
[Test]
public void TestUnmnagedINetworkSerializableNetworkVariablesDeserializeInPlace()
{
var variable = new NetworkVariable<UnmanagedNetworkSerializableType>();
variable.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");
}
#endregion
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();
}
@@ -818,8 +1259,8 @@ namespace Unity.Netcode.RuntimeTests
/// <returns></returns>
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" +
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";
}

View File

@@ -0,0 +1,209 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
namespace Unity.Netcode.RuntimeTests
{
public class OwnerPermissionObject : NetworkBehaviour
{
// indexed by [object, machine]
public static OwnerPermissionObject[,] Objects = new OwnerPermissionObject[3, 3];
public static int CurrentlySpawning = 0;
public static List<OwnerPermissionObject> ClientTargetedNetworkObjects = new List<OwnerPermissionObject>();
// a client-owned NetworkVariable
public NetworkVariable<int> MyNetworkVariableOwner;
// a server-owned NetworkVariable
public NetworkVariable<int> MyNetworkVariableServer;
// a client-owned NetworkVariable
public NetworkList<int> MyNetworkListOwner;
// a server-owned NetworkVariable
public NetworkList<int> MyNetworkListServer;
// verifies two lists are identical
public static void CheckLists(NetworkList<int> listA, NetworkList<int> listB)
{
Debug.Assert(listA.Count == listB.Count);
for (var i = 0; i < listA.Count; i++)
{
Debug.Assert(listA[i] == listB[i]);
}
}
// verifies all objects have consistent lists on all clients
public static void VerifyConsistency()
{
for (var objectIndex = 0; objectIndex < 3; objectIndex++)
{
CheckLists(Objects[objectIndex, 0].MyNetworkListOwner, Objects[objectIndex, 1].MyNetworkListOwner);
CheckLists(Objects[objectIndex, 0].MyNetworkListOwner, Objects[objectIndex, 2].MyNetworkListOwner);
CheckLists(Objects[objectIndex, 0].MyNetworkListServer, Objects[objectIndex, 1].MyNetworkListServer);
CheckLists(Objects[objectIndex, 0].MyNetworkListServer, Objects[objectIndex, 2].MyNetworkListServer);
}
}
public override void OnNetworkSpawn()
{
Objects[CurrentlySpawning, NetworkManager.LocalClientId] = GetComponent<OwnerPermissionObject>();
Debug.Log($"Object index ({CurrentlySpawning}) spawned on client {NetworkManager.LocalClientId}");
}
private void Awake()
{
MyNetworkVariableOwner = new NetworkVariable<int>(writePerm: NetworkVariableWritePermission.Owner);
MyNetworkVariableOwner.OnValueChanged += OwnerChanged;
MyNetworkVariableServer = new NetworkVariable<int>(writePerm: NetworkVariableWritePermission.Server);
MyNetworkVariableServer.OnValueChanged += ServerChanged;
MyNetworkListOwner = new NetworkList<int>(writePerm: NetworkVariableWritePermission.Owner);
MyNetworkListOwner.OnListChanged += ListOwnerChanged;
MyNetworkListServer = new NetworkList<int>(writePerm: NetworkVariableWritePermission.Server);
MyNetworkListServer.OnListChanged += ListServerChanged;
}
public void OwnerChanged(int before, int after)
{
}
public void ServerChanged(int before, int after)
{
}
public void ListOwnerChanged(NetworkListEvent<int> listEvent)
{
}
public void ListServerChanged(NetworkListEvent<int> listEvent)
{
}
}
public class OwnerPermissionHideTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 2;
private GameObject m_PrefabToSpawn;
protected override void OnServerAndClientsCreated()
{
m_PrefabToSpawn = CreateNetworkObjectPrefab("OwnerPermissionObject");
m_PrefabToSpawn.AddComponent<OwnerPermissionObject>();
}
[UnityTest]
public IEnumerator OwnerPermissionTest()
{
// create 3 objects
for (var objectIndex = 0; objectIndex < 3; objectIndex++)
{
OwnerPermissionObject.CurrentlySpawning = objectIndex;
NetworkManager ownerManager = m_ServerNetworkManager;
if (objectIndex != 0)
{
ownerManager = m_ClientNetworkManagers[objectIndex - 1];
}
SpawnObject(m_PrefabToSpawn, ownerManager);
// wait for each object to spawn on each client
for (var clientIndex = 0; clientIndex < 3; clientIndex++)
{
while (OwnerPermissionObject.Objects[objectIndex, clientIndex] == null)
{
yield return new WaitForSeconds(0.0f);
}
}
}
var nextValueToWrite = 1;
var serverIndex = 0;
for (var objectIndex = 0; objectIndex < 3; objectIndex++)
{
for (var clientWriting = 0; clientWriting < 3; clientWriting++)
{
// ==== Server-writable NetworkVariable ====
var gotException = false;
Debug.Log($"Writing to server-write variable on object {objectIndex} on client {clientWriting}");
try
{
nextValueToWrite++;
OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkVariableServer.Value = nextValueToWrite;
}
catch (Exception)
{
gotException = true;
}
// Verify server-owned netvar can only be written by server
Debug.Assert(gotException == (clientWriting != serverIndex));
// ==== Owner-writable NetworkVariable ====
gotException = false;
Debug.Log($"Writing to owner-write variable on object {objectIndex} on client {clientWriting}");
try
{
nextValueToWrite++;
OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkVariableOwner.Value = nextValueToWrite;
}
catch (Exception)
{
gotException = true;
}
// Verify client-owned netvar can only be written by owner
Debug.Assert(gotException == (clientWriting != objectIndex));
// ==== Server-writable NetworkList ====
gotException = false;
Debug.Log($"Writing to server-write list on object {objectIndex} on client {clientWriting}");
try
{
nextValueToWrite++;
OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkListServer.Add(nextValueToWrite);
}
catch (Exception)
{
gotException = true;
}
// Verify server-owned networkList can only be written by server
Debug.Assert(gotException == (clientWriting != serverIndex));
// ==== Owner-writable NetworkList ====
gotException = false;
Debug.Log($"Writing to owner-write list on object {objectIndex} on client {clientWriting}");
try
{
nextValueToWrite++;
OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkListOwner.Add(nextValueToWrite);
}
catch (Exception)
{
gotException = true;
}
// Verify client-owned networkList can only be written by owner
Debug.Assert(gotException == (clientWriting != objectIndex));
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 5);
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ClientNetworkManagers[0], 5);
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ClientNetworkManagers[1], 5);
OwnerPermissionObject.VerifyConsistency();
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88c657dcbe9a2414ba551b60dab19acd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -110,6 +110,32 @@ namespace Unity.Netcode.RuntimeTests
}
}
[Test]
public void TestImplicitConversionToGameObject()
{
using var networkObjectContext = UnityObjectContext.CreateNetworkObject();
networkObjectContext.Object.Spawn();
NetworkObjectReference outReference = networkObjectContext.Object.gameObject;
GameObject go = outReference;
Assert.AreEqual(networkObjectContext.Object.gameObject, go);
}
[Test]
public void TestImplicitToGameObjectIsNullWhenNotFound()
{
using var networkObjectContext = UnityObjectContext.CreateNetworkObject();
networkObjectContext.Object.Spawn();
NetworkObjectReference outReference = networkObjectContext.Object.gameObject;
networkObjectContext.Object.Despawn();
Object.DestroyImmediate(networkObjectContext.Object.gameObject);
GameObject go = outReference;
Assert.IsNull(go);
}
[Test]
public void TestTryGet()
{

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1f398e1797944b5db4d3aa473629f46e
timeCreated: 1661800773

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
namespace Unity.Netcode.RuntimeTests
{
internal class MessageCatcher<TMessageType> : INetworkHooks where TMessageType : INetworkMessage
{
private NetworkManager m_OwnerNetworkManager;
public MessageCatcher(NetworkManager ownerNetworkManager)
{
m_OwnerNetworkManager = ownerNetworkManager;
}
private struct TriggerData
{
public FastBufferReader Reader;
public MessageHeader Header;
public ulong SenderId;
public float Timestamp;
public int SerializedHeaderSize;
}
private readonly List<TriggerData> m_CaughtMessages = new List<TriggerData>();
public void ReleaseMessages()
{
foreach (var caughtSpawn in m_CaughtMessages)
{
// Reader will be disposed within HandleMessage
m_OwnerNetworkManager.MessagingSystem.HandleMessage(caughtSpawn.Header, caughtSpawn.Reader, caughtSpawn.SenderId, caughtSpawn.Timestamp, caughtSpawn.SerializedHeaderSize);
}
}
public int CaughtMessageCount => m_CaughtMessages.Count;
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
{
}
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
{
}
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
{
return true;
}
public bool OnVerifyCanReceive(ulong senderId, Type messageType, FastBufferReader messageContent, ref NetworkContext context)
{
if (messageType == typeof(TMessageType))
{
m_CaughtMessages.Add(new TriggerData
{
Reader = new FastBufferReader(messageContent, Allocator.Persistent),
Header = context.Header,
Timestamp = context.Timestamp,
SenderId = context.SenderId,
SerializedHeaderSize = context.SerializedHeaderSize
});
return false;
}
return true;
}
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f008d074bcb841ae90b1949f9e2f0854
timeCreated: 1661796973

View File

@@ -0,0 +1,69 @@
using System;
using UnityEngine;
namespace Unity.Netcode.RuntimeTests
{
internal class MessageLogger : INetworkHooks
{
private NetworkManager m_OwningNetworkManager;
public MessageLogger(NetworkManager owningNetworkManager)
{
m_OwningNetworkManager = owningNetworkManager;
}
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
{
Debug.Log($"{(m_OwningNetworkManager.IsServer ? "Server" : "Client")} {m_OwningNetworkManager.LocalClientId}: Sending {message.GetType().FullName} to {clientId} with {delivery}");
}
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
{
}
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
Debug.Log($"{(m_OwningNetworkManager.IsServer ? "Server" : "Client")} {m_OwningNetworkManager.LocalClientId}: Receiving {messageType.FullName} from {senderId}");
}
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
Debug.Log($"{(m_OwningNetworkManager.IsServer ? "Server" : "Client")} {m_OwningNetworkManager.LocalClientId}: Sending a batch of to {clientId}: {messageCount} messages, {batchSizeInBytes} bytes, with {delivery}");
}
public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
Debug.Log($"{(m_OwningNetworkManager.IsServer ? "Server" : "Client")} {m_OwningNetworkManager.LocalClientId}: Received a batch from {senderId}, {messageCount} messages, {batchSizeInBytes} bytes");
}
public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
{
return true;
}
public bool OnVerifyCanReceive(ulong senderId, Type messageType, FastBufferReader messageContent, ref NetworkContext context)
{
return true;
}
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
Debug.Log($"{(m_OwningNetworkManager.IsServer ? "Server" : "Client")} {m_OwningNetworkManager.LocalClientId}: Handling message {message.GetType().FullName}");
}
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4dbf404fdf544a409bf1bcab2c3f8b3e
timeCreated: 1661799489

View File

@@ -1,6 +1,7 @@
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.Assertions.Comparers;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
@@ -11,25 +12,45 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
public class NetworkTimeSystemTests
{
private MonoBehaviourTest<PlayerLoopTimeTestComponent> m_MonoBehaviourTest; // cache for teardown
private MonoBehaviourTest<PlayerLoopFixedTimeTestComponent> m_PlayerLoopFixedTimeTestComponent; // cache for teardown
private MonoBehaviourTest<PlayerLoopTimeTestComponent> m_PlayerLoopTimeTestComponent; // cache for teardown
private float m_OriginalTimeScale = 1.0f;
[SetUp]
public void Setup()
{
m_OriginalTimeScale = Time.timeScale;
// Create, instantiate, and host
Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _));
}
/// <summary>
/// Tests whether time is accessible and has correct values inside Update/FixedUpdate.
/// This test applies only when <see cref="Time.timeScale"> is 1.
/// </summary>
/// <returns></returns>
[UnityTest]
public IEnumerator PlayerLoopTimeTest()
public IEnumerator PlayerLoopFixedTimeTest()
{
m_MonoBehaviourTest = new MonoBehaviourTest<PlayerLoopTimeTestComponent>();
m_PlayerLoopFixedTimeTestComponent = new MonoBehaviourTest<PlayerLoopFixedTimeTestComponent>();
yield return m_MonoBehaviourTest;
yield return m_PlayerLoopFixedTimeTestComponent;
}
/// <summary>
/// Tests whether time is accessible and has correct values inside Update, for multiples <see cref="Time.timeScale"/> values.
/// </summary>
/// <returns></returns>
[UnityTest]
public IEnumerator PlayerLoopTimeTest_WithDifferentTimeScale([Values(0.0f, 0.1f, 0.5f, 1.0f, 2.0f, 5.0f)] float timeScale)
{
Time.timeScale = timeScale;
m_PlayerLoopTimeTestComponent = new MonoBehaviourTest<PlayerLoopTimeTestComponent>();
yield return m_PlayerLoopTimeTestComponent;
}
/// <summary>
@@ -40,10 +61,10 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator CorrectAmountTicksTest()
{
var tickSystem = NetworkManager.Singleton.NetworkTickSystem;
var delta = tickSystem.LocalTime.FixedDeltaTime;
var previous_localTickCalculated = 0;
var previous_serverTickCalculated = 0;
NetworkTickSystem tickSystem = NetworkManager.Singleton.NetworkTickSystem;
float delta = tickSystem.LocalTime.FixedDeltaTime;
int previous_localTickCalculated = 0;
int previous_serverTickCalculated = 0;
while (tickSystem.LocalTime.Time < 3f)
{
@@ -70,7 +91,7 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(previous_localTickCalculated, NetworkManager.Singleton.LocalTime.Tick, $"Calculated local tick {previous_localTickCalculated} does not match local tick {NetworkManager.Singleton.LocalTime.Tick}!");
Assert.AreEqual(previous_serverTickCalculated, NetworkManager.Singleton.ServerTime.Tick, $"Calculated server tick {previous_serverTickCalculated} does not match server tick {NetworkManager.Singleton.ServerTime.Tick}!");
Assert.True(Mathf.Approximately((float)NetworkManager.Singleton.LocalTime.Time, (float)NetworkManager.Singleton.ServerTime.Time), $"Local time {(float)NetworkManager.Singleton.LocalTime.Time} is not approximately server time {(float)NetworkManager.Singleton.ServerTime.Time}!");
Assert.AreEqual((float)NetworkManager.Singleton.LocalTime.Time, (float)NetworkManager.Singleton.ServerTime.Time, $"Local time {(float)NetworkManager.Singleton.LocalTime.Time} is not approximately server time {(float)NetworkManager.Singleton.ServerTime.Time}!", FloatComparer.s_ComparerWithDefaultTolerance);
}
}
@@ -80,15 +101,23 @@ namespace Unity.Netcode.RuntimeTests
// Stop, shutdown, and destroy
NetworkManagerHelper.ShutdownNetworkManager();
if (m_MonoBehaviourTest != null)
Time.timeScale = m_OriginalTimeScale;
if (m_PlayerLoopFixedTimeTestComponent != null)
{
Object.DestroyImmediate(m_MonoBehaviourTest.gameObject);
Object.DestroyImmediate(m_PlayerLoopFixedTimeTestComponent.gameObject);
m_PlayerLoopFixedTimeTestComponent = null;
}
if (m_PlayerLoopTimeTestComponent != null)
{
Object.DestroyImmediate(m_PlayerLoopTimeTestComponent.gameObject);
m_PlayerLoopTimeTestComponent = null;
}
}
}
public class PlayerLoopTimeTestComponent : MonoBehaviour, IMonoBehaviourTest
public class PlayerLoopFixedTimeTestComponent : MonoBehaviour, IMonoBehaviourTest
{
public const int Passes = 100;
@@ -101,7 +130,7 @@ namespace Unity.Netcode.RuntimeTests
private NetworkTime m_ServerTimePreviousUpdate;
private NetworkTime m_LocalTimePreviousFixedUpdate;
public void Start()
private void Start()
{
// Run fixed update at same rate as network tick
Time.fixedDeltaTime = NetworkManager.Singleton.LocalTime.FixedDeltaTime;
@@ -110,23 +139,23 @@ namespace Unity.Netcode.RuntimeTests
Time.maximumDeltaTime = float.MaxValue;
}
public void Update()
private void Update()
{
// This must run first else it wont run if there is an exception
m_UpdatePasses++;
var localTime = NetworkManager.Singleton.LocalTime;
var serverTime = NetworkManager.Singleton.ServerTime;
NetworkTime localTime = NetworkManager.Singleton.LocalTime;
NetworkTime serverTime = NetworkManager.Singleton.ServerTime;
// time should have advanced on the host/server
Assert.True(m_LocalTimePreviousUpdate.Time < localTime.Time);
Assert.True(m_ServerTimePreviousUpdate.Time < serverTime.Time);
Assert.Less(m_LocalTimePreviousUpdate.Time, localTime.Time);
Assert.Less(m_ServerTimePreviousUpdate.Time, serverTime.Time);
// time should be further then last fixed step in update
Assert.True(m_LocalTimePreviousFixedUpdate.FixedTime < localTime.Time);
Assert.Less(m_LocalTimePreviousFixedUpdate.FixedTime, localTime.Time);
// we should be in same or further tick then fixed update
Assert.True(m_LocalTimePreviousFixedUpdate.Tick <= localTime.Tick);
Assert.LessOrEqual(m_LocalTimePreviousFixedUpdate.Tick, localTime.Tick);
// fixed update should result in same amounts of tick as network time
if (m_TickOffset == -1)
@@ -135,26 +164,64 @@ namespace Unity.Netcode.RuntimeTests
}
else
{
// offset of 1 is ok, this happens due to different tick duration offsets
Assert.True(Mathf.Abs(serverTime.Tick - m_TickOffset - m_LastFixedUpdateTick) <= 1);
// offset of 1 is ok, this happens due to different tick duration offsets
Assert.LessOrEqual(Mathf.Abs(serverTime.Tick - m_TickOffset - m_LastFixedUpdateTick), 1);
}
m_LocalTimePreviousUpdate = localTime;
m_ServerTimePreviousUpdate = serverTime;
}
public void FixedUpdate()
private void FixedUpdate()
{
var time = NetworkManager.Singleton.LocalTime;
m_LocalTimePreviousFixedUpdate = time;
Assert.AreEqual(Time.fixedDeltaTime, time.FixedDeltaTime);
Assert.True(Mathf.Approximately((float)NetworkManager.Singleton.LocalTime.Time, (float)NetworkManager.Singleton.ServerTime.Time));
m_LocalTimePreviousFixedUpdate = NetworkManager.Singleton.LocalTime;
Assert.AreEqual(Time.fixedDeltaTime, m_LocalTimePreviousFixedUpdate.FixedDeltaTime);
Assert.AreEqual((float)NetworkManager.Singleton.LocalTime.Time, (float)NetworkManager.Singleton.ServerTime.Time, null, FloatComparer.s_ComparerWithDefaultTolerance);
m_LastFixedUpdateTick++;
}
public bool IsTestFinished => m_UpdatePasses >= Passes;
}
public class PlayerLoopTimeTestComponent : MonoBehaviour, IMonoBehaviourTest
{
public const int Passes = 100;
private int m_UpdatePasses = 0;
private NetworkTime m_LocalTimePreviousUpdate;
private NetworkTime m_ServerTimePreviousUpdate;
private NetworkTime m_LocalTimePreviousFixedUpdate;
private void Update()
{
// This must run first else it wont run if there is an exception
m_UpdatePasses++;
NetworkTime localTime = NetworkManager.Singleton.LocalTime;
NetworkTime serverTime = NetworkManager.Singleton.ServerTime;
// time should have advanced on the host/server
Assert.Less(m_LocalTimePreviousUpdate.Time, localTime.Time);
Assert.Less(m_ServerTimePreviousUpdate.Time, serverTime.Time);
// time should be further then last fixed step in update
Assert.Less(m_LocalTimePreviousFixedUpdate.FixedTime, localTime.Time);
// we should be in same or further tick then fixed update
Assert.LessOrEqual(m_LocalTimePreviousFixedUpdate.Tick, localTime.Tick);
m_LocalTimePreviousUpdate = localTime;
m_ServerTimePreviousUpdate = serverTime;
}
private void FixedUpdate()
{
m_LocalTimePreviousFixedUpdate = NetworkManager.Singleton.LocalTime;
}
public bool IsTestFinished => m_UpdatePasses >= Passes;
}
}

View File

@@ -11,7 +11,7 @@ namespace Unity.Netcode.RuntimeTests
public class TransformInterpolationObject : NetworkBehaviour
{
// Set the minimum threshold which we will use as our margin of error
public const float MinThreshold = 0.001f;
public const float MinThreshold = 0.005f;
public bool CheckPosition;
public bool IsMoving;
@@ -24,7 +24,7 @@ namespace Unity.Netcode.RuntimeTests
{
if (transform.position.y < -MinThreshold || transform.position.y > 100.0f + MinThreshold)
{
Debug.LogError($"Interpolation failure. transform.position.y is {transform.position.y}. Should be between 0.0 and 100.0");
Debug.LogError($"Interpolation failure. transform.position.y is {transform.position.y}. Should be between 0.0 and 100.0. Current threshold is [+/- {MinThreshold}].");
}
}

View File

@@ -65,7 +65,11 @@ namespace Unity.Netcode.RuntimeTests
public void Connect()
{
#if UTP_TRANSPORT_2_0_ABOVE
var endpoint = NetworkEndpoint.LoopbackIpv4;
#else
var endpoint = NetworkEndPoint.LoopbackIpv4;
#endif
endpoint.Port = 7777;
m_Connection = m_Driver.Connect(endpoint);

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode.Transports.UTP;
using Unity.Networking.Transport;
using UnityEngine;
namespace Unity.Netcode.RuntimeTests
@@ -36,14 +37,23 @@ namespace Unity.Netcode.RuntimeTests
}
// Common code to initialize a UnityTransport that logs its events.
public static void InitializeTransport(out UnityTransport transport, out List<TransportEvent> events, int maxPayloadSize = UnityTransport.InitialMaxPayloadSize)
public static void InitializeTransport(out UnityTransport transport, out List<TransportEvent> events,
int maxPayloadSize = UnityTransport.InitialMaxPayloadSize, int maxSendQueueSize = 0, NetworkFamily family = NetworkFamily.Ipv4)
{
var logger = new TransportEventLogger();
events = logger.Events;
transport = new GameObject().AddComponent<UnityTransport>();
transport.OnTransportEvent += logger.HandleEvent;
transport.SetMaxPayloadSize(maxPayloadSize);
transport.MaxPayloadSize = maxPayloadSize;
transport.MaxSendQueueSize = maxSendQueueSize;
if (family == NetworkFamily.Ipv6)
{
transport.SetConnectionData("::1", 7777);
}
transport.Initialize();
}

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Unity.Netcode.Transports.UTP;
using Unity.Networking.Transport;
using UnityEngine;
using UnityEngine.TestTools;
using static Unity.Netcode.RuntimeTests.UnityTransportTestHelpers;
@@ -21,6 +22,15 @@ namespace Unity.Netcode.RuntimeTests
NetworkDelivery.Reliable
};
private static readonly NetworkFamily[] k_NetworkFamiltyParameters =
{
NetworkFamily.Ipv4,
#if !(UNITY_SWITCH || UNITY_PS4 || UNITY_PS5)
// IPv6 is not supported on Switch, PS4, and PS5.
NetworkFamily.Ipv6
#endif
};
private UnityTransport m_Server, m_Client1, m_Client2;
private List<TransportEvent> m_ServerEvents, m_Client1Events, m_Client2Events;
@@ -60,10 +70,12 @@ namespace Unity.Netcode.RuntimeTests
// Check if can make a simple data exchange.
[UnityTest]
public IEnumerator PingPong([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
public IEnumerator PingPong(
[ValueSource("k_DeliveryParameters")] NetworkDelivery delivery,
[ValueSource("k_NetworkFamiltyParameters")] NetworkFamily family)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
InitializeTransport(out m_Server, out m_ServerEvents, family: family);
InitializeTransport(out m_Client1, out m_Client1Events, family: family);
m_Server.StartServer();
m_Client1.StartClient();
@@ -89,10 +101,12 @@ namespace Unity.Netcode.RuntimeTests
// Check if can make a simple data exchange (both ways at a time).
[UnityTest]
public IEnumerator PingPongSimultaneous([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
public IEnumerator PingPongSimultaneous(
[ValueSource("k_DeliveryParameters")] NetworkDelivery delivery,
[ValueSource("k_NetworkFamiltyParameters")] NetworkFamily family)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
InitializeTransport(out m_Server, out m_ServerEvents, family: family);
InitializeTransport(out m_Client1, out m_Client1Events, family: family);
m_Server.StartServer();
m_Client1.StartClient();
@@ -126,13 +140,15 @@ namespace Unity.Netcode.RuntimeTests
// loopback traffic are too small for the amount of data sent in a single update here.
[UnityTest]
[UnityPlatform(exclude = new[] { RuntimePlatform.Switch, RuntimePlatform.PS4, RuntimePlatform.PS5 })]
public IEnumerator SendMaximumPayloadSize([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
public IEnumerator SendMaximumPayloadSize(
[ValueSource("k_DeliveryParameters")] NetworkDelivery delivery,
[ValueSource("k_NetworkFamiltyParameters")] NetworkFamily family)
{
// We want something that's over the old limit of ~44KB for reliable payloads.
var payloadSize = 64 * 1024;
InitializeTransport(out m_Server, out m_ServerEvents, payloadSize);
InitializeTransport(out m_Client1, out m_Client1Events, payloadSize);
InitializeTransport(out m_Server, out m_ServerEvents, payloadSize, family: family);
InitializeTransport(out m_Client1, out m_Client1Events, payloadSize, family: family);
m_Server.StartServer();
m_Client1.StartClient();
@@ -164,10 +180,12 @@ namespace Unity.Netcode.RuntimeTests
// Check making multiple sends to a client in a single frame.
[UnityTest]
public IEnumerator MultipleSendsSingleFrame([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
public IEnumerator MultipleSendsSingleFrame(
[ValueSource("k_DeliveryParameters")] NetworkDelivery delivery,
[ValueSource("k_NetworkFamiltyParameters")] NetworkFamily family)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
InitializeTransport(out m_Server, out m_ServerEvents, family: family);
InitializeTransport(out m_Client1, out m_Client1Events, family: family);
m_Server.StartServer();
m_Client1.StartClient();
@@ -193,11 +211,13 @@ namespace Unity.Netcode.RuntimeTests
// Check sending data to multiple clients.
[UnityTest]
public IEnumerator SendMultipleClients([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
public IEnumerator SendMultipleClients(
[ValueSource("k_DeliveryParameters")] NetworkDelivery delivery,
[ValueSource("k_NetworkFamiltyParameters")] NetworkFamily family)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
InitializeTransport(out m_Client2, out m_Client2Events);
InitializeTransport(out m_Server, out m_ServerEvents, family: family);
InitializeTransport(out m_Client1, out m_Client1Events, family: family);
InitializeTransport(out m_Client2, out m_Client2Events, family: family);
m_Server.StartServer();
m_Client1.StartClient();
@@ -234,11 +254,13 @@ namespace Unity.Netcode.RuntimeTests
// Check receiving data from multiple clients.
[UnityTest]
public IEnumerator ReceiveMultipleClients([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
public IEnumerator ReceiveMultipleClients(
[ValueSource("k_DeliveryParameters")] NetworkDelivery delivery,
[ValueSource("k_NetworkFamiltyParameters")] NetworkFamily family)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
InitializeTransport(out m_Client2, out m_Client2Events);
InitializeTransport(out m_Server, out m_ServerEvents, family: family);
InitializeTransport(out m_Client1, out m_Client1Events, family: family);
InitializeTransport(out m_Client2, out m_Client2Events, family: family);
m_Server.StartServer();
m_Client1.StartClient();
@@ -273,8 +295,10 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator DisconnectOnReliableSendQueueOverflow()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
const int maxSendQueueSize = 16 * 1024;
InitializeTransport(out m_Server, out m_ServerEvents, maxSendQueueSize: maxSendQueueSize);
InitializeTransport(out m_Client1, out m_Client1Events, maxSendQueueSize: maxSendQueueSize);
m_Server.StartServer();
m_Client1.StartClient();
@@ -283,7 +307,7 @@ namespace Unity.Netcode.RuntimeTests
m_Server.Shutdown();
var numSends = (UnityTransport.InitialMaxSendQueueSize / 1024);
var numSends = (maxSendQueueSize / 1024);
for (int i = 0; i < numSends; i++)
{
@@ -292,8 +316,7 @@ namespace Unity.Netcode.RuntimeTests
}
LogAssert.Expect(LogType.Error, "Couldn't add payload of size 1024 to reliable send queue. " +
$"Closing connection {m_Client1.ServerClientId} as reliability guarantees can't be maintained. " +
$"Perhaps 'Max Send Queue Size' ({UnityTransport.InitialMaxSendQueueSize}) is too small for workload.");
$"Closing connection {m_Client1.ServerClientId} as reliability guarantees can't be maintained.");
Assert.AreEqual(2, m_Client1Events.Count);
Assert.AreEqual(NetworkEvent.Disconnect, m_Client1Events[1].Type);
@@ -308,15 +331,17 @@ namespace Unity.Netcode.RuntimeTests
[UnityPlatform(exclude = new[] { RuntimePlatform.Switch, RuntimePlatform.PS4, RuntimePlatform.PS5 })]
public IEnumerator SendCompletesOnUnreliableSendQueueOverflow()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
const int maxSendQueueSize = 16 * 1024;
InitializeTransport(out m_Server, out m_ServerEvents, maxSendQueueSize: maxSendQueueSize);
InitializeTransport(out m_Client1, out m_Client1Events, maxSendQueueSize: maxSendQueueSize);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var numSends = (UnityTransport.InitialMaxSendQueueSize / 1024) + 1;
var numSends = (maxSendQueueSize / 1024) + 1;
for (int i = 0; i < numSends; i++)
{
@@ -340,6 +365,7 @@ namespace Unity.Netcode.RuntimeTests
yield return null;
}
#if !UTP_TRANSPORT_2_0_ABOVE
// Check that simulator parameters are effective. We only check with the drop rate, because
// that's easy to check and we only really want to make sure the simulator parameters are
// configured properly (the simulator pipeline stage is already well-tested in UTP).
@@ -394,6 +420,7 @@ namespace Unity.Netcode.RuntimeTests
yield return null;
}
#endif
[UnityTest]
public IEnumerator SendQueuesFlushedOnShutdown([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)

View File

@@ -39,6 +39,11 @@
"name": "com.unity.modules.physics",
"expression": "",
"define": "COM_UNITY_MODULES_PHYSICS"
},
{
"name": "com.unity.transport",
"expression": "2.0.0-exp",
"define": "UTP_TRANSPORT_2_0_ABOVE"
}
]
}
}