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:
Unity Technologies
2024-12-12 00:00:00 +00:00
parent 016788c21e
commit 8fe07bbad2
78 changed files with 2243 additions and 1187 deletions

View File

@@ -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))

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 62913c78d28137141acc47ea8f63e657