com.unity.netcode.gameobjects@2.2.0
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [2.2.0] - 2024-12-12 ### Added - Added `NetworkObject.OwnershipStatus.SessionOwner` to allow Network Objects to be distributable and only owned by the Session Owner. This flag will override all other `OwnershipStatus` flags. (#3175) - Added `UnityTransport.GetEndpoint` method to provide a way to obtain `NetworkEndpoint` information of a connection via client identifier. (#3130) - Added `NetworkTransport.OnEarlyUpdate` and `NetworkTransport.OnPostLateUpdate` methods to provide more control over handling transport related events at the start and end of each frame. (#3113) ### Fixed - Fixed issue where the server, host, or session owner would not populate the in-scene place `NetworkObject` table if the scene was loaded prior to starting the `NetworkManager`. (#3177) - Fixed issue where the `NetworkObjectIdHash` value could be incorrect when entering play mode while still in prefab edit mode with pending changes and using MPPM. (#3162) - Fixed issue where a sever only `NetworkManager` instance would spawn the actual `NetworkPrefab`'s `GameObject` as opposed to creating an instance of it. (#3160) - Fixed issue where only the session owner (as opposed to all clients) would handle spawning prefab overrides properly when using a distributed authority network topology. (#3160) - Fixed issue where an exception was thrown when calling `NetworkManager.Shutdown` after calling `UnityTransport.Shutdown`. (#3118) - Fixed issue where `NetworkList` properties on in-scene placed `NetworkObject`s could cause small memory leaks when entering playmode. (#3147) - Fixed in-scene `NertworkObject` synchronization issue when loading a scene with currently connected clients connected to a session created by a `NetworkManager` started as a server (i.e. not as a host). (#3133) - Fixed issue where a `NetworkManager` started as a server would not add itself as an observer to in-scene placed `NetworkObject`s instantiated and spawned by a scene loading event. (#3133) - Fixed issue where spawning a player using `NetworkObject.InstantiateAndSpawn` or `NetworkSpawnManager.InstantiateAndSpawn` would not update the `NetworkSpawnManager.PlayerObjects` or assign the newly spawned player to the `NetworkClient.PlayerObject`. (#3122) - Fixed issue where queued UnitTransport (NetworkTransport) message batches were being sent on the next frame. They are now sent at the end of the frame during `PostLateUpdate`. (#3113) - Fixed issue where `NotOwnerRpcTarget` or `OwnerRpcTarget` were not using their replacements `NotAuthorityRpcTarget` and `AuthorityRpcTarget` which would invoke a warning. (#3111) - Fixed issue where client is removed as an observer from spawned objects when their player instance is despawned. (#3110) - Fixed issue where `NetworkAnimator` would statically allocate write buffer space for `Animator` parameters that could cause a write error if the number of parameters exceeded the space allocated. (#3108) ### Changed - In-scene placed `NetworkObject`s have been made distributable when balancing object distribution after a connection event. (#3175) - Optimised `NetworkVariable` and `NetworkTransform` related packets when in Distributed Authority mode. - The Debug Simulator section of the Unity Transport component was removed. This section was not functional anymore and users are now recommended to use the more featureful [Network Simulator](https://docs-multiplayer.unity3d.com/tools/current/tools-network-simulator/) tool from the Multiplayer Tools package instead. (#3121)
This commit is contained in:
@@ -205,13 +205,14 @@ namespace Unity.Netcode.RuntimeTests
|
||||
[UnityTest]
|
||||
public IEnumerator NetworkVariableDelta()
|
||||
{
|
||||
var component = Client.LocalClient.PlayerObject.GetComponent<TestNetworkComponent>();
|
||||
var message = new NetworkVariableDeltaMessage
|
||||
{
|
||||
NetworkObjectId = 0,
|
||||
NetworkBehaviourIndex = 1,
|
||||
DeliveryMappedNetworkVariableIndex = new HashSet<int> { 2, 3, 4 },
|
||||
NetworkObjectId = Client.LocalClient.PlayerObject.NetworkObjectId,
|
||||
NetworkBehaviourIndex = component.NetworkBehaviourId,
|
||||
DeliveryMappedNetworkVariableIndex = new HashSet<int> { 0, 1 },
|
||||
TargetClientId = 5,
|
||||
NetworkBehaviour = Client.LocalClient.PlayerObject.GetComponent<TestNetworkComponent>(),
|
||||
NetworkBehaviour = component,
|
||||
};
|
||||
|
||||
yield return SendMessage(ref message);
|
||||
@@ -569,7 +570,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
private IEnumerator SendMessage<T>(ref T message) where T : INetworkMessage
|
||||
{
|
||||
Client.MessageManager.SetVersion(k_ClientId, XXHash.Hash32(typeof(T).FullName), 0);
|
||||
Client.MessageManager.SetVersion(k_ClientId, XXHash.Hash32(typeof(T).FullName), message.Version);
|
||||
|
||||
var clientIds = new NativeArray<ulong>(1, Allocator.Temp);
|
||||
clientIds[0] = k_ClientId;
|
||||
@@ -625,7 +626,8 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
|
||||
var writer = new FastBufferWriter(1024, Allocator.Temp);
|
||||
message.Serialize(writer, 0);
|
||||
// Serialize the message using the known message version
|
||||
message.Serialize(writer, message.Version);
|
||||
|
||||
var testName = TestContext.CurrentContext.Test.Name;
|
||||
if (!m_ExpectedMessages.ContainsKey(testName))
|
||||
|
||||
@@ -216,6 +216,21 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_ErrorLogLevel2.Append($"[Client-{client.LocalClientId} Prefab Mismatch][Expected GlobalObjectIdHash: {expectedGlobalObjectIdHash} but was {remoteNetworkClient.PlayerObject.GlobalObjectIdHash}]");
|
||||
success = false;
|
||||
}
|
||||
|
||||
var foundPlayer = false;
|
||||
foreach (var playerObject in client.SpawnManager.PlayerObjects)
|
||||
{
|
||||
if (playerObject.NetworkObjectId == clientToValidate.LocalClient.PlayerObject.NetworkObjectId)
|
||||
{
|
||||
foundPlayer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundPlayer)
|
||||
{
|
||||
m_ErrorLogLevel1.AppendLine($"[Client-{client.LocalClientId}] Local {nameof(NetworkSpawnManager.PlayerObjects)} does not contain {clientToValidate.LocalClient.PlayerObject.name}!");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -231,6 +246,12 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_ErrorLogLevel1.AppendLine($"[Client-{m_ServerNetworkManager.LocalClientId}]{m_ErrorLogLevel2}");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (m_ServerNetworkManager.LocalClient.PlayerObject == null)
|
||||
{
|
||||
m_ErrorLogLevel1.AppendLine($"[Client-{m_ServerNetworkManager.LocalClientId}] Local {nameof(NetworkClient.PlayerObject)} is null!");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var client in m_ClientNetworkManagers)
|
||||
@@ -240,6 +261,17 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_ErrorLogLevel1.AppendLine($"[Client-{client.LocalClientId}]{m_ErrorLogLevel2}");
|
||||
success = false;
|
||||
}
|
||||
if (client.LocalClient.PlayerObject == null)
|
||||
{
|
||||
m_ErrorLogLevel1.AppendLine($"[Client-{client.LocalClientId}] Local {nameof(NetworkClient.PlayerObject)} is null!");
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
if (!client.SpawnManager.PlayerObjects.Contains(client.LocalClient.PlayerObject))
|
||||
{
|
||||
m_ErrorLogLevel1.AppendLine($"[Client-{client.LocalClientId}] Local {nameof(NetworkSpawnManager.PlayerObjects)} does not contain {client.LocalClient.PlayerObject.name}!");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
@@ -250,12 +282,19 @@ namespace Unity.Netcode.RuntimeTests
|
||||
return m_PlayerPrefabs[Random.Range(0, m_PlayerPrefabs.Count() - 1)].GetComponent<NetworkObject>();
|
||||
}
|
||||
|
||||
public enum PlayerSpawnTypes
|
||||
{
|
||||
Normal,
|
||||
NetworkObject,
|
||||
SpawnManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that when a client changes their player object that all connected client instances mirror the
|
||||
/// client's new player object.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator ValidatePlayerObjects()
|
||||
public IEnumerator ValidatePlayerObjects([Values] PlayerSpawnTypes playerSpawnType)
|
||||
{
|
||||
// Just do a quick validation for all connected client's NetworkClients
|
||||
yield return WaitForConditionOrTimeOut(AllNetworkClientsValidated);
|
||||
@@ -268,14 +307,51 @@ namespace Unity.Netcode.RuntimeTests
|
||||
if (m_UseHost)
|
||||
{
|
||||
playerPrefabToSpawn = GetRandomPlayerPrefab();
|
||||
playerInstance = SpawnPlayerObject(playerPrefabToSpawn.gameObject, m_ServerNetworkManager);
|
||||
switch (playerSpawnType)
|
||||
{
|
||||
case PlayerSpawnTypes.Normal:
|
||||
{
|
||||
playerInstance = SpawnPlayerObject(playerPrefabToSpawn.gameObject, m_ServerNetworkManager);
|
||||
break;
|
||||
}
|
||||
case PlayerSpawnTypes.NetworkObject:
|
||||
{
|
||||
playerInstance = NetworkObject.InstantiateAndSpawn(playerPrefabToSpawn.gameObject, m_ServerNetworkManager, isPlayerObject: true).gameObject;
|
||||
break;
|
||||
}
|
||||
case PlayerSpawnTypes.SpawnManager:
|
||||
{
|
||||
playerInstance = m_ServerNetworkManager.SpawnManager.InstantiateAndSpawn(playerPrefabToSpawn, isPlayerObject: true).gameObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_ChangedPlayerPrefabs.Add(m_ServerNetworkManager.LocalClientId, playerPrefabToSpawn.GlobalObjectIdHash);
|
||||
}
|
||||
|
||||
foreach (var client in m_ClientNetworkManagers)
|
||||
{
|
||||
playerPrefabToSpawn = GetRandomPlayerPrefab();
|
||||
playerInstance = SpawnPlayerObject(playerPrefabToSpawn.gameObject, client);
|
||||
var networkManager = m_DistributedAuthority ? client : m_ServerNetworkManager;
|
||||
|
||||
switch (playerSpawnType)
|
||||
{
|
||||
case PlayerSpawnTypes.Normal:
|
||||
{
|
||||
playerInstance = SpawnPlayerObject(playerPrefabToSpawn.gameObject, client);
|
||||
break;
|
||||
}
|
||||
case PlayerSpawnTypes.NetworkObject:
|
||||
{
|
||||
playerInstance = NetworkObject.InstantiateAndSpawn(playerPrefabToSpawn.gameObject, networkManager, client.LocalClientId, isPlayerObject: true).gameObject;
|
||||
break;
|
||||
}
|
||||
case PlayerSpawnTypes.SpawnManager:
|
||||
{
|
||||
playerInstance = networkManager.SpawnManager.InstantiateAndSpawn(playerPrefabToSpawn, client.LocalClientId, isPlayerObject: true).gameObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_ChangedPlayerPrefabs.Add(client.LocalClientId, playerPrefabToSpawn.GlobalObjectIdHash);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return WaitForConditionOrTimeOut(ValidateObjectSpawnedOnAllClients);
|
||||
AssertOnTimeout($"[Failed To Spawn] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
// Validate the base non-assigned persmissions value for all instances are the same.
|
||||
// Validate the base non-assigned permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -141,9 +141,15 @@ namespace Unity.Netcode.RuntimeTests
|
||||
foreach (var permissionObject in Enum.GetValues(typeof(NetworkObject.OwnershipStatus)))
|
||||
{
|
||||
var permission = (NetworkObject.OwnershipStatus)permissionObject;
|
||||
// Adding the SessionOwner flag here should fail as this NetworkObject is not owned by the Session Owner
|
||||
if (permission == NetworkObject.OwnershipStatus.SessionOwner)
|
||||
{
|
||||
Assert.IsFalse(firstInstance.SetOwnershipStatus(permission), $"[Add][IncorrectPermissions] Setting {NetworkObject.OwnershipStatus.SessionOwner} is not valid when the client is not the Session Owner: \n {m_ErrorLog}!");
|
||||
continue;
|
||||
}
|
||||
// Add the status
|
||||
firstInstance.SetOwnershipStatus(permission);
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Add][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -153,7 +159,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
continue;
|
||||
}
|
||||
firstInstance.RemoveOwnershipStatus(permission);
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Remove][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
}
|
||||
@@ -163,7 +169,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
firstInstance.SetOwnershipStatus(multipleFlags, true);
|
||||
Assert.IsTrue(firstInstance.HasOwnershipStatus(multipleFlags), $"[Set][Multi-flag Failure] Expected: {(ushort)multipleFlags} but was {(ushort)firstInstance.Ownership}!");
|
||||
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Set Multiple][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -175,7 +181,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// Validate that the Distributable flag is still set
|
||||
Assert.IsTrue(firstInstance.HasOwnershipStatus(NetworkObject.OwnershipStatus.Distributable), $"[Remove][Multi-flag Failure] Expected: {(ushort)NetworkObject.OwnershipStatus.Distributable} but was {(ushort)firstInstance.Ownership}!");
|
||||
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Set Multiple][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -186,7 +192,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// Clear the flags, set the permissions to transferrable, and lock ownership in one pass.
|
||||
firstInstance.SetOwnershipStatus(NetworkObject.OwnershipStatus.Transferable, true, NetworkObject.OwnershipLockActions.SetAndLock);
|
||||
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Reset][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -199,7 +205,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
$" status is {secondInstanceHelper.OwnershipPermissionsFailureStatus}!");
|
||||
|
||||
firstInstance.SetOwnershipLock(false);
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Unlock][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -208,7 +214,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// Now try to acquire ownership
|
||||
secondInstance.ChangeOwnership(m_ClientNetworkManagers[1].LocalClientId);
|
||||
|
||||
// Validate the persmissions value for all instances are the same
|
||||
// Validate the permissions value for all instances are the same
|
||||
yield return WaitForConditionOrTimeOut(() => secondInstance.IsOwner);
|
||||
AssertOnTimeout($"[Acquire Ownership Failed] Client-{m_ClientNetworkManagers[1].LocalClientId} failed to get ownership!");
|
||||
|
||||
@@ -220,7 +226,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// Clear the flags, set the permissions to RequestRequired, and lock ownership in one pass.
|
||||
secondInstance.SetOwnershipStatus(NetworkObject.OwnershipStatus.RequestRequired, true);
|
||||
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Unlock][Permissions Mismatch] {secondInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -238,7 +244,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
// Start with a request for the client we expect to be given ownership
|
||||
var requestStatus = firstInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
|
||||
// Get the 3rd client to send a request at the "relatively" same time
|
||||
var thirdInstance = m_ClientNetworkManagers[2].SpawnManager.SpawnedObjects[networkObjectId];
|
||||
@@ -248,7 +254,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
requestStatus = thirdInstance.RequestOwnership();
|
||||
|
||||
// We expect the 3rd client's request should be able to be sent at this time as well (i.e. creates the race condition between two clients)
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{m_ServerNetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{m_ServerNetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
|
||||
// We expect the first requesting client to be given ownership
|
||||
yield return WaitForConditionOrTimeOut(() => firstInstance.IsOwner);
|
||||
@@ -263,7 +269,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return WaitForConditionOrTimeOut(() => thirdInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.RequestInProgress);
|
||||
AssertOnTimeout($"[Request In Progress Failed] Client-{thirdInstanceHelper.NetworkManager.LocalClientId} did not get the right request denied reponse!");
|
||||
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Unlock][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -278,11 +284,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// Send out a request from three clients at the same time
|
||||
// The first one sent (and received for this test) gets ownership
|
||||
requestStatus = secondInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{secondInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{secondInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
requestStatus = thirdInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
requestStatus = fourthInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{fourthInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{fourthInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
|
||||
// The 2nd and 3rd client should be denied and the 4th client should be approved
|
||||
yield return WaitForConditionOrTimeOut(() =>
|
||||
@@ -296,7 +302,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return WaitForConditionOrTimeOut(() => ValidateAllInstancesAreOwnedByClient(secondInstance.NetworkManager.LocalClientId));
|
||||
AssertOnTimeout($"[Ownership Mismatch] {secondInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
// Validate the persmissions value for all instances are the same.
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Unlock][Permissions Mismatch] {secondInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
@@ -314,22 +320,84 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
// Send out a request from all three clients
|
||||
requestStatus = firstInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
requestStatus = thirdInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
requestStatus = fourthInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{fourthInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{fourthInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
requestStatus = daHostInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{daHostInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{daHostInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
|
||||
// The server and the 2nd client should be denied and the third client should be approved
|
||||
// Only the client marked as ClientToAllowOwnership (daHost) should be approved. All others should be denied.
|
||||
yield return WaitForConditionOrTimeOut(() =>
|
||||
(firstInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Denied) &&
|
||||
(thirdInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Denied) &&
|
||||
(fourthInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Denied) &&
|
||||
(daHostInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Approved)
|
||||
);
|
||||
AssertOnTimeout($"[Targeted Owner] Client-{daHostInstance.NetworkManager.LocalClientId} did not get the right request reponse: {daHostInstanceHelper.OwnershipRequestResponseStatus} Expecting: {NetworkObject.OwnershipRequestResponseStatus.Approved}!");
|
||||
AssertOnTimeout($"[Targeted Owner] Client-{daHostInstance.NetworkManager.LocalClientId} did not get the right request response: {daHostInstanceHelper.OwnershipRequestResponseStatus} Expecting: {NetworkObject.OwnershipRequestResponseStatus.Approved}!");
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// Test OwnershipStatus.SessionOwner:
|
||||
///////////////////////////////////////////////
|
||||
|
||||
OwnershipPermissionsTestHelper.CurrentOwnedInstance = daHostInstance;
|
||||
m_ObjectToValidate = OwnershipPermissionsTestHelper.CurrentOwnedInstance;
|
||||
|
||||
// Add multiple statuses
|
||||
daHostInstance.SetOwnershipStatus(NetworkObject.OwnershipStatus.Transferable | NetworkObject.OwnershipStatus.SessionOwner);
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Add][Permissions Mismatch] {daHostInstance.name}: \n {m_ErrorLog}");
|
||||
|
||||
// Trying to set SessionOwner flag should override any other flags.
|
||||
Assert.IsFalse(daHostInstance.HasOwnershipStatus(NetworkObject.OwnershipStatus.Transferable), $"[Set][SessionOwner flag Failure] Expected: {NetworkObject.OwnershipStatus.Transferable} not to be set!");
|
||||
|
||||
// Add another status. Should fail as SessionOwner should be exclusive
|
||||
daHostInstance.SetOwnershipStatus(NetworkObject.OwnershipStatus.Distributable);
|
||||
Assert.IsFalse(daHostInstance.HasOwnershipStatus(NetworkObject.OwnershipStatus.Distributable), $"[Add][SessionOwner flag Failure] Expected: {NetworkObject.OwnershipStatus.Transferable} not to be set!");
|
||||
|
||||
// Request ownership of the SessionOwner flag instance
|
||||
requestStatus = firstInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestRequiredNotSet, $"Client-{firstInstance.NetworkManager.LocalClientId} should not be able to send a request for ownership because object is marked as owned by the session owner. {requestStatus}!");
|
||||
|
||||
// Set ownership directly on local object. This will allow the request to be sent
|
||||
firstInstance.Ownership = NetworkObject.OwnershipStatus.RequestRequired;
|
||||
requestStatus = firstInstance.RequestOwnership();
|
||||
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{firstInstance.NetworkManager.LocalClientId} was unable to send a request for ownership because: {requestStatus}!");
|
||||
|
||||
// Request should be denied with CannotRequest
|
||||
yield return WaitForConditionOrTimeOut(() => firstInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.CannotRequest);
|
||||
AssertOnTimeout($"[Targeted Owner] Client-{firstInstance.NetworkManager.LocalClientId} did not get the right request response: {daHostInstanceHelper.OwnershipRequestResponseStatus} Expecting: {NetworkObject.OwnershipRequestResponseStatus.CannotRequest}!");
|
||||
|
||||
// Try changing the ownership explicitly
|
||||
// Get the cloned daHostInstance instance on a client side
|
||||
var clientInstance = m_ClientNetworkManagers[2].SpawnManager.SpawnedObjects[daHostInstance.NetworkObjectId];
|
||||
|
||||
// Get the client instance of the OwnershipPermissionsTestHelper component
|
||||
var clientInstanceHelper = clientInstance.GetComponent<OwnershipPermissionsTestHelper>();
|
||||
|
||||
// Have the client attempt to change ownership
|
||||
clientInstance.ChangeOwnership(m_ClientNetworkManagers[2].LocalClientId);
|
||||
|
||||
// Verify the client side gets a permission failure status of NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly
|
||||
Assert.IsTrue(clientInstanceHelper.OwnershipPermissionsFailureStatus == NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly,
|
||||
$"Expected {clientInstance.name} to return {NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly} but its permission failure" +
|
||||
$" status is {clientInstanceHelper.OwnershipPermissionsFailureStatus}!");
|
||||
|
||||
// Have the session owner attempt to change ownership to a non-session owner
|
||||
daHostInstance.ChangeOwnership(m_ClientNetworkManagers[2].LocalClientId);
|
||||
|
||||
// Verify the session owner cannot assign a SessionOwner permission NetworkObject to a non-sessionowner client
|
||||
Assert.IsTrue(daHostInstanceHelper.OwnershipPermissionsFailureStatus == NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly,
|
||||
$"Expected {daHostInstance.name} to return {NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly} but its permission failure" +
|
||||
$" status is {daHostInstanceHelper.OwnershipPermissionsFailureStatus}!");
|
||||
|
||||
// Remove status
|
||||
daHostInstance.RemoveOwnershipStatus(NetworkObject.OwnershipStatus.SessionOwner);
|
||||
// Validate the permissions value for all instances are the same.
|
||||
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
|
||||
AssertOnTimeout($"[Remove][Permissions Mismatch] {daHostInstance.name}: \n {m_ErrorLog}");
|
||||
}
|
||||
|
||||
internal class OwnershipPermissionsTestHelper : NetworkBehaviour
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
internal class SessionVersionConnectionRequest : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 0;
|
||||
|
||||
public SessionVersionConnectionRequest() : base(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost) { }
|
||||
|
||||
private bool m_UseValidSessionVersion;
|
||||
private bool m_ClientWasDisconnected;
|
||||
private NetworkManager m_ClientNetworkManager;
|
||||
|
||||
/// <summary>
|
||||
/// Callback used to mock the scenario where a client has an invalid session version
|
||||
/// </summary>
|
||||
/// <returns><see cref="SessionConfig"/></returns>
|
||||
private SessionConfig GetInavlidSessionConfig()
|
||||
{
|
||||
return new SessionConfig(m_ServerNetworkManager.SessionConfig.SessionVersion - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overriding this method allows us to configure the newly instantiated client's
|
||||
/// NetworkManager prior to it being started.
|
||||
/// </summary>
|
||||
/// <param name="networkManager">the newly instantiated NetworkManager</param>
|
||||
protected override void OnNewClientCreated(NetworkManager networkManager)
|
||||
{
|
||||
m_ClientWasDisconnected = false;
|
||||
m_ClientNetworkManager = networkManager;
|
||||
m_ClientNetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback;
|
||||
if (!m_UseValidSessionVersion)
|
||||
{
|
||||
networkManager.OnGetSessionConfig = GetInavlidSessionConfig;
|
||||
}
|
||||
base.OnNewClientCreated(networkManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks if the client was disconnected or not
|
||||
/// </summary>
|
||||
private void OnClientDisconnectCallback(ulong clientId)
|
||||
{
|
||||
m_ClientWasDisconnected = true;
|
||||
m_ClientNetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This handles disabling the internal integration test logic that waits for
|
||||
/// clients to be connected. When we know the client will be disconnected,
|
||||
/// we want to have the NetcodeIntegrationTest not wait for the client to
|
||||
/// connect (otherwise it will timeout there and fail the test).
|
||||
/// </summary>
|
||||
/// <param name="networkManager"></param>
|
||||
/// <returns>true = wait | false = don't wait</returns>
|
||||
protected override bool ShouldWaitForNewClientToConnect(NetworkManager networkManager)
|
||||
{
|
||||
return m_UseValidSessionVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that when the client's session config version is valid a client will be
|
||||
/// allowed to connect and when it is not valid the client will be disconnected.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is just a mock of the service logic to validate everything on the NGO side is
|
||||
/// working correctly.
|
||||
/// </remarks>
|
||||
/// <param name="useValidSessionVersion">true = use valid session version | false = use invalid session version</param>
|
||||
[UnityTest]
|
||||
public IEnumerator ValidateSessionVersion([Values] bool useValidSessionVersion)
|
||||
{
|
||||
// Test client being disconnected due to invalid session version
|
||||
m_UseValidSessionVersion = useValidSessionVersion;
|
||||
yield return CreateAndStartNewClient();
|
||||
yield return s_DefaultWaitForTick;
|
||||
if (!m_UseValidSessionVersion)
|
||||
{
|
||||
yield return WaitForConditionOrTimeOut(() => m_ClientWasDisconnected);
|
||||
AssertOnTimeout("Client was not disconnected when it should have been!");
|
||||
Assert.True(m_ClientNetworkManager.DisconnectReason == ConnectionRequestMessage.InvalidSessionVersionMessage, "Client did not receive the correct invalid session version message!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.False(m_ClientWasDisconnected, "Client was disconnected when it was expected to connect!");
|
||||
Assert.True(m_ClientNetworkManager.IsConnectedClient, "Client did not connect properly using the correct session version!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked at the end of each integration test pass.
|
||||
/// Primarily used to clean up for the next pass.
|
||||
/// </summary>
|
||||
protected override IEnumerator OnTearDown()
|
||||
{
|
||||
m_ClientNetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback;
|
||||
m_ClientNetworkManager = null;
|
||||
yield return base.OnTearDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62913c78d28137141acc47ea8f63e657
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
using Unity.Netcode.Transports.UTP;
|
||||
using Unity.Networking.Transport;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
@@ -157,4 +160,49 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the UnityTransport.GetEndpoint method returns
|
||||
/// valid NetworkEndpoint information.
|
||||
/// </summary>
|
||||
internal class TransportEndpointTests : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 2;
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator GetEndpointReportedCorrectly()
|
||||
{
|
||||
var serverUnityTransport = m_ServerNetworkManager.NetworkConfig.NetworkTransport as UnityTransport;
|
||||
var serverEndpoint = new NetworkEndpoint();
|
||||
var clientEndpoint = new NetworkEndpoint();
|
||||
foreach (var client in m_ClientNetworkManagers)
|
||||
{
|
||||
var unityTransport = client.NetworkConfig.NetworkTransport as UnityTransport;
|
||||
serverEndpoint = unityTransport.GetEndpoint(m_ServerNetworkManager.LocalClientId);
|
||||
clientEndpoint = serverUnityTransport.GetEndpoint(client.LocalClientId);
|
||||
Assert.IsTrue(serverEndpoint.IsValid);
|
||||
Assert.IsTrue(clientEndpoint.IsValid);
|
||||
Assert.IsTrue(clientEndpoint.Address.Split(":")[0] == unityTransport.ConnectionData.Address);
|
||||
Assert.IsTrue(serverEndpoint.Address.Split(":")[0] == serverUnityTransport.ConnectionData.Address);
|
||||
Assert.IsTrue(serverEndpoint.Port == unityTransport.ConnectionData.Port);
|
||||
Assert.IsTrue(clientEndpoint.Port >= serverUnityTransport.ConnectionData.Port);
|
||||
}
|
||||
|
||||
// Now validate that when disconnected it returns a non-valid NetworkEndPoint
|
||||
var clientId = m_ClientNetworkManagers[0].LocalClientId;
|
||||
m_ClientNetworkManagers[0].Shutdown();
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
serverEndpoint = (m_ClientNetworkManagers[0].NetworkConfig.NetworkTransport as UnityTransport).GetEndpoint(m_ServerNetworkManager.LocalClientId);
|
||||
clientEndpoint = serverUnityTransport.GetEndpoint(clientId);
|
||||
Assert.IsFalse(serverEndpoint.IsValid);
|
||||
Assert.IsFalse(clientEndpoint.IsValid);
|
||||
|
||||
// Validate that invalid client identifiers return an invalid NetworkEndPoint
|
||||
serverEndpoint = (m_ClientNetworkManagers[0].NetworkConfig.NetworkTransport as UnityTransport).GetEndpoint((ulong)UnityEngine.Random.Range(NumberOfClients + 1, 30));
|
||||
clientEndpoint = serverUnityTransport.GetEndpoint((ulong)UnityEngine.Random.Range(NumberOfClients + 1, 30));
|
||||
Assert.IsFalse(serverEndpoint.IsValid);
|
||||
Assert.IsFalse(clientEndpoint.IsValid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Distributed authority does not allow remove ownership (users are instructed to use change ownership)
|
||||
if (!m_DistributedAuthority)
|
||||
{
|
||||
m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject);
|
||||
}
|
||||
|
||||
var serverObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
|
||||
var clientObject = m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
|
||||
@@ -237,7 +241,12 @@ namespace Unity.Netcode.RuntimeTests
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for all clients to change ownership!");
|
||||
|
||||
// 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);
|
||||
// Distributed authority does not allow remove ownership (users are instructed to use change ownership)
|
||||
if (!m_DistributedAuthority)
|
||||
{
|
||||
m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject);
|
||||
}
|
||||
|
||||
var serverObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
|
||||
Assert.That(serverObject, Is.Not.Null);
|
||||
var clientObject = (NetworkObject)null;
|
||||
|
||||
@@ -36,13 +36,25 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
VerifyObjectIsSpawnedOnClient.ResetObjectTable();
|
||||
m_ClientNetworkTransformPrefab = CreateNetworkObjectPrefab("OwnerAuthorityTest");
|
||||
var clientNetworkObject = m_ClientNetworkTransformPrefab.GetComponent<NetworkObject>();
|
||||
// When running in distributed authority mode, make the NetworkObject transferable
|
||||
clientNetworkObject.SetOwnershipStatus(m_DistributedAuthority ? NetworkObject.OwnershipStatus.Transferable : NetworkObject.OwnershipStatus.None);
|
||||
var clientNetworkTransform = m_ClientNetworkTransformPrefab.AddComponent<TestClientNetworkTransform>();
|
||||
clientNetworkTransform.AuthorityMode = NetworkTransform.AuthorityModes.Owner;
|
||||
clientNetworkTransform.Interpolate = false;
|
||||
clientNetworkTransform.UseHalfFloatPrecision = false;
|
||||
var rigidBody = m_ClientNetworkTransformPrefab.AddComponent<Rigidbody>();
|
||||
rigidBody.useGravity = false;
|
||||
rigidBody.maxDepenetrationVelocity = 0;
|
||||
rigidBody.mass = 100;
|
||||
rigidBody.linearDamping = 100;
|
||||
rigidBody.interpolation = RigidbodyInterpolation.None;
|
||||
rigidBody.maxLinearVelocity = 0;
|
||||
rigidBody.detectCollisions = false;
|
||||
rigidBody.position = Vector3.zero;
|
||||
rigidBody.rotation = Quaternion.identity;
|
||||
rigidBody.transform.position = Vector3.zero;
|
||||
rigidBody.transform.rotation = Quaternion.identity;
|
||||
// NOTE: We don't use a sphere collider for this integration test because by the time we can
|
||||
// assure they don't collide and skew the results the NetworkObjects are already synchronized
|
||||
// with skewed results
|
||||
@@ -51,11 +63,22 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_ClientNetworkTransformPrefab.AddComponent<VerifyObjectIsSpawnedOnClient>();
|
||||
|
||||
m_NetworkTransformPrefab = CreateNetworkObjectPrefab("ServerAuthorityTest");
|
||||
var networkTransform = m_NetworkTransformPrefab.AddComponent<NetworkTransform>();
|
||||
var networkObject = m_ClientNetworkTransformPrefab.GetComponent<NetworkObject>();
|
||||
// When running in distributed authority mode, make the NetworkObject transferable
|
||||
networkObject.SetOwnershipStatus(m_DistributedAuthority ? NetworkObject.OwnershipStatus.Transferable : NetworkObject.OwnershipStatus.None);
|
||||
var networkTransform = m_NetworkTransformPrefab.AddComponent<TestClientNetworkTransform>();
|
||||
rigidBody = m_NetworkTransformPrefab.AddComponent<Rigidbody>();
|
||||
rigidBody.useGravity = false;
|
||||
rigidBody.maxDepenetrationVelocity = 0;
|
||||
rigidBody.linearDamping = 100;
|
||||
rigidBody.mass = 100;
|
||||
rigidBody.interpolation = RigidbodyInterpolation.None;
|
||||
rigidBody.maxLinearVelocity = 0;
|
||||
rigidBody.detectCollisions = false;
|
||||
rigidBody.position = Vector3.zero;
|
||||
rigidBody.rotation = Quaternion.identity;
|
||||
rigidBody.transform.position = Vector3.zero;
|
||||
rigidBody.transform.rotation = Quaternion.identity;
|
||||
// NOTE: We don't use a sphere collider for this integration test because by the time we can
|
||||
// assure they don't collide and skew the results the NetworkObjects are already synchronized
|
||||
// with skewed results
|
||||
@@ -276,10 +299,12 @@ namespace Unity.Netcode.RuntimeTests
|
||||
};
|
||||
if (m_MotionModel == MotionModels.UseRigidbody)
|
||||
{
|
||||
TestClientNetworkTransform.EnableLogState(m_EnableVerboseDebug);
|
||||
var ownerRigidbody = ownerInstance.GetComponent<Rigidbody>();
|
||||
ownerRigidbody.Move(valueSetByOwner, rotation);
|
||||
yield return new WaitForFixedUpdate();
|
||||
ownerRigidbody.linearVelocity = Vector3.zero;
|
||||
yield return s_DefaultWaitForTick;
|
||||
yield return new WaitForFixedUpdate();
|
||||
ownerInstance.transform.localScale = valueSetByOwner;
|
||||
}
|
||||
else
|
||||
@@ -312,15 +337,22 @@ namespace Unity.Netcode.RuntimeTests
|
||||
VerifyObjectIsSpawnedOnClient.ResetObjectTable();
|
||||
if (m_DistributedAuthority)
|
||||
{
|
||||
ownerInstance.NetworkObject.ChangeOwnership(networkManagerNonOwner.LocalClientId);
|
||||
Assert.True(nonOwnerInstance.OwnerClientId != networkManagerNonOwner.LocalClientId, $"Non-Owner Client-{networkManagerNonOwner.LocalClientId} was already the owner prior to changing ownership!");
|
||||
nonOwnerInstance.NetworkObject.ChangeOwnership(networkManagerNonOwner.LocalClientId);
|
||||
nonOwnerInstance.GetComponent<Rigidbody>().linearVelocity = Vector3.zero;
|
||||
Assert.True(nonOwnerInstance.OwnerClientId == networkManagerNonOwner.LocalClientId, $"Client-{networkManagerNonOwner.LocalClientId} failed to change ownership!");
|
||||
|
||||
LogNonOwnerRigidBody(3);
|
||||
yield return WaitForConditionOrTimeOut(() => ownerInstance.GetComponent<NetworkObject>().OwnerClientId == networkManagerNonOwner.LocalClientId);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for original owner {networkManagerOwner.name}'s object instance {nonOwnerInstance.name} to change ownership!");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ServerNetworkManager.SpawnManager.ChangeOwnership(serverSideInstance.GetComponent<NetworkObject>(), networkManagerNonOwner.LocalClientId, true);
|
||||
LogNonOwnerRigidBody(3);
|
||||
yield return WaitForConditionOrTimeOut(() => nonOwnerInstance.GetComponent<NetworkObject>().OwnerClientId == networkManagerNonOwner.LocalClientId);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for {networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} to change ownership!");
|
||||
}
|
||||
LogNonOwnerRigidBody(3);
|
||||
yield return WaitForConditionOrTimeOut(() => nonOwnerInstance.GetComponent<NetworkObject>().OwnerClientId == networkManagerNonOwner.LocalClientId);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for {networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} to change ownership!");
|
||||
|
||||
LogNonOwnerRigidBody(4);
|
||||
// Re-assign the ownership references and wait for the non-owner instance to be notified of ownership change
|
||||
@@ -331,6 +363,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return WaitForConditionOrTimeOut(() => VerifyObjectIsSpawnedOnClient.GetClientInstance(networkManagerNonOwner.LocalClientId) != null);
|
||||
nonOwnerInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(networkManagerNonOwner.LocalClientId);
|
||||
Assert.NotNull(nonOwnerInstance);
|
||||
Assert.True(!nonOwnerInstance.IsOwner, $"Ownership failed to change on Client-{networkManagerNonOwner.LocalClientId} side! Expected owner to be {networkManagerOwner.LocalClientId} but owner is still {networkManagerNonOwner.LocalClientId}!");
|
||||
|
||||
// Make sure the owner is not kinematic and the non-owner(s) are kinematic
|
||||
Assert.False(ownerInstance.GetComponent<Rigidbody>().isKinematic, $"{networkManagerOwner.name}'s object instance {ownerInstance.name} is kinematic when it should not be!");
|
||||
@@ -344,7 +377,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for {networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} to change its transform!\n" +
|
||||
$"Expected Position: {valueSetByOwner} | Current Position: {transformToTest.position}\n" +
|
||||
$"Expected Rotation: {valueSetByOwner} | Current Rotation: {transformToTest.rotation.eulerAngles}\n" +
|
||||
$"Expected Scale: {valueSetByOwner} | Current Scale: {transformToTest.localScale}");
|
||||
$"Expected Scale: {valueSetByOwner} | Current Scale: {transformToTest.localScale}\n {nonOwnerInstance.GetComponent<TestClientNetworkTransform>().LogInfoBuilder}");
|
||||
|
||||
LogNonOwnerRigidBody(5);
|
||||
// Have the new owner change transform values and wait for those values to be applied on the non-owner side.
|
||||
@@ -358,6 +391,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
var ownerRigidbody = ownerInstance.GetComponent<Rigidbody>();
|
||||
ownerRigidbody.Move(valueSetByOwner, rotation);
|
||||
LogOwnerRigidBody(2);
|
||||
yield return new WaitForFixedUpdate();
|
||||
ownerInstance.GetComponent<NetworkTransform>().LogMotion = m_EnableVerboseDebug;
|
||||
nonOwnerInstance.GetComponent<NetworkTransform>().LogMotion = m_EnableVerboseDebug;
|
||||
ownerRigidbody.linearVelocity = Vector3.zero;
|
||||
@@ -377,10 +411,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
LogOwnerRigidBody(4);
|
||||
LogNonOwnerRigidBody(7);
|
||||
}
|
||||
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for {networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} to change its transform!\n" +
|
||||
$"Expected Position: {valueSetByOwner} | Current Position: {transformToTest.position}\n" +
|
||||
$"Expected Rotation: {valueSetByOwner} | Current Rotation: {transformToTest.rotation.eulerAngles}\n" +
|
||||
$"Expected Scale: {valueSetByOwner} | Current Scale: {transformToTest.localScale}");
|
||||
$"Expected Scale: {valueSetByOwner} | Current Scale: {transformToTest.localScale}\n {nonOwnerInstance.GetComponent<TestClientNetworkTransform>().LogInfoBuilder}");
|
||||
|
||||
// The last check is to verify non-owners cannot change transform values after ownership has changed
|
||||
nonOwnerInstance.transform.position = Vector3.zero;
|
||||
@@ -445,6 +480,12 @@ namespace Unity.Netcode.RuntimeTests
|
||||
Assert.True(nonOwnerInstance.transform.position == valueSetByOwner, $"{m_ClientNetworkManagers[0].name}'s object instance {nonOwnerInstance.name} was allowed to change its position! Expected: {Vector3.one} Is Currently:{nonOwnerInstance.transform.position}");
|
||||
}
|
||||
|
||||
protected override IEnumerator OnTearDown()
|
||||
{
|
||||
TestClientNetworkTransform.EnableLogState(false);
|
||||
return base.OnTearDown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetworkTransformOwnershipTests helper behaviour
|
||||
/// </summary>
|
||||
@@ -457,6 +498,10 @@ namespace Unity.Netcode.RuntimeTests
|
||||
NetworkManagerRelativeSpawnedObjects.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For testing, just before changing ownership the table is cleared to assure that
|
||||
/// ownership tansfer occurs. This will add the new owner to the table.
|
||||
/// </summary>
|
||||
public override void OnGainedOwnership()
|
||||
{
|
||||
if (!NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
|
||||
@@ -466,6 +511,10 @@ namespace Unity.Netcode.RuntimeTests
|
||||
base.OnGainedOwnership();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For testing, just before changing ownership the table is cleared to assure that
|
||||
/// ownership tansfer occurs. This will add the previous owner to the table.
|
||||
/// </summary>
|
||||
public override void OnLostOwnership()
|
||||
{
|
||||
if (!NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
|
||||
@@ -515,28 +564,57 @@ namespace Unity.Netcode.RuntimeTests
|
||||
[DisallowMultipleComponent]
|
||||
internal class TestClientNetworkTransform : NetworkTransform
|
||||
{
|
||||
//public override void OnNetworkSpawn()
|
||||
//{
|
||||
// base.OnNetworkSpawn();
|
||||
// CanCommitToTransform = IsOwner;
|
||||
//}
|
||||
|
||||
//protected override void Update()
|
||||
//{
|
||||
// CanCommitToTransform = IsOwner;
|
||||
// base.Update();
|
||||
// if (NetworkManager.Singleton != null && (NetworkManager.Singleton.IsConnectedClient || NetworkManager.Singleton.IsListening))
|
||||
// {
|
||||
// if (CanCommitToTransform)
|
||||
// {
|
||||
// TryCommitTransformToServer(transform, NetworkManager.LocalTime.Time);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
protected override bool OnIsServerAuthoritative()
|
||||
public static void EnableLogState(bool enable)
|
||||
{
|
||||
return false;
|
||||
s_LogStateEnabled = enable;
|
||||
TrackByStateId = enable;
|
||||
}
|
||||
|
||||
private static bool s_LogStateEnabled;
|
||||
|
||||
internal StringBuilder LogInfoBuilder = new StringBuilder();
|
||||
|
||||
private void LogInfo(NetworkTransformState state)
|
||||
{
|
||||
if (s_LogStateEnabled)
|
||||
{
|
||||
LogInfoBuilder.AppendLine($"N:{name} | CID:{NetworkManager.LocalClientId} | SID: {state.StateId} | NT:{NetworkManager.ServerTime.Tick} | Pos: {transform.position} | Sc: {transform.localScale}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnOwnershipChanged(ulong previous, ulong current)
|
||||
{
|
||||
|
||||
if (s_LogStateEnabled)
|
||||
{
|
||||
LogInfoBuilder.AppendLine($"Ownership Changed: {previous} --> {current} | Position: {transform.position}");
|
||||
}
|
||||
LogInfo(LocalAuthoritativeNetworkState);
|
||||
base.OnOwnershipChanged(previous, current);
|
||||
|
||||
// Assure no velocity is set on this object for this particular test
|
||||
if (current == NetworkManager.LocalClientId)
|
||||
{
|
||||
GetComponent<Rigidbody>().linearVelocity = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAuthorityPushTransformState(ref NetworkTransformState networkTransformState)
|
||||
{
|
||||
LogInfo(networkTransformState);
|
||||
base.OnAuthorityPushTransformState(ref networkTransformState);
|
||||
}
|
||||
|
||||
protected override void OnBeforeUpdateTransformState()
|
||||
{
|
||||
LogInfo(LocalAuthoritativeNetworkState);
|
||||
base.OnBeforeUpdateTransformState();
|
||||
}
|
||||
|
||||
protected override void OnNetworkTransformStateUpdated(ref NetworkTransformState oldState, ref NetworkTransformState newState)
|
||||
{
|
||||
LogInfo(newState);
|
||||
base.OnNetworkTransformStateUpdated(ref oldState, ref newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -791,7 +869,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
foreach (var networkManager in m_NetworkManagers)
|
||||
{
|
||||
// Randomize the position
|
||||
// Randomize the position
|
||||
RandomizeObjectTransformPositions(m_SpawnObject);
|
||||
|
||||
// Create an instance owned by the specified networkmanager
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test validates the SwitchTransformSpaceWhenParented setting under all network topologies
|
||||
/// This test validates the SwitchTransformSpaceWhenParented setting under all network topologies
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SwitchTransformSpaceWhenParentedTest([Values(0.5f, 1.0f, 5.0f)] float scale)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
/// - HashSet
|
||||
/// This also does some testing on nested collections, but does
|
||||
/// not test every possible combination.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
[TestFixture(HostOrServer.Host)]
|
||||
[TestFixture(HostOrServer.Server)]
|
||||
public class NetworkVariableCollectionsTests : NetcodeIntegrationTest
|
||||
@@ -218,7 +218,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return WaitForConditionOrTimeOut(() => compInt.CompareTrackedChanges(ListTestHelperBase.Targets.Owner));
|
||||
AssertOnTimeout($"Client-{client.LocalClientId} full set failed to synchronize on {nameof(ListTestHelperInt)} {compInt.name}!");
|
||||
//////////////////////////////////
|
||||
// Server Full Set
|
||||
// Server Full Set
|
||||
compIntServer.FullSet(GetRandomIntList(5), ListTestHelperBase.Targets.Server);
|
||||
yield return WaitForConditionOrTimeOut(() => compIntServer.CompareTrackedChanges(ListTestHelperBase.Targets.Server));
|
||||
AssertOnTimeout($"Server full set failed to synchronize on {nameof(ListTestHelperInt)} {compIntServer.name}!");
|
||||
@@ -285,7 +285,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// Only test restore on non-host clients (otherwise a host is both server and client/owner)
|
||||
if (!client.IsServer)
|
||||
{
|
||||
// No Write Client Remove List<int> item with CheckDirtyState restore
|
||||
// No Write Client Remove List<int> item with CheckDirtyState restore
|
||||
compListInt.Remove(compListInt.ListCollectionServer.Value[index], ListTestHelperBase.Targets.Server);
|
||||
yield return WaitForConditionOrTimeOut(() => compListInt.CompareTrackedChanges(ListTestHelperBase.Targets.Server));
|
||||
AssertOnTimeout($"Client-{client.LocalClientId} remove failed to restore on {nameof(ListTestHelperListInt)} {compListIntServer.name}! {compListIntServer.GetLog()}");
|
||||
@@ -474,7 +474,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return WaitForConditionOrTimeOut(() => compObjectServer.CompareTrackedChanges(ListTestHelperBase.Targets.Server));
|
||||
AssertOnTimeout($"Client-{client.LocalClientId} change failed to restore on {nameof(ListTestHelperSerializableObject)} {compObjectServer.name}!");
|
||||
|
||||
// No Write Client Remove Serializable item with owner state update restore
|
||||
// No Write Client Remove Serializable item with owner state update restore
|
||||
compObject.ListCollectionServer.Value[index] = SerializableObject.GetRandomObject();
|
||||
}
|
||||
compObjectServer.ListCollectionServer.Value[index] = SerializableObject.GetRandomObject();
|
||||
@@ -838,7 +838,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
compDictionaryServer.ListCollectionOwner.IsDirty();
|
||||
yield return WaitForConditionOrTimeOut(() => compDictionaryServer.CompareTrackedChanges(ListTestHelperBase.Targets.Owner));
|
||||
AssertOnTimeout($"Server add to owner write collection property failed to restore on {className} {compDictionaryServer.name}! {compDictionaryServer.GetLog()}");
|
||||
// Server-side add the same key and SerializableObject to owner write permission (would throw key exists exception too if previous failed)
|
||||
// Server-side add the same key and SerializableObject to owner write permission (would throw key exists exception too if previous failed)
|
||||
compDictionaryServer.ListCollectionOwner.Value.Add(newEntry.Item1, newEntry.Item2);
|
||||
// Server-side add a completely new key and SerializableObject to to owner write permission property
|
||||
compDictionaryServer.ListCollectionOwner.Value.Add(GetNextKey(), SerializableObject.GetRandomObject());
|
||||
@@ -864,7 +864,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
compDictionary.ListCollectionServer.IsDirty();
|
||||
yield return WaitForConditionOrTimeOut(() => compDictionary.CompareTrackedChanges(ListTestHelperBase.Targets.Server));
|
||||
AssertOnTimeout($"Client-{client.LocalClientId} add to server write collection property failed to restore on {className} {compDictionary.name}! {compDictionary.GetLog()}");
|
||||
// Client-side add the same key and SerializableObject to server write permission property (would throw key exists exception too if previous failed)
|
||||
// Client-side add the same key and SerializableObject to server write permission property (would throw key exists exception too if previous failed)
|
||||
compDictionary.ListCollectionServer.Value.Add(newEntry.Item1, newEntry.Item2);
|
||||
// Client-side add a completely new key and SerializableObject to to server write permission property
|
||||
compDictionary.ListCollectionServer.Value.Add(GetNextKey(), SerializableObject.GetRandomObject());
|
||||
|
||||
@@ -1012,8 +1012,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
// Exceptions should be thrown in DA mode with UserNetworkVariableSerialization
|
||||
public void TestUnsupportedManagedTypesWithUserSerializationDoNotThrowExceptionsInClientServerMode()
|
||||
public void TestUnsupportedManagedTypesWithUserSerializationDoNotThrowExceptions()
|
||||
{
|
||||
var variable = new NetworkVariable<string>();
|
||||
UserNetworkVariableSerialization<string>.ReadValue = (FastBufferReader reader, out string value) =>
|
||||
@@ -1039,10 +1038,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
variable.ReadField(reader);
|
||||
Assert.AreEqual("012345", variable.Value);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Assert.True(NetworkVariableSerialization<UserNetworkVariableSerialization<string>>.IsDistributedAuthority);
|
||||
}
|
||||
finally
|
||||
{
|
||||
UserNetworkVariableSerialization<string>.ReadValue = null;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
using UnityEngine;
|
||||
@@ -184,4 +187,150 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test validates that when a player is spawned it has all observers
|
||||
/// properly set and the spawned and player object lists are properly populated.
|
||||
/// It also validates that when a player is despawned and the client is still connected
|
||||
/// that the client maintains its observers for other players.
|
||||
/// </summary>
|
||||
[TestFixture(HostOrServer.DAHost)]
|
||||
[TestFixture(HostOrServer.Host)]
|
||||
[TestFixture(HostOrServer.Server)]
|
||||
internal class PlayerSpawnAndDespawnTests : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 4;
|
||||
|
||||
private StringBuilder m_ErrorLog = new StringBuilder();
|
||||
|
||||
public PlayerSpawnAndDespawnTests(HostOrServer hostOrServer) : base(hostOrServer) { }
|
||||
|
||||
private bool ValidateObservers(ulong playerClientId, NetworkObject player, ref List<NetworkManager> networkManagers)
|
||||
{
|
||||
foreach (var networkManager in networkManagers)
|
||||
{
|
||||
if (player != null && player.IsSpawned)
|
||||
{
|
||||
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(player.NetworkObjectId))
|
||||
{
|
||||
m_ErrorLog.AppendLine($"Client-{networkManager.LocalClientId} does not contain a spawned object entry {nameof(NetworkObject)}-{player.NetworkObjectId} for Client-{playerClientId}!");
|
||||
return false;
|
||||
}
|
||||
|
||||
var playerClone = networkManager.SpawnManager.SpawnedObjects[player.NetworkObjectId];
|
||||
foreach (var clientId in networkManager.ConnectedClientsIds)
|
||||
{
|
||||
if (!playerClone.IsNetworkVisibleTo(clientId))
|
||||
{
|
||||
m_ErrorLog.AppendLine($"Client-{networkManager.LocalClientId} failed visibility check for Client-{clientId} on {nameof(NetworkObject)}-{player.NetworkObjectId} for Client-{playerClientId}!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var foundPlayerClone = (NetworkObject)null;
|
||||
foreach (var playerObject in networkManager.SpawnManager.PlayerObjects)
|
||||
{
|
||||
if (playerObject.OwnerClientId == playerClientId && playerObject.NetworkObjectId == player.NetworkObjectId)
|
||||
{
|
||||
foundPlayerClone = playerObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundPlayerClone)
|
||||
{
|
||||
m_ErrorLog.AppendLine($"Client-{networkManager.LocalClientId} does not contain a player entry for {nameof(NetworkObject)}-{player.NetworkObjectId} for Client-{playerClientId}!");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the player client in question is despawned, then no NetworkManager instance
|
||||
// should contain a clone of that (or the client's NetworkManager instance as well)
|
||||
foreach (var playerClone in networkManager.SpawnManager.PlayerObjects)
|
||||
{
|
||||
if (playerClone.OwnerClientId == playerClientId)
|
||||
{
|
||||
m_ErrorLog.AppendLine($"Client-{networkManager.LocalClientId} contains a player {nameof(NetworkObject)}-{playerClone.NetworkObjectId} for Client-{playerClientId} when it should be despawned!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ValidateAllClientPlayerObservers()
|
||||
{
|
||||
var networkManagers = new List<NetworkManager>();
|
||||
|
||||
if (m_ServerNetworkManager.IsHost)
|
||||
{
|
||||
networkManagers.Add(m_ServerNetworkManager);
|
||||
}
|
||||
foreach (var networkManager in m_ClientNetworkManagers)
|
||||
{
|
||||
networkManagers.Add(networkManager);
|
||||
}
|
||||
|
||||
m_ErrorLog.Clear();
|
||||
var success = true;
|
||||
foreach (var networkManager in networkManagers)
|
||||
{
|
||||
var spawnedOrNot = networkManager.LocalClient.PlayerObject == null ? "despawned" : "spawned";
|
||||
m_ErrorLog.AppendLine($"Validating Client-{networkManager.LocalClientId} {spawnedOrNot} player.");
|
||||
if (networkManager.LocalClient == null)
|
||||
{
|
||||
m_ErrorLog.AppendLine($"No {nameof(NetworkClient)} found for Client-{networkManager.LocalClientId}!");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
if (!ValidateObservers(networkManager.LocalClientId, networkManager.LocalClient.PlayerObject, ref networkManagers))
|
||||
{
|
||||
m_ErrorLog.AppendLine($"Client-{networkManager.LocalClientId} validation pass failed.");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
networkManagers.Clear();
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator PlayerSpawnDespawn()
|
||||
{
|
||||
// Validate all observers are properly set with all players spawned
|
||||
yield return WaitForConditionOrTimeOut(ValidateAllClientPlayerObservers);
|
||||
AssertOnTimeout($"First Validation Failed:\n {m_ErrorLog}");
|
||||
var selectedClient = m_ClientNetworkManagers[Random.Range(0, m_ClientNetworkManagers.Count() - 1)];
|
||||
var playerSelected = selectedClient.LocalClient.PlayerObject;
|
||||
|
||||
if (m_DistributedAuthority)
|
||||
{
|
||||
playerSelected.Despawn(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ServerNetworkManager.SpawnManager.SpawnedObjects[playerSelected.NetworkObjectId].Despawn(true);
|
||||
}
|
||||
|
||||
// Validate all observers are properly set with one of the players despawned
|
||||
yield return WaitForConditionOrTimeOut(ValidateAllClientPlayerObservers);
|
||||
AssertOnTimeout($"Second Validation Failed:\n {m_ErrorLog}");
|
||||
|
||||
if (m_DistributedAuthority)
|
||||
{
|
||||
playerSelected.SpawnAsPlayerObject(selectedClient.LocalClientId, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpawnPlayerObject(m_ServerNetworkManager.NetworkConfig.PlayerPrefab, selectedClient);
|
||||
}
|
||||
|
||||
// Validate all observers are properly set when the client's player is respawned.
|
||||
yield return WaitForConditionOrTimeOut(ValidateAllClientPlayerObservers);
|
||||
AssertOnTimeout($"Third Validation Failed:\n {m_ErrorLog}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
Tests/Runtime/Prefabs.meta
Normal file
8
Tests/Runtime/Prefabs.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83133d71f2112db45ba626157c46e7f8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -85,9 +85,14 @@ namespace Unity.Netcode.RuntimeTests
|
||||
private const string k_PrefabObjectName = "NetworkPrefabHandlerTestObject";
|
||||
|
||||
[Test]
|
||||
public void NetworkPrefabHandlerClass()
|
||||
public void NetworkPrefabHandlerClass([Values] bool distributedAuthority)
|
||||
{
|
||||
Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _));
|
||||
var networkConfig = new NetworkConfig()
|
||||
{
|
||||
NetworkTopology = distributedAuthority ? NetworkTopologyTypes.DistributedAuthority : NetworkTopologyTypes.ClientServer,
|
||||
};
|
||||
|
||||
Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _, networkConfig: networkConfig));
|
||||
var testPrefabObjectName = k_PrefabObjectName;
|
||||
|
||||
Guid baseObjectID = NetworkManagerHelper.AddGameNetworkObject(testPrefabObjectName);
|
||||
268
Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs
Normal file
268
Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs
Normal file
@@ -0,0 +1,268 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Integration test that validates spawning instances of <see cref="NetworkPrefab"/>s with overrides and
|
||||
/// <see cref="NetworkPrefabHandler"/> registered overrides.
|
||||
/// </summary>
|
||||
[TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Server)]
|
||||
[TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Host)]
|
||||
[TestFixture(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost)]
|
||||
internal class NetworkPrefabOverrideTests : NetcodeIntegrationTest
|
||||
{
|
||||
private const string k_PrefabRootName = "PrefabObj";
|
||||
protected override int NumberOfClients => 2;
|
||||
|
||||
private NetworkPrefab m_ClientSidePlayerPrefab;
|
||||
private NetworkPrefab m_PrefabOverride;
|
||||
|
||||
public NetworkPrefabOverrideTests(NetworkTopologyTypes networkTopologyType, HostOrServer hostOrServer) : base(networkTopologyType, hostOrServer) { }
|
||||
|
||||
/// <summary>
|
||||
/// Prefab override handler that will instantiate the ServerSideInstance (m_PlayerPrefab) only on server instances
|
||||
/// and will spawn the ClientSideInstance (m_ClientSidePlayerPrefab.Prefab) only on clients and/or a host.
|
||||
/// </summary>
|
||||
public class TestPrefabOverrideHandler : MonoBehaviour, INetworkPrefabInstanceHandler
|
||||
{
|
||||
public GameObject ServerSideInstance;
|
||||
public GameObject ClientSideInstance;
|
||||
private NetworkManager m_NetworkManager;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
m_NetworkManager = GetComponent<NetworkManager>();
|
||||
m_NetworkManager.PrefabHandler.AddHandler(ServerSideInstance, this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (m_NetworkManager != null && m_NetworkManager.PrefabHandler != null)
|
||||
{
|
||||
m_NetworkManager.PrefabHandler.RemoveHandler(ServerSideInstance);
|
||||
}
|
||||
}
|
||||
|
||||
public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
var instance = m_NetworkManager.IsClient ? Instantiate(ClientSideInstance) : Instantiate(ServerSideInstance);
|
||||
return instance.GetComponent<NetworkObject>();
|
||||
}
|
||||
|
||||
public void Destroy(NetworkObject networkObject)
|
||||
{
|
||||
Object.Destroy(networkObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mock component for testing that the client-side player is using the right
|
||||
/// network prefab.
|
||||
/// </summary>
|
||||
public class ClientSideOnlyComponent : MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When we create the player prefab, make a modified instance that will be used
|
||||
/// with the <see cref="TestPrefabOverrideHandler"/>.
|
||||
/// </summary>
|
||||
protected override void OnCreatePlayerPrefab()
|
||||
{
|
||||
var clientPlayer = Object.Instantiate(m_PlayerPrefab);
|
||||
clientPlayer.AddComponent<ClientSideOnlyComponent>();
|
||||
Object.DontDestroyOnLoad(clientPlayer);
|
||||
m_ClientSidePlayerPrefab = new NetworkPrefab()
|
||||
{
|
||||
Prefab = clientPlayer,
|
||||
};
|
||||
|
||||
base.OnCreatePlayerPrefab();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the additional <see cref="NetworkPrefab"/>s and <see cref="TestPrefabOverrideHandler"/>s to
|
||||
/// all <see cref="NetworkManager"/> instances.
|
||||
/// </summary>
|
||||
protected override void OnServerAndClientsCreated()
|
||||
{
|
||||
// Create a NetworkPrefab with an override
|
||||
var basePrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-base", m_ServerNetworkManager, true);
|
||||
var targetPrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-over", m_ServerNetworkManager, true);
|
||||
m_PrefabOverride = new NetworkPrefab()
|
||||
{
|
||||
Prefab = basePrefab,
|
||||
Override = NetworkPrefabOverride.Prefab,
|
||||
SourcePrefabToOverride = basePrefab,
|
||||
OverridingTargetPrefab = targetPrefab,
|
||||
};
|
||||
|
||||
// Add the prefab override handler for instance specific player prefabs to the server side
|
||||
var playerPrefabOverrideHandler = m_ServerNetworkManager.gameObject.AddComponent<TestPrefabOverrideHandler>();
|
||||
playerPrefabOverrideHandler.ServerSideInstance = m_PlayerPrefab;
|
||||
playerPrefabOverrideHandler.ClientSideInstance = m_ClientSidePlayerPrefab.Prefab;
|
||||
|
||||
// Add the NetworkPrefab with override
|
||||
m_ServerNetworkManager.NetworkConfig.Prefabs.Add(m_PrefabOverride);
|
||||
// Add the client player prefab that will be used on clients (and the host)
|
||||
m_ServerNetworkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab);
|
||||
|
||||
foreach (var networkManager in m_ClientNetworkManagers)
|
||||
{
|
||||
// Add the prefab override handler for instance specific player prefabs to the client side
|
||||
playerPrefabOverrideHandler = networkManager.gameObject.AddComponent<TestPrefabOverrideHandler>();
|
||||
playerPrefabOverrideHandler.ServerSideInstance = m_PlayerPrefab;
|
||||
playerPrefabOverrideHandler.ClientSideInstance = m_ClientSidePlayerPrefab.Prefab;
|
||||
|
||||
// Add the NetworkPrefab with override
|
||||
networkManager.NetworkConfig.Prefabs.Add(m_PrefabOverride);
|
||||
// Add the client player prefab that will be used on clients (and the host)
|
||||
networkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab);
|
||||
}
|
||||
|
||||
m_PrefabOverride.Prefab.GetComponent<NetworkObject>().IsSceneObject = false;
|
||||
m_PrefabOverride.SourcePrefabToOverride.GetComponent<NetworkObject>().IsSceneObject = false;
|
||||
m_PrefabOverride.OverridingTargetPrefab.GetComponent<NetworkObject>().IsSceneObject = false;
|
||||
m_ClientSidePlayerPrefab.Prefab.GetComponent<NetworkObject>().IsSceneObject = false;
|
||||
|
||||
base.OnServerAndClientsCreated();
|
||||
}
|
||||
|
||||
protected override IEnumerator OnTearDown()
|
||||
{
|
||||
if (m_PrefabOverride != null)
|
||||
{
|
||||
if (m_PrefabOverride.SourcePrefabToOverride)
|
||||
{
|
||||
Object.Destroy(m_PrefabOverride.SourcePrefabToOverride);
|
||||
}
|
||||
|
||||
if (m_PrefabOverride.OverridingTargetPrefab)
|
||||
{
|
||||
Object.Destroy(m_PrefabOverride.OverridingTargetPrefab);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ClientSidePlayerPrefab != null)
|
||||
{
|
||||
if (m_ClientSidePlayerPrefab.Prefab)
|
||||
{
|
||||
Object.Destroy(m_ClientSidePlayerPrefab.Prefab);
|
||||
}
|
||||
}
|
||||
m_ClientSidePlayerPrefab = null;
|
||||
m_PrefabOverride = null;
|
||||
|
||||
yield return base.OnTearDown();
|
||||
}
|
||||
|
||||
|
||||
private GameObject GetPlayerNetworkPrefabObject(NetworkManager networkManager)
|
||||
{
|
||||
return networkManager.IsClient ? m_ClientSidePlayerPrefab.Prefab : m_PlayerPrefab;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator PrefabOverrideTests()
|
||||
{
|
||||
var prefabNetworkObject = (NetworkObject)null;
|
||||
var spawnedGlobalObjectId = (uint)0;
|
||||
|
||||
var networkManagers = m_ClientNetworkManagers.ToList();
|
||||
if (m_UseHost)
|
||||
{
|
||||
networkManagers.Insert(0, m_ServerNetworkManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If running as just a server, validate that all player prefab clone instances are the server side version
|
||||
prefabNetworkObject = GetPlayerNetworkPrefabObject(m_ServerNetworkManager).GetComponent<NetworkObject>();
|
||||
foreach (var playerEntry in m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId])
|
||||
{
|
||||
spawnedGlobalObjectId = playerEntry.Value.GlobalObjectIdHash;
|
||||
Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"Server-Side {playerEntry.Value.name} was spawned as prefab ({spawnedGlobalObjectId}) but we expected ({prefabNetworkObject.GlobalObjectIdHash})!");
|
||||
}
|
||||
}
|
||||
|
||||
// Validates prefab overrides via the NetworkPrefabHandler.
|
||||
// Validate the player prefab instance clones relative to all NetworkManagers.
|
||||
foreach (var networkManager in networkManagers)
|
||||
{
|
||||
// Get the expected player prefab to be spawned based on the NetworkManager
|
||||
prefabNetworkObject = GetPlayerNetworkPrefabObject(networkManager).GetComponent<NetworkObject>();
|
||||
if (networkManager.IsClient)
|
||||
{
|
||||
spawnedGlobalObjectId = networkManager.LocalClient.PlayerObject.GlobalObjectIdHash;
|
||||
Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"{networkManager.name} spawned player prefab ({spawnedGlobalObjectId}) did not match the expected one ({prefabNetworkObject.GlobalObjectIdHash})!");
|
||||
}
|
||||
|
||||
foreach (var playerEntry in m_PlayerNetworkObjects[networkManager.LocalClientId])
|
||||
{
|
||||
// We already checked our locally spawned player prefab above
|
||||
if (playerEntry.Key == networkManager.LocalClientId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
spawnedGlobalObjectId = playerEntry.Value.GlobalObjectIdHash;
|
||||
Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"Client-{networkManager.LocalClientId} clone of {playerEntry.Value.name} was spawned as prefab ({spawnedGlobalObjectId}) but we expected ({prefabNetworkObject.GlobalObjectIdHash})!");
|
||||
}
|
||||
}
|
||||
|
||||
// Validates prefab overrides via NetworkPrefab configuration.
|
||||
var spawnedInstance = (NetworkObject)null;
|
||||
var networkManagerOwner = m_ServerNetworkManager;
|
||||
|
||||
if (m_DistributedAuthority)
|
||||
{
|
||||
networkManagerOwner = m_ClientNetworkManagers[0];
|
||||
}
|
||||
// Clients and Host will spawn the OverridingTargetPrefab while a dedicated server will spawn the SourcePrefabToOverride
|
||||
var expectedServerGlobalObjectIdHash = networkManagerOwner.IsClient ? m_PrefabOverride.OverridingTargetPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash : m_PrefabOverride.SourcePrefabToOverride.GetComponent<NetworkObject>().GlobalObjectIdHash;
|
||||
var expectedClientGlobalObjectIdHash = m_PrefabOverride.OverridingTargetPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash;
|
||||
|
||||
spawnedInstance = NetworkObject.InstantiateAndSpawn(m_PrefabOverride.SourcePrefabToOverride, networkManagerOwner, networkManagerOwner.LocalClientId);
|
||||
var builder = new StringBuilder();
|
||||
bool ObjectSpawnedOnAllNetworkMangers()
|
||||
{
|
||||
builder.Clear();
|
||||
if (!m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(spawnedInstance.NetworkObjectId))
|
||||
{
|
||||
builder.AppendLine($"Client-{m_ServerNetworkManager.LocalClientId} failed to spawn {spawnedInstance.name}-{spawnedInstance.NetworkObjectId}!");
|
||||
return false;
|
||||
}
|
||||
var instanceGID = m_ServerNetworkManager.SpawnManager.SpawnedObjects[spawnedInstance.NetworkObjectId].GlobalObjectIdHash;
|
||||
if (instanceGID != expectedServerGlobalObjectIdHash)
|
||||
{
|
||||
builder.AppendLine($"Client-{m_ServerNetworkManager.LocalClientId} instance {spawnedInstance.name}-{spawnedInstance.NetworkObjectId} GID is {instanceGID} but was expected to be {expectedServerGlobalObjectIdHash}!");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var networkManger in m_ClientNetworkManagers)
|
||||
{
|
||||
if (!networkManger.SpawnManager.SpawnedObjects.ContainsKey(spawnedInstance.NetworkObjectId))
|
||||
{
|
||||
builder.AppendLine($"Client-{networkManger.LocalClientId} failed to spawn {spawnedInstance.name}-{spawnedInstance.NetworkObjectId}!");
|
||||
return false;
|
||||
}
|
||||
instanceGID = networkManger.SpawnManager.SpawnedObjects[spawnedInstance.NetworkObjectId].GlobalObjectIdHash;
|
||||
if (instanceGID != expectedClientGlobalObjectIdHash)
|
||||
{
|
||||
builder.AppendLine($"Client-{networkManger.LocalClientId} instance {spawnedInstance.name}-{spawnedInstance.NetworkObjectId} GID is {instanceGID} but was expected to be {expectedClientGlobalObjectIdHash}!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
yield return WaitForConditionOrTimeOut(ObjectSpawnedOnAllNetworkMangers);
|
||||
AssertOnTimeout($"The spawned prefab override validation failed!\n {builder}");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs.meta
Normal file
2
Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5e27e24bc5f783458d6a45585308f87
|
||||
@@ -33,7 +33,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
if (transport)
|
||||
{
|
||||
transport.Shutdown();
|
||||
UnityEngine.Object.DestroyImmediate(transport);
|
||||
UnityEngine.Object.DestroyImmediate(transport.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
transportEvents?.Clear();
|
||||
}
|
||||
|
||||
UnityTransportTestComponent.CleanUp();
|
||||
yield return null;
|
||||
}
|
||||
|
||||
@@ -59,6 +60,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
Assert.False(m_Clients[0].StartClient(), "Client failed to detect invalid endpoint!");
|
||||
netcodeLogAssert.LogWasReceived(LogType.Error, $"Network listen address ({m_Server.ConnectionData.Address}) is Invalid!");
|
||||
netcodeLogAssert.LogWasReceived(LogType.Error, $"Target server network address ({m_Clients[0].ConnectionData.Address}) is Invalid!");
|
||||
UnityTransportTestComponent.CleanUp();
|
||||
}
|
||||
|
||||
// Check connection with a single client.
|
||||
@@ -161,8 +163,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// 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.
|
||||
@@ -188,13 +188,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
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.
|
||||
@@ -207,14 +205,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
m_Clients[i].DisconnectLocalClient();
|
||||
}
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Disconnect, m_ServerEvents, 5);
|
||||
|
||||
// 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.
|
||||
@@ -244,8 +239,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// 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.
|
||||
@@ -275,8 +268,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// 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.
|
||||
|
||||
@@ -36,6 +36,24 @@ namespace Unity.Netcode.RuntimeTests
|
||||
Assert.Fail("Timed out while waiting for network event.");
|
||||
}
|
||||
|
||||
// Wait to ensure no event is sent.
|
||||
public static IEnumerator EnsureNoNetworkEvent(List<TransportEvent> events, float timeout = MaxNetworkEventWaitTime)
|
||||
{
|
||||
int initialCount = events.Count;
|
||||
float startTime = Time.realtimeSinceStartup;
|
||||
|
||||
while (Time.realtimeSinceStartup - startTime < timeout)
|
||||
{
|
||||
if (events.Count > initialCount)
|
||||
{
|
||||
Assert.Fail("Received unexpected network event.");
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(0.01f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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, int maxSendQueueSize = 0, NetworkFamily family = NetworkFamily.Ipv4)
|
||||
@@ -43,7 +61,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
var logger = new TransportEventLogger();
|
||||
events = logger.Events;
|
||||
|
||||
transport = new GameObject().AddComponent<UnityTransport>();
|
||||
transport = new GameObject().AddComponent<UnityTransportTestComponent>();
|
||||
|
||||
transport.OnTransportEvent += logger.HandleEvent;
|
||||
transport.MaxPayloadSize = maxPayloadSize;
|
||||
@@ -91,5 +109,58 @@ namespace Unity.Netcode.RuntimeTests
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal class UnityTransportTestComponent : UnityTransport, INetworkUpdateSystem
|
||||
{
|
||||
private static List<UnityTransportTestComponent> s_Instances = new List<UnityTransportTestComponent>();
|
||||
|
||||
public static void CleanUp()
|
||||
{
|
||||
for (int i = s_Instances.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var instance = s_Instances[i];
|
||||
instance.Shutdown();
|
||||
DestroyImmediate(instance.gameObject);
|
||||
}
|
||||
s_Instances.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulate the <see cref="NetworkManager.NetworkUpdate"/> being invoked so <see cref="UnityTransport.EarlyUpdate"/>
|
||||
/// and <see cref="UnityTransport.PostLateUpdate"/> are invoked.
|
||||
/// </summary>
|
||||
public void NetworkUpdate(NetworkUpdateStage updateStage)
|
||||
{
|
||||
switch (updateStage)
|
||||
{
|
||||
case NetworkUpdateStage.EarlyUpdate:
|
||||
{
|
||||
EarlyUpdate();
|
||||
break;
|
||||
}
|
||||
case NetworkUpdateStage.PostLateUpdate:
|
||||
{
|
||||
PostLateUpdate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
s_Instances.Remove(this);
|
||||
base.Shutdown();
|
||||
this.UnregisterAllNetworkUpdates();
|
||||
}
|
||||
|
||||
public override void Initialize(NetworkManager networkManager = null)
|
||||
{
|
||||
base.Initialize(networkManager);
|
||||
this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate);
|
||||
this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate);
|
||||
this.RegisterNetworkUpdate(NetworkUpdateStage.PostLateUpdate);
|
||||
s_Instances.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// 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();
|
||||
|
||||
UnityTransportTestComponent.CleanUp();
|
||||
yield return null;
|
||||
}
|
||||
|
||||
@@ -217,8 +217,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
Assert.AreEqual(33, m_ServerEvents[3].Data.First());
|
||||
Assert.AreEqual(10, m_ServerEvents[3].Data.Count);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Check sending data to multiple clients.
|
||||
@@ -260,8 +258,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
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.
|
||||
@@ -299,8 +295,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
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.
|
||||
@@ -332,8 +326,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
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).
|
||||
@@ -373,8 +365,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
Assert.AreEqual(NetworkEvent.Data, m_ServerEvents[i].Type);
|
||||
Assert.AreEqual(1024, m_ServerEvents[i].Data.Count);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
#if !UTP_TRANSPORT_2_0_ABOVE
|
||||
@@ -451,8 +441,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_Client1.Shutdown();
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
@@ -472,8 +460,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_Client1.DisconnectLocalClient();
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
@@ -493,8 +479,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_Server.DisconnectRemoteClient(m_ServerEvents[0].ClientID);
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
@@ -514,8 +498,47 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_Server.Send(m_Client1.ServerClientId, data, NetworkDelivery.Reliable);
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
public enum AfterShutdownAction
|
||||
{
|
||||
Send,
|
||||
DisconnectRemoteClient,
|
||||
DisconnectLocalClient,
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DoesNotActAfterShutdown([Values] AfterShutdownAction afterShutdownAction)
|
||||
{
|
||||
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();
|
||||
|
||||
if (afterShutdownAction == AfterShutdownAction.Send)
|
||||
{
|
||||
var data = new ArraySegment<byte>(new byte[16]);
|
||||
m_Server.Send(m_Client1.ServerClientId, data, NetworkDelivery.Reliable);
|
||||
|
||||
yield return EnsureNoNetworkEvent(m_Client1Events);
|
||||
}
|
||||
else if (afterShutdownAction == AfterShutdownAction.DisconnectRemoteClient)
|
||||
{
|
||||
m_Server.DisconnectRemoteClient(m_Client1.ServerClientId);
|
||||
|
||||
LogAssert.Expect(LogType.Assert, "DisconnectRemoteClient should be called on a listening server");
|
||||
}
|
||||
else if (afterShutdownAction == AfterShutdownAction.DisconnectLocalClient)
|
||||
{
|
||||
m_Server.DisconnectLocalClient();
|
||||
|
||||
yield return EnsureNoNetworkEvent(m_Client1Events);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user