com.unity.netcode.gameobjects@1.0.0-pre.7

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.0.0-pre.7] - 2022-04-01

### Added

- Added editor only check prior to entering into play mode if the currently open and active scene is in the build list and if not displays a dialog box asking the user if they would like to automatically add it prior to entering into play mode. (#1828)
- Added `UnityTransport` implementation and `com.unity.transport` package dependency (#1823)
- Added `NetworkVariableWritePermission` to `NetworkVariableBase` and implemented `Owner` client writable netvars. (#1762)
- `UnityTransport` settings can now be set programmatically. (#1845)
- `FastBufferWriter` and Reader IsInitialized property. (#1859)

### Changed

- Updated `UnityTransport` dependency on `com.unity.transport` to 1.0.0 (#1849)

### Removed

- Removed `SnapshotSystem` (#1852)
- Removed `com.unity.modules.animation`, `com.unity.modules.physics` and `com.unity.modules.physics2d` dependencies from the package (#1812)
- Removed `com.unity.collections` dependency from the package (#1849)

### Fixed
- Fixed in-scene placed NetworkObjects not being found/ignored after a client disconnects and then reconnects. (#1850)
- Fixed issue where `UnityTransport` send queues were not flushed when calling `DisconnectLocalClient` or `DisconnectRemoteClient`. (#1847)
- Fixed NetworkBehaviour dependency verification check for an existing NetworkObject not searching from root parent transform relative GameObject. (#1841)
- Fixed issue where entries were not being removed from the NetworkSpawnManager.OwnershipToObjectsTable. (#1838)
- Fixed ClientRpcs would always send to all connected clients by default as opposed to only sending to the NetworkObject's Observers list by default. (#1836)
- Fixed clarity for NetworkSceneManager client side notification when it receives a scene hash value that does not exist in its local hash table. (#1828)
- Fixed client throws a key not found exception when it times out using UNet or UTP. (#1821)
- Fixed network variable updates are no longer limited to 32,768 bytes when NetworkConfig.EnsureNetworkVariableLengthSafety is enabled. The limits are now determined by what the transport can send in a message. (#1811)
- Fixed in-scene NetworkObjects get destroyed if a client fails to connect and shuts down the NetworkManager. (#1809)
- Fixed user never being notified in the editor that a NetworkBehaviour requires a NetworkObject to function properly. (#1808)
- Fixed PlayerObjects and dynamically spawned NetworkObjects not being added to the NetworkClient's OwnedObjects (#1801)
- Fixed issue where NetworkManager would continue starting even if the NetworkTransport selected failed. (#1780)
- Fixed issue when spawning new player if an already existing player exists it does not remove IsPlayer from the previous player (#1779)
- Fixed lack of notification that NetworkManager and NetworkObject cannot be added to the same GameObject with in-editor notifications (#1777)
- Fixed parenting warning printing for false positives (#1855)
This commit is contained in:
Unity Technologies
2022-04-01 00:00:00 +00:00
parent 5b4aaa8b59
commit 60e2dabef4
123 changed files with 5751 additions and 3419 deletions

View File

@@ -1,116 +0,0 @@
using NUnit.Framework;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
public class FixedAllocatorTest
{
[Test]
public void SimpleTest()
{
int pos;
var allocator = new IndexAllocator(20000, 200);
allocator.DebugDisplay();
// allocate 20 bytes
Assert.IsTrue(allocator.Allocate(0, 20, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// can't ask for negative amount of memory
Assert.IsFalse(allocator.Allocate(1, -20, out pos));
Assert.IsTrue(allocator.Verify());
// can't ask for deallocation of negative index
Assert.IsFalse(allocator.Deallocate(-1));
Assert.IsTrue(allocator.Verify());
// can't ask for the same index twice
Assert.IsFalse(allocator.Allocate(0, 20, out pos));
Assert.IsTrue(allocator.Verify());
// allocate another 20 bytes
Assert.IsTrue(allocator.Allocate(1, 20, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate a third 20 bytes
Assert.IsTrue(allocator.Allocate(2, 20, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// deallocate 0
Assert.IsTrue(allocator.Deallocate(0));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// deallocate 1
allocator.Deallocate(1);
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// deallocate 2
allocator.Deallocate(2);
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate 50 bytes
Assert.IsTrue(allocator.Allocate(0, 50, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate another 50 bytes
Assert.IsTrue(allocator.Allocate(1, 50, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate a third 50 bytes
Assert.IsTrue(allocator.Allocate(2, 50, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// deallocate 1, a block in the middle this time
allocator.Deallocate(1);
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate a smaller one in its place
allocator.Allocate(1, 25, out pos);
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
}
[Test]
public void ReuseTest()
{
int count = 100;
bool[] used = new bool[count];
int[] pos = new int[count];
int iterations = 10000;
var allocator = new IndexAllocator(20000, 200);
for (int i = 0; i < iterations; i++)
{
int index = Random.Range(0, count);
if (used[index])
{
Assert.IsTrue(allocator.Deallocate(index));
used[index] = false;
}
else
{
int position;
int length = 10 * Random.Range(1, 10);
Assert.IsTrue(allocator.Allocate(index, length, out position));
pos[index] = position;
used[index] = true;
}
Assert.IsTrue(allocator.Verify());
}
allocator.DebugDisplay();
}
}
}

View File

@@ -34,5 +34,49 @@ namespace Unity.Netcode.EditorTests
// Clean up
Object.DestroyImmediate(parent);
}
public enum NetworkObjectPlacement
{
Root, // Added to the same root GameObject
Child // Added to a child GameObject
}
[Test]
public void NetworkObjectNotAllowed([Values] NetworkObjectPlacement networkObjectPlacement)
{
var gameObject = new GameObject(nameof(NetworkManager));
var targetforNetworkObject = gameObject;
if (networkObjectPlacement == NetworkObjectPlacement.Child)
{
var childGameObject = new GameObject($"{nameof(NetworkManager)}-Child");
childGameObject.transform.parent = targetforNetworkObject.transform;
targetforNetworkObject = childGameObject;
}
var networkManager = gameObject.AddComponent<NetworkManager>();
// Trap for the error message generated when a NetworkObject is discovered on the same GameObject or any children under it
LogAssert.Expect(LogType.Error, NetworkManagerHelper.Singleton.NetworkManagerAndNetworkObjectNotAllowedMessage());
// Add the NetworkObject
var networkObject = targetforNetworkObject.AddComponent<NetworkObject>();
// Since this is an in-editor test, we must force this invocation
NetworkManagerHelper.Singleton.CheckAndNotifyUserNetworkObjectRemoved(networkManager, true);
// Validate that the NetworkObject has been removed
if (networkObjectPlacement == NetworkObjectPlacement.Root)
{
Assert.IsNull(networkManager.gameObject.GetComponent<NetworkObject>(), $"There is still a {nameof(NetworkObject)} on {nameof(NetworkManager)}'s GameObject!");
}
else
{
Assert.IsNull(networkManager.gameObject.GetComponentInChildren<NetworkObject>(), $"There is still a {nameof(NetworkObject)} on {nameof(NetworkManager)}'s child GameObject!");
}
// Clean up
Object.DestroyImmediate(gameObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ed3be13d96c34bc4b8676ce550cee041
timeCreated: 1647861659

View File

@@ -0,0 +1,41 @@
using NUnit.Framework;
namespace Unity.Netcode.EditorTests.NetworkVar
{
public class NetworkVarTests
{
[Test]
public void TestAssignmentUnchanged()
{
var intVar = new NetworkVariable<int>();
intVar.Value = 314159265;
intVar.OnValueChanged += (value, newValue) =>
{
Assert.Fail("OnValueChanged was invoked when setting the same value");
};
intVar.Value = 314159265;
}
[Test]
public void TestAssignmentChanged()
{
var intVar = new NetworkVariable<int>();
intVar.Value = 314159265;
var changed = false;
intVar.OnValueChanged += (value, newValue) =>
{
changed = true;
};
intVar.Value = 314159266;
Assert.True(changed);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a7cdd8c1251f4352b1f1d4825dc85182
timeCreated: 1647861669

View File

@@ -393,6 +393,34 @@ namespace Unity.Netcode.EditorTests
}
}
[Test]
public void WhenCreatingNewFastBufferReader_IsInitializedIsTrue()
{
var array = new NativeArray<byte>(100, Allocator.Temp);
var reader = new FastBufferReader(array, Allocator.Temp);
Assert.AreEqual(true, reader.IsInitialized);
reader.Dispose();
array.Dispose();
}
[Test]
public void WhenDisposingFastBufferReader_IsInitializedIsFalse()
{
var array = new NativeArray<byte>(100, Allocator.Temp);
var reader = new FastBufferReader(array, Allocator.Temp);
reader.Dispose();
Assert.AreEqual(false, reader.IsInitialized);
array.Dispose();
Assert.AreEqual(false, reader.IsInitialized);
}
[Test]
public void WhenUsingDefaultFastBufferReader_IsInitializedIsFalse()
{
FastBufferReader writer = default;
Assert.AreEqual(false, writer.IsInitialized);
}
[Test]
public void WhenCallingReadByteWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
{

View File

@@ -891,6 +891,29 @@ namespace Unity.Netcode.EditorTests
writer.Dispose();
}
[Test]
public void WhenCreatingNewFastBufferWriter_IsInitializedIsTrue()
{
var writer = new FastBufferWriter(100, Allocator.Temp);
Assert.AreEqual(true, writer.IsInitialized);
writer.Dispose();
}
[Test]
public void WhenDisposingFastBufferWriter_IsInitializedIsFalse()
{
var writer = new FastBufferWriter(100, Allocator.Temp);
writer.Dispose();
Assert.AreEqual(false, writer.IsInitialized);
}
[Test]
public void WhenUsingDefaultFastBufferWriter_IsInitializedIsFalse()
{
FastBufferWriter writer = default;
Assert.AreEqual(false, writer.IsInitialized);
}
[Test]
public void WhenRequestingWritePastBoundsForNonGrowingWriter_TryBeginWriteReturnsFalse()
{

View File

@@ -1,73 +0,0 @@
using NUnit.Framework;
namespace Unity.Netcode.EditorTests
{
public class SnapshotRttTests
{
private const double k_Epsilon = 0.0001;
[Test]
public void TestBasicRtt()
{
var snapshot = new SnapshotSystem(null, new NetworkConfig(), null);
var client1 = snapshot.GetConnectionRtt(0);
client1.NotifySend(0, 0.0);
client1.NotifySend(1, 10.0);
client1.NotifyAck(1, 15.0);
client1.NotifySend(2, 20.0);
client1.NotifySend(3, 30.0);
client1.NotifySend(4, 32.0);
client1.NotifyAck(4, 38.0);
client1.NotifyAck(3, 40.0);
ConnectionRtt.Rtt ret = client1.GetRtt();
Assert.True(ret.AverageSec < 7.0 + k_Epsilon);
Assert.True(ret.AverageSec > 7.0 - k_Epsilon);
Assert.True(ret.WorstSec < 10.0 + k_Epsilon);
Assert.True(ret.WorstSec > 10.0 - k_Epsilon);
Assert.True(ret.BestSec < 5.0 + k_Epsilon);
Assert.True(ret.BestSec > 5.0 - k_Epsilon);
// note: `last` latency is latest received Ack, not latest sent sequence.
Assert.True(ret.LastSec < 10.0 + k_Epsilon);
Assert.True(ret.LastSec > 10.0 - k_Epsilon);
}
[Test]
public void TestEdgeCasesRtt()
{
var snapshot = new SnapshotSystem(null, new NetworkConfig(), null);
var client1 = snapshot.GetConnectionRtt(0);
var iterationCount = NetworkConfig.RttWindowSize * 3;
var extraCount = NetworkConfig.RttWindowSize * 2;
// feed in some messages
for (var iteration = 0; iteration < iterationCount; iteration++)
{
client1.NotifySend(iteration, 25.0 * iteration);
}
// ack some random ones in there (1 out of each 9), always 7.0 later
for (var iteration = 0; iteration < iterationCount; iteration += 9)
{
client1.NotifyAck(iteration, 25.0 * iteration + 7.0);
}
// ack some unused key, to check it doesn't throw off the values
for (var iteration = iterationCount; iteration < iterationCount + extraCount; iteration++)
{
client1.NotifyAck(iteration, 42.0);
}
ConnectionRtt.Rtt ret = client1.GetRtt();
Assert.True(ret.AverageSec < 7.0 + k_Epsilon);
Assert.True(ret.AverageSec > 7.0 - k_Epsilon);
Assert.True(ret.WorstSec < 7.0 + k_Epsilon);
Assert.True(ret.WorstSec > 7.0 - k_Epsilon);
Assert.True(ret.BestSec < 7.0 + k_Epsilon);
Assert.True(ret.BestSec > 7.0 - k_Epsilon);
}
}
}

View File

@@ -1,363 +0,0 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using NUnit.Framework;
using Random = System.Random;
namespace Unity.Netcode.EditorTests
{
public class SnapshotTests
{
private SnapshotSystem m_SendSnapshot;
private SnapshotSystem m_RecvSnapshot;
private NetworkTimeSystem m_SendTimeSystem;
private NetworkTickSystem m_SendTickSystem;
private NetworkTimeSystem m_RecvTimeSystem;
private NetworkTickSystem m_RecvTickSystem;
private int m_SpawnedObjectCount;
private int m_DespawnedObjectCount;
private int m_NextSequence;
private uint m_TicksPerSec = 15;
private int m_MinSpawns;
private int m_MinDespawns;
private bool m_ExpectSpawns;
private bool m_ExpectDespawns;
private bool m_LoseNextMessage;
private bool m_PassBackResponses;
public void Prepare()
{
PrepareSendSideSnapshot();
PrepareRecvSideSnapshot();
}
public void AdvanceOneTickSendSide()
{
m_SendTimeSystem.Advance(1.0f / m_TicksPerSec);
m_SendTickSystem.UpdateTick(m_SendTimeSystem.LocalTime, m_SendTimeSystem.ServerTime);
m_SendSnapshot.NetworkUpdate(NetworkUpdateStage.EarlyUpdate);
}
public void AdvanceOneTickRecvSide()
{
m_RecvTimeSystem.Advance(1.0f / m_TicksPerSec);
m_RecvTickSystem.UpdateTick(m_RecvTimeSystem.LocalTime, m_RecvTimeSystem.ServerTime);
m_RecvSnapshot.NetworkUpdate(NetworkUpdateStage.EarlyUpdate);
}
public void AdvanceOneTick()
{
AdvanceOneTickSendSide();
AdvanceOneTickRecvSide();
}
internal int SpawnObject(SnapshotSpawnCommand command)
{
m_SpawnedObjectCount++;
return 0;
}
internal int DespawnObject(SnapshotDespawnCommand command)
{
m_DespawnedObjectCount++;
return 0;
}
internal int SendMessage(ref SnapshotDataMessage message, NetworkDelivery delivery, ulong clientId)
{
if (!m_PassBackResponses)
{
// we're not ack'ing anything, so those should stay 0
Debug.Assert(message.Ack.LastReceivedSequence == 0);
}
Debug.Assert(message.Ack.ReceivedSequenceMask == 0);
Debug.Assert(message.Sequence == m_NextSequence); // sequence has to be the expected one
if (m_ExpectSpawns)
{
Debug.Assert(message.Spawns.Length >= m_MinSpawns); // there has to be multiple spawns per SnapshotMessage
}
else
{
Debug.Assert(message.Spawns.Length == 0); // Spawns were not expected
}
if (m_ExpectDespawns)
{
Debug.Assert(message.Despawns.Length >= m_MinDespawns); // there has to be multiple despawns per SnapshotMessage
}
else
{
Debug.Assert(message.Despawns.IsEmpty); // this test should not have despawns
}
Debug.Assert(message.Entries.Length == 0);
m_NextSequence++;
if (!m_LoseNextMessage)
{
using var writer = new FastBufferWriter(1024, Allocator.Temp);
message.Serialize(writer);
using var reader = new FastBufferReader(writer, Allocator.Temp);
var context = new NetworkContext { SenderId = 0, Timestamp = 0.0f, SystemOwner = new Tuple<SnapshotSystem, ulong>(m_RecvSnapshot, 0) };
var newMessage = new SnapshotDataMessage();
newMessage.Deserialize(reader, ref context);
newMessage.Handle(ref context);
}
return 0;
}
internal int SendMessageRecvSide(ref SnapshotDataMessage message, NetworkDelivery delivery, ulong clientId)
{
if (m_PassBackResponses)
{
using var writer = new FastBufferWriter(1024, Allocator.Temp);
message.Serialize(writer);
using var reader = new FastBufferReader(writer, Allocator.Temp);
var context = new NetworkContext { SenderId = 0, Timestamp = 0.0f, SystemOwner = new Tuple<SnapshotSystem, ulong>(m_SendSnapshot, 1) };
var newMessage = new SnapshotDataMessage();
newMessage.Deserialize(reader, ref context);
newMessage.Handle(ref context);
}
return 0;
}
private void PrepareSendSideSnapshot()
{
var config = new NetworkConfig();
m_SendTickSystem = new NetworkTickSystem(m_TicksPerSec, 0.0, 0.0);
m_SendTimeSystem = new NetworkTimeSystem(0.2, 0.2, 1.0);
config.UseSnapshotDelta = false;
config.UseSnapshotSpawn = true;
m_SendSnapshot = new SnapshotSystem(null, config, m_SendTickSystem);
m_SendSnapshot.IsServer = true;
m_SendSnapshot.IsConnectedClient = false;
m_SendSnapshot.ServerClientId = 0;
m_SendSnapshot.ConnectedClientsId.Clear();
m_SendSnapshot.ConnectedClientsId.Add(0);
m_SendSnapshot.ConnectedClientsId.Add(1);
m_SendSnapshot.MockSendMessage = SendMessage;
m_SendSnapshot.MockSpawnObject = SpawnObject;
m_SendSnapshot.MockDespawnObject = DespawnObject;
}
private void PrepareRecvSideSnapshot()
{
var config = new NetworkConfig();
m_RecvTickSystem = new NetworkTickSystem(m_TicksPerSec, 0.0, 0.0);
m_RecvTimeSystem = new NetworkTimeSystem(0.2, 0.2, 1.0);
config.UseSnapshotDelta = false;
config.UseSnapshotSpawn = true;
m_RecvSnapshot = new SnapshotSystem(null, config, m_RecvTickSystem);
m_RecvSnapshot.IsServer = false;
m_RecvSnapshot.IsConnectedClient = true;
m_RecvSnapshot.ServerClientId = 0;
m_RecvSnapshot.ConnectedClientsId.Clear();
m_SendSnapshot.ConnectedClientsId.Add(0);
m_SendSnapshot.ConnectedClientsId.Add(1);
m_RecvSnapshot.MockSendMessage = SendMessageRecvSide;
m_RecvSnapshot.MockSpawnObject = SpawnObject;
m_RecvSnapshot.MockDespawnObject = DespawnObject;
}
private void SendSpawnToSnapshot(ulong objectId)
{
SnapshotSpawnCommand command = default;
// identity
command.NetworkObjectId = objectId;
// archetype
command.GlobalObjectIdHash = 0;
command.IsSceneObject = true;
// parameters
command.IsPlayerObject = false;
command.OwnerClientId = 0;
command.ParentNetworkId = 0;
command.ObjectPosition = default;
command.ObjectRotation = default;
command.ObjectScale = new Vector3(1.0f, 1.0f, 1.0f);
command.TargetClientIds = new List<ulong> { 1 };
m_SendSnapshot.Spawn(command);
}
private void SendDespawnToSnapshot(ulong objectId)
{
SnapshotDespawnCommand command = default;
// identity
command.NetworkObjectId = objectId;
command.TargetClientIds = new List<ulong> { 1 };
m_SendSnapshot.Despawn(command);
}
[Test]
public void TestSnapshotSpawn()
{
Prepare();
m_SpawnedObjectCount = 0;
m_NextSequence = 0;
m_ExpectSpawns = true;
m_ExpectDespawns = false;
m_MinSpawns = 2; // many spawns are to be sent together
m_LoseNextMessage = false;
m_PassBackResponses = false;
var ticksToRun = 20;
// spawns one more than current buffer size
var objectsToSpawn = m_SendSnapshot.SpawnsBufferCount + 1;
for (int i = 0; i < objectsToSpawn; i++)
{
SendSpawnToSnapshot((ulong)i);
}
for (int i = 0; i < ticksToRun; i++)
{
AdvanceOneTick();
}
Debug.Assert(m_SpawnedObjectCount == objectsToSpawn);
Debug.Assert(m_SendSnapshot.SpawnsBufferCount > objectsToSpawn); // spawn buffer should have grown
}
[Test]
public void TestSnapshotSpawnDespawns()
{
Prepare();
// test that buffers actually shrink and will grow back to needed size
m_SendSnapshot.ReduceBufferUsage();
m_RecvSnapshot.ReduceBufferUsage();
Debug.Assert(m_SendSnapshot.SpawnsBufferCount == 1);
Debug.Assert(m_SendSnapshot.DespawnsBufferCount == 1);
Debug.Assert(m_RecvSnapshot.SpawnsBufferCount == 1);
Debug.Assert(m_RecvSnapshot.DespawnsBufferCount == 1);
m_SpawnedObjectCount = 0;
m_DespawnedObjectCount = 0;
m_NextSequence = 0;
m_ExpectSpawns = true;
m_ExpectDespawns = false;
m_MinDespawns = 2; // many despawns are to be sent together
m_LoseNextMessage = false;
m_PassBackResponses = false;
var ticksToRun = 20;
// spawns one more than current buffer size
var objectsToSpawn = 10;
for (int i = 0; i < objectsToSpawn; i++)
{
SendSpawnToSnapshot((ulong)i);
}
for (int i = 0; i < ticksToRun; i++)
{
AdvanceOneTick();
}
for (int i = 0; i < objectsToSpawn; i++)
{
SendDespawnToSnapshot((ulong)i);
}
m_ExpectSpawns = true; // the un'acked spawns will still be present
m_MinSpawns = 1; // but we don't really care how they are grouped then
m_ExpectDespawns = true;
for (int i = 0; i < ticksToRun; i++)
{
AdvanceOneTick();
}
Debug.Assert(m_DespawnedObjectCount == objectsToSpawn);
}
[Test]
public void TestSnapshotMessageLoss()
{
var r = new Random();
Prepare();
m_SpawnedObjectCount = 0;
m_NextSequence = 0;
m_ExpectSpawns = true;
m_ExpectDespawns = false;
m_MinSpawns = 1;
m_LoseNextMessage = false;
m_PassBackResponses = false;
var ticksToRun = 10;
for (int i = 0; i < ticksToRun; i++)
{
m_LoseNextMessage = (r.Next() % 2) > 0;
SendSpawnToSnapshot((ulong)i);
AdvanceOneTick();
}
m_LoseNextMessage = false;
AdvanceOneTick();
AdvanceOneTick();
Debug.Assert(m_SpawnedObjectCount == ticksToRun);
}
[Test]
public void TestSnapshotAcks()
{
Prepare();
m_SpawnedObjectCount = 0;
m_NextSequence = 0;
m_ExpectSpawns = true;
m_ExpectDespawns = false;
m_MinSpawns = 1;
m_LoseNextMessage = false;
m_PassBackResponses = true;
var objectsToSpawn = 10;
for (int i = 0; i < objectsToSpawn; i++)
{
SendSpawnToSnapshot((ulong)i);
}
AdvanceOneTickSendSide(); // let's tick the send multiple time, to check it still tries to send
AdvanceOneTick();
m_ExpectSpawns = false; // all spawns should have made it back and forth and be absent from next messages
AdvanceOneTick();
for (int i = 0; i < objectsToSpawn; i++)
{
SendDespawnToSnapshot((ulong)i);
}
m_ExpectDespawns = true; // we should now be seeing despawns
AdvanceOneTickSendSide(); // let's tick the send multiple time, to check it still tries to send
AdvanceOneTick();
Debug.Assert(m_SpawnedObjectCount == objectsToSpawn);
}
}
}

View File

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

View File

@@ -0,0 +1,193 @@
using System;
using NUnit.Framework;
using Unity.Collections;
using Unity.Netcode.Transports.UTP;
using Unity.Networking.Transport;
namespace Unity.Netcode.EditorTests
{
public class BatchedReceiveQueueTests
{
[Test]
public void BatchedReceiveQueue_EmptyReader()
{
var data = new NativeArray<byte>(0, Allocator.Temp);
var reader = new DataStreamReader(data);
var q = new BatchedReceiveQueue(reader);
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
Assert.True(q.IsEmpty);
}
[Test]
public void BatchedReceiveQueue_SingleMessage()
{
var dataLength = sizeof(int) + 1;
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
var writer = new DataStreamWriter(data);
writer.WriteInt(1);
writer.WriteByte((byte)42);
var reader = new DataStreamReader(data);
var q = new BatchedReceiveQueue(reader);
Assert.False(q.IsEmpty);
var message = q.PopMessage();
Assert.AreEqual(1, message.Count);
Assert.AreEqual((byte)42, message.Array[message.Offset]);
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
Assert.True(q.IsEmpty);
}
[Test]
public void BatchedReceiveQueue_MultipleMessages()
{
var dataLength = (sizeof(int) + 1) * 2;
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
var writer = new DataStreamWriter(data);
writer.WriteInt(1);
writer.WriteByte((byte)42);
writer.WriteInt(1);
writer.WriteByte((byte)142);
var reader = new DataStreamReader(data);
var q = new BatchedReceiveQueue(reader);
Assert.False(q.IsEmpty);
var message1 = q.PopMessage();
Assert.AreEqual(1, message1.Count);
Assert.AreEqual((byte)42, message1.Array[message1.Offset]);
var message2 = q.PopMessage();
Assert.AreEqual(1, message2.Count);
Assert.AreEqual((byte)142, message2.Array[message2.Offset]);
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
Assert.True(q.IsEmpty);
}
[Test]
public void BatchedReceiveQueue_PartialMessage()
{
var dataLength = sizeof(int);
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
var writer = new DataStreamWriter(data);
writer.WriteInt(42);
var reader = new DataStreamReader(data);
var q = new BatchedReceiveQueue(reader);
Assert.False(q.IsEmpty);
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
}
[Test]
public void BatchedReceiveQueue_PushReader_ToFilledQueue()
{
var data1Length = sizeof(int);
var data2Length = sizeof(byte);
var data1 = new NativeArray<byte>(data1Length, Allocator.Temp);
var data2 = new NativeArray<byte>(data2Length, Allocator.Temp);
var writer1 = new DataStreamWriter(data1);
writer1.WriteInt(1);
var writer2 = new DataStreamWriter(data2);
writer2.WriteByte(42);
var reader1 = new DataStreamReader(data1);
var reader2 = new DataStreamReader(data2);
var q = new BatchedReceiveQueue(reader1);
Assert.False(q.IsEmpty);
q.PushReader(reader2);
Assert.False(q.IsEmpty);
var message = q.PopMessage();
Assert.AreEqual(1, message.Count);
Assert.AreEqual((byte)42, message.Array[message.Offset]);
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
Assert.True(q.IsEmpty);
}
[Test]
public void BatchedReceiveQueue_PushReader_ToPartiallyFilledQueue()
{
var dataLength = sizeof(int) + 1;
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
var writer = new DataStreamWriter(data);
writer.WriteInt(1);
writer.WriteByte((byte)42);
var reader = new DataStreamReader(data);
var q = new BatchedReceiveQueue(reader);
reader = new DataStreamReader(data);
q.PushReader(reader);
var message = q.PopMessage();
Assert.AreEqual(1, message.Count);
Assert.AreEqual((byte)42, message.Array[message.Offset]);
reader = new DataStreamReader(data);
q.PushReader(reader);
message = q.PopMessage();
Assert.AreEqual(1, message.Count);
Assert.AreEqual((byte)42, message.Array[message.Offset]);
message = q.PopMessage();
Assert.AreEqual(1, message.Count);
Assert.AreEqual((byte)42, message.Array[message.Offset]);
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
Assert.True(q.IsEmpty);
}
[Test]
public void BatchedReceiveQueue_PushReader_ToEmptyQueue()
{
var dataLength = sizeof(int) + 1;
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
var writer = new DataStreamWriter(data);
writer.WriteInt(1);
writer.WriteByte((byte)42);
var reader = new DataStreamReader(data);
var q = new BatchedReceiveQueue(reader);
Assert.False(q.IsEmpty);
q.PopMessage();
Assert.True(q.IsEmpty);
reader = new DataStreamReader(data);
q.PushReader(reader);
var message = q.PopMessage();
Assert.AreEqual(1, message.Count);
Assert.AreEqual((byte)42, message.Array[message.Offset]);
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
Assert.True(q.IsEmpty);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 3d41788be1de34b7c8bcfce6a2877754
guid: aabb21b30a80142ea86e59d1b4d5c587
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,266 @@
using System;
using NUnit.Framework;
using Unity.Collections;
using Unity.Netcode.Transports.UTP;
using Unity.Networking.Transport;
namespace Unity.Netcode.EditorTests
{
public class BatchedSendQueueTests
{
private const int k_TestQueueCapacity = 1024;
private const int k_TestMessageSize = 42;
private ArraySegment<byte> m_TestMessage;
private void AssertIsTestMessage(NativeArray<byte> data)
{
var reader = new DataStreamReader(data);
Assert.AreEqual(k_TestMessageSize, reader.ReadInt());
for (int i = 0; i < k_TestMessageSize; i++)
{
Assert.AreEqual(m_TestMessage.Array[i], reader.ReadByte());
}
}
[OneTimeSetUp]
public void InitializeTestMessage()
{
var data = new byte[k_TestMessageSize];
for (int i = 0; i < k_TestMessageSize; i++)
{
data[i] = (byte)i;
}
m_TestMessage = new ArraySegment<byte>(data);
}
[Test]
public void BatchedSendQueue_EmptyOnCreation()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
Assert.AreEqual(0, q.Length);
Assert.True(q.IsEmpty);
}
[Test]
public void BatchedSendQueue_NotCreatedAfterDispose()
{
var q = new BatchedSendQueue(k_TestQueueCapacity);
q.Dispose();
Assert.False(q.IsCreated);
}
[Test]
public void BatchedSendQueue_PushMessageReturnValue()
{
// Will fit a single test message, but not two (with overhead included).
var queueCapacity = (k_TestMessageSize * 2) + BatchedSendQueue.PerMessageOverhead;
using var q = new BatchedSendQueue(queueCapacity);
Assert.True(q.PushMessage(m_TestMessage));
Assert.False(q.PushMessage(m_TestMessage));
}
[Test]
public void BatchedSendQueue_LengthIncreasedAfterPush()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
q.PushMessage(m_TestMessage);
Assert.AreEqual(k_TestMessageSize + BatchedSendQueue.PerMessageOverhead, q.Length);
}
[Test]
public void BatchedSendQueue_PushedMessageGeneratesCopy()
{
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);
q.Consume(messageLength);
Assert.IsTrue(q.PushMessage(m_TestMessage));
Assert.AreEqual(queueCapacity, q.Length);
}
[Test]
public void BatchedSendQueue_FillWriterWithMessages_ReturnValue()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
q.PushMessage(m_TestMessage);
var writer = new DataStreamWriter(data);
var filled = q.FillWriterWithMessages(ref writer);
Assert.AreEqual(k_TestMessageSize + BatchedSendQueue.PerMessageOverhead, filled);
}
[Test]
public void BatchedSendQueue_FillWriterWithMessages_NoopIfNoPushedMessages()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
var writer = new DataStreamWriter(data);
Assert.AreEqual(0, q.FillWriterWithMessages(ref writer));
}
[Test]
public void BatchedSendQueue_FillWriterWithMessages_NoopIfNotEnoughCapacity()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(2, Allocator.Temp);
q.PushMessage(m_TestMessage);
var writer = new DataStreamWriter(data);
Assert.AreEqual(0, q.FillWriterWithMessages(ref writer));
}
[Test]
public void BatchedSendQueue_FillWriterWithMessages_SinglePushedMessage()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
q.PushMessage(m_TestMessage);
var writer = new DataStreamWriter(data);
q.FillWriterWithMessages(ref writer);
AssertIsTestMessage(data);
}
[Test]
public void BatchedSendQueue_FillWriterWithMessages_MultiplePushedMessages()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
q.PushMessage(m_TestMessage);
q.PushMessage(m_TestMessage);
var writer = new DataStreamWriter(data);
q.FillWriterWithMessages(ref writer);
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
AssertIsTestMessage(data);
AssertIsTestMessage(data.GetSubArray(messageLength, messageLength));
}
[Test]
public void BatchedSendQueue_FillWriterWithMessages_PartialPushedMessages()
{
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(messageLength, Allocator.Temp);
q.PushMessage(m_TestMessage);
q.PushMessage(m_TestMessage);
var writer = new DataStreamWriter(data);
Assert.AreEqual(messageLength, q.FillWriterWithMessages(ref writer));
AssertIsTestMessage(data);
}
[Test]
public void BatchedSendQueue_FillWriterWithBytes_NoopIfNoData()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
var writer = new DataStreamWriter(data);
Assert.AreEqual(0, q.FillWriterWithBytes(ref writer));
}
[Test]
public void BatchedSendQueue_FillWriterWithBytes_WriterCapacityMoreThanLength()
{
var dataLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
q.PushMessage(m_TestMessage);
var writer = new DataStreamWriter(data);
Assert.AreEqual(dataLength, q.FillWriterWithBytes(ref writer));
AssertIsTestMessage(data);
}
[Test]
public void BatchedSendQueue_FillWriterWithBytes_WriterCapacityLessThanLength()
{
var dataLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(dataLength, Allocator.Temp);
q.PushMessage(m_TestMessage);
q.PushMessage(m_TestMessage);
var writer = new DataStreamWriter(data);
Assert.AreEqual(dataLength, q.FillWriterWithBytes(ref writer));
AssertIsTestMessage(data);
}
[Test]
public void BatchedSendQueue_FillWriterWithBytes_WriterCapacityEqualToLength()
{
var dataLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
using var q = new BatchedSendQueue(k_TestQueueCapacity);
using var data = new NativeArray<byte>(dataLength, Allocator.Temp);
q.PushMessage(m_TestMessage);
var writer = new DataStreamWriter(data);
Assert.AreEqual(dataLength, q.FillWriterWithBytes(ref writer));
AssertIsTestMessage(data);
}
[Test]
public void BatchedSendQueue_ConsumeLessThanLength()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
q.PushMessage(m_TestMessage);
q.PushMessage(m_TestMessage);
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
q.Consume(messageLength);
Assert.AreEqual(messageLength, q.Length);
}
[Test]
public void BatchedSendQueue_ConsumeExactLength()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
q.PushMessage(m_TestMessage);
q.Consume(k_TestMessageSize + BatchedSendQueue.PerMessageOverhead);
Assert.AreEqual(0, q.Length);
Assert.True(q.IsEmpty);
}
[Test]
public void BatchedSendQueue_ConsumeMoreThanLength()
{
using var q = new BatchedSendQueue(k_TestQueueCapacity);
q.PushMessage(m_TestMessage);
q.Consume(k_TestQueueCapacity);
Assert.AreEqual(0, q.Length);
Assert.True(q.IsEmpty);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 85ac488e1432d49668c711fa625a0743
guid: 51a68dc80bf18443180f3600eb5890d7
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,84 @@
using NUnit.Framework;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
public class UnityTransportTests
{
// Check that starting a server doesn't immediately result in faulted tasks.
[Test]
public void BasicInitServer()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
Assert.True(transport.StartServer());
transport.Shutdown();
}
// Check that starting a client doesn't immediately result in faulted tasks.
[Test]
public void BasicInitClient()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
Assert.True(transport.StartClient());
transport.Shutdown();
}
// Check that we can't restart a server.
[Test]
public void NoRestartServer()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
transport.StartServer();
Assert.False(transport.StartServer());
transport.Shutdown();
}
// Check that we can't restart a client.
[Test]
public void NoRestartClient()
{
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
transport.StartClient();
Assert.False(transport.StartClient());
transport.Shutdown();
}
// Check that we can't start both a server and client on the same transport.
[Test]
public void NotBothServerAndClient()
{
UnityTransport transport;
// Start server then client.
transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
transport.StartServer();
Assert.False(transport.StartClient());
transport.Shutdown();
// Start client then server.
transport = new GameObject().AddComponent<UnityTransport>();
transport.Initialize();
transport.StartClient();
Assert.False(transport.StartServer());
transport.Shutdown();
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a05afab7f08d44c07b2c5e144ba0b45a
guid: 1b0137a26ef0140f0bf5167c09eecb96
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -9,7 +9,8 @@
"Unity.Multiplayer.MetricTypes",
"Unity.Multiplayer.NetStats",
"Unity.Multiplayer.Tools.MetricTypes",
"Unity.Multiplayer.Tools.NetStats"
"Unity.Multiplayer.Tools.NetStats",
"Unity.Networking.Transport"
],
"optionalUnityReferences": [
"TestAssemblies"

View File

@@ -0,0 +1,78 @@
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
#if UNITY_UNET_PRESENT
using Unity.Netcode.Transports.UNET;
#else
using Unity.Netcode.Transports.UTP;
#endif
namespace Unity.Netcode.RuntimeTests
{
public class ClientOnlyConnectionTests
{
private NetworkManager m_ClientNetworkManager;
private GameObject m_NetworkManagerGameObject;
private WaitForSeconds m_DefaultWaitForTick = new WaitForSeconds(1.0f / 30);
private bool m_WasDisconnected;
private TimeoutHelper m_TimeoutHelper;
[SetUp]
public void Setup()
{
m_WasDisconnected = false;
m_NetworkManagerGameObject = new GameObject();
m_ClientNetworkManager = m_NetworkManagerGameObject.AddComponent<NetworkManager>();
m_ClientNetworkManager.NetworkConfig = new NetworkConfig();
#if UNITY_UNET_PRESENT
m_TimeoutHelper = new TimeoutHelper(30);
m_ClientNetworkManager.NetworkConfig.NetworkTransport = m_NetworkManagerGameObject.AddComponent<UNetTransport>();
#else
// Default is 1000ms per connection attempt and 60 connection attempts (60s)
// Currently there is no easy way to set these values other than in-editor
m_TimeoutHelper = new TimeoutHelper(70);
m_ClientNetworkManager.NetworkConfig.NetworkTransport = m_NetworkManagerGameObject.AddComponent<UnityTransport>();
#endif
}
[UnityTest]
public IEnumerator ClientFailsToConnect()
{
// Wait for the disconnected event
m_ClientNetworkManager.OnClientDisconnectCallback += ClientNetworkManager_OnClientDisconnectCallback;
// Only start the client (so it will timeout)
m_ClientNetworkManager.StartClient();
#if !UNITY_UNET_PRESENT
// Unity Transport throws an error when it times out
LogAssert.Expect(LogType.Error, "Failed to connect to server.");
#endif
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => m_WasDisconnected, m_TimeoutHelper);
Assert.False(m_TimeoutHelper.TimedOut, "Timed out waiting for client to timeout waiting to connect!");
// Shutdown the client
m_ClientNetworkManager.Shutdown();
// Wait for a tick
yield return m_DefaultWaitForTick;
}
private void ClientNetworkManager_OnClientDisconnectCallback(ulong clientId)
{
m_WasDisconnected = true;
}
[TearDown]
public void TearDown()
{
if (m_NetworkManagerGameObject != null)
{
Object.DestroyImmediate(m_NetworkManagerGameObject);
}
}
}
}

View File

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

View File

@@ -103,46 +103,6 @@ namespace Unity.Netcode.RuntimeTests
m_NetworkVariableUInt = new NetworkVariable<uint>(1);
m_NetworkVariableUShort = new NetworkVariable<ushort>(1);
m_NetworkVariableBool = new NetworkVariable<bool>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableByte = new NetworkVariable<byte>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableColor = new NetworkVariable<Color>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableColor32 = new NetworkVariable<Color32>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableDouble = new NetworkVariable<double>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableFloat = new NetworkVariable<float>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableInt = new NetworkVariable<int>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableLong = new NetworkVariable<long>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableSByte = new NetworkVariable<sbyte>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableQuaternion = new NetworkVariable<Quaternion>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableShort = new NetworkVariable<short>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableVector4 = new NetworkVariable<Vector4>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableVector3 = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableVector2 = new NetworkVariable<Vector2>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableRay = new NetworkVariable<Ray>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableULong = new NetworkVariable<ulong>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableUInt = new NetworkVariable<uint>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableUShort = new NetworkVariable<ushort>(NetworkVariableReadPermission.Everyone);
// NetworkVariable Value Type and NetworkVariableSettings Constructor Test Coverage
m_NetworkVariableBool = new NetworkVariable<bool>(NetworkVariableReadPermission.Everyone, true);
m_NetworkVariableByte = new NetworkVariable<byte>(NetworkVariableReadPermission.Everyone, 0);
m_NetworkVariableColor = new NetworkVariable<Color>(NetworkVariableReadPermission.Everyone, new Color(1, 1, 1, 1));
m_NetworkVariableColor32 = new NetworkVariable<Color32>(NetworkVariableReadPermission.Everyone, new Color32(1, 1, 1, 1));
m_NetworkVariableDouble = new NetworkVariable<double>(NetworkVariableReadPermission.Everyone, 1.0);
m_NetworkVariableFloat = new NetworkVariable<float>(NetworkVariableReadPermission.Everyone, 1.0f);
m_NetworkVariableInt = new NetworkVariable<int>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableLong = new NetworkVariable<long>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableSByte = new NetworkVariable<sbyte>(NetworkVariableReadPermission.Everyone, 0);
m_NetworkVariableQuaternion = new NetworkVariable<Quaternion>(NetworkVariableReadPermission.Everyone, Quaternion.identity);
m_NetworkVariableShort = new NetworkVariable<short>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableVector4 = new NetworkVariable<Vector4>(NetworkVariableReadPermission.Everyone, new Vector4(1, 1, 1, 1));
m_NetworkVariableVector3 = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone, new Vector3(1, 1, 1));
m_NetworkVariableVector2 = new NetworkVariable<Vector2>(NetworkVariableReadPermission.Everyone, new Vector2(1, 1));
m_NetworkVariableRay = new NetworkVariable<Ray>(NetworkVariableReadPermission.Everyone, new Ray());
m_NetworkVariableULong = new NetworkVariable<ulong>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableUInt = new NetworkVariable<uint>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableUShort = new NetworkVariable<ushort>(NetworkVariableReadPermission.Everyone, 1);
// Use this nifty class: NetworkVariableHelper
// Tracks if NetworkVariable changed invokes the OnValueChanged callback for the given instance type
Bool_Var = new NetworkVariableHelper<bool>(m_NetworkVariableBool);
@@ -193,7 +153,7 @@ namespace Unity.Netcode.RuntimeTests
{
if (EnableTesting)
{
//Added timeout functionality for near future changes to NetworkVariables and the Snapshot system
//Added timeout functionality for near future changes to NetworkVariables
if (!m_FinishedTests && m_ChangesAppliedToNetworkVariables)
{
//We finish testing if all NetworkVariables changed their value or we timed out waiting for

View File

@@ -0,0 +1,69 @@
#if MULTIPLAYER_TOOLS
#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;
using Unity.Netcode.TestHelpers.Runtime.Metrics;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests.Metrics
{
[TestFixture(ClientCount.OneClient, HostOrServer.Host)]
[TestFixture(ClientCount.TwoClients, HostOrServer.Host)]
[TestFixture(ClientCount.OneClient, HostOrServer.Server)]
[TestFixture(ClientCount.TwoClients, HostOrServer.Server)]
public class ConnectionMetricsTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => m_ClientCount;
private int m_ClientCount;
public enum ClientCount
{
OneClient = 1,
TwoClients,
}
public ConnectionMetricsTests(ClientCount clientCount, HostOrServer hostOrServer)
: base(hostOrServer)
{
m_ClientCount = (int)clientCount;
}
private int GetClientCountForFixture()
{
return m_ClientCount + ((m_UseHost) ? 1 : 0);
}
[UnityTest]
public IEnumerator UpdateConnectionCountOnServer()
{
var waitForGaugeValues = new WaitForGaugeMetricValues((m_ServerNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher, NetworkMetricTypes.ConnectedClients);
yield return waitForGaugeValues.WaitForMetricsReceived();
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
Assert.AreEqual(GetClientCountForFixture(), value);
}
[UnityTest]
public IEnumerator UpdateConnectionCountOnClient()
{
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var waitForGaugeValues = new WaitForGaugeMetricValues((clientNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher, NetworkMetricTypes.ConnectedClients);
yield return waitForGaugeValues.WaitForMetricsReceived();
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
Assert.AreEqual(1, value);
}
}
}
}
#endif
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1845aef61dbb4f2b9d2be9145262ab90
timeCreated: 1647023529

View File

@@ -39,7 +39,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
var networkMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
// We should have 1 NamedMessage and some potential SnapshotMessage
// We should have 1 NamedMessage
Assert.That(networkMessageSentMetricValues, Has.Exactly(1).Matches<NetworkMessageEvent>(x => x.Name == nameof(NamedMessage)));
}
@@ -85,7 +85,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
yield return waitForMetricValues.WaitForMetricsReceived();
var networkMessageReceivedValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
// We should have 1 NamedMessage and some potential SnapshotMessage
// We should have 1 NamedMessage
Assert.That(networkMessageReceivedValues, Has.Exactly(1).Matches<NetworkMessageEvent>(x => x.Name == nameof(NamedMessage)));
}

View File

@@ -189,6 +189,76 @@ namespace Unity.Netcode.RuntimeTests.Metrics
Assert.AreEqual(1, objectDestroyedSentMetricValues.Select(x => x.BytesCount).Distinct().Count());
Assert.That(objectDestroyedSentMetricValues.Select(x => x.BytesCount), Has.All.Not.EqualTo(0));
}
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
[UnityTest]
public IEnumerator TrackNetworkObjectCountAfterSpawnOnServer()
{
SpawnNetworkObject();
var waitForGaugeValues = new WaitForGaugeMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects);
yield return s_DefaultWaitForTick;
yield return waitForGaugeValues.WaitForMetricsReceived();
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
Assert.AreEqual(3, value);
}
[UnityTest]
public IEnumerator TrackNetworkObjectCountAfterSpawnOnClient()
{
SpawnNetworkObject();
//By default, we have 2 network objects
//There's a slight delay between the spawn on the server and the spawn on the client
//We want to have metrics when the value is different than the 2 default one to confirm the client has the new value
var waitForGaugeValues = new WaitForGaugeMetricValues(ClientMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects, metric => (int)metric != 2);
yield return waitForGaugeValues.WaitForMetricsReceived();
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
Assert.AreEqual(3, value);
}
[UnityTest]
public IEnumerator TrackNetworkObjectCountAfterDespawnOnServer()
{
var objectList = Server.SpawnManager.SpawnedObjectsList;
for (int i = objectList.Count - 1; i >= 0; --i)
{
objectList.ElementAt(i).Despawn();
}
var waitForGaugeValues = new WaitForGaugeMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects);
yield return s_DefaultWaitForTick;
yield return waitForGaugeValues.WaitForMetricsReceived();
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
Assert.AreEqual(0, value);
}
[UnityTest]
public IEnumerator TrackNetworkObjectCountAfterDespawnOnClient()
{
var objectList = Server.SpawnManager.SpawnedObjectsList;
for (int i = objectList.Count - 1; i >= 0; --i)
{
objectList.ElementAt(i).Despawn();
}
//By default, we have 2 network objects
//There's a slight delay between the spawn on the server and the spawn on the client
//We want to have metrics when the value is different than the 2 default one to confirm the client has the new value
var waitForGaugeValues = new WaitForGaugeMetricValues(ClientMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects, metric => (int)metric != 2);
yield return waitForGaugeValues.WaitForMetricsReceived();
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
Assert.AreEqual(0, value);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,89 @@
#if MULTIPLAYER_TOOLS
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
using System;
using System.Collections;
using NUnit.Framework;
using Unity.Collections;
using Unity.Multiplayer.Tools.MetricTypes;
using Unity.Netcode.TestHelpers.Runtime;
using Unity.Netcode.TestHelpers.Runtime.Metrics;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests.Metrics
{
public class PacketLossMetricsTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
private readonly int m_PacketLossRate = 25;
private int m_DropInterval = 5;
public PacketLossMetricsTests()
: base(HostOrServer.Server)
{}
protected override void OnOneTimeSetup()
{
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
}
protected override void OnServerAndClientsCreated()
{
var clientTransport = (UnityTransport)m_ClientNetworkManagers[0].NetworkConfig.NetworkTransport;
clientTransport.SetDebugSimulatorParameters(0, 0, m_PacketLossRate);
base.OnServerAndClientsCreated();
}
[UnityTest]
public IEnumerator TrackPacketLossAsServer()
{
var waitForPacketLossMetric = new WaitForGaugeMetricValues((m_ServerNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher,
NetworkMetricTypes.PacketLoss,
metric => metric == 0.0d);
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);
}
}
yield return waitForPacketLossMetric.WaitForMetricsReceived();
var packetLossValue = waitForPacketLossMetric.AssertMetricValueHaveBeenFound();
Assert.AreEqual(0d, packetLossValue);
}
[UnityTest]
public IEnumerator TrackPacketLossAsClient()
{
double packetLossRate = m_PacketLossRate/100d;
var clientNetworkManager = m_ClientNetworkManagers[0];
var waitForPacketLossMetric = new WaitForGaugeMetricValues((clientNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher,
NetworkMetricTypes.PacketLoss,
metric => Math.Abs(metric - packetLossRate) < Double.Epsilon);
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);
}
}
yield return waitForPacketLossMetric.WaitForMetricsReceived();
var packetLossValue = waitForPacketLossMetric.AssertMetricValueHaveBeenFound();
Assert.AreEqual(packetLossRate, packetLossValue);
}
}
}
#endif
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 12e64da4670d49a4a89da38d18e64396
timeCreated: 1648133968

View File

@@ -1,5 +1,5 @@
#if MULTIPLAYER_TOOLS
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
using System.Collections;
using NUnit.Framework;
using Unity.Collections;
@@ -15,11 +15,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
protected override void OnOneTimeSetup()
{
#if UTP_ADAPTER
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
#else
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.SIP;
#endif
base.OnOneTimeSetup();
}

View File

@@ -1,5 +1,5 @@
#if MULTIPLAYER_TOOLS
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
using System.Collections;
using System.Collections.Generic;
@@ -46,11 +46,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
/// </summary>
protected override void OnOneTimeSetup()
{
#if UTP_ADAPTER
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
#else
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.SIP;
#endif
}
[UnityTest]
@@ -92,7 +88,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
foreach (var clientGaugeMetricValue in clientGaugeMetricValues)
{
var rttValue = clientGaugeMetricValue.AssertMetricValueHaveBeenFound();
Assert.That(rttValue, Is.GreaterThanOrEqualTo(1f));
Assert.That(rttValue, Is.GreaterThanOrEqualTo(1e-3f));
}
}
}

View File

@@ -1,3 +1,4 @@
#if COM_UNITY_MODULES_ANIMATION
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
@@ -235,3 +236,4 @@ namespace Unity.Netcode.RuntimeTests
}
}
}
#endif // COM_UNITY_MODULES_ANIMATION

View File

@@ -0,0 +1,133 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using Object = UnityEngine.Object;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
public class NetworkManagerTransportTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
private bool m_CanStartServerAndClients = false;
public NetworkManagerTransportTests(HostOrServer hostOrServer) : base(hostOrServer) { }
protected override IEnumerator OnSetup()
{
m_CanStartServerAndClients = false;
return base.OnSetup();
}
protected override bool CanStartServerAndClients()
{
return m_CanStartServerAndClients;
}
/// <summary>
/// Validate that if the NetworkTransport fails to start the NetworkManager
/// will not continue the startup process and will shut itself down.
/// </summary>
/// <param name="testClient">if true it will test the client side</param>
[UnityTest]
public IEnumerator DoesNotStartWhenTransportFails([Values] bool testClient)
{
// The error message we should expect
var messageToCheck = "";
if (!testClient)
{
Object.DestroyImmediate(m_ServerNetworkManager.NetworkConfig.NetworkTransport);
m_ServerNetworkManager.NetworkConfig.NetworkTransport = m_ServerNetworkManager.gameObject.AddComponent<FailedTransport>();
m_ServerNetworkManager.NetworkConfig.NetworkTransport.Initialize(m_ServerNetworkManager);
// The error message we should expect
messageToCheck = $"Server is shutting down due to network transport start failure of {m_ServerNetworkManager.NetworkConfig.NetworkTransport.GetType().Name}!";
}
else
{
foreach (var client in m_ClientNetworkManagers)
{
Object.DestroyImmediate(client.NetworkConfig.NetworkTransport);
client.NetworkConfig.NetworkTransport = client.gameObject.AddComponent<FailedTransport>();
client.NetworkConfig.NetworkTransport.Initialize(m_ServerNetworkManager);
}
// The error message we should expect
messageToCheck = $"Client is shutting down due to network transport start failure of {m_ClientNetworkManagers[0].NetworkConfig.NetworkTransport.GetType().Name}!";
}
// Trap for the nested NetworkManager exception
LogAssert.Expect(LogType.Error, messageToCheck);
m_CanStartServerAndClients = true;
// Due to other errors, we must not send clients if testing the server-host side
// We can test both server and client(s) when testing client-side only
if (testClient)
{
NetcodeIntegrationTestHelpers.Start(m_UseHost, m_ServerNetworkManager, m_ClientNetworkManagers);
yield return s_DefaultWaitForTick;
foreach (var client in m_ClientNetworkManagers)
{
Assert.False(client.IsListening);
Assert.False(client.IsConnectedClient);
}
}
else
{
NetcodeIntegrationTestHelpers.Start(m_UseHost, m_ServerNetworkManager, new NetworkManager[] { });
yield return s_DefaultWaitForTick;
Assert.False(m_ServerNetworkManager.IsListening);
}
}
}
/// <summary>
/// Does nothing but simulate a transport that failed to start
/// </summary>
public class FailedTransport : TestingNetworkTransport
{
public override void Shutdown()
{
}
public override ulong ServerClientId => 0;
public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
{
clientId = 0;
payload = new ArraySegment<byte>();
receiveTime = 0;
return NetworkEvent.Nothing;
}
public override bool StartClient()
{
// Simulate failure, always return false
return false;
}
public override bool StartServer()
{
// Simulate failure, always return false
return false;
}
public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery networkDelivery)
{
}
public override void DisconnectRemoteClient(ulong clientId)
{
}
public override void Initialize(NetworkManager networkManager = null)
{
}
public override ulong GetCurrentRtt(ulong clientId)
{
return 0;
}
public override void DisconnectLocalClient()
{
}
}
}

View File

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

View File

@@ -1,78 +1,55 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
using Object = UnityEngine.Object;
namespace Unity.Netcode.RuntimeTests
{
public class NetworkObjectDontDestroyWithOwnerTests
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
public class NetworkObjectDontDestroyWithOwnerTests : NetcodeIntegrationTest
{
private const int k_NumberObjectsToSpawn = 32;
protected override int NumberOfClients => 1;
protected GameObject m_PrefabToSpawn;
public NetworkObjectDontDestroyWithOwnerTests(HostOrServer hostOrServer) : base(hostOrServer) { }
protected override void OnServerAndClientsCreated()
{
m_PrefabToSpawn = CreateNetworkObjectPrefab("ClientOwnedObject");
m_PrefabToSpawn.GetComponent<NetworkObject>().DontDestroyWithOwner = true;
}
[UnityTest]
public IEnumerator DontDestroyWithOwnerTest()
{
// create server and client instances
NetcodeIntegrationTestHelpers.Create(1, out NetworkManager server, out NetworkManager[] clients);
var client = m_ClientNetworkManagers[0];
var clientId = client.LocalClientId;
var networkObjects = SpawnObjects(m_PrefabToSpawn, m_ClientNetworkManagers[0], k_NumberObjectsToSpawn);
// create prefab
var gameObject = new GameObject("ClientOwnedObject");
var networkObject = gameObject.AddComponent<NetworkObject>();
networkObject.DontDestroyWithOwner = true;
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
server.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab()
{
Prefab = gameObject
});
for (int i = 0; i < clients.Length; i++)
{
clients[i].NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab()
{
Prefab = 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);
// network objects
var networkObjects = new List<NetworkObject>();
// create instances
for (int i = 0; i < 32; i++)
{
var no = Object.Instantiate(gameObject).GetComponent<NetworkObject>();
no.NetworkManagerOwner = server;
networkObjects.Add(no);
no.SpawnWithOwnership(clients[0].LocalClientId);
}
// wait for object spawn on client
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => clients[0].SpawnManager.SpawnedObjects.Count == 32);
// wait for object spawn on client to reach k_NumberObjectsToSpawn + 1 (k_NumberObjectsToSpawn and 1 for the player)
yield return WaitForConditionOrTimeOut(() => client.SpawnManager.GetClientOwnedObjects(clientId).Count() == k_NumberObjectsToSpawn + 1);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client to have 33 NetworkObjects spawned! Only {client.SpawnManager.GetClientOwnedObjects(clientId).Count()} were assigned!");
// disconnect the client that owns all the clients
NetcodeIntegrationTestHelpers.StopOneClient(clients[0]);
NetcodeIntegrationTestHelpers.StopOneClient(client);
var remainingClients = Mathf.Max(0, TotalClients - 1);
// wait for disconnect
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => server.ConnectedClients.Count == 0);
yield return WaitForConditionOrTimeOut(() => m_ServerNetworkManager.ConnectedClients.Count == remainingClients);
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client to disconnect!");
for (int i = 0; i < networkObjects.Count; i++)
{
var networkObject = networkObjects[i].GetComponent<NetworkObject>();
// ensure ownership was transferred back
Assert.That(networkObjects[i].OwnerClientId == server.ServerClientId);
Assert.That(networkObject.OwnerClientId == m_ServerNetworkManager.LocalClientId);
}
// cleanup
NetcodeIntegrationTestHelpers.Destroy();
}
}
}

View File

@@ -42,7 +42,7 @@ namespace Unity.Netcode.RuntimeTests
yield return s_DefaultWaitForTick;
// The object is owned by server
Assert.False(m_ServerNetworkManager.ConnectedClients[m_ClientNetworkManagers[0].LocalClientId].OwnedObjects.Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
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);
@@ -51,8 +51,8 @@ namespace Unity.Netcode.RuntimeTests
yield return s_DefaultWaitForTick;
// Ensure it's now added to the list
Assert.True(m_ServerNetworkManager.ConnectedClients[m_ClientNetworkManagers[0].LocalClientId].OwnedObjects.Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
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));
}
}
}

View File

@@ -1,4 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
@@ -9,118 +11,77 @@ namespace Unity.Netcode.RuntimeTests
public class NetworkObjectOwnershipComponent : NetworkBehaviour
{
public bool OnLostOwnershipFired = false;
public ulong CachedOwnerIdOnLostOwnership = 0;
public bool OnGainedOwnershipFired = false;
public override void OnLostOwnership()
{
OnLostOwnershipFired = true;
CachedOwnerIdOnLostOwnership = OwnerClientId;
}
public bool OnGainedOwnershipFired = false;
public ulong CachedOwnerIdOnGainedOwnership = 0;
public override void OnGainedOwnership()
{
OnGainedOwnershipFired = true;
CachedOwnerIdOnGainedOwnership = OwnerClientId;
}
public void ResetFlags()
{
OnLostOwnershipFired = false;
OnGainedOwnershipFired = false;
}
}
[TestFixture(true)]
[TestFixture(false)]
public class NetworkObjectOwnershipTests
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
public class NetworkObjectOwnershipTests : NetcodeIntegrationTest
{
private const int k_ClientInstanceCount = 1;
protected override int NumberOfClients => 9;
private NetworkManager m_ServerNetworkManager;
private NetworkManager[] m_ClientNetworkManagers;
private GameObject m_OwnershipPrefab;
private GameObject m_OwnershipObject;
private NetworkObject m_OwnershipNetworkObject;
private GameObject m_DummyPrefab;
private GameObject m_DummyGameObject;
public NetworkObjectOwnershipTests(HostOrServer hostOrServer) : base(hostOrServer) { }
private readonly bool m_IsHost;
public NetworkObjectOwnershipTests(bool isHost)
protected override void OnServerAndClientsCreated()
{
m_IsHost = isHost;
m_OwnershipPrefab = CreateNetworkObjectPrefab("OnwershipPrefab");
m_OwnershipPrefab.AddComponent<NetworkObjectOwnershipComponent>();
base.OnServerAndClientsCreated();
}
[UnitySetUp]
public IEnumerator Setup()
[Test]
public void TestPlayerIsOwned()
{
// we need at least 1 client for tests
Assert.That(k_ClientInstanceCount, Is.GreaterThan(0));
var clientOwnedObjects = m_ClientNetworkManagers[0].SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId);
// create NetworkManager instances
Assert.That(NetcodeIntegrationTestHelpers.Create(k_ClientInstanceCount, out m_ServerNetworkManager, out m_ClientNetworkManagers));
Assert.That(m_ServerNetworkManager, Is.Not.Null);
Assert.That(m_ClientNetworkManagers, Is.Not.Null);
Assert.That(m_ClientNetworkManagers.Length, Is.EqualTo(k_ClientInstanceCount));
var clientPlayerObject = clientOwnedObjects.Where((c) => c.IsLocalPlayer).FirstOrDefault();
Assert.NotNull(clientPlayerObject, $"Client Id {m_ClientNetworkManagers[0].LocalClientId} does not have its local player marked as an owned object!");
// create and register our ad-hoc DummyPrefab (we'll spawn it later during tests)
m_DummyPrefab = new GameObject("DummyPrefabPrototype");
m_DummyPrefab.AddComponent<NetworkObject>();
m_DummyPrefab.AddComponent<NetworkObjectOwnershipComponent>();
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(m_DummyPrefab.GetComponent<NetworkObject>());
m_ServerNetworkManager.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab { Prefab = m_DummyPrefab });
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
clientNetworkManager.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab { Prefab = m_DummyPrefab });
}
// start server and client NetworkManager instances
Assert.That(NetcodeIntegrationTestHelpers.Start(m_IsHost, m_ServerNetworkManager, m_ClientNetworkManagers));
// wait for connection on client side
yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(m_ClientNetworkManagers);
// wait for connection on server side
yield return NetcodeIntegrationTestHelpers.WaitForClientConnectedToServer(m_ServerNetworkManager);
}
[TearDown]
public void Teardown()
{
NetcodeIntegrationTestHelpers.Destroy();
if (m_DummyGameObject != null)
{
Object.DestroyImmediate(m_DummyGameObject);
}
if (m_DummyPrefab != null)
{
Object.DestroyImmediate(m_DummyPrefab);
}
clientPlayerObject = m_ClientNetworkManagers[0].LocalClient.OwnedObjects.Where((c) => c.IsLocalPlayer).FirstOrDefault();
Assert.NotNull(clientPlayerObject, $"Client Id {m_ClientNetworkManagers[0].LocalClientId} does not have its local player marked as an owned object using local client!");
}
[UnityTest]
public IEnumerator TestOwnershipCallbacks()
{
m_DummyGameObject = Object.Instantiate(m_DummyPrefab);
var dummyNetworkObject = m_DummyGameObject.GetComponent<NetworkObject>();
Assert.That(dummyNetworkObject, Is.Not.Null);
dummyNetworkObject.NetworkManagerOwner = m_ServerNetworkManager;
dummyNetworkObject.Spawn();
var dummyNetworkObjectId = dummyNetworkObject.NetworkObjectId;
Assert.That(dummyNetworkObjectId, Is.GreaterThan(0));
m_OwnershipObject = SpawnObject(m_OwnershipPrefab, m_ServerNetworkManager);
m_OwnershipNetworkObject = m_OwnershipObject.GetComponent<NetworkObject>();
yield return NetcodeIntegrationTestHelpers.WaitForMessageOfType<CreateObjectMessage>(m_ClientNetworkManagers[0]);
Assert.That(m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(dummyNetworkObjectId));
var ownershipNetworkObjectId = m_OwnershipNetworkObject.NetworkObjectId;
Assert.That(ownershipNetworkObjectId, Is.GreaterThan(0));
Assert.That(m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(ownershipNetworkObjectId));
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
Assert.That(clientNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(dummyNetworkObjectId));
Assert.That(clientNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(ownershipNetworkObjectId));
}
// Verifies that removing the ownership when the default (server) is already set does not cause
// a Key Not Found Exception
m_ServerNetworkManager.SpawnManager.RemoveOwnership(dummyNetworkObject);
// Verifies that removing the ownership when the default (server) is already set does not cause a Key Not Found Exception
m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject);
var serverObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[dummyNetworkObjectId];
var clientObject = m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects[dummyNetworkObjectId];
var serverObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
var clientObject = m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
Assert.That(serverObject, Is.Not.Null);
Assert.That(clientObject, Is.Not.Null);
@@ -129,25 +90,164 @@ namespace Unity.Netcode.RuntimeTests
Assert.That(serverComponent, Is.Not.Null);
Assert.That(clientComponent, Is.Not.Null);
Assert.That(serverObject.OwnerClientId, Is.EqualTo(m_ServerNetworkManager.ServerClientId));
Assert.That(clientObject.OwnerClientId, Is.EqualTo(m_ClientNetworkManagers[0].ServerClientId));
Assert.That(serverObject.OwnerClientId, Is.EqualTo(NetworkManager.ServerClientId));
Assert.That(clientObject.OwnerClientId, Is.EqualTo(NetworkManager.ServerClientId));
Assert.That(m_ServerNetworkManager.ConnectedClients.ContainsKey(m_ClientNetworkManagers[0].LocalClientId));
serverObject.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 2);
yield return NetcodeIntegrationTestHelpers.WaitForMessageOfType<ChangeOwnershipMessage>(m_ClientNetworkManagers[0]);
Assert.That(serverComponent.OnLostOwnershipFired);
Assert.That(serverComponent.OwnerClientId, Is.EqualTo(m_ClientNetworkManagers[0].LocalClientId));
Assert.That(clientComponent.OnGainedOwnershipFired);
Assert.That(clientComponent.CachedOwnerIdOnGainedOwnership, Is.EqualTo(m_ClientNetworkManagers[0].LocalClientId));
serverObject.ChangeOwnership(m_ServerNetworkManager.ServerClientId);
Assert.That(clientComponent.OwnerClientId, Is.EqualTo(m_ClientNetworkManagers[0].LocalClientId));
yield return NetcodeIntegrationTestHelpers.WaitForMessageOfType<ChangeOwnershipMessage>(m_ClientNetworkManagers[0]);
serverComponent.ResetFlags();
clientComponent.ResetFlags();
Assert.That(serverObject.OwnerClientId, Is.EqualTo(m_ServerNetworkManager.LocalClientId));
serverObject.ChangeOwnership(NetworkManager.ServerClientId);
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 2);
Assert.That(serverComponent.OnGainedOwnershipFired);
Assert.That(serverComponent.OwnerClientId, Is.EqualTo(m_ServerNetworkManager.LocalClientId));
Assert.That(clientComponent.OnLostOwnershipFired);
Assert.That(clientComponent.CachedOwnerIdOnLostOwnership, Is.EqualTo(m_ClientNetworkManagers[0].LocalClientId));
Assert.That(clientComponent.OwnerClientId, Is.EqualTo(m_ServerNetworkManager.LocalClientId));
}
/// <summary>
/// Verifies that switching ownership between several clients works properly
/// </summary>
[UnityTest]
public IEnumerator TestOwnershipCallbacksSeveralClients()
{
// Build our message hook entries tables so we can determine if all clients received spawn or ownership messages
var messageHookEntriesForSpawn = new List<MessageHookEntry>();
var messageHookEntriesForOwnership = new List<MessageHookEntry>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var messageHook = new MessageHookEntry(clientNetworkManager);
messageHook.AssignMessageType<CreateObjectMessage>();
messageHookEntriesForSpawn.Add(messageHook);
messageHook = new MessageHookEntry(clientNetworkManager);
messageHook.AssignMessageType<ChangeOwnershipMessage>();
messageHookEntriesForOwnership.Add(messageHook);
}
// Used to determine if all clients received the CreateObjectMessage
var spawnMessageHooks = new MessageHooksConditional(messageHookEntriesForSpawn);
// Used to determine if all clients received the ChangeOwnershipMessage
var ownershipMessageHooks = new MessageHooksConditional(messageHookEntriesForOwnership);
// Spawn our test object from server with server ownership
m_OwnershipObject = SpawnObject(m_OwnershipPrefab, m_ServerNetworkManager);
m_OwnershipNetworkObject = m_OwnershipObject.GetComponent<NetworkObject>();
// Wait for all clients to receive the CreateObjectMessage
yield return WaitForConditionOrTimeOut(spawnMessageHooks);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for all clients to receive the {nameof(CreateObjectMessage)} message.");
// Validate the NetworkObjectId and that the server and all clients have this NetworkObject
var ownershipNetworkObjectId = m_OwnershipNetworkObject.NetworkObjectId;
Assert.That(ownershipNetworkObjectId, Is.GreaterThan(0));
Assert.That(m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(ownershipNetworkObjectId));
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
Assert.That(clientNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(ownershipNetworkObjectId));
}
// Verifies that removing the ownership when the default (server) is already set does not cause a Key Not Found Exception
m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject);
var serverObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
Assert.That(serverObject, Is.Not.Null);
var clientObjects = new List<NetworkObject>();
for (int i = 0; i < NumberOfClients; i++)
{
var clientObject = m_ClientNetworkManagers[i].SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
Assert.That(clientObject, Is.Not.Null);
clientObjects.Add(clientObject);
}
// Verify the server side component
var serverComponent = serverObject.GetComponent<NetworkObjectOwnershipComponent>();
Assert.That(serverComponent, Is.Not.Null);
Assert.That(serverObject.OwnerClientId, Is.EqualTo(NetworkManager.ServerClientId));
// Verify the clients components
for (int i = 0; i < NumberOfClients; i++)
{
var clientComponent = clientObjects[i].GetComponent<NetworkObjectOwnershipComponent>();
Assert.That(clientComponent.OwnerClientId, Is.EqualTo(NetworkManager.ServerClientId));
clientComponent.ResetFlags();
}
// After the 1st client has been given ownership to the object, this will be used to make sure each previous owner properly received the remove ownership message
var previousClientComponent = (NetworkObjectOwnershipComponent)null;
for (int clientIndex = 0; clientIndex < NumberOfClients; clientIndex++)
{
var clientObject = clientObjects[clientIndex];
var clientId = m_ClientNetworkManagers[clientIndex].LocalClientId;
Assert.That(m_ServerNetworkManager.ConnectedClients.ContainsKey(clientId));
serverObject.ChangeOwnership(clientId);
yield return s_DefaultWaitForTick;
Assert.That(serverComponent.OnLostOwnershipFired);
Assert.That(serverComponent.OwnerClientId, Is.EqualTo(clientId));
// Wait for all clients to receive the CreateObjectMessage
yield return WaitForConditionOrTimeOut(ownershipMessageHooks);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for all clients to receive the {nameof(ChangeOwnershipMessage)} message.");
var previousNetworkManager = m_ServerNetworkManager;
if (previousClientComponent != null)
{
// Once we have a previousClientComponent, we want to verify the server is keeping track for the removal of ownership in the OwnershipToObjectsTable
Assert.That(!m_ServerNetworkManager.SpawnManager.OwnershipToObjectsTable[m_ServerNetworkManager.LocalClientId].ContainsKey(serverObject.NetworkObjectId));
previousNetworkManager = previousClientComponent.NetworkManager;
Assert.That(previousClientComponent.OnLostOwnershipFired);
Assert.That(previousClientComponent.OwnerClientId, Is.EqualTo(clientId));
}
// Assure the previous owner is no longer in the local table of the previous owner.
Assert.That(!previousNetworkManager.SpawnManager.OwnershipToObjectsTable[previousNetworkManager.LocalClientId].ContainsKey(serverObject.NetworkObjectId));
var currentClientComponent = clientObjects[clientIndex].GetComponent<NetworkObjectOwnershipComponent>();
Assert.That(currentClientComponent.OnGainedOwnershipFired);
// Possibly the more important part of this test:
// Check to make sure all other non-former or current ownership clients are synchronized to each ownership change
for (int i = 0; i < NumberOfClients; i++)
{
var clientComponent = clientObjects[i].GetComponent<NetworkObjectOwnershipComponent>();
Assert.That(clientComponent, Is.Not.Null);
Assert.That(clientComponent.OwnerClientId, Is.EqualTo(clientId));
clientComponent.ResetFlags();
}
// We must reset this for each iteration in order to make sure all clients receive the ChangeOwnershipMessage
ownershipMessageHooks.Reset();
// Set the current owner client to the previous one
previousClientComponent = currentClientComponent;
}
// Now change ownership back to the server
serverObject.ChangeOwnership(NetworkManager.ServerClientId);
yield return WaitForConditionOrTimeOut(ownershipMessageHooks);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for all clients to receive the {nameof(ChangeOwnershipMessage)} message (back to server).");
Assert.That(serverComponent.OnGainedOwnershipFired);
Assert.That(serverComponent.OwnerClientId, Is.EqualTo(m_ServerNetworkManager.LocalClientId));
Assert.That(previousClientComponent.OnLostOwnershipFired);
// Make sure all client-side versions of the object is once again owned by the server
for (int i = 0; i < NumberOfClients; i++)
{
var clientComponent = clientObjects[i].GetComponent<NetworkObjectOwnershipComponent>();
Assert.That(clientComponent, Is.Not.Null);
Assert.That(clientComponent.OwnerClientId, Is.EqualTo(m_ServerNetworkManager.LocalClientId));
clientComponent.ResetFlags();
}
serverComponent.ResetFlags();
}
}
}

View File

@@ -8,7 +8,7 @@ namespace Unity.Netcode.RuntimeTests
{
public class NetworkSpawnManagerTests : NetcodeIntegrationTest
{
private ulong serverSideClientId => m_ServerNetworkManager.ServerClientId;
private ulong serverSideClientId => NetworkManager.ServerClientId;
private ulong clientSideClientId => m_ClientNetworkManagers[0].LocalClientId;
private ulong otherClientSideClientId => m_ClientNetworkManagers[1].LocalClientId;

View File

@@ -6,9 +6,229 @@ using NUnit.Framework;
using Unity.Collections;
using Unity.Netcode.TestHelpers.Runtime;
using Random = UnityEngine.Random;
using UnityEngine;
namespace Unity.Netcode.RuntimeTests
{
public class NetVarPermTestComp : NetworkBehaviour
{
public NetworkVariable<Vector3> OwnerWritable_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableBase.DefaultReadPerm, NetworkVariableWritePermission.Owner);
public NetworkVariable<Vector3> ServerWritable_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableBase.DefaultReadPerm, NetworkVariableWritePermission.Server);
}
[TestFixtureSource(nameof(TestDataSource))]
public class NetworkVariablePermissionTests : NetcodeIntegrationTest
{
public static IEnumerable<TestFixtureData> TestDataSource()
{
foreach (HostOrServer hostOrServer in Enum.GetValues(typeof(HostOrServer)))
{
yield return new TestFixtureData(hostOrServer);
}
}
protected override int NumberOfClients => 3;
public NetworkVariablePermissionTests(HostOrServer hostOrServer)
: base(hostOrServer)
{
}
private GameObject m_TestObjPrefab;
private ulong m_TestObjId = 0;
protected override void OnServerAndClientsCreated()
{
m_TestObjPrefab = CreateNetworkObjectPrefab($"[{nameof(NetworkVariablePermissionTests)}.{nameof(m_TestObjPrefab)}]");
var testComp = m_TestObjPrefab.AddComponent<NetVarPermTestComp>();
}
protected override IEnumerator OnServerAndClientsConnected()
{
m_TestObjId = SpawnObject(m_TestObjPrefab, m_ServerNetworkManager).GetComponent<NetworkObject>().NetworkObjectId;
yield return null;
}
private IEnumerator WaitForPositionsAreEqual(NetworkVariable<Vector3> netvar, Vector3 expected)
{
yield return WaitForConditionOrTimeOut(() => netvar.Value == expected);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private IEnumerator WaitForOwnerWritableAreEqualOnAll()
{
yield return WaitForConditionOrTimeOut(CheckOwnerWritableAreEqualOnAll);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private bool CheckOwnerWritableAreEqualOnAll()
{
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testObjServer.OwnerClientId != testObjClient.OwnerClientId ||
testCompServer.OwnerWritable_Position.Value != testCompClient.OwnerWritable_Position.Value ||
testCompServer.OwnerWritable_Position.ReadPerm != testCompClient.OwnerWritable_Position.ReadPerm ||
testCompServer.OwnerWritable_Position.WritePerm != testCompClient.OwnerWritable_Position.WritePerm)
{
return false;
}
}
return true;
}
private IEnumerator WaitForServerWritableAreEqualOnAll()
{
yield return WaitForConditionOrTimeOut(CheckServerWritableAreEqualOnAll);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private bool CheckServerWritableAreEqualOnAll()
{
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testCompServer.ServerWritable_Position.Value != testCompClient.ServerWritable_Position.Value ||
testCompServer.ServerWritable_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm ||
testCompServer.ServerWritable_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm)
{
return false;
}
}
return true;
}
[UnityTest]
public IEnumerator ServerChangesOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
var oldValue = testCompServer.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompServer.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ServerChangesServerWritableNetVar()
{
yield return WaitForServerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
var oldValue = testCompServer.ServerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompServer.ServerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, newValue);
yield return WaitForServerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ClientChangesOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
var oldValue = testCompClient.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompClient.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ClientCannotChangeServerWritableNetVar()
{
yield return WaitForServerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForServerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
var oldValue = testCompClient.ServerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
Assert.That(() => testCompClient.ServerWritable_Position.Value = newValue, Throws.TypeOf<InvalidOperationException>());
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, oldValue);
yield return WaitForServerWritableAreEqualOnAll();
testCompServer.ServerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, newValue);
yield return WaitForServerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ServerCannotChangeOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForOwnerWritableAreEqualOnAll();
var oldValue = testCompServer.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
Assert.That(() => testCompServer.OwnerWritable_Position.Value = newValue, Throws.TypeOf<InvalidOperationException>());
yield return WaitForPositionsAreEqual(testCompServer.OwnerWritable_Position, oldValue);
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
testCompClient.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
}
public struct TestStruct : INetworkSerializable, IEquatable<TestStruct>
{
public uint SomeInt;
@@ -95,8 +315,6 @@ namespace Unity.Netcode.RuntimeTests
private const int k_TestVal2 = 222;
private const int k_TestVal3 = 333;
private const int k_TestKey1 = 0x0f0f;
private static List<NetworkVariableTest> s_ClientNetworkVariableTestInstances = new List<NetworkVariableTest>();
public static void ClientNetworkVariableTestSpawned(NetworkVariableTest networkVariableTest)
{
@@ -111,7 +329,7 @@ namespace Unity.Netcode.RuntimeTests
private NetworkListTestPredicate m_NetworkListPredicateHandler;
private bool m_EnsureLengthSafety;
private readonly bool m_EnsureLengthSafety;
public NetworkVariableTests(bool ensureLengthSafety)
{

View File

@@ -1,3 +1,4 @@
#if COM_UNITY_MODULES_PHYSICS2D
using System.Collections;
using NUnit.Framework;
using Unity.Netcode.Components;
@@ -76,3 +77,4 @@ namespace Unity.Netcode.RuntimeTests
}
}
}
#endif // COM_UNITY_MODULES_PHYSICS2D

View File

@@ -1,3 +1,4 @@
#if COM_UNITY_MODULES_PHYSICS
using System.Collections;
using NUnit.Framework;
using Unity.Netcode.Components;
@@ -75,3 +76,4 @@ namespace Unity.Netcode.RuntimeTests
}
}
}
#endif // COM_UNITY_MODULES_PHYSICS

View File

@@ -0,0 +1,51 @@
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
public class PlayerObjectTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
protected GameObject m_NewPlayerToSpawn;
public PlayerObjectTests(HostOrServer hostOrServer) : base(hostOrServer) { }
protected override void OnServerAndClientsCreated()
{
m_NewPlayerToSpawn = CreateNetworkObjectPrefab("NewPlayerInstance");
base.OnServerAndClientsCreated();
}
[UnityTest]
public IEnumerator SpawnAndReplaceExistingPlayerObject()
{
// Get the server-side player NetworkObject
var originalPlayer = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId];
// Get the client-side player NetworkObject
var playerLocalClient = m_ClientNetworkManagers[0].LocalClient.PlayerObject;
// Create a new player prefab instance
var newPlayer = Object.Instantiate(m_NewPlayerToSpawn);
var newPlayerNetworkObject = newPlayer.GetComponent<NetworkObject>();
newPlayerNetworkObject.NetworkManagerOwner = m_ServerNetworkManager;
// Spawn this instance as a new player object for the client who already has an assigned player object
newPlayerNetworkObject.SpawnAsPlayerObject(m_ClientNetworkManagers[0].LocalClientId);
// Make sure server-side changes are detected, the original player object should no longer be marked as a player
// and the local new player object should.
yield return WaitForConditionOrTimeOut(() => !originalPlayer.IsPlayerObject && newPlayerNetworkObject.IsPlayerObject);
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for server-side player object to change!");
// Make sure the client-side changes are the same
yield return WaitForConditionOrTimeOut(() => m_ClientNetworkManagers[0].LocalClient.PlayerObject != playerLocalClient && !playerLocalClient.IsPlayerObject
&& m_ClientNetworkManagers[0].LocalClient.PlayerObject.IsPlayerObject);
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client-side player object to change!");
}
}
}

View File

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

View File

@@ -16,10 +16,15 @@ namespace Unity.Netcode.RuntimeTests
private void Update()
{
// Since the local position is transformed from local to global and vice-versa on the server and client
// it may accumulate some error. We allow an error of 0.01 over the range of 1000 used in this test.
// This requires precision to 5 digits, so it doesn't weaken the test, while preventing spurious failures
const float maxRoundingError = 0.01f;
// Check the position of the nested object on the client
if (CheckPosition)
{
if (transform.position.y < 0.0f || transform.position.y > 100.0f)
if (transform.position.y < -maxRoundingError || transform.position.y > 100.0f + maxRoundingError)
{
Debug.LogError($"Interpolation failure. transform.position.y is {transform.position.y}. Should be between 0.0 and 100.0");
}

View File

@@ -0,0 +1,383 @@
using NUnit.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using UnityEngine.TestTools;
using static Unity.Netcode.RuntimeTests.UnityTransportTestHelpers;
namespace Unity.Netcode.RuntimeTests
{
public class UnityTransportConnectionTests
{
// For tests using multiple clients.
private const int k_NumClients = 5;
private UnityTransport m_Server;
private UnityTransport[] m_Clients = new UnityTransport[k_NumClients];
private List<TransportEvent> m_ServerEvents;
private List<TransportEvent>[] m_ClientsEvents = new List<TransportEvent>[k_NumClients];
[UnityTearDown]
public IEnumerator Cleanup()
{
if (m_Server)
{
m_Server.Shutdown();
UnityEngine.Object.DestroyImmediate(m_Server);
}
foreach (var transport in m_Clients)
{
if (transport)
{
transport.Shutdown();
UnityEngine.Object.DestroyImmediate(transport);
}
}
foreach (var transportEvents in m_ClientsEvents)
{
transportEvents?.Clear();
}
yield return null;
}
// Check connection with a single client.
[UnityTest]
public IEnumerator ConnectSingleClient()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
m_Server.StartServer();
m_Clients[0].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]);
// Check we've received Connect event on server too.
Assert.AreEqual(1, m_ServerEvents.Count);
Assert.AreEqual(NetworkEvent.Connect, m_ServerEvents[0].Type);
yield return null;
}
// Check connection with multiple clients.
[UnityTest]
public IEnumerator ConnectMultipleClients()
{
InitializeTransport(out m_Server, out m_ServerEvents);
m_Server.StartServer();
for (int i = 0; i < k_NumClients; i++)
{
InitializeTransport(out m_Clients[i], out m_ClientsEvents[i]);
m_Clients[i].StartClient();
}
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[k_NumClients - 1]);
// Check that every client received a Connect event.
Assert.True(m_ClientsEvents.All(evs => evs.Count == 1));
Assert.True(m_ClientsEvents.All(evs => evs[0].Type == NetworkEvent.Connect));
// Check we've received Connect events on server too.
Assert.AreEqual(k_NumClients, m_ServerEvents.Count);
Assert.True(m_ServerEvents.All(ev => ev.Type == NetworkEvent.Connect));
yield return null;
}
// Check server disconnection with a single client.
[UnityTest]
public IEnumerator ServerDisconnectSingleClient()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
m_Server.StartServer();
m_Clients[0].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]);
m_Server.DisconnectRemoteClient(m_ServerEvents[0].ClientID);
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ClientsEvents[0]);
yield return null;
}
// Check server disconnection with multiple clients.
[UnityTest]
public IEnumerator ServerDisconnectMultipleClients()
{
InitializeTransport(out m_Server, out m_ServerEvents);
m_Server.StartServer();
for (int i = 0; i < k_NumClients; i++)
{
InitializeTransport(out m_Clients[i], out m_ClientsEvents[i]);
m_Clients[i].StartClient();
}
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[k_NumClients - 1]);
// Disconnect a single client.
m_Server.DisconnectRemoteClient(m_ServerEvents[0].ClientID);
// Need to manually wait since we don't know which client will get the Disconnect.
yield return new WaitForSeconds(MaxNetworkEventWaitTime);
// Check that we received a Disconnect event on only one client.
Assert.AreEqual(1, m_ClientsEvents.Count(evs => evs.Count == 2 && evs[1].Type == NetworkEvent.Disconnect));
// Disconnect all the other clients.
for (int i = 1; i < k_NumClients; i++)
{
m_Server.DisconnectRemoteClient(m_ServerEvents[i].ClientID);
}
// Need to manually wait since we don't know which client got the Disconnect.
yield return new WaitForSeconds(MaxNetworkEventWaitTime);
// Check that all clients got a Disconnect event.
Assert.True(m_ClientsEvents.All(evs => evs.Count == 2));
Assert.True(m_ClientsEvents.All(evs => evs[1].Type == NetworkEvent.Disconnect));
yield return null;
}
// Check client disconnection from a single client.
[UnityTest]
public IEnumerator ClientDisconnectSingleClient()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
m_Server.StartServer();
m_Clients[0].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]);
m_Clients[0].DisconnectLocalClient();
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ServerEvents);
}
// Check client disconnection with multiple clients.
[UnityTest]
public IEnumerator ClientDisconnectMultipleClients()
{
InitializeTransport(out m_Server, out m_ServerEvents);
m_Server.StartServer();
for (int i = 0; i < k_NumClients; i++)
{
InitializeTransport(out m_Clients[i], out m_ClientsEvents[i]);
m_Clients[i].StartClient();
}
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[k_NumClients - 1]);
// Disconnect a single client.
m_Clients[0].DisconnectLocalClient();
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ServerEvents);
// Disconnect all the other clients.
for (int i = 1; i < k_NumClients; i++)
{
m_Clients[i].DisconnectLocalClient();
}
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ServerEvents);
// Check that we got the correct number of Disconnect events on the server.
Assert.AreEqual(k_NumClients * 2, m_ServerEvents.Count);
Assert.AreEqual(k_NumClients, m_ServerEvents.Count(e => e.Type == NetworkEvent.Disconnect));
yield return null;
}
// Check that server re-disconnects are no-ops.
[UnityTest]
public IEnumerator RepeatedServerDisconnectsNoop()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
m_Server.StartServer();
m_Clients[0].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]);
m_Server.DisconnectRemoteClient(m_ServerEvents[0].ClientID);
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ClientsEvents[0]);
var previousServerEventsCount = m_ServerEvents.Count;
var previousClientEventsCount = m_ClientsEvents[0].Count;
m_Server.DisconnectRemoteClient(m_ServerEvents[0].ClientID);
// Need to wait manually since no event should be generated.
yield return new WaitForSeconds(MaxNetworkEventWaitTime);
// Check we haven't received anything else on the client or server.
Assert.AreEqual(m_ServerEvents.Count, previousServerEventsCount);
Assert.AreEqual(m_ClientsEvents[0].Count, previousClientEventsCount);
yield return null;
}
// Check that client re-disconnects are no-ops.
[UnityTest]
public IEnumerator RepeatedClientDisconnectsNoop()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
m_Server.StartServer();
m_Clients[0].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]);
m_Clients[0].DisconnectLocalClient();
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ServerEvents);
var previousServerEventsCount = m_ServerEvents.Count;
var previousClientEventsCount = m_ClientsEvents[0].Count;
m_Clients[0].DisconnectLocalClient();
// Need to wait manually since no event should be generated.
yield return new WaitForSeconds(MaxNetworkEventWaitTime);
// Check we haven't received anything else on the client or server.
Assert.AreEqual(m_ServerEvents.Count, previousServerEventsCount);
Assert.AreEqual(m_ClientsEvents[0].Count, previousClientEventsCount);
yield return null;
}
// Check connection with different server/listen addresses.
[UnityTest]
public IEnumerator DifferentServerAndListenAddresses()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
m_Server.SetConnectionData("127.0.0.1", 10042, "0.0.0.0");
m_Clients[0].SetConnectionData("127.0.0.1", 10042);
m_Server.StartServer();
m_Clients[0].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]);
// Check we've received Connect event on server too.
Assert.AreEqual(1, m_ServerEvents.Count);
Assert.AreEqual(NetworkEvent.Connect, m_ServerEvents[0].Type);
yield return null;
}
// Check server disconnection with data in send queue.
[UnityTest]
public IEnumerator ServerDisconnectWithDataInQueue()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
m_Server.StartServer();
m_Clients[0].StartClient();
// Wait for the client to connect before we disconnect the client
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]);
var data = new ArraySegment<byte>(new byte[] { 42 });
m_Server.Send(m_ServerEvents[0].ClientID, data, NetworkDelivery.Unreliable);
m_Server.DisconnectRemoteClient(m_ServerEvents[0].ClientID);
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ClientsEvents[0]);
if (m_ClientsEvents[0].Count >= 3)
{
Assert.AreEqual(NetworkEvent.Disconnect, m_ClientsEvents[0][2].Type);
}
else
{
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ClientsEvents[0]);
}
}
// Check client disconnection with data in send queue.
[UnityTest]
public IEnumerator ClientDisconnectWithDataInQueue()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
m_Server.StartServer();
m_Clients[0].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ServerEvents);
var data = new ArraySegment<byte>(new byte[] { 42 });
m_Clients[0].Send(m_Clients[0].ServerClientId, data, NetworkDelivery.Unreliable);
m_Clients[0].DisconnectLocalClient();
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
if (m_ServerEvents.Count >= 3)
{
Assert.AreEqual(NetworkEvent.Disconnect, m_ServerEvents[2].Type);
}
else
{
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ServerEvents);
}
}
// Check that a server can disconnect a client after another client has disconnected.
[UnityTest]
public IEnumerator ServerDisconnectAfterClientDisconnect()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]);
InitializeTransport(out m_Clients[1], out m_ClientsEvents[1]);
m_Server.StartServer();
m_Clients[0].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]);
m_Clients[1].StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[1]);
m_Clients[0].DisconnectLocalClient();
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ServerEvents);
// Pick the client ID of the still connected client.
var clientId = m_ServerEvents[0].ClientID;
if (m_ServerEvents[2].ClientID == clientId)
{
clientId = m_ServerEvents[1].ClientID;
}
m_Server.DisconnectRemoteClient(clientId);
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ClientsEvents[1]);
yield return null;
}
}
}

View File

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

View File

@@ -0,0 +1,94 @@
using NUnit.Framework;
using System.Collections;
using Unity.Networking.Transport;
using Unity.Networking.Transport.Utilities;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using UTPNetworkEvent = Unity.Networking.Transport.NetworkEvent;
using static Unity.Netcode.RuntimeTests.UnityTransportTestHelpers;
namespace Unity.Netcode.RuntimeTests
{
// Thin wrapper around a UTP NetworkDriver that can act as a client to a UnityTransport server.
// In particular that means the pipelines are set up the same way as in UnityTransport.
//
// The only reason it's defined as a MonoBehaviour is that OnDestroy is the only reliable way
// to get the driver's Dispose method called from a UnityTest. Making it disposable would be
// the preferred solution, but that doesn't always mesh well with coroutines.
public class UnityTransportDriverClient : MonoBehaviour
{
private NetworkDriver m_Driver;
public NetworkDriver Driver => m_Driver;
private NetworkConnection m_Connection;
private NetworkPipeline m_UnreliableSequencedPipeline;
private NetworkPipeline m_ReliableSequencedPipeline;
private NetworkPipeline m_ReliableSequencedFragmentedPipeline;
public NetworkPipeline UnreliableSequencedPipeline => m_UnreliableSequencedPipeline;
public NetworkPipeline ReliableSequencedPipeline => m_ReliableSequencedPipeline;
public NetworkPipeline ReliableSequencedFragmentedPipeline => m_ReliableSequencedFragmentedPipeline;
private NetworkPipeline m_LastEventPipeline;
public NetworkPipeline LastEventPipeline => m_LastEventPipeline;
private void Awake()
{
var maxCap = UnityTransport.InitialMaxPayloadSize + 128;
var settings = new NetworkSettings();
settings.WithFragmentationStageParameters(payloadCapacity: maxCap);
var fragParams = new FragmentationUtility.Parameters() { PayloadCapacity = maxCap };
m_Driver = NetworkDriver.Create(settings);
m_UnreliableSequencedPipeline = m_Driver.CreatePipeline(typeof(UnreliableSequencedPipelineStage));
m_ReliableSequencedPipeline = m_Driver.CreatePipeline(typeof(ReliableSequencedPipelineStage));
m_ReliableSequencedFragmentedPipeline = m_Driver.CreatePipeline(typeof(FragmentationPipelineStage), typeof(ReliableSequencedPipelineStage));
}
private void Update()
{
m_Driver.ScheduleUpdate().Complete();
}
private void OnDestroy()
{
if (m_Driver.IsCreated)
{
m_Driver.Dispose();
}
}
public void Connect()
{
var endpoint = NetworkEndPoint.LoopbackIpv4;
endpoint.Port = 7777;
m_Connection = m_Driver.Connect(endpoint);
}
// Wait for the given event to be generated by the client's driver.
public IEnumerator WaitForNetworkEvent(UTPNetworkEvent.Type type)
{
float startTime = Time.realtimeSinceStartup;
while (Time.realtimeSinceStartup - startTime < MaxNetworkEventWaitTime)
{
UTPNetworkEvent.Type eventType = m_Driver.PopEvent(out _, out _, out m_LastEventPipeline);
if (eventType != UTPNetworkEvent.Type.Empty)
{
Assert.AreEqual(type, eventType);
yield break;
}
yield return null;
}
Assert.Fail("Timed out while waiting for network event.");
}
}
}

View File

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

View File

@@ -0,0 +1,85 @@
using NUnit.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
namespace Unity.Netcode.RuntimeTests
{
public static class UnityTransportTestHelpers
{
// Half a second might seem like a very long time to wait for a network event, but in CI
// many of the machines are underpowered (e.g. old Android devices or Macs) and there are
// sometimes very high lag spikes. PS4 and Switch are particularly sensitive in this regard
// so we allow even more time for these platforms.
public const float MaxNetworkEventWaitTime = 0.5f;
// Wait for an event to appear in the given event list (must be the very next event).
public static IEnumerator WaitForNetworkEvent(NetworkEvent type, List<TransportEvent> events, float timeout = MaxNetworkEventWaitTime)
{
int initialCount = events.Count;
float startTime = Time.realtimeSinceStartup;
while (Time.realtimeSinceStartup - startTime < timeout)
{
if (events.Count > initialCount)
{
Assert.AreEqual(type, events[initialCount].Type);
yield break;
}
yield return new WaitForSeconds(0.01f);
}
Assert.Fail("Timed out while waiting for network event.");
}
// 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)
{
var logger = new TransportEventLogger();
events = logger.Events;
transport = new GameObject().AddComponent<UnityTransport>();
transport.OnTransportEvent += logger.HandleEvent;
transport.SetMaxPayloadSize(maxPayloadSize);
transport.Initialize();
}
// Information about an event generated by a transport (basically just the parameters that
// are normally passed along to a TransportEventDelegate).
public struct TransportEvent
{
public NetworkEvent Type;
public ulong ClientID;
public ArraySegment<byte> Data;
public float ReceiveTime;
}
// Utility class that logs events generated by a UnityTransport. Set it up by adding the
// HandleEvent method as an OnTransportEvent delegate of the transport. The list of events
// (in order in which they were generated) can be accessed through the Events property.
public class TransportEventLogger
{
private readonly List<TransportEvent> m_Events = new List<TransportEvent>();
public List<TransportEvent> Events => m_Events;
public void HandleEvent(NetworkEvent type, ulong clientID, ArraySegment<byte> data, float receiveTime)
{
// Copy the data since the backing array will be reused for future messages.
if (data != default(ArraySegment<byte>))
{
var dataCopy = new byte[data.Count];
Array.Copy(data.Array, data.Offset, dataCopy, 0, data.Count);
data = new ArraySegment<byte>(dataCopy);
}
m_Events.Add(new TransportEvent
{
Type = type,
ClientID = clientID,
Data = data,
ReceiveTime = receiveTime
});
}
}
}
}

View File

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

View File

@@ -0,0 +1,461 @@
using NUnit.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using UnityEngine.TestTools;
using static Unity.Netcode.RuntimeTests.UnityTransportTestHelpers;
namespace Unity.Netcode.RuntimeTests
{
public class UnityTransportTests
{
// No need to test all reliable delivery methods since they all map to the same pipeline.
private static readonly NetworkDelivery[] k_DeliveryParameters =
{
NetworkDelivery.Unreliable,
NetworkDelivery.UnreliableSequenced,
NetworkDelivery.Reliable
};
private UnityTransport m_Server, m_Client1, m_Client2;
private List<TransportEvent> m_ServerEvents, m_Client1Events, m_Client2Events;
[UnityTearDown]
public IEnumerator Cleanup()
{
if (m_Server)
{
m_Server.Shutdown();
// Need to destroy the GameObject (all assigned components will get destroyed too)
UnityEngine.Object.DestroyImmediate(m_Server.gameObject);
}
if (m_Client1)
{
m_Client1.Shutdown();
// Need to destroy the GameObject (all assigned components will get destroyed too)
UnityEngine.Object.DestroyImmediate(m_Client1.gameObject);
}
if (m_Client2)
{
m_Client2.Shutdown();
// Need to destroy the GameObject (all assigned components will get destroyed too)
UnityEngine.Object.DestroyImmediate(m_Client2.gameObject);
}
m_ServerEvents?.Clear();
m_Client1Events?.Clear();
m_Client2Events?.Clear();
yield return null;
}
// Check if can make a simple data exchange.
[UnityTest]
public IEnumerator PingPong([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var ping = new ArraySegment<byte>(Encoding.ASCII.GetBytes("ping"));
m_Client1.Send(m_Client1.ServerClientId, ping, delivery);
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
Assert.That(m_ServerEvents[1].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("ping")));
var pong = new ArraySegment<byte>(Encoding.ASCII.GetBytes("pong"));
m_Server.Send(m_ServerEvents[0].ClientID, pong, delivery);
yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);
Assert.That(m_Client1Events[1].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("pong")));
yield return null;
}
// Check if can make a simple data exchange (both ways at a time).
[UnityTest]
public IEnumerator PingPongSimultaneous([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var ping = new ArraySegment<byte>(Encoding.ASCII.GetBytes("ping"));
m_Server.Send(m_ServerEvents[0].ClientID, ping, delivery);
m_Client1.Send(m_Client1.ServerClientId, ping, delivery);
// Once one event is in the other should be too.
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
Assert.That(m_ServerEvents[1].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("ping")));
Assert.That(m_Client1Events[1].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("ping")));
var pong = new ArraySegment<byte>(Encoding.ASCII.GetBytes("pong"));
m_Server.Send(m_ServerEvents[0].ClientID, pong, delivery);
m_Client1.Send(m_Client1.ServerClientId, pong, delivery);
// Once one event is in the other should be too.
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
Assert.That(m_ServerEvents[2].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("pong")));
Assert.That(m_Client1Events[2].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("pong")));
yield return null;
}
// Test is ignored on Switch, PS4, and PS5 because on these platforms the OS buffers for
// 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)
{
// 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);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var payloadData = new byte[payloadSize];
for (int i = 0; i < payloadData.Length; i++)
{
payloadData[i] = (byte)i;
}
var payload = new ArraySegment<byte>(payloadData);
m_Client1.Send(m_Client1.ServerClientId, payload, delivery);
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents, MaxNetworkEventWaitTime * 4);
Assert.AreEqual(payloadSize, m_ServerEvents[1].Data.Count);
var receivedArray = m_ServerEvents[1].Data.Array;
var receivedArrayOffset = m_ServerEvents[1].Data.Offset;
for (int i = 0; i < payloadSize; i++)
{
Assert.AreEqual(payloadData[i], receivedArray[receivedArrayOffset + i]);
}
yield return null;
}
// Check making multiple sends to a client in a single frame.
[UnityTest]
public IEnumerator MultipleSendsSingleFrame([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var data1 = new ArraySegment<byte>(new byte[] { 11 });
m_Client1.Send(m_Client1.ServerClientId, data1, delivery);
var data2 = new ArraySegment<byte>(new byte[] { 22 });
m_Client1.Send(m_Client1.ServerClientId, data2, delivery);
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
Assert.AreEqual(3, m_ServerEvents.Count);
Assert.AreEqual(NetworkEvent.Data, m_ServerEvents[2].Type);
Assert.AreEqual(11, m_ServerEvents[1].Data.First());
Assert.AreEqual(22, m_ServerEvents[2].Data.First());
yield return null;
}
// Check sending data to multiple clients.
[UnityTest]
public IEnumerator SendMultipleClients([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
InitializeTransport(out m_Client2, out m_Client2Events);
m_Server.StartServer();
m_Client1.StartClient();
m_Client2.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
if (m_Client2Events.Count == 0)
{
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client2Events);
}
// Ensure we got both Connect events.
Assert.AreEqual(2, m_ServerEvents.Count);
var data1 = new ArraySegment<byte>(new byte[] { 11 });
m_Server.Send(m_ServerEvents[0].ClientID, data1, delivery);
var data2 = new ArraySegment<byte>(new byte[] { 22 });
m_Server.Send(m_ServerEvents[1].ClientID, data2, delivery);
// Once one has received its data, the other should have too.
yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);
// Do make sure the other client got its Data event.
Assert.AreEqual(2, m_Client2Events.Count);
Assert.AreEqual(NetworkEvent.Data, m_Client2Events[1].Type);
byte c1Data = m_Client1Events[1].Data.First();
byte c2Data = m_Client2Events[1].Data.First();
Assert.That((c1Data == 11 && c2Data == 22) || (c1Data == 22 && c2Data == 11));
yield return null;
}
// Check receiving data from multiple clients.
[UnityTest]
public IEnumerator ReceiveMultipleClients([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
InitializeTransport(out m_Client2, out m_Client2Events);
m_Server.StartServer();
m_Client1.StartClient();
m_Client2.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
if (m_Client2Events.Count == 0)
{
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client2Events);
}
var data1 = new ArraySegment<byte>(new byte[] { 11 });
m_Client1.Send(m_Client1.ServerClientId, data1, delivery);
var data2 = new ArraySegment<byte>(new byte[] { 22 });
m_Client2.Send(m_Client2.ServerClientId, data2, delivery);
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
// Make sure we got both data messages.
Assert.AreEqual(4, m_ServerEvents.Count);
Assert.AreEqual(NetworkEvent.Data, m_ServerEvents[3].Type);
byte sData1 = m_ServerEvents[2].Data.First();
byte sData2 = m_ServerEvents[3].Data.First();
Assert.That((sData1 == 11 && sData2 == 22) || (sData1 == 22 && sData2 == 11));
yield return null;
}
// Check that we get disconnected when overflowing the reliable send queue.
[UnityTest]
public IEnumerator DisconnectOnReliableSendQueueOverflow()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
m_Server.Shutdown();
var numSends = (UnityTransport.InitialMaxSendQueueSize / 1024);
for (int i = 0; i < numSends; i++)
{
var payload = new ArraySegment<byte>(new byte[1024]);
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.Reliable);
}
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.");
Assert.AreEqual(2, m_Client1Events.Count);
Assert.AreEqual(NetworkEvent.Disconnect, m_Client1Events[1].Type);
yield return null;
}
// Check that it's fine to overflow the unreliable send queue (traffic is flushed on overflow).
// Test is ignored on Switch, PS4, and PS5 because on these platforms the OS buffers for
// 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 SendCompletesOnUnreliableSendQueueOverflow()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var numSends = (UnityTransport.InitialMaxSendQueueSize / 1024) + 1;
for (int i = 0; i < numSends; i++)
{
var payload = new ArraySegment<byte>(new byte[1024]);
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.Unreliable);
}
// Manually wait. This ends up generating quite a bit of packets and it might take a
// while for everything to make it to the server.
yield return new WaitForSeconds(numSends * 0.02f);
// Extra event is the connect event.
Assert.AreEqual(numSends + 1, m_ServerEvents.Count);
for (int i = 1; i <= numSends; i++)
{
Assert.AreEqual(NetworkEvent.Data, m_ServerEvents[i].Type);
Assert.AreEqual(1024, m_ServerEvents[i].Data.Count);
}
yield return null;
}
// 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).
[UnityTest]
[UnityPlatform(include = new[] { RuntimePlatform.OSXEditor, RuntimePlatform.WindowsEditor, RuntimePlatform.LinuxEditor })]
public IEnumerator SimulatorParametersAreEffective()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.SetDebugSimulatorParameters(0, 0, 100);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var data = new ArraySegment<byte>(new byte[] { 42 });
m_Client1.Send(m_Client1.ServerClientId, data, NetworkDelivery.Reliable);
yield return new WaitForSeconds(MaxNetworkEventWaitTime);
Assert.AreEqual(1, m_ServerEvents.Count);
yield return null;
}
// Check that RTT is reported correctly.
[UnityTest]
[UnityPlatform(include = new[] { RuntimePlatform.OSXEditor, RuntimePlatform.WindowsEditor, RuntimePlatform.LinuxEditor })]
public IEnumerator CurrentRttReportedCorrectly()
{
const int simulatedRtt = 25;
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.SetDebugSimulatorParameters(simulatedRtt, 0, 0);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var data = new ArraySegment<byte>(new byte[] { 42 });
m_Client1.Send(m_Client1.ServerClientId, data, NetworkDelivery.Reliable);
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents,
timeout: MaxNetworkEventWaitTime + (2 * simulatedRtt));
Assert.GreaterOrEqual(m_Client1.GetCurrentRtt(m_Client1.ServerClientId), simulatedRtt);
yield return null;
}
[UnityTest]
public IEnumerator SendQueuesFlushedOnShutdown([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var data = new ArraySegment<byte>(new byte[] { 42 });
m_Client1.Send(m_Client1.ServerClientId, data, delivery);
m_Client1.Shutdown();
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
yield return null;
}
[UnityTest]
public IEnumerator SendQueuesFlushedOnLocalClientDisconnect([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var data = new ArraySegment<byte>(new byte[] { 42 });
m_Client1.Send(m_Client1.ServerClientId, data, delivery);
m_Client1.DisconnectLocalClient();
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
yield return null;
}
[UnityTest]
public IEnumerator SendQueuesFlushedOnRemoteClientDisconnect([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
m_Server.StartServer();
m_Client1.StartClient();
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
var data = new ArraySegment<byte>(new byte[] { 42 });
m_Server.Send(m_Client1.ServerClientId, data, delivery);
m_Server.DisconnectRemoteClient(m_ServerEvents[0].ClientID);
yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);
yield return null;
}
}
}

View File

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

View File

@@ -9,7 +9,7 @@
"Unity.Multiplayer.NetStats",
"Unity.Multiplayer.Tools.MetricTypes",
"Unity.Multiplayer.Tools.NetStats",
"Unity.Netcode.Adapter.UTP",
"Unity.Networking.Transport",
"ClientNetworkTransform",
"Unity.Netcode.TestHelpers.Runtime"
],
@@ -27,15 +27,10 @@
"expression": "(0,2022.2.0a5)",
"define": "UNITY_UNET_PRESENT"
},
{
"name": "com.unity.netcode.adapter.utp",
"expression": "",
"define": "UTP_ADAPTER"
},
{
"name": "com.unity.multiplayer.tools",
"expression": "1.0.0-pre.4",
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_4"
"expression": "1.0.0-pre.7",
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_7"
}
]
}
}