com.unity.netcode.gameobjects@2.0.0-exp.2

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.0.0-exp.2] - 2024-04-02

### Added
- Added updates to all internal messages to account for a distributed authority network session connection.  (#2863)
- Added `NetworkRigidbodyBase` that provides users with a more customizable network rigidbody, handles both `Rigidbody` and `Rigidbody2D`, and provides an option to make `NetworkTransform` use the rigid body for motion.  (#2863)
  - For a customized `NetworkRigidbodyBase` class:
    - `NetworkRigidbodyBase.AutoUpdateKinematicState` provides control on whether the kinematic setting will be automatically set or not when ownership changes.
    - `NetworkRigidbodyBase.AutoSetKinematicOnDespawn` provides control on whether isKinematic will automatically be set to true when the associated `NetworkObject` is despawned.
    - `NetworkRigidbodyBase.Initialize` is a protected method that, when invoked, will initialize the instance. This includes options to:
      - Set whether using a `RigidbodyTypes.Rigidbody` or `RigidbodyTypes.Rigidbody2D`.
      - Includes additional optional parameters to set the `NetworkTransform`, `Rigidbody`, and `Rigidbody2d` to use.
  - Provides additional public methods:
    - `NetworkRigidbodyBase.GetPosition` to return the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting).
    - `NetworkRigidbodyBase.GetRotation` to return the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting).
    - `NetworkRigidbodyBase.MovePosition` to move to the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting).
    - `NetworkRigidbodyBase.MoveRotation` to move to the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting).
    - `NetworkRigidbodyBase.Move` to move to the position and rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting).
    - `NetworkRigidbodyBase.Move` to move to the position and rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting).
    - `NetworkRigidbodyBase.SetPosition` to set the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting).
    - `NetworkRigidbodyBase.SetRotation` to set the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting).
    - `NetworkRigidbodyBase.ApplyCurrentTransform` to set the position and rotation of the `Rigidbody` or `Rigidbody2d` based on the associated `GameObject` transform (depending upon its initialized setting).
    - `NetworkRigidbodyBase.WakeIfSleeping` to wake up the rigid body if sleeping.
    - `NetworkRigidbodyBase.SleepRigidbody` to put the rigid body to sleep.
    - `NetworkRigidbodyBase.IsKinematic` to determine if the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) is currently kinematic.
    - `NetworkRigidbodyBase.SetIsKinematic` to set the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) current kinematic state.
    - `NetworkRigidbodyBase.ResetInterpolation` to reset the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) back to its original interpolation value when initialized.
  - Now includes a `MonoBehaviour.FixedUpdate` implementation that will update the assigned `NetworkTransform` when `NetworkRigidbodyBase.UseRigidBodyForMotion` is true. (#2863)
- Added `RigidbodyContactEventManager` that provides a more optimized way to process collision enter and collision stay events as opposed to the `Monobehaviour` approach. (#2863)
  - Can be used in client-server and distributed authority modes, but is particularly useful in distributed authority.
- Added rigid body motion updates to `NetworkTransform` which allows users to set interolation on rigid bodies. (#2863)
  - Extrapolation is only allowed on authoritative instances, but custom class derived from `NetworkRigidbodyBase` or `NetworkRigidbody` or `NetworkRigidbody2D` automatically switches non-authoritative instances to interpolation if set to extrapolation.
- Added distributed authority mode support to `NetworkAnimator`. (#2863)
- Added session mode selection to `NetworkManager` inspector view. (#2863)
- Added distributed authority permissions feature. (#2863)
- Added distributed authority mode specific `NetworkObject` permissions flags (Distributable, Transferable, and RequestRequired). (#2863)
- Added distributed authority mode specific `NetworkObject.SetOwnershipStatus` method that applies one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863)
- Added distributed authority mode specific `NetworkObject.RemoveOwnershipStatus` method that removes one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863)
- Added distributed authority mode specific `NetworkObject.HasOwnershipStatus` method that will return (true or false) whether one or more ownership flags is set. (#2863)
- Added distributed authority mode specific `NetworkObject.SetOwnershipLock` method that locks ownership of a `NetworkObject` to prevent ownership from changing until the current owner releases the lock. (#2863)
- Added distributed authority mode specific `NetworkObject.RequestOwnership` method that sends an ownership request to the current owner of a spawned `NetworkObject` instance. (#2863)
- Added distributed authority mode specific `NetworkObject.OnOwnershipRequested` callback handler that is invoked on the owner/authoritative side when a non-owner requests ownership. Depending upon the boolean returned value depends upon whether the request is approved or denied. (#2863)
- Added distributed authority mode specific `NetworkObject.OnOwnershipRequestResponse` callback handler that is invoked when a non-owner's request has been processed. This callback includes a `NetworkObjet.OwnershipRequestResponseStatus` response parameter that describes whether the request was approved or the reason why it was not approved. (#2863)
- Added distributed authority mode specific `NetworkObject.DeferDespawn` method that defers the despawning of `NetworkObject` instances on non-authoritative clients based on the tick offset parameter. (#2863)
- Added distributed authority mode specific `NetworkObject.OnDeferredDespawnComplete` callback handler that can be used to further control when deferring the despawning of a `NetworkObject` on non-authoritative instances. (#2863)
- Added `NetworkClient.SessionModeType` as one way to determine the current session mode of the network session a client is connected to. (#2863)
- Added distributed authority mode specific `NetworkClient.IsSessionOwner` property to determine if the current local client is the current session owner of a distributed authority session. (#2863)
- Added distributed authority mode specific client side spawning capabilities. When running in distributed authority mode, clients can instantiate and spawn `NetworkObject` instances (the local client is authomatically the owner of the spawned object). (#2863)
  - This is useful to better visually synchronize owner authoritative motion models and newly spawned `NetworkObject` instances (i.e. projectiles for example).
- Added distributed authority mode specific client side player spawning capabilities. Clients will automatically spawn their associated player object locally. (#2863)
- Added distributed authority mode specific `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property (default is true) to provide control over the automatic spawning of player prefabs on the local client side. (#2863)
- Added distributed authority mode specific `NetworkManager.OnFetchLocalPlayerPrefabToSpawn` callback that, when assigned, will allow the local client to provide the player prefab to be spawned for the local client. (#2863)
  - This is only invoked if the `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property is set to true.
- Added distributed authority mode specific `NetworkBehaviour.HasAuthority` property that determines if the local client has authority over the associated `NetworkObject` instance (typical use case is within a `NetworkBehaviour` script much like that of `IsServer` or `IsClient`). (#2863)
- Added distributed authority mode specific `NetworkBehaviour.IsSessionOwner` property that determines if the local client is the session owner (typical use case would be to determine if the local client can has scene management authority within a `NetworkBehaviour` script). (#2863)
- Added support for distributed authority mode scene management where the currently assigned session owner can start scene events (i.e. scene loading and scene unloading). (#2863)

### Fixed

- Fixed issue where the host was not invoking `OnClientDisconnectCallback` for its own local client when internally shutting down. (#2822)
- Fixed issue where NetworkTransform could potentially attempt to "unregister" a named message prior to it being registered. (#2807)
- Fixed issue where in-scene placed `NetworkObject`s with complex nested children `NetworkObject`s (more than one child in depth) would not synchronize properly if WorldPositionStays was set to true. (#2796)

### Changed
- Changed client side awareness of other clients is now the same as a server or host. (#2863)
- Changed `NetworkManager.ConnectedClients` can now be accessed by both server and clients. (#2863)
- Changed `NetworkManager.ConnectedClientsList` can now be accessed by both server and clients. (#2863)
- Changed `NetworkTransform` defaults to owner authoritative when connected to a distributed authority session. (#2863)
- Changed `NetworkVariable` defaults to owner write and everyone read permissions when connected to a distributed authority session (even if declared with server read or write permissions).  (#2863)
- Changed `NetworkObject` no longer implements the `MonoBehaviour.Update` method in order to determine whether a `NetworkObject` instance has been migrated to a different scene. Instead, only `NetworkObjects` with the `SceneMigrationSynchronization` property set will be updated internally during the `NetworkUpdateStage.PostLateUpdate` by `NetworkManager`. (#2863)
- Changed `NetworkManager` inspector view layout where properties are now organized by category. (#2863)
- Changed `NetworkTransform` to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810)
- Changed `CustomMessageManager` so it no longer attempts to register or "unregister" a null or empty string and will log an error if this condition occurs. (#2807)
This commit is contained in:
Unity Technologies
2024-04-02 00:00:00 +00:00
parent f8ebf679ec
commit 143a6cbd34
140 changed files with 18009 additions and 2672 deletions

View File

@@ -1,3 +1,4 @@
#if !MULTIPLAYER_TOOLS
using System.IO;
using System.Reflection;
using NUnit.Framework;
@@ -38,3 +39,4 @@ namespace Unity.Netcode.EditorTests
}
}
}
#endif

View File

@@ -311,8 +311,11 @@ namespace Unity.Netcode.EditorTests
{
int* sizeValue = (int*)(unsafePtr + offset);
Assert.AreEqual(value.Length, *sizeValue);
#if UTP_TRANSPORT_2_0_ABOVE
var asTPointer = value.GetUnsafePtr();
#else
var asTPointer = (T*)value.GetUnsafePtr();
#endif
var underlyingTArray = (T*)(unsafePtr + sizeof(int) + offset);
for (var i = 0; i < value.Length; ++i)
{

View File

@@ -2,7 +2,9 @@ using System;
using NUnit.Framework;
using Unity.Collections;
using Unity.Netcode.Transports.UTP;
#if !UTP_TRANSPORT_2_0_ABOVE
using Unity.Networking.Transport;
#endif
namespace Unity.Netcode.EditorTests
{

View File

@@ -2,7 +2,9 @@ using System;
using NUnit.Framework;
using Unity.Collections;
using Unity.Netcode.Transports.UTP;
#if !UTP_TRANSPORT_2_0_ABOVE
using Unity.Networking.Transport;
#endif
namespace Unity.Netcode.EditorTests
{

View File

@@ -26,9 +26,9 @@ namespace Unity.Netcode.RuntimeTests
m_ApprovalFailureType = approvalFailureType;
}
// Must be >= 2 since this is an int value and the test waits for timeout - 1 to try to verify it doesn't
// Must be >= 5 since this is an int value and the test waits for timeout - 1 to try to verify it doesn't
// time out early
private const int k_TestTimeoutPeriod = 1;
private const int k_TestTimeoutPeriod = 5;
private Regex m_ExpectedLogMessage;
private LogType m_LogType;
@@ -59,6 +59,7 @@ namespace Unity.Netcode.RuntimeTests
{
if (m_ApprovalFailureType == ApprovalTimedOutTypes.ServerDoesNotRespond)
{
m_ServerNetworkManager.ConnectionManager.MockSkippingApproval = true;
// We catch (don't process) the incoming approval message to simulate the server not sending the approved message in time
m_ClientNetworkManagers[0].ConnectionManager.MessageManager.Hook(new MessageCatcher<ConnectionApprovedMessage>(m_ClientNetworkManagers[0]));
m_ExpectedLogMessage = new Regex("Timed out waiting for the server to approve the connection request.");
@@ -80,18 +81,18 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator ValidateApprovalTimeout()
{
// Delay for half of the wait period
yield return new WaitForSeconds(k_TestTimeoutPeriod * 0.5f);
// Just delay for a second
yield return new WaitForSeconds(1);
// Verify we haven't received the time out message yet
NetcodeLogAssert.LogWasNotReceived(LogType.Log, m_ExpectedLogMessage);
yield return new WaitForSeconds(k_TestTimeoutPeriod * 1.5f);
yield return new WaitForSeconds(k_TestTimeoutPeriod * 1.25f);
// We should have the test relative log message by this time.
NetcodeLogAssert.LogWasReceived(m_LogType, m_ExpectedLogMessage);
Debug.Log("Checking connected client count");
VerboseDebug("Checking connected client count");
// It should only have the host client connected
Assert.AreEqual(1, m_ServerNetworkManager.ConnectedClients.Count, $"Expected only one client when there were {m_ServerNetworkManager.ConnectedClients.Count} clients connected!");

View File

@@ -5,6 +5,7 @@ using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace Unity.Netcode.RuntimeTests
@@ -84,12 +85,11 @@ namespace Unity.Netcode.RuntimeTests
return 0;
}
public override void DeferMessage(IDeferredNetworkMessageManager.TriggerType trigger, ulong key, FastBufferReader reader, ref NetworkContext context)
public override void DeferMessage(IDeferredNetworkMessageManager.TriggerType trigger, ulong key, FastBufferReader reader, ref NetworkContext context, string messageType)
{
OnBeforeDefer?.Invoke(this, key);
DeferMessageCalled = true;
base.DeferMessage(trigger, key, reader, ref context);
base.DeferMessage(trigger, key, reader, ref context, messageType);
}
public override void ProcessTriggers(IDeferredNetworkMessageManager.TriggerType trigger, ulong key)
@@ -203,6 +203,9 @@ namespace Unity.Netcode.RuntimeTests
protected override void OnInlineSetup()
{
// Revert back to standard deferred message format for tests (for now)
DeferredMessageManager.IncludeMessageType = false;
DeferredMessageTestRpcAndNetworkVariableComponent.ClientInstances.Clear();
DeferredMessageTestRpcComponent.ClientInstances.Clear();
DeferredMessageTestNetworkVariableComponent.ClientInstances.Clear();
@@ -894,7 +897,7 @@ namespace Unity.Netcode.RuntimeTests
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} associated with id ({serverObject.GetComponent<NetworkObject>().NetworkObjectId}), but the {nameof(NetworkObject)} was not received within the timeout period {timeout} second(s).");
}
int purgeCount = 0;
@@ -904,7 +907,7 @@ namespace Unity.Netcode.RuntimeTests
{
++purgeCount;
var elapsed = client.RealTimeProvider.RealTimeSinceStartup - start;
Debug.Log(client.RealTimeProvider.GetType().FullName);
VerboseDebug(client.RealTimeProvider.GetType().FullName);
Assert.GreaterOrEqual(elapsed, timeout);
Assert.AreEqual(1, manager.DeferredMessageCountTotal());
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredNetworkMessageManager.TriggerType.OnSpawn));
@@ -990,7 +993,7 @@ namespace Unity.Netcode.RuntimeTests
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} associated with id ({serverObject.GetComponent<NetworkObject>().NetworkObjectId}), but the {nameof(NetworkObject)} was not received within the timeout period {timeout} second(s).");
}
int purgeCount = 0;
@@ -1095,9 +1098,8 @@ namespace Unity.Netcode.RuntimeTests
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject2.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} associated with id ({serverObject.GetComponent<NetworkObject>().NetworkObjectId}), but the {nameof(NetworkObject)} was not received within the timeout period {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} associated with id ({serverObject2.GetComponent<NetworkObject>().NetworkObjectId}), but the {nameof(NetworkObject)} was not received within the timeout period {timeout} second(s).");
}
int purgeCount = 0;
@@ -1188,7 +1190,7 @@ namespace Unity.Netcode.RuntimeTests
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} associated with id ({serverObject.GetComponent<NetworkObject>().NetworkObjectId}), but the {nameof(NetworkObject)} was not received within the timeout period {timeout} second(s).");
}
int purgeCount = 0;
@@ -1271,7 +1273,11 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(0, manager.DeferredMessageCountForKey(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serverObject2.GetComponent<NetworkObject>().NetworkObjectId));
}
serverObject2.GetComponent<NetworkObject>().ChangeOwnership(m_ServerNetworkManager.LocalClientId);
// KITTY-TODO: Review this change please:
// Changing ownership when the owner specified is already an owner should not send any messages
// The original test was changing ownership to the server when the object was spawned with the server being an owner.
//serverObject2.GetComponent<NetworkObject>().ChangeOwnership(m_ServerNetworkManager.LocalClientId);
serverObject2.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[1].LocalClientId);
WaitForAllClientsToReceive<ChangeOwnershipMessage>();
foreach (var client in m_ClientNetworkManagers)
@@ -1285,7 +1291,7 @@ namespace Unity.Netcode.RuntimeTests
foreach (var unused in m_ClientNetworkManagers)
{
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} with key {serverObject.GetComponent<NetworkObject>().NetworkObjectId}, but that trigger was not received within within {timeout} second(s).");
LogAssert.Expect(LogType.Warning, $"[Netcode] Deferred messages were received for a trigger of type {IDeferredNetworkMessageManager.TriggerType.OnSpawn} associated with id ({serverObject.GetComponent<NetworkObject>().NetworkObjectId}), but the {nameof(NetworkObject)} was not received within the timeout period {timeout} second(s).");
}
int purgeCount = 0;

View File

@@ -1,4 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
@@ -37,7 +38,10 @@ namespace Unity.Netcode.RuntimeTests
protected override int NumberOfClients => 1;
private OwnerPersistence m_OwnerPersistence;
private ClientDisconnectType m_ClientDisconnectType;
private bool m_ClientDisconnected;
private Dictionary<NetworkManager, ConnectionEventData> m_DisconnectedEvent = new Dictionary<NetworkManager, ConnectionEventData>();
private ulong m_DisconnectEventClientId;
private ulong m_TransportClientId;
private ulong m_ClientId;
@@ -89,6 +93,16 @@ namespace Unity.Netcode.RuntimeTests
m_ClientDisconnected = true;
}
private void OnConnectionEvent(NetworkManager networkManager, ConnectionEventData connectionEventData)
{
if (connectionEventData.EventType != ConnectionEvent.ClientDisconnected)
{
return;
}
m_DisconnectedEvent.Add(networkManager, connectionEventData);
}
/// <summary>
/// Conditional check to assure the transport to client (and vice versa) mappings are cleaned up
/// </summary>
@@ -126,6 +140,7 @@ namespace Unity.Netcode.RuntimeTests
public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType clientDisconnectType)
{
m_ClientId = m_ClientNetworkManagers[0].LocalClientId;
m_ClientDisconnectType = clientDisconnectType;
var serverSideClientPlayer = m_ServerNetworkManager.ConnectionManager.ConnectedClients[m_ClientId].PlayerObject;
@@ -134,11 +149,15 @@ namespace Unity.Netcode.RuntimeTests
if (clientDisconnectType == ClientDisconnectType.ServerDisconnectsClient)
{
m_ClientNetworkManagers[0].OnClientDisconnectCallback += OnClientDisconnectCallback;
m_ClientNetworkManagers[0].OnConnectionEvent += OnConnectionEvent;
m_ServerNetworkManager.OnConnectionEvent += OnConnectionEvent;
m_ServerNetworkManager.DisconnectClient(m_ClientId);
}
else
{
m_ServerNetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback;
m_ServerNetworkManager.OnConnectionEvent += OnConnectionEvent;
m_ClientNetworkManagers[0].OnConnectionEvent += OnConnectionEvent;
yield return StopOneClient(m_ClientNetworkManagers[0]);
}
@@ -146,6 +165,23 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForConditionOrTimeOut(() => m_ClientDisconnected);
AssertOnTimeout("Timed out waiting for client to disconnect!");
if (clientDisconnectType == ClientDisconnectType.ServerDisconnectsClient)
{
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ServerNetworkManager), $"Could not find the server {nameof(NetworkManager)} disconnect event entry!");
Assert.IsTrue(m_DisconnectedEvent[m_ServerNetworkManager].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the server {nameof(NetworkManager)} disconnect event entry!");
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ClientNetworkManagers[0]), $"Could not find the client {nameof(NetworkManager)} disconnect event entry!");
Assert.IsTrue(m_DisconnectedEvent[m_ClientNetworkManagers[0]].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the client {nameof(NetworkManager)} disconnect event entry!");
// Unregister for this event otherwise it will be invoked during teardown
m_ServerNetworkManager.OnConnectionEvent -= OnConnectionEvent;
}
else
{
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ServerNetworkManager), $"Could not find the server {nameof(NetworkManager)} disconnect event entry!");
Assert.IsTrue(m_DisconnectedEvent[m_ServerNetworkManager].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the server {nameof(NetworkManager)} disconnect event entry!");
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ClientNetworkManagers[0]), $"Could not find the client {nameof(NetworkManager)} disconnect event entry!");
Assert.IsTrue(m_DisconnectedEvent[m_ClientNetworkManagers[0]].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the client {nameof(NetworkManager)} disconnect event entry!");
}
if (m_OwnerPersistence == OwnerPersistence.DestroyWithOwner)
{
// When we are destroying with the owner, validate the player object is destroyed on the server side
@@ -161,6 +197,21 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForConditionOrTimeOut(TransportIdCleanedUp);
AssertOnTimeout("Timed out waiting for transport and client id mappings to be cleaned up!");
// Validate the host-client generates a OnClientDisconnected event when it shutsdown.
// Only test when the test run is the client disconnecting from the server (otherwise the server will be shutdown already)
if (clientDisconnectType == ClientDisconnectType.ClientDisconnectsFromServer)
{
m_DisconnectedEvent.Clear();
m_ClientDisconnected = false;
m_ServerNetworkManager.Shutdown();
yield return WaitForConditionOrTimeOut(() => m_ClientDisconnected);
AssertOnTimeout("Timed out waiting for host-client to generate disconnect message!");
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ServerNetworkManager), $"Could not find the server {nameof(NetworkManager)} disconnect event entry!");
Assert.IsTrue(m_DisconnectedEvent[m_ServerNetworkManager].ClientId == NetworkManager.ServerClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the server {nameof(NetworkManager)} disconnect event entry!");
}
}
}
}

View File

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

View File

@@ -0,0 +1,266 @@
using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
public class DeferredDespawningTests : IntegrationTestWithApproximation
{
private const int k_DaisyChainedCount = 5;
protected override int NumberOfClients => 2;
private List<GameObject> m_DaisyChainedDespawnObjects = new List<GameObject>();
private List<ulong> m_HasReachedEnd = new List<ulong>();
public DeferredDespawningTests() : base(HostOrServer.DAHost)
{
}
protected override void OnServerAndClientsCreated()
{
var daisyChainPrevious = (DeferredDespawnDaisyChained)null;
for (int i = 0; i < k_DaisyChainedCount; i++)
{
var daisyChainNode = CreateNetworkObjectPrefab($"Daisy-{i}");
var daisyChainBehaviour = daisyChainNode.AddComponent<DeferredDespawnDaisyChained>();
daisyChainBehaviour.IsRoot = i == 0;
if (daisyChainPrevious != null)
{
daisyChainPrevious.PrefabToSpawnWhenDespawned = daisyChainBehaviour.gameObject;
}
m_DaisyChainedDespawnObjects.Add(daisyChainNode);
daisyChainPrevious = daisyChainBehaviour;
}
base.OnServerAndClientsCreated();
}
[UnityTest]
public IEnumerator DeferredDespawning()
{
DeferredDespawnDaisyChained.EnableVerbose = m_EnableVerboseDebug;
var rootInstance = SpawnObject(m_DaisyChainedDespawnObjects[0], m_ServerNetworkManager);
DeferredDespawnDaisyChained.ReachedLastChainInstance = ReachedLastChainObject;
var timeoutHelper = new TimeoutHelper(300);
yield return WaitForConditionOrTimeOut(HaveAllClientsReachedEndOfChain, timeoutHelper);
AssertOnTimeout($"Timed out waiting for all children to reach the end of their chained deferred despawns!", timeoutHelper);
}
private bool HaveAllClientsReachedEndOfChain()
{
if (!m_HasReachedEnd.Contains(m_ServerNetworkManager.LocalClientId))
{
return false;
}
foreach (var client in m_ClientNetworkManagers)
{
if (!m_HasReachedEnd.Contains(client.LocalClientId))
{
return false;
}
}
return true;
}
private void ReachedLastChainObject(ulong clientId)
{
m_HasReachedEnd.Add(clientId);
}
}
/// <summary>
/// This helper behaviour handles the majority of the validation for deferred despawning.
/// Each instance triggers a series of deferred despawns where the owner validates the
/// NetworkVariables are updated and spawns another prefab prior to despawning locally
/// and the non-owners validate receiving the NetworkVariable change notification which
/// contains a reference to a DeferredDespawnDaisyChained component on the newly spawned
/// prefab driven by the authority. This repeats for the number specified in the integration
/// test.
/// </summary>
public class DeferredDespawnDaisyChained : NetworkBehaviour
{
public static bool EnableVerbose;
public static Action<ulong> ReachedLastChainInstance;
private const int k_StartingDeferTick = 4;
public static Dictionary<ulong, Dictionary<ulong, DeferredDespawnDaisyChained>> ClientRelativeInstances = new Dictionary<ulong, Dictionary<ulong, DeferredDespawnDaisyChained>>();
public bool IsRoot;
public GameObject PrefabToSpawnWhenDespawned;
public bool WasContactedByPeviousChainMember { get; private set; }
public int DeferDespawnTick { get; private set; }
private void PingInstance()
{
WasContactedByPeviousChainMember = true;
}
/// <summary>
/// This hits two birds with one NetworkVariable:
/// - Validates that NetworkVariables modified while the authority is in the middle of deferring a despawn are serialized and received by non-authority instances.
/// - Validates that the non-authority instances receive the updates within the deferred tick period of time and can use them to handle other visual synchronization
/// realted tasks (or the like).
/// </summary>
private NetworkVariable<NetworkBehaviourReference> m_ValidateDirtyNetworkVarUpdate = new NetworkVariable<NetworkBehaviourReference>();
private DeferredDespawnDaisyChained m_NextNodeSpawned = null;
private void FailTest(string msg)
{
Assert.Fail($"[{nameof(DeferredDespawnDaisyChained)}][Client-{NetworkManager.LocalClientId}] {msg}");
}
public override void OnNetworkSpawn()
{
var localId = NetworkManager.LocalClientId;
if (!ClientRelativeInstances.ContainsKey(localId))
{
ClientRelativeInstances.Add(localId, new Dictionary<ulong, DeferredDespawnDaisyChained>());
}
if (ClientRelativeInstances[localId].ContainsKey(NetworkObject.NetworkObjectId))
{
FailTest($"[{nameof(OnNetworkSpawn)}] Client already has a table entry for NetworkObject-{NetworkObject.NetworkObjectId} | {name}!");
}
ClientRelativeInstances[localId].Add(NetworkObject.NetworkObjectId, this);
if (!HasAuthority)
{
m_ValidateDirtyNetworkVarUpdate.OnValueChanged += OnValidateDirtyChanged;
}
if (HasAuthority && IsRoot)
{
DeferDespawnTick = k_StartingDeferTick;
}
base.OnNetworkSpawn();
}
private void OnValidateDirtyChanged(NetworkBehaviourReference previous, NetworkBehaviourReference current)
{
if (!HasAuthority)
{
if (!current.TryGet(out m_NextNodeSpawned, NetworkManager))
{
FailTest($"[{nameof(OnValidateDirtyChanged)}][{nameof(NetworkBehaviourReference)}] Failed to get the {nameof(DeferredDespawnDaisyChained)} behaviour from the {nameof(NetworkBehaviourReference)}!");
}
if (m_NextNodeSpawned.NetworkManager != NetworkManager)
{
FailTest($"[{nameof(NetworkManager)}][{nameof(NetworkBehaviourReference.TryGet)}] The {nameof(NetworkManager)} of {nameof(m_NextNodeSpawned)} does not match the local relative {nameof(NetworkManager)} instance!");
}
}
}
public override void OnNetworkDespawn()
{
if (!HasAuthority && !NetworkManager.ShutdownInProgress)
{
if (PrefabToSpawnWhenDespawned != null)
{
m_NextNodeSpawned.PingInstance();
}
else
{
ReachedLastChainInstance?.Invoke(NetworkManager.LocalClientId);
}
}
base.OnNetworkDespawn();
}
private void InvokeDespawn()
{
if (!HasAuthority)
{
FailTest($"[{nameof(InvokeDespawn)}] Client is not the authority but this was invoked (integration test logic issue)!");
}
NetworkObject.DeferDespawn(DeferDespawnTick);
}
public override void OnDeferringDespawn(int despawnTick)
{
if (!HasAuthority)
{
FailTest($"[{nameof(OnDeferringDespawn)}] Client is not the authority but this was invoked (integration test logic issue)!");
}
if (despawnTick != (DeferDespawnTick + NetworkManager.ServerTime.Tick))
{
FailTest($"[{nameof(OnDeferringDespawn)}] The passed in {despawnTick} parameter ({despawnTick}) does not equal the expected value of ({DeferDespawnTick + NetworkManager.ServerTime.Tick})!");
}
if (PrefabToSpawnWhenDespawned != null)
{
var deferNetworkObject = PrefabToSpawnWhenDespawned.GetComponent<NetworkObject>().InstantiateAndSpawn(NetworkManager);
var deferComponent = deferNetworkObject.GetComponent<DeferredDespawnDaisyChained>();
// Slowly increment the despawn tick count as we process the chain of deferred despawns
deferComponent.DeferDespawnTick = DeferDespawnTick + 1;
// This should get updated on all non-authority instances before they despawn
m_ValidateDirtyNetworkVarUpdate.Value = new NetworkBehaviourReference(deferComponent);
}
else
{
ReachedLastChainInstance?.Invoke(NetworkManager.LocalClientId);
}
base.OnDeferringDespawn(despawnTick);
}
private bool m_DeferredDespawn;
private void Update()
{
if (!IsSpawned || !HasAuthority || m_DeferredDespawn)
{
return;
}
// Wait until all clients have this instance
foreach (var clientId in NetworkManager.ConnectedClientsIds)
{
if (!ClientRelativeInstances.ContainsKey(clientId))
{
// exit early if the client doesn't exist yet
return;
}
if (!ClientRelativeInstances[clientId].ContainsKey(NetworkObjectId))
{
// exit early if the client hasn't spawned a clone of this instance yet
return;
}
if (clientId == NetworkManager.LocalClientId)
{
continue;
}
// This should happen shortly afte the instances spawns (based on the deferred despawn count)
if (!IsRoot && !ClientRelativeInstances[clientId][NetworkObjectId].WasContactedByPeviousChainMember)
{
// exit early if the non-authority instance has not been contacted yet
return;
}
}
// If we made it here, then defer despawn this instance
InvokeDespawn();
m_DeferredDespawn = true;
}
private void Log(string message)
{
if (!EnableVerbose)
{
return;
}
Debug.Log($"[{name}][Client-{NetworkManager.LocalClientId}][{NetworkObjectId}] {message}");
}
}
}

View File

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

View File

@@ -0,0 +1,520 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Unity.Netcode.Components;
using Unity.Netcode.TestHelpers.Runtime;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using UnityEngine.TestTools;
using Random = UnityEngine.Random;
namespace Unity.Netcode.RuntimeTests
{
/// <summary>
/// Validates that distributable NetworkObjects are distributed upon
/// a client connecting or disconnecting.
/// </summary>
public class DistributeObjectsTests : IntegrationTestWithApproximation
{
private GameObject m_DistributeObject;
private StringBuilder m_ErrorLog = new StringBuilder();
private const int k_LateJoinClientCount = 4;
protected override int NumberOfClients => 0;
public DistributeObjectsTests() : base(HostOrServer.DAHost)
{
}
protected override IEnumerator OnSetup()
{
m_ObjectToValidate = null;
return base.OnSetup();
}
protected override void OnServerAndClientsCreated()
{
var serverTransport = m_ServerNetworkManager.NetworkConfig.NetworkTransport as UnityTransport;
// I hate having to add time to our tests, but in case a VM is running slow the disconnect timeout needs to be reasonably high
serverTransport.DisconnectTimeoutMS = 1000;
m_DistributeObject = CreateNetworkObjectPrefab("DisObject");
m_DistributeObject.AddComponent<DistributeObjectsTestHelper>();
m_DistributeObject.AddComponent<DistributeTestTransform>();
// Set baseline to be distributable
var networkObject = m_DistributeObject.GetComponent<NetworkObject>();
networkObject.SetOwnershipStatus(NetworkObject.OwnershipStatus.Distributable);
networkObject.DontDestroyWithOwner = true;
base.OnServerAndClientsCreated();
}
protected override IEnumerator OnServerAndClientsConnected()
{
m_ServerNetworkManager.SpawnManager.EnableDistributeLogging = m_EnableVerboseDebug;
m_ServerNetworkManager.ConnectionManager.EnableDistributeLogging = m_EnableVerboseDebug;
return base.OnServerAndClientsConnected();
}
private NetworkObject m_ObjectToValidate;
private bool ValidateObjectSpawnedOnAllClients()
{
m_ErrorLog.Clear();
var networkObjectId = m_ObjectToValidate.NetworkObjectId;
var name = m_ObjectToValidate.name;
if (!UseCMBService() && !m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
{
m_ErrorLog.Append($"Client-{m_ServerNetworkManager.LocalClientId} has not spawned {name}!");
return false;
}
foreach (var client in m_ClientNetworkManagers)
{
if (!client.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
{
m_ErrorLog.Append($"Client-{client.LocalClientId} has not spawned {name}!");
return false;
}
}
return true;
}
private const int k_ObjectCount = 20;
private bool ValidateDistributedObjectsSpawned(bool lateJoining)
{
m_ErrorLog.Clear();
var hostId = m_ServerNetworkManager.LocalClientId;
if (!DistributeObjectsTestHelper.DistributedObjects.ContainsKey(hostId))
{
m_ErrorLog.AppendLine($"[Client-{hostId}] Does not have an entry in the root of the {nameof(DistributeObjectsTestHelper.DistributedObjects)} table!");
return false;
}
var daHostObjectTracking = DistributeObjectsTestHelper.DistributedObjects[hostId];
if (!daHostObjectTracking.ContainsKey(hostId))
{
m_ErrorLog.AppendLine($"[Client-{hostId}] Does not have a local an entry in the {nameof(DistributeObjectsTestHelper.DistributedObjects)} table!");
return false;
}
var daHostObjects = daHostObjectTracking[hostId];
var expected = 0;
if (lateJoining)
{
expected = k_ObjectCount / (m_ClientNetworkManagers.Count() + 1);
}
else
{
expected = k_ObjectCount / (m_ClientNetworkManagers.Where((c) => c.IsConnectedClient).Count() + 1);
}
// It should theoretically be the expected or...
if (daHostObjects.Count != expected)
{
// due to not rounding one more than the expected
expected++;
if (daHostObjects.Count != expected)
{
m_ErrorLog.AppendLine($"[Client-{hostId}][General] Expected {expected} spawned objects, but only {daHostObjects.Count} exist!");
return false;
}
}
foreach (var networkObject in daHostObjects)
{
m_ObjectToValidate = networkObject.Value;
if (!ValidateObjectSpawnedOnAllClients())
{
m_ErrorLog.AppendLine($"[{m_ObjectToValidate.name}] Was not spawned on all clients!");
return false;
}
}
return true;
}
private bool ValidateOwnershipTablesMatch()
{
m_ErrorLog.Clear();
var hostId = m_ServerNetworkManager.LocalClientId;
var expectedEntries = m_ClientNetworkManagers.Where((c) => c.IsListening && c.IsConnectedClient).Count() + 1;
// Make sure all clients have an table created
if (DistributeObjectsTestHelper.DistributedObjects.Count < expectedEntries)
{
m_ErrorLog.AppendLine($"[General] Expected {expectedEntries} entries in the root of the {nameof(DistributeObjectsTestHelper.DistributedObjects)} table but only {DistributeObjectsTestHelper.DistributedObjects.Count} exist!");
return false;
}
if (!DistributeObjectsTestHelper.DistributedObjects.ContainsKey(hostId))
{
m_ErrorLog.AppendLine($"[Client-{hostId}] Does not have an entry in the root of the {nameof(DistributeObjectsTestHelper.DistributedObjects)} table!");
return false;
}
var daHostEntries = DistributeObjectsTestHelper.DistributedObjects[hostId];
if (!daHostEntries.ContainsKey(hostId))
{
m_ErrorLog.AppendLine($"[Client-{hostId}] Does not have a local an entry in the {nameof(DistributeObjectsTestHelper.DistributedObjects)} table!");
return false;
}
var clients = m_ServerNetworkManager.ConnectedClientsIds.ToList();
clients.Remove(0);
// Cycle through each client's entry on the DAHost to run a comparison
foreach (var hostClientEntry in daHostEntries)
{
foreach (var ownerEntry in hostClientEntry.Value)
{
foreach (var client in clients)
{
var clientOwnerTable = DistributeObjectsTestHelper.DistributedObjects[client];
if (!clientOwnerTable.ContainsKey(hostClientEntry.Key))
{
m_ErrorLog.AppendLine($"[Client-{client}] No ownership table exists the client relative section of the {nameof(DistributeObjectsTestHelper.DistributedObjects)} table!");
return false;
}
var clientEntry = clientOwnerTable[hostClientEntry.Key];
if (!clientEntry.ContainsKey(ownerEntry.Key))
{
m_ErrorLog.AppendLine($"[Client-{client}] {ownerEntry.Value.name} does not exists in Client-{client}'s sub-section for Owner-{hostClientEntry.Key} relative section of the {nameof(DistributeObjectsTestHelper.DistributedObjects)} table!");
return false;
}
var clientObjectEntry = clientEntry[ownerEntry.Key];
if (clientObjectEntry.OwnerClientId != ownerEntry.Value.OwnerClientId)
{
m_ErrorLog.AppendLine($"[Client-{client}][Owner Mismatch] {clientObjectEntry.OwnerClientId} does equal {ownerEntry.Value.OwnerClientId}!");
return false;
}
// Assure the observers match
foreach (var observer in ownerEntry.Value.Observers)
{
if (!clientObjectEntry.Observers.Contains(observer))
{
m_ErrorLog.AppendLine($"[Client-{client}][Observer Mismatch] {nameof(NetworkObject)} {clientObjectEntry.name}'s observers does not contain {observer}, but the authority instance does!");
return false;
}
}
}
}
}
return true;
}
private bool ValidateTransformsMatch()
{
m_ErrorLog.Clear();
var hostId = m_ServerNetworkManager.LocalClientId;
var daHostEntries = DistributeObjectsTestHelper.DistributedObjects[hostId];
var clients = m_ServerNetworkManager.ConnectedClientsIds.ToList();
foreach (var clientOwner in daHostEntries.Keys)
{
// Cycle through the owner's objects
foreach (var entry in DistributeObjectsTestHelper.DistributedObjects[clientOwner][clientOwner].Values)
{
var ownerTestTransform = entry.GetComponent<DistributeTestTransform>();
// Compare against the other client instances of that object
foreach (var client in clients)
{
if (client == clientOwner)
{
continue;
}
var clientObjectInstance = DistributeObjectsTestHelper.DistributedObjects[client][clientOwner][entry.NetworkObjectId];
if (!ownerTestTransform.IsPositionClose(clientObjectInstance.transform.position))
{
m_ErrorLog.AppendLine($"[Position Mismatch] Client-{client} Instance: {GetVector3Values(clientObjectInstance.transform.position)} != Owner Instance: {GetVector3Values(ownerTestTransform.transform.position)}!");
return false;
}
}
}
}
return true;
}
protected override void OnNewClientCreated(NetworkManager networkManager)
{
networkManager.NetworkConfig.Prefabs = m_ServerNetworkManager.NetworkConfig.Prefabs;
base.OnNewClientCreated(networkManager);
}
private bool SpawnCountsMatch()
{
var passed = true;
var spawnCount = 0;
m_ErrorLog.Clear();
if (!UseCMBService())
{
spawnCount = m_ServerNetworkManager.SpawnManager.SpawnedObjects.Count;
}
else
{
spawnCount = m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects.Count;
}
foreach (var client in m_ClientNetworkManagers)
{
var clientCount = client.SpawnManager.SpawnedObjects.Count;
if (clientCount != spawnCount)
{
m_ErrorLog.AppendLine($"[Client-{client.LocalClientId}] Has a spawn count of {clientCount} but {spawnCount} was expected!");
passed = false;
}
}
return passed;
}
/// <summary>
/// This is a straight forward validation for the distribution of NetworkObjects
/// upon a client connecting or disconnecting. It also validates that the observers
/// on each non-authority instance matches the authority instance's. Finally, it
/// also includes validation that NetworkTransform updates continue to update and
/// synchronize properly after ownership for a set number of objects has changed.
/// </summary>
[UnityTest]
public IEnumerator DistributeNetworkObjects()
{
for (int i = 0; i < k_ObjectCount; i++)
{
SpawnObject(m_DistributeObject, m_ServerNetworkManager);
}
// Validate NetworkObjects get redistributed properly when a client joins
for (int j = 0; j < k_LateJoinClientCount; j++)
{
yield return CreateAndStartNewClient();
yield return WaitForConditionOrTimeOut(() => ValidateDistributedObjectsSpawned(true));
AssertOnTimeout($"[Client-{j + 1}][Initial Spawn] Not all clients spawned all objects!\n {m_ErrorLog}");
yield return WaitForConditionOrTimeOut(ValidateOwnershipTablesMatch);
AssertOnTimeout($"[Client-{j + 1}][OnwershipTable Mismatch] {m_ErrorLog}");
// When ownership changes, the new owner will randomly pick a new target to move towards and will move towards the target.
// Validate all other instances of the NetworkObjects that have had newly assigned owners have matching positions to the
// newly assigned owenr's instance.
yield return WaitForConditionOrTimeOut(ValidateTransformsMatch);
AssertOnTimeout($"[Client-{j + 1}][Transform Mismatch] {m_ErrorLog}");
DisplayOwnership();
yield return WaitForConditionOrTimeOut(SpawnCountsMatch);
AssertOnTimeout($"[Spawn Count Mismatch] {m_ErrorLog}");
}
// Validate NetworkObjects get redistributed properly when a client disconnects
for (int j = k_LateJoinClientCount - 1; j >= 0; j--)
{
var client = m_ClientNetworkManagers[j];
// Remove the client from the other clients' ownership tracking table
DistributeObjectsTestHelper.RemoveClient(client.LocalClientId);
// Disconnect the client
yield return StopOneClient(client, true);
//yield return new WaitForSeconds(0.1f);
// Validate all tables match
yield return WaitForConditionOrTimeOut(ValidateOwnershipTablesMatch);
AssertOnTimeout($"[Client-{j + 1}][OnwershipTable Mismatch] {m_ErrorLog}");
// When ownership changes, the new owner will randomly pick a new target to move towards and will move towards the target.
// Validate all other instances of the NetworkObjects that have had newly assigned owners have matching positions to the
// newly assigned owenr's instance.
yield return WaitForConditionOrTimeOut(ValidateTransformsMatch);
AssertOnTimeout($"[Client-{j + 1}][Transform Mismatch] {m_ErrorLog}");
// DANGO-TODO: Make this tied to verbose mode once we know the CMB Service integration works properly
DisplayOwnership();
yield return WaitForConditionOrTimeOut(SpawnCountsMatch);
AssertOnTimeout($"[Spawn Count Mismatch] {m_ErrorLog}");
}
}
private void DisplayOwnership()
{
m_ErrorLog.Clear();
var daHostEntries = DistributeObjectsTestHelper.DistributedObjects[0];
foreach (var entry in daHostEntries)
{
m_ErrorLog.AppendLine($"[Client-{entry.Key}][Owned Objects: {entry.Value.Count}]");
}
VerboseDebug($"{m_ErrorLog}");
}
/// <summary>
/// This keeps track of each clients perspective of which NetworkObjects are owned by which client.
/// It is used to validate that all clients are in synch with ownership updates.
/// </summary>
public class DistributeObjectsTestHelper : NetworkBehaviour
{
/// <summary>
/// [Client Context][Client Owners][NetworkObjectId][NetworkObject]
/// </summary>
public static Dictionary<ulong, Dictionary<ulong, Dictionary<ulong, NetworkObject>>> DistributedObjects = new Dictionary<ulong, Dictionary<ulong, Dictionary<ulong, NetworkObject>>>();
public static void RemoveClient(ulong clientId)
{
foreach (var clients in DistributedObjects.Values)
{
clients.Remove(clientId);
}
DistributedObjects.Remove(clientId);
}
internal ulong ClientId;
public override void OnNetworkSpawn()
{
ClientId = NetworkManager.LocalClientId;
UpdateOwnerTableAdd();
base.OnNetworkSpawn();
}
private void UpdateOwnerTableAdd()
{
if (!DistributedObjects.ContainsKey(ClientId))
{
DistributedObjects.Add(ClientId, new Dictionary<ulong, Dictionary<ulong, NetworkObject>>());
}
if (!DistributedObjects[ClientId].ContainsKey(OwnerClientId))
{
DistributedObjects[ClientId].Add(OwnerClientId, new Dictionary<ulong, NetworkObject>());
}
if (DistributedObjects[ClientId][OwnerClientId].ContainsKey(NetworkObject.NetworkObjectId))
{
throw new Exception($"[Client-{ClientId}][{name}] {nameof(NetworkObject)} already exists in Client-{ClientId}'s " +
$"DistributedObjects being tracking under Client-{OwnerClientId}'s list of owned {nameof(NetworkObject)}s!");
}
DistributedObjects[ClientId][OwnerClientId].Add(NetworkObject.NetworkObjectId, NetworkObject);
}
private void UpdateOwnerTableRemove(ulong previous)
{
// This does not need to exist when first starting, but will (at one point in testing)
// become valid.
if (DistributedObjects[ClientId].ContainsKey(previous))
{
if (DistributedObjects[ClientId][previous].ContainsKey(NetworkObject.NetworkObjectId))
{
DistributedObjects[ClientId][previous].Remove(NetworkObject.NetworkObjectId);
}
}
}
protected override void OnOwnershipChanged(ulong previous, ulong current)
{
// At start, if NetworkSpawn has not been completed the local client ignores this
if (!DistributedObjects.ContainsKey(ClientId))
{
return;
}
UpdateOwnerTableRemove(previous);
UpdateOwnerTableAdd();
base.OnOwnershipChanged(previous, current);
}
}
/// <summary>
/// This is used to validate that upon distributed ownership changes NetworkTransform sycnhronization
/// still works properly.
/// </summary>
public class DistributeTestTransform : NetworkTransform
{
private float m_DeltaVarPosition = 0.15f;
private float m_DeltaVarQauternion = 0.015f;
protected Vector3 GetRandomVector3(float min, float max, Vector3 baseLine, bool randomlyApplySign = false)
{
var retValue = new Vector3(baseLine.x * Random.Range(min, max), baseLine.y * Random.Range(min, max), baseLine.z * Random.Range(min, max));
if (!randomlyApplySign)
{
return retValue;
}
retValue.x *= Random.Range(1, 100) >= 50 ? -1 : 1;
retValue.y *= Random.Range(1, 100) >= 50 ? -1 : 1;
retValue.z *= Random.Range(1, 100) >= 50 ? -1 : 1;
return retValue;
}
protected override bool OnIsServerAuthoritative()
{
var isOwnerAuth = base.OnIsServerAuthoritative();
Assert.IsFalse(isOwnerAuth, $"Base {nameof(NetworkTransform)} did not automatically return false in distributed authority mode!");
return isOwnerAuth;
}
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
if (CanCommitToTransform)
{
var randomPos = GetRandomVector3(1.0f, 10.0f, Vector3.one, true);
SetState(randomPos, null, null, false);
m_TargetPosition = randomPos;
}
}
private Vector3 m_TargetPosition;
private Vector3 m_DirToTarget;
private bool m_ReachedTarget;
protected override void OnOwnershipChanged(ulong previous, ulong current)
{
base.OnOwnershipChanged(previous, current);
m_TargetPosition = transform.position + GetRandomVector3(4.0f, 8.0f, Vector3.one, true);
m_DirToTarget = (m_TargetPosition - transform.position).normalized;
m_ReachedTarget = false;
}
protected override void Update()
{
base.Update();
if (CanCommitToTransform)
{
if (!m_ReachedTarget)
{
var distance = Vector3.Distance(transform.position, m_TargetPosition);
var speed = Mathf.Clamp(distance, 0.10f, 2.0f);
transform.position += m_DirToTarget * speed * Time.deltaTime;
m_ReachedTarget = IsPositionClose(m_TargetPosition);
}
}
}
public bool IsPositionClose(Vector3 position)
{
return Approximately(transform.position, position);
}
protected bool Approximately(Vector3 a, Vector3 b)
{
var deltaVariance = m_DeltaVarPosition;
return Math.Round(Mathf.Abs(a.x - b.x), 2) <= deltaVariance &&
Math.Round(Mathf.Abs(a.y - b.y), 2) <= deltaVariance &&
Math.Round(Mathf.Abs(a.z - b.z), 2) <= deltaVariance;
}
protected bool Approximately(Quaternion a, Quaternion b)
{
var deltaVariance = m_DeltaVarQauternion;
return Mathf.Abs(a.x - b.x) <= deltaVariance &&
Mathf.Abs(a.y - b.y) <= deltaVariance &&
Mathf.Abs(a.z - b.z) <= deltaVariance &&
Mathf.Abs(a.w - b.w) <= deltaVariance;
}
}
}
}

View File

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

View File

@@ -0,0 +1,718 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using NUnit.Framework;
using Unity.Collections;
using Unity.Netcode.TestHelpers.Runtime;
using Unity.Netcode.Transports.UTP;
#if UTP_TRANSPORT_2_0_ABOVE
using Unity.Networking.Transport;
#endif
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
public class DistributedAuthorityCodecTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
// Use the CMB Service for all tests
protected override bool UseCMBService() => true;
// Set the session mode to distributed authority for all tests
protected override SessionModeTypes OnGetSessionmode() => SessionModeTypes.DistributedAuthority;
private CodecTestHooks m_ClientCodecHook;
private NetworkManager Client => m_ClientNetworkManagers[0];
private string m_TransportHost = Environment.GetEnvironmentVariable("NGO_HOST") ?? "127.0.0.1";
private const int k_TransportPort = 7777;
private const int k_ClientId = 0;
private GameObject m_SpawnObject;
public class TestNetworkComponent : NetworkBehaviour
{
public NetworkList<int> MyNetworkList = new NetworkList<int>(new List<int> { 1, 2, 3 });
[Rpc(SendTo.NotAuthority)]
public void TestNotAuthorityRpc(byte[] _)
{
}
[Rpc(SendTo.Authority)]
public void TestAuthorityRpc(byte[] _)
{
}
}
protected override void OnOneTimeSetup()
{
// Prevents the tests from running if no CMB Service is detected
#if !UTP_TRANSPORT_2_0_ABOVE
Assert.Ignore("ignoring DA codec tests because UTP transport must be 2.0");
#else
if (!CanConnectToServer(m_TransportHost, k_TransportPort))
{
Assert.Ignore("ignoring DA codec tests because UTP transport cannot connect to the runtime");
}
#endif
base.OnOneTimeSetup();
}
/// <summary>
/// Add any additional components to default player prefab
/// </summary>
protected override void OnCreatePlayerPrefab()
{
m_PlayerPrefab.AddComponent<TestNetworkComponent>();
base.OnCreatePlayerPrefab();
}
/// <summary>
/// Modify NetworkManager instances for settings specific to tests
/// </summary>
protected override void OnServerAndClientsCreated()
{
var utpTransport = Client.gameObject.AddComponent<UnityTransport>();
Client.NetworkConfig.NetworkTransport = utpTransport;
Client.NetworkConfig.EnableSceneManagement = false;
Client.NetworkConfig.AutoSpawnPlayerPrefabClientSide = true;
utpTransport.ConnectionData.Address = Dns.GetHostAddresses(m_TransportHost).First().ToString();
utpTransport.ConnectionData.Port = k_TransportPort;
Client.LogLevel = LogLevel.Developer;
// Validate we are in distributed authority mode with client side spawning and using CMB Service
Assert.True(Client.DistributedAuthorityMode, "Distributed authority is not set!");
Assert.True(Client.AutoSpawnPlayerPrefabClientSide, "Client side spawning is not set!");
Assert.True(Client.CMBServiceConnection, "CMBServiceConnection is not set!");
// Create a prefab for creating and destroying tests (auto-registers with NetworkManagers)
m_SpawnObject = CreateNetworkObjectPrefab("TestObject");
m_SpawnObject.AddComponent<TestNetworkComponent>();
// Ignore the client connection timeout after starting the client
m_BypassConnectionTimeout = true;
}
protected override IEnumerator OnStartedServerAndClients()
{
// Register hooks after starting clients and server (in this case just the one client)
// We do this at this point in time because the MessageManager exists (happens within the same call stack when starting NetworkManagers)
m_ClientCodecHook = new CodecTestHooks();
Client.MessageManager.Hook(m_ClientCodecHook);
yield return base.OnStartedServerAndClients();
// wait for client to connect since m_BypassConnectionTimeout
yield return WaitForConditionOrTimeOut(() => Client.LocalClient.PlayerObject != null);
AssertOnTimeout($"Timed out waiting for the client's player to be spanwed!");
}
[UnityTest]
public IEnumerator AuthorityRpc()
{
var player = Client.LocalClient.PlayerObject;
player.OwnerClientId = Client.LocalClientId + 1;
var networkComponent = player.GetComponent<TestNetworkComponent>();
networkComponent.UpdateNetworkProperties();
networkComponent.TestAuthorityRpc(new byte[] { 1, 2, 3, 4 });
// Universal Rpcs are sent as a ProxyMessage (which contains an RpcMessage)
yield return m_ClientCodecHook.WaitForMessageReceived<ProxyMessage>();
}
[UnityTest]
public IEnumerator ChangeOwnership()
{
var message = new ChangeOwnershipMessage
{
DistributedAuthorityMode = true,
NetworkObjectId = 100,
OwnerClientId = 2,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator ClientConnected()
{
var message = new ClientConnectedMessage()
{
ClientId = 2,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator ClientDisconnected()
{
var message = new ClientDisconnectedMessage()
{
ClientId = 2,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator CreateObject()
{
SpawnObject(m_SpawnObject, Client);
yield return m_ClientCodecHook.WaitForMessageReceived<CreateObjectMessage>();
}
[UnityTest]
public IEnumerator DestroyObject()
{
var spawnedObject = SpawnObject(m_SpawnObject, Client);
yield return m_ClientCodecHook.WaitForMessageReceived<CreateObjectMessage>();
spawnedObject.GetComponent<NetworkObject>().Despawn();
yield return m_ClientCodecHook.WaitForMessageReceived<DestroyObjectMessage>();
}
[UnityTest]
public IEnumerator Disconnect()
{
var message = new DisconnectReasonMessage
{
Reason = "test"
};
return SendMessage(ref message);
}
[UnityTest]
public IEnumerator NamedMessage()
{
var writeBuffer = new FastBufferWriter(sizeof(int), Allocator.Temp);
writeBuffer.WriteValueSafe(5);
var message = new NamedMessage
{
Hash = 3,
SendData = writeBuffer,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator NetworkVariableDelta()
{
var message = new NetworkVariableDeltaMessage
{
NetworkObjectId = 0,
NetworkBehaviourIndex = 1,
DeliveryMappedNetworkVariableIndex = new HashSet<int> { 2, 3, 4 },
TargetClientId = 5,
NetworkBehaviour = Client.LocalClient.PlayerObject.GetComponent<TestNetworkComponent>(),
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator NotAuthorityRpc()
{
Client.LocalClient.PlayerObject.GetComponent<TestNetworkComponent>().TestNotAuthorityRpc(new byte[] { 1, 2, 3, 4 });
// Universal Rpcs are sent as a ProxyMessage (which contains an RpcMessage)
yield return m_ClientCodecHook.WaitForMessageReceived<ProxyMessage>();
}
[UnityTest]
public IEnumerator ParentSync()
{
var message = new ParentSyncMessage
{
NetworkObjectId = 0,
WorldPositionStays = true,
IsLatestParentSet = false,
Position = new Vector3(1, 2, 3),
Rotation = new Quaternion(4, 5, 6, 7),
Scale = new Vector3(8, 9, 10),
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SessionOwner()
{
var message = new SessionOwnerMessage()
{
SessionOwner = 2,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator ServerLog()
{
var message = new ServerLogMessage()
{
LogType = NetworkLog.LogType.Info,
Message = "test",
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator UnnamedMessage()
{
var writeBuffer = new FastBufferWriter(sizeof(int), Allocator.Temp);
writeBuffer.WriteValueSafe(5);
var message = new UnnamedMessage
{
SendData = writeBuffer,
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageLoad()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.Load,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageLoadWithObjects()
{
Client.SceneManager.SkipSceneHandling = true;
var prefabNetworkObject = m_SpawnObject.GetComponent<NetworkObject>();
Client.SceneManager.ScenePlacedObjects.Add(0, new Dictionary<int, NetworkObject>()
{
{ 1, prefabNetworkObject }
});
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.Load,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageUnload()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.Unload,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageLoadComplete()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.LoadComplete,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageUnloadComplete()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.UnloadComplete,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageLoadCompleted()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.LoadEventCompleted,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
ClientsCompleted = new List<ulong>() { k_ClientId },
ClientsTimedOut = new List<ulong>() { 123456789 },
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageUnloadLoadCompleted()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.UnloadEventCompleted,
LoadSceneMode = LoadSceneMode.Single,
SceneEventProgressId = Guid.NewGuid(),
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
ClientsCompleted = new List<ulong>() { k_ClientId },
ClientsTimedOut = new List<ulong>() { 123456789 },
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageSynchronize()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.Synchronize,
LoadSceneMode = LoadSceneMode.Single,
ClientSynchronizationMode = LoadSceneMode.Single,
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
ScenesToSynchronize = new Queue<uint>()
};
eventData.ScenesToSynchronize.Enqueue(101);
eventData.SceneHandlesToSynchronize = new Queue<uint>();
eventData.SceneHandlesToSynchronize.Enqueue(202);
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageReSynchronize()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.ReSynchronize,
LoadSceneMode = LoadSceneMode.Single,
ClientSynchronizationMode = LoadSceneMode.Single,
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageSynchronizeComplete()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.ReSynchronize,
LoadSceneMode = LoadSceneMode.Single,
ClientSynchronizationMode = LoadSceneMode.Single,
SceneHash = XXHash.Hash32("SomeRandomSceneName"),
SceneHandle = 23456,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest]
public IEnumerator SceneEventMessageActiveSceneChanged()
{
Client.SceneManager.SkipSceneHandling = true;
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.ActiveSceneChanged,
ActiveSceneHash = XXHash.Hash32("ActiveScene")
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
[UnityTest, Ignore("Serializing twice causes data to disappear in the SceneManager for this event")]
public IEnumerator SceneEventMessageObjectSceneChanged()
{
Client.SceneManager.SkipSceneHandling = true;
var prefabNetworkObject = m_SpawnObject.GetComponent<NetworkObject>();
Client.SceneManager.ObjectsMigratedIntoNewScene = new Dictionary<int, Dictionary<ulong, List<NetworkObject>>>
{
{ 0, new Dictionary<ulong, List<NetworkObject>>()}
};
Client.SceneManager.ObjectsMigratedIntoNewScene[0].Add(Client.LocalClientId, new List<NetworkObject>() { prefabNetworkObject });
var eventData = new SceneEventData(Client)
{
SceneEventType = SceneEventType.ObjectSceneChanged,
};
var message = new SceneEventMessage()
{
EventData = eventData
};
yield return SendMessage(ref message);
}
private IEnumerator SendMessage<T>(ref T message) where T : INetworkMessage
{
Client.MessageManager.SetVersion(k_ClientId, XXHash.Hash32(typeof(T).FullName), 0);
var clientIds = new NativeArray<ulong>(1, Allocator.Temp);
clientIds[0] = k_ClientId;
Client.MessageManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientIds);
Client.MessageManager.ProcessSendQueues();
return m_ClientCodecHook.WaitForMessageReceived(message);
}
#if UTP_TRANSPORT_2_0_ABOVE
private static bool CanConnectToServer(string host, ushort port, double timeoutMs = 100)
{
var address = Dns.GetHostAddresses(host).First();
var endpoint = NetworkEndpoint.Parse(address.ToString(), port);
var driver = NetworkDriver.Create();
var connection = driver.Connect(endpoint);
var start = DateTime.Now;
var ev = Networking.Transport.NetworkEvent.Type.Empty;
while (ev != Networking.Transport.NetworkEvent.Type.Connect)
{
driver.ScheduleUpdate().Complete();
ev = driver.PopEventForConnection(connection, out _, out _);
if (DateTime.Now - start > TimeSpan.FromMilliseconds(timeoutMs))
{
return false;
}
}
driver.Disconnect(connection);
return true;
}
#endif
}
internal class CodecTestHooks : INetworkHooks
{
private Dictionary<string, Queue<TestMessage>> m_ExpectedMessages = new Dictionary<string, Queue<TestMessage>>();
private Dictionary<string, HashSet<string>> m_ReceivedMessages = new Dictionary<string, HashSet<string>>();
private struct TestMessage
{
public string Name;
public byte[] Data;
}
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
{
if (message is ConnectionRequestMessage)
{
return;
}
var writer = new FastBufferWriter(1024, Allocator.Temp);
message.Serialize(writer, 0);
var testName = TestContext.CurrentContext.Test.Name;
if (!m_ExpectedMessages.ContainsKey(testName))
{
m_ExpectedMessages[testName] = new Queue<TestMessage>();
}
m_ExpectedMessages[testName].Enqueue(new TestMessage
{
Name = typeof(T).ToString(),
Data = writer.ToArray(),
});
writer.Dispose();
}
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
{
}
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
}
public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
{
}
public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
{
}
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
{
return true;
}
public bool OnVerifyCanReceive(ulong senderId, Type messageType, FastBufferReader messageContent, ref NetworkContext context)
{
if (messageType == typeof(ConnectionApprovedMessage))
{
return true;
}
var testName = TestContext.CurrentContext.Test.Name;
Assert.True(m_ExpectedMessages.ContainsKey(testName));
Assert.IsNotEmpty(m_ExpectedMessages[testName]);
var nextMessage = m_ExpectedMessages[testName].Dequeue();
Assert.AreEqual(messageType.ToString(), nextMessage.Name, $"received unexpected message type: {messageType}");
if (!m_ReceivedMessages.ContainsKey(testName))
{
m_ReceivedMessages[testName] = new HashSet<string>();
}
m_ReceivedMessages[testName].Add(messageType.ToString());
// ServerLogMessage is an exception - it gets decoded correctly, but the bytes from the runtime do not directly match those sent by the SDK.
if (messageType == typeof(ServerLogMessage))
{
return true;
}
var expectedBytes = nextMessage.Data;
var receivedBytes = messageContent.ToArray();
Assert.AreEqual(expectedBytes, receivedBytes);
return true;
}
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
}
public IEnumerator WaitForMessageReceived<T>(float timeout = 5) where T : INetworkMessage
{
var testName = TestContext.CurrentContext.Test.Name;
var messageType = typeof(T).FullName;
var startTime = Time.realtimeSinceStartup;
while ((!m_ReceivedMessages.ContainsKey(testName) || !m_ReceivedMessages[testName].Contains(messageType)) && Time.realtimeSinceStartup - startTime < timeout)
{
yield return null;
}
Assert.True(m_ReceivedMessages.ContainsKey(testName), "failed to receive any messages");
Assert.True(m_ReceivedMessages[testName].Contains(messageType), $"failed to receive {messageType} message, received: {string.Join(", ", m_ReceivedMessages[testName])}");
// Reset received messages
m_ReceivedMessages[testName] = new HashSet<string>();
}
public IEnumerator WaitForMessageReceived<T>(T _, float timeout = 5) where T : INetworkMessage
{
return WaitForMessageReceived<T>(timeout: timeout);
}
}
}

View File

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

View File

@@ -0,0 +1,298 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
using Random = UnityEngine.Random;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
[TestFixture(HostOrServer.DAHost)]
public class NetworkClientAndPlayerObjectTests : NetcodeIntegrationTest
{
private const int k_PlayerPrefabCount = 6;
protected override int NumberOfClients => 2;
private List<GameObject> m_PlayerPrefabs = new List<GameObject>();
private Dictionary<ulong, uint> m_ChangedPlayerPrefabs = new Dictionary<ulong, uint>();
public NetworkClientAndPlayerObjectTests(HostOrServer hostOrServer) : base(hostOrServer)
{
}
protected override IEnumerator OnTearDown()
{
m_PlayerPrefabs.Clear();
return base.OnTearDown();
}
protected override void OnServerAndClientsCreated()
{
m_PlayerPrefabs.Clear();
for (int i = 0; i < k_PlayerPrefabCount; i++)
{
m_PlayerPrefabs.Add(CreateNetworkObjectPrefab($"PlayerPrefab{i}"));
}
base.OnServerAndClientsCreated();
}
protected override void OnNewClientCreated(NetworkManager networkManager)
{
networkManager.NetworkConfig.Prefabs = m_ServerNetworkManager.NetworkConfig.Prefabs;
if (m_DistributedAuthority)
{
networkManager.OnFetchLocalPlayerPrefabToSpawn = FetchPlayerPrefabToSpawn;
}
base.OnNewClientCreated(networkManager);
}
/// <summary>
/// Only for distributed authority mode
/// </summary>
/// <returns>a unique player prefab for the player</returns>
private GameObject FetchPlayerPrefabToSpawn()
{
var prefabObject = GetRandomPlayerPrefab();
var clientId = m_ClientNetworkManagers[m_ClientNetworkManagers.Length - 1].LocalClientId;
m_ChangedPlayerPrefabs.Add(clientId, prefabObject.GlobalObjectIdHash);
return prefabObject.gameObject;
}
private StringBuilder m_ErrorLogLevel3 = new StringBuilder();
private StringBuilder m_ErrorLogLevel2 = new StringBuilder();
private StringBuilder m_ErrorLogLevel1 = new StringBuilder();
private bool ValidateNetworkClient(NetworkClient networkClient)
{
m_ErrorLogLevel3.Clear();
var success = true;
if (networkClient == null)
{
m_ErrorLogLevel3.Append($"[NetworkClient is NULL]");
// Log error
success = false;
}
if (!networkClient.IsConnected)
{
m_ErrorLogLevel3.Append($"[NetworkClient {nameof(NetworkClient.IsConnected)}] is false]");
// Log error
success = false;
}
if (networkClient.PlayerObject == null)
{
m_ErrorLogLevel3.Append($"[NetworkClient {nameof(NetworkClient.PlayerObject)}] is NULL]");
// Log error
success = false;
}
return success;
}
private bool ValidateNetworkManagerNetworkClients(NetworkManager networkManager)
{
var success = true;
m_ErrorLogLevel2.Clear();
// Number of connected clients plus the DAHost
var expectedCount = m_ClientNetworkManagers.Length + (m_UseHost ? 1 : 0);
if (networkManager.ConnectedClients.Count != expectedCount)
{
m_ErrorLogLevel2.Append($"[{nameof(NetworkManager.ConnectedClients)} count: {networkManager.ConnectedClients.Count} vs expected count: {expectedCount}]");
// Log error
success = false;
}
if (m_UseHost && !ValidateNetworkClient(networkManager.LocalClient))
{
m_ErrorLogLevel2.Append($"[Local NetworkClient: --({m_ErrorLogLevel3})--]");
// Log error
success = false;
}
foreach (var networkClient in networkManager.ConnectedClients)
{
// When just running a server, ignore the server's local NetworkClient
if (!m_UseHost && networkManager.IsServer)
{
continue;
}
if (!ValidateNetworkClient(networkManager.LocalClient))
{
// Log error
success = false;
m_ErrorLogLevel2.Append($"[NetworkClient-{networkManager.LocalClientId}: --({m_ErrorLogLevel3})--]");
}
}
return success;
}
private bool AllNetworkClientsValidated()
{
m_ErrorLogLevel1.Clear();
var success = true;
if (!UseCMBService())
{
if (!ValidateNetworkManagerNetworkClients(m_ServerNetworkManager))
{
m_ErrorLogLevel1.AppendLine($"[Client-{m_ServerNetworkManager.LocalClientId}]{m_ErrorLogLevel2}");
// Log error
success = false;
}
}
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
if (!ValidateNetworkManagerNetworkClients(clientNetworkManager))
{
m_ErrorLogLevel1.AppendLine($"[Client-{clientNetworkManager.LocalClientId}]{m_ErrorLogLevel2}");
// Log error
success = false;
}
}
return success;
}
/// <summary>
/// Validates that all NetworkManager instances have valid NetworkClients for all connected clients
/// Validates the same thing when a client late joins and when a client disconnects.
/// </summary>
[UnityTest]
public IEnumerator ValidateNetworkClients()
{
// Validate the initial clients created
yield return WaitForConditionOrTimeOut(AllNetworkClientsValidated);
AssertOnTimeout($"[Start] Not all NetworkClients were valid!\n{m_ErrorLogLevel1}");
// Late join a player and revalidate all instances
yield return CreateAndStartNewClient();
yield return WaitForConditionOrTimeOut(AllNetworkClientsValidated);
AssertOnTimeout($"[Late Join] Not all NetworkClients were valid!\n{m_ErrorLogLevel1}");
// Disconnect a player and revalidate all instances
var initialCount = m_ClientNetworkManagers.Length;
yield return StopOneClient(m_ClientNetworkManagers[m_ClientNetworkManagers.Length - 1], true);
// Sanity check to assure we removed the NetworkManager from m_ClientNetworkManagers
Assert.False(initialCount == m_ClientNetworkManagers.Length, $"Disconnected player and expected total number of client {nameof(NetworkManager)}s " +
$"to be {initialCount - 1} but it was still {initialCount}!");
yield return WaitForConditionOrTimeOut(AllNetworkClientsValidated);
AssertOnTimeout($"[Client Disconnect] Not all NetworkClients were valid!\n{m_ErrorLogLevel1}");
}
/// <summary>
/// Verify that all NetworkClients are pointing to the correct player object, even if
/// the player object is changed.
/// </summary>
private bool ValidatePlayerObjectOnClients(NetworkManager clientToValidate)
{
m_ErrorLogLevel2.Clear();
var success = true;
var expectedGlobalObjectIdHash = m_ChangedPlayerPrefabs[clientToValidate.LocalClientId];
if (expectedGlobalObjectIdHash != clientToValidate.LocalClient.PlayerObject.GlobalObjectIdHash)
{
m_ErrorLogLevel2.Append($"[Local Prefab Mismatch][Expected GlobalObjectIdHash: {expectedGlobalObjectIdHash} but was {clientToValidate.LocalClient.PlayerObject.GlobalObjectIdHash}]");
success = false;
}
foreach (var client in m_ClientNetworkManagers)
{
if (client == clientToValidate)
{
continue;
}
var remoteNetworkClient = client.ConnectedClients[clientToValidate.LocalClientId];
if (expectedGlobalObjectIdHash != remoteNetworkClient.PlayerObject.GlobalObjectIdHash)
{
m_ErrorLogLevel2.Append($"[Client-{client.LocalClientId} Prefab Mismatch][Expected GlobalObjectIdHash: {expectedGlobalObjectIdHash} but was {remoteNetworkClient.PlayerObject.GlobalObjectIdHash}]");
success = false;
}
}
return success;
}
private bool ValidateAllPlayerObjects()
{
m_ErrorLogLevel1.Clear();
var success = true;
if (m_UseHost && !UseCMBService())
{
if (!ValidatePlayerObjectOnClients(m_ServerNetworkManager))
{
m_ErrorLogLevel1.AppendLine($"[Client-{m_ServerNetworkManager.LocalClientId}]{m_ErrorLogLevel2}");
success = false;
}
}
foreach (var client in m_ClientNetworkManagers)
{
if (!ValidatePlayerObjectOnClients(client))
{
m_ErrorLogLevel1.AppendLine($"[Client-{client.LocalClientId}]{m_ErrorLogLevel2}");
success = false;
}
}
return success;
}
private NetworkObject GetRandomPlayerPrefab()
{
return m_PlayerPrefabs[Random.Range(0, m_PlayerPrefabs.Count() - 1)].GetComponent<NetworkObject>();
}
/// <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()
{
// Just do a quick validation for all connected client's NetworkClients
yield return WaitForConditionOrTimeOut(AllNetworkClientsValidated);
AssertOnTimeout($"Not all NetworkClients were valid!\n{m_ErrorLogLevel1}");
// Now, have each client spawn a new player object
m_ChangedPlayerPrefabs.Clear();
var playerInstance = (GameObject)null;
var playerPrefabToSpawn = (NetworkObject)null;
if (m_UseHost)
{
playerPrefabToSpawn = GetRandomPlayerPrefab();
playerInstance = SpawnPlayerObject(playerPrefabToSpawn.gameObject, m_ServerNetworkManager);
m_ChangedPlayerPrefabs.Add(m_ServerNetworkManager.LocalClientId, playerPrefabToSpawn.GlobalObjectIdHash);
}
foreach (var client in m_ClientNetworkManagers)
{
playerPrefabToSpawn = GetRandomPlayerPrefab();
playerInstance = SpawnPlayerObject(playerPrefabToSpawn.gameObject, client);
m_ChangedPlayerPrefabs.Add(client.LocalClientId, playerPrefabToSpawn.GlobalObjectIdHash);
}
// Validate that all connected clients' NetworkClient instances have the correct player object for each connected client
yield return WaitForConditionOrTimeOut(ValidateAllPlayerObjects);
AssertOnTimeout($"[Existing Clients] Not all NetworkClient player objects were valid!\n{m_ErrorLogLevel1}");
// Distributed authority only feature validation (NetworkManager.OnFetchLocalPlayerPrefabToSpawn)
if (m_DistributedAuthority)
{
// Now test the fetch prefab callback to assure that this is working correctly.
// Start a new client and wait for it to connect
yield return CreateAndStartNewClient();
// Do another validation pass.
yield return WaitForConditionOrTimeOut(ValidateAllPlayerObjects);
AssertOnTimeout($"[Late Joined Client] Not all NetworkClient player objects were valid!\n{m_ErrorLogLevel1}");
}
}
}
}

View File

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

View File

@@ -0,0 +1,406 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
public class OwnershipPermissionsTests : IntegrationTestWithApproximation
{
private GameObject m_PermissionsObject;
private StringBuilder m_ErrorLog = new StringBuilder();
protected override int NumberOfClients => 4;
public OwnershipPermissionsTests() : base(HostOrServer.DAHost)
{
}
protected override IEnumerator OnSetup()
{
m_ObjectToValidate = null;
OwnershipPermissionsTestHelper.CurrentOwnedInstance = null;
return base.OnSetup();
}
protected override void OnServerAndClientsCreated()
{
m_PermissionsObject = CreateNetworkObjectPrefab("PermObject");
m_PermissionsObject.AddComponent<OwnershipPermissionsTestHelper>();
base.OnServerAndClientsCreated();
}
private NetworkObject m_ObjectToValidate;
private bool ValidateObjectSpawnedOnAllClients()
{
m_ErrorLog.Clear();
var networkObjectId = m_ObjectToValidate.NetworkObjectId;
var name = m_ObjectToValidate.name;
if (!UseCMBService() && !m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
{
m_ErrorLog.Append($"Client-{m_ServerNetworkManager.LocalClientId} has not spawned {name}!");
return false;
}
foreach (var client in m_ClientNetworkManagers)
{
if (!client.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
{
m_ErrorLog.Append($"Client-{client.LocalClientId} has not spawned {name}!");
return false;
}
}
return true;
}
private bool ValidatePermissionsOnAllClients()
{
var currentPermissions = (ushort)m_ObjectToValidate.Ownership;
var otherPermissions = (ushort)0;
var networkObjectId = m_ObjectToValidate.NetworkObjectId;
var objectName = m_ObjectToValidate.name;
m_ErrorLog.Clear();
if (!UseCMBService())
{
otherPermissions = (ushort)m_ServerNetworkManager.SpawnManager.SpawnedObjects[networkObjectId].Ownership;
if (currentPermissions != otherPermissions)
{
m_ErrorLog.Append($"Client-{m_ServerNetworkManager.LocalClientId} permissions for {objectName} is {otherPermissions} when it should be {currentPermissions}!");
return false;
}
}
foreach (var client in m_ClientNetworkManagers)
{
otherPermissions = (ushort)client.SpawnManager.SpawnedObjects[networkObjectId].Ownership;
if (currentPermissions != otherPermissions)
{
m_ErrorLog.Append($"Client-{client.LocalClientId} permissions for {objectName} is {otherPermissions} when it should be {currentPermissions}!");
return false;
}
}
return true;
}
private bool ValidateAllInstancesAreOwnedByClient(ulong clientId)
{
var networkObjectId = m_ObjectToValidate.NetworkObjectId;
var otherNetworkObject = (NetworkObject)null;
m_ErrorLog.Clear();
if (!UseCMBService())
{
otherNetworkObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[networkObjectId];
if (otherNetworkObject.OwnerClientId != clientId)
{
m_ErrorLog.Append($"[Client-{m_ServerNetworkManager.LocalClientId}][{otherNetworkObject.name}] Expected owner to be {clientId} but it was {otherNetworkObject.OwnerClientId}!");
return false;
}
}
foreach (var client in m_ClientNetworkManagers)
{
otherNetworkObject = client.SpawnManager.SpawnedObjects[networkObjectId];
if (otherNetworkObject.OwnerClientId != clientId)
{
m_ErrorLog.Append($"[Client-{client.LocalClientId}][{otherNetworkObject.name}] Expected owner to be {clientId} but it was {otherNetworkObject.OwnerClientId}!");
return false;
}
}
return true;
}
[UnityTest]
public IEnumerator ValidateOwnershipPermissionsTest()
{
var firstInstance = SpawnObject(m_PermissionsObject, m_ClientNetworkManagers[0]).GetComponent<NetworkObject>();
OwnershipPermissionsTestHelper.CurrentOwnedInstance = firstInstance;
var firstInstanceHelper = firstInstance.GetComponent<OwnershipPermissionsTestHelper>();
var networkObjectId = firstInstance.NetworkObjectId;
m_ObjectToValidate = OwnershipPermissionsTestHelper.CurrentOwnedInstance;
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.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
//////////////////////////////////////
// Setting & Removing Ownership Flags:
//////////////////////////////////////
// Now, cycle through all permissions and validate that when the owner changes them the change
// is synchronized on all non-owner clients.
foreach (var permissionObject in Enum.GetValues(typeof(NetworkObject.OwnershipStatus)))
{
var permission = (NetworkObject.OwnershipStatus)permissionObject;
// Add the status
firstInstance.SetOwnershipStatus(permission);
// Validate the persmissions value for all instances are the same.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Add][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
// Remove the status unless it is None (ignore None).
if (permission == NetworkObject.OwnershipStatus.None)
{
continue;
}
firstInstance.RemoveOwnershipStatus(permission);
// Validate the persmissions value for all instances are the same.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Remove][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
}
//Add multiple flags at the same time
var multipleFlags = NetworkObject.OwnershipStatus.Transferable | NetworkObject.OwnershipStatus.Distributable | NetworkObject.OwnershipStatus.RequestRequired;
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.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Set Multiple][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
// Remove multiple flags at the same time
multipleFlags = NetworkObject.OwnershipStatus.Transferable | NetworkObject.OwnershipStatus.RequestRequired;
firstInstance.RemoveOwnershipStatus(multipleFlags);
// Validate the two flags no longer are set
Assert.IsFalse(firstInstance.HasOwnershipStatus(multipleFlags), $"[Remove][Multi-flag Failure] Expected: {(ushort)NetworkObject.OwnershipStatus.Distributable} but was {(ushort)firstInstance.Ownership}!");
// 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.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Set Multiple][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
//////////////////////
// Changing Ownership:
//////////////////////
// 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.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Reset][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
var secondInstance = m_ClientNetworkManagers[1].SpawnManager.SpawnedObjects[networkObjectId];
var secondInstanceHelper = secondInstance.GetComponent<OwnershipPermissionsTestHelper>();
secondInstance.ChangeOwnership(m_ClientNetworkManagers[1].LocalClientId);
Assert.IsTrue(secondInstanceHelper.OwnershipPermissionsFailureStatus == NetworkObject.OwnershipPermissionsFailureStatus.Locked,
$"Expected {secondInstance.name} to return {NetworkObject.OwnershipPermissionsFailureStatus.Locked} but its permission failure" +
$" status is {secondInstanceHelper.OwnershipPermissionsFailureStatus}!");
firstInstance.SetOwnershipLock(false);
// Validate the persmissions value for all instances are the same.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Unlock][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
// Sanity check to assure this client's instance isn't already the owner.
Assert.True(!secondInstance.IsOwner, $"[Ownership Check] Client-{m_ClientNetworkManagers[1].LocalClientId} already is the owner!");
// Now try to acquire ownership
secondInstance.ChangeOwnership(m_ClientNetworkManagers[1].LocalClientId);
// Validate the persmissions 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!");
m_ObjectToValidate = OwnershipPermissionsTestHelper.CurrentOwnedInstance;
// Validate all other client instances are showing the same owner
yield return WaitForConditionOrTimeOut(() => ValidateAllInstancesAreOwnedByClient(m_ClientNetworkManagers[1].LocalClientId));
AssertOnTimeout($"[Ownership Mismatch] {secondInstance.name}: \n {m_ErrorLog}");
// 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.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Unlock][Permissions Mismatch] {secondInstance.name}: \n {m_ErrorLog}");
// Attempt to acquire ownership by just changing it
firstInstance.ChangeOwnership(firstInstance.NetworkManager.LocalClientId);
// Assure we are denied ownership due to it requiring ownership be requested
Assert.IsTrue(firstInstanceHelper.OwnershipPermissionsFailureStatus == NetworkObject.OwnershipPermissionsFailureStatus.RequestRequired,
$"Expected {secondInstance.name} to return {NetworkObject.OwnershipPermissionsFailureStatus.RequestRequired} but its permission failure" +
$" status is {secondInstanceHelper.OwnershipPermissionsFailureStatus}!");
//////////////////////////////////
// Test for single race condition:
//////////////////////////////////
// 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}!");
// Get the 3rd client to send a request at the "relatively" same time
var thirdInstance = m_ClientNetworkManagers[2].SpawnManager.SpawnedObjects[networkObjectId];
var thirdInstanceHelper = thirdInstance.GetComponent<OwnershipPermissionsTestHelper>();
// At the same time send a request by the third client.
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}!");
// We expect the first requesting client to be given ownership
yield return WaitForConditionOrTimeOut(() => firstInstance.IsOwner);
AssertOnTimeout($"[Acquire Ownership Failed] Client-{firstInstance.NetworkManager.LocalClientId} failed to get ownership! ({firstInstanceHelper.OwnershipRequestResponseStatus})(Owner: {OwnershipPermissionsTestHelper.CurrentOwnedInstance.OwnerClientId}");
m_ObjectToValidate = OwnershipPermissionsTestHelper.CurrentOwnedInstance;
// Just do a sanity check to assure ownership has changed on all clients.
yield return WaitForConditionOrTimeOut(() => ValidateAllInstancesAreOwnedByClient(firstInstance.NetworkManager.LocalClientId));
AssertOnTimeout($"[Ownership Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
// Now, the third client should get a RequestInProgress returned as their request response
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.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Unlock][Permissions Mismatch] {firstInstance.name}: \n {m_ErrorLog}");
///////////////////////////////////////////////
// Test for multiple ownership race conditions:
///////////////////////////////////////////////
// Get the 4th client's instance
var fourthInstance = m_ClientNetworkManagers[3].SpawnManager.SpawnedObjects[networkObjectId];
var fourthInstanceHelper = fourthInstance.GetComponent<OwnershipPermissionsTestHelper>();
// 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}!");
requestStatus = thirdInstance.RequestOwnership();
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unabled 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}!");
// The 2nd and 3rd client should be denied and the 4th client should be approved
yield return WaitForConditionOrTimeOut(() =>
(fourthInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.RequestInProgress) &&
(thirdInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.RequestInProgress) &&
(secondInstanceHelper.OwnershipRequestResponseStatus == NetworkObject.OwnershipRequestResponseStatus.Approved)
);
AssertOnTimeout($"[Targeted Owner] Client-{secondInstanceHelper.NetworkManager.LocalClientId} did not get the right request denied reponse: {secondInstanceHelper.OwnershipRequestResponseStatus}!");
m_ObjectToValidate = OwnershipPermissionsTestHelper.CurrentOwnedInstance;
// Just do a sanity check to assure ownership has changed on all clients.
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.
yield return WaitForConditionOrTimeOut(ValidatePermissionsOnAllClients);
AssertOnTimeout($"[Unlock][Permissions Mismatch] {secondInstance.name}: \n {m_ErrorLog}");
///////////////////////////////////////////////
// Test for targeted ownership request:
///////////////////////////////////////////////
// Now get the DAHost's client's instance
var daHostInstance = m_ServerNetworkManager.SpawnManager.SpawnedObjects[networkObjectId];
var daHostInstanceHelper = daHostInstance.GetComponent<OwnershipPermissionsTestHelper>();
secondInstanceHelper.AllowOwnershipRequest = true;
secondInstanceHelper.OnlyAllowTargetClientId = true;
secondInstanceHelper.ClientToAllowOwnership = daHostInstance.NetworkManager.LocalClientId;
// 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}!");
requestStatus = thirdInstance.RequestOwnership();
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{thirdInstance.NetworkManager.LocalClientId} was unabled 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}!");
requestStatus = daHostInstance.RequestOwnership();
Assert.True(requestStatus == NetworkObject.OwnershipRequestStatus.RequestSent, $"Client-{daHostInstance.NetworkManager.LocalClientId} was unabled to send a request for ownership because: {requestStatus}!");
// The server and the 2nd client should be denied and the third client should be approved
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}!");
}
public class OwnershipPermissionsTestHelper : NetworkBehaviour
{
public static NetworkObject CurrentOwnedInstance;
public static Dictionary<ulong, Dictionary<ulong, List<NetworkObject>>> DistributedObjects = new Dictionary<ulong, Dictionary<ulong, List<NetworkObject>>>();
public bool AllowOwnershipRequest = true;
public bool OnlyAllowTargetClientId = false;
public ulong ClientToAllowOwnership;
public NetworkObject.OwnershipRequestResponseStatus OwnershipRequestResponseStatus { get; private set; }
public NetworkObject.OwnershipPermissionsFailureStatus OwnershipPermissionsFailureStatus { get; private set; }
public NetworkObject.OwnershipRequestResponseStatus ExpectOwnershipRequestResponseStatus { get; set; }
public override void OnNetworkSpawn()
{
NetworkObject.OnOwnershipRequested = OnOwnershipRequested;
NetworkObject.OnOwnershipRequestResponse = OnOwnershipRequestResponse;
NetworkObject.OnOwnershipPermissionsFailure = OnOwnershipPermissionsFailure;
base.OnNetworkSpawn();
}
private bool OnOwnershipRequested(ulong clientId)
{
// If we are not allowing any client to request (without locking), then deny all requests
if (!AllowOwnershipRequest)
{
return false;
}
// If we are only allowing a specific client and the requesting client is not the target,
// then deny the request
if (OnlyAllowTargetClientId && clientId != ClientToAllowOwnership)
{
return false;
}
// Otherwise, approve the request
return true;
}
private void OnOwnershipRequestResponse(NetworkObject.OwnershipRequestResponseStatus ownershipRequestResponseStatus)
{
OwnershipRequestResponseStatus = ownershipRequestResponseStatus;
}
private void OnOwnershipPermissionsFailure(NetworkObject.OwnershipPermissionsFailureStatus ownershipPermissionsFailureStatus)
{
OwnershipPermissionsFailureStatus = ownershipPermissionsFailureStatus;
}
public override void OnNetworkDespawn()
{
NetworkObject.OnOwnershipRequested = null;
NetworkObject.OnOwnershipRequestResponse = null;
base.OnNetworkSpawn();
}
protected override void OnOwnershipChanged(ulong previous, ulong current)
{
if (current == NetworkManager.LocalClientId)
{
CurrentOwnedInstance = NetworkObject;
}
base.OnOwnershipChanged(previous, current);
}
}
}
}

View File

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

View File

@@ -22,13 +22,24 @@ namespace Unity.Netcode.RuntimeTests
public static int ExpectedSize = 0;
public static int SpawnCount = 0;
public static bool EnableVerbose;
public static void VerboseDebug(string message)
{
if (!EnableVerbose)
{
return;
}
Debug.Log(message);
}
public override void OnNetworkSpawn()
{
if (!IsServer)
{
ClientInstancesSpawned.Add(NetworkObject);
}
Debug.Log($"{nameof(HiddenVariableObject)}.{nameof(OnNetworkSpawn)}() with value {MyNetworkVariable.Value}");
VerboseDebug($"{nameof(HiddenVariableObject)}.{nameof(OnNetworkSpawn)}() with value {MyNetworkVariable.Value}");
MyNetworkVariable.OnValueChanged += Changed;
MyNetworkList.OnListChanged += ListChanged;
@@ -48,7 +59,7 @@ namespace Unity.Netcode.RuntimeTests
public void Changed(int before, int after)
{
Debug.Log($"Value changed from {before} to {after} on {NetworkManager.LocalClientId}");
VerboseDebug($"Value changed from {before} to {after} on {NetworkManager.LocalClientId}");
ValueOnClient[NetworkManager.LocalClientId] = after;
}
public void ListChanged(NetworkListEvent<int> listEvent)
@@ -155,18 +166,19 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForConditionOrTimeOut(VerifyLists);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for all clients to have identical values!");
Debug.Log("Value changed");
VerboseDebug("Value changed");
}
[UnityTest]
public IEnumerator HiddenVariableTest()
{
HiddenVariableObject.EnableVerbose = m_EnableVerboseDebug;
HiddenVariableObject.SpawnCount = 0;
HiddenVariableObject.ValueOnClient.Clear();
HiddenVariableObject.ExpectedSize = 0;
HiddenVariableObject.SpawnCount = 0;
Debug.Log("Running test");
VerboseDebug("Running test");
// ==== Spawn object with ownership on one client
var client = m_ServerNetworkManager.ConnectedClientsList[1];
@@ -178,7 +190,7 @@ namespace Unity.Netcode.RuntimeTests
// === Check spawn occurred
yield return WaitForSpawnCount(NumberOfClients + 1);
Debug.Assert(HiddenVariableObject.SpawnCount == NumberOfClients + 1);
Debug.Log("Objects spawned");
VerboseDebug("Objects spawned");
// ==== Set the NetworkVariable value to 2
HiddenVariableObject.ExpectedSize = 1;
@@ -207,7 +219,7 @@ namespace Unity.Netcode.RuntimeTests
// ==== Wait for object to be spawned
yield return WaitForSpawnCount(1);
Debug.Assert(HiddenVariableObject.SpawnCount == 1);
Debug.Log("Object spawned");
VerboseDebug("Object spawned");
// ==== We need a refresh for the newly re-spawned object
yield return RefreshGameObects(4);

View File

@@ -1,4 +1,5 @@
using System.Collections;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
@@ -45,6 +46,8 @@ namespace Unity.Netcode.RuntimeTests
}
}
[TestFixture(SessionModeTypes.DistributedAuthority)]
[TestFixture(SessionModeTypes.ClientServer)]
public class NetworkListChangedTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 2;
@@ -54,6 +57,8 @@ namespace Unity.Netcode.RuntimeTests
private NetworkObject m_NetSpawnedObject1;
public NetworkListChangedTests(SessionModeTypes sessionModeType) : base(sessionModeType) { }
protected override void OnServerAndClientsCreated()
{
m_PrefabToSpawn = CreateNetworkObjectPrefab("ListChangedObject");

View File

@@ -86,6 +86,24 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
}
private void MockNamedMessageCallback(ulong sender, FastBufferReader reader)
{
}
[Test]
public void NullOrEmptyNamedMessageDoesNotThrowException()
{
LogAssert.Expect(UnityEngine.LogType.Error, $"[{nameof(CustomMessagingManager.RegisterNamedMessageHandler)}] Cannot register a named message of type null or empty!");
m_ServerNetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(string.Empty, MockNamedMessageCallback);
LogAssert.Expect(UnityEngine.LogType.Error, $"[{nameof(CustomMessagingManager.RegisterNamedMessageHandler)}] Cannot register a named message of type null or empty!");
m_ServerNetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(null, MockNamedMessageCallback);
LogAssert.Expect(UnityEngine.LogType.Error, $"[{nameof(CustomMessagingManager.UnregisterNamedMessageHandler)}] Cannot unregister a named message of type null or empty!");
m_ServerNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler(string.Empty);
LogAssert.Expect(UnityEngine.LogType.Error, $"[{nameof(CustomMessagingManager.UnregisterNamedMessageHandler)}] Cannot unregister a named message of type null or empty!");
m_ServerNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler(null);
}
[UnityTest]
public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent()
{

View File

@@ -73,20 +73,18 @@ namespace Unity.Netcode.RuntimeTests.Metrics
[UnityTest]
public IEnumerator TrackNetworkObjectDestroySentMetric()
{
var networkObject = SpawnNetworkObject();
yield return s_DefaultWaitForTick;
var waitForMetricEvent = new WaitForEventMetricValues<ObjectDestroyedEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.ObjectDestroyedSent);
var networkObject = SpawnNetworkObject();
var objectName = networkObject.name;
Server.SpawnManager.OnDespawnObject(networkObject, true);
yield return s_DefaultWaitForTick;
// Wait for the metric to be received
yield return waitForMetricEvent.WaitForMetricsReceived();
var objectDestroyedSentMetricValues = waitForMetricEvent.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(2, objectDestroyedSentMetricValues.Count);
// The server should have sent 1 destroy message to the 1 client connected
Assert.AreEqual(1, objectDestroyedSentMetricValues.Count);
var objectDestroyed = objectDestroyedSentMetricValues.Last();
Assert.AreEqual(Client.LocalClientId, objectDestroyed.Connection.Id);
@@ -242,20 +240,47 @@ namespace Unity.Netcode.RuntimeTests.Metrics
[UnityTest]
public IEnumerator TrackNetworkObjectCountAfterDespawnOnClient()
{
var objectList = Server.SpawnManager.SpawnedObjectsList;
for (int i = objectList.Count - 1; i >= 0; --i)
var initialSpawnCount = Server.SpawnManager.SpawnedObjectsList.Count;
var spawnedObjects = new List<GameObject>();
//By default, we have 2 network objects and will have spawned 4 so we want to wait for metrics to tell us we have 6 spawned objects
var waitForGaugeValues = new WaitForGaugeMetricValues(ClientMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects, metric => (int)metric == 6);
for (int i = 0; i < 4; i++)
{
objectList.ElementAt(i).Despawn();
spawnedObjects.Add(SpawnObject(m_NewNetworkPrefab, Server));
}
//By default, we have 2 network objects
//There's a slight delay between the spawn on the server and the spawn on the client
//We want to have metrics when the value is different than the 2 default one to confirm the client has the new value
var waitForGaugeValues = new WaitForGaugeMetricValues(ClientMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects, metric => (int)metric != 2);
yield return waitForGaugeValues.WaitForMetricsReceived();
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
Assert.AreEqual(6, value);
// Create a new gauge that waits for the initial spawned client count
waitForGaugeValues = new WaitForGaugeMetricValues(ClientMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects, metric => (int)metric != 6);
// Now despawn the 4 spawned objects
foreach (var spawnedObject in spawnedObjects)
{
spawnedObject.GetComponent<NetworkObject>().Despawn();
}
spawnedObjects.Clear();
yield return waitForGaugeValues.WaitForMetricsReceived();
value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
// Validate the value is equals to the spawned objects prior to spawning the network prefab instances
Assert.AreEqual(initialSpawnCount, value);
// Create a new gauge that waits for the initial spawned client count
waitForGaugeValues = new WaitForGaugeMetricValues(ClientMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects, metric => (int)metric == 0);
// Now assure despawning players are being tracked too
var spawnedPlayers = Server.SpawnManager.SpawnedObjectsList.ToList();
foreach (var spawnedObject in spawnedPlayers)
{
spawnedObject.Despawn();
}
yield return waitForGaugeValues.WaitForMetricsReceived();
value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
// Nothing should be spawned on the client at this point
Assert.AreEqual(0, value);
}
#endif

View File

@@ -14,33 +14,6 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
public class NetVarContainer : NetworkBehaviour
{
/// <summary>
/// Creates a prefab with two instances of this NetworkBehaviour
/// </summary>
/// <returns></returns>
public static GameObject CreatePrefabGameObject(NetVarCombinationTypes netVarsToCheck)
{
var gameObject = new GameObject
{
// Always a good idea to name the Prefab for easy identification purposes
name = "NetVarContainerObject"
};
var networkObject = gameObject.AddComponent<NetworkObject>();
// Create the two instances of the NetVarContainer components and add them to the
// GameObject of this prefab
var netVarContainer = gameObject.AddComponent<NetVarContainer>();
netVarContainer.NumberOfNetVarsToCheck = netVarsToCheck.FirstType;
netVarContainer.ValueToSetNetVarTo = NetworkBehaviourUpdaterTests.NetVarValueToSet;
netVarContainer = gameObject.AddComponent<NetVarContainer>();
netVarContainer.NumberOfNetVarsToCheck = netVarsToCheck.SecondType;
netVarContainer.ValueToSetNetVarTo = NetworkBehaviourUpdaterTests.NetVarValueToSet;
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
return gameObject;
}
public enum NetVarsToCheck
{
One,
@@ -106,10 +79,16 @@ namespace Unity.Netcode.RuntimeTests
private NetworkVariable<int> m_FirstValue = new NetworkVariable<int>();
private NetworkVariable<int> m_SeconValue = new NetworkVariable<int>();
public void SetOwnerWrite()
{
m_FirstValue = new NetworkVariable<int>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
m_SeconValue = new NetworkVariable<int>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
}
public override void OnNetworkSpawn()
{
// Clients will register each NetworkObject when it is spawned
if (!IsServer)
// Non-Authority will register each NetworkObject when it is spawned
if ((NetworkManager.DistributedAuthorityMode && !IsOwner) || (!NetworkManager.DistributedAuthorityMode && !IsServer))
{
NetworkBehaviourUpdaterTests.ClientSideNotifyObjectSpawned(gameObject);
}
@@ -121,7 +100,7 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
public void SetNetworkVariableValues()
{
if (IsServer)
if ((NetworkManager.DistributedAuthorityMode && IsOwner) || (!NetworkManager.DistributedAuthorityMode && IsServer))
{
switch (NumberOfNetVarsToCheck)
{
@@ -153,13 +132,56 @@ namespace Unity.Netcode.RuntimeTests
public NetVarContainer.NetVarsToCheck SecondType;
}
/// <summary>
/// Server and Distributed Authority modes require at least 1 client while the host does not.
/// </summary>
/// [Session Mode][Number of Clients][First NetVar Type][Second NetVar Type]
[TestFixture(HostOrServer.DAHost, 1, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.One)]
[TestFixture(HostOrServer.DAHost, 1, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.DAHost, 1, NetVarContainer.NetVarsToCheck.Two, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.DAHost, 2, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.One)]
[TestFixture(HostOrServer.DAHost, 2, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.DAHost, 2, NetVarContainer.NetVarsToCheck.Two, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Server, 1, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.One)]
[TestFixture(HostOrServer.Server, 1, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Server, 1, NetVarContainer.NetVarsToCheck.Two, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Server, 2, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.One)]
[TestFixture(HostOrServer.Server, 2, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Server, 2, NetVarContainer.NetVarsToCheck.Two, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Host, 0, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.One)]
[TestFixture(HostOrServer.Host, 0, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Host, 0, NetVarContainer.NetVarsToCheck.Two, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Host, 1, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.One)]
[TestFixture(HostOrServer.Host, 1, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Host, 1, NetVarContainer.NetVarsToCheck.Two, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Host, 2, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.One)]
[TestFixture(HostOrServer.Host, 2, NetVarContainer.NetVarsToCheck.One, NetVarContainer.NetVarsToCheck.Two)]
[TestFixture(HostOrServer.Host, 2, NetVarContainer.NetVarsToCheck.Two, NetVarContainer.NetVarsToCheck.Two)]
public class NetworkBehaviourUpdaterTests : NetcodeIntegrationTest
{
// Go ahead and create maximum number of clients (not all tests will use them)
protected override int NumberOfClients => 2;
protected override int NumberOfClients => m_NumberOfClients;
public const int NetVarValueToSet = 1;
private static List<GameObject> s_ClientSpawnedNetworkObjects = new List<GameObject>();
private List<NetworkManager> m_ActiveClientsForCurrentTest;
private GameObject m_PrefabToSpawn;
private NetVarCombinationTypes m_NetVarCombinationTypes;
private int m_NumberOfClients = 0;
public NetworkBehaviourUpdaterTests(HostOrServer hostOrServer, int numberOfClients, NetVarContainer.NetVarsToCheck first, NetVarContainer.NetVarsToCheck second) : base(hostOrServer)
{
m_NetVarCombinationTypes = new NetVarCombinationTypes()
{
FirstType = first,
SecondType = second
};
m_NumberOfClients = numberOfClients;
}
protected override IEnumerator OnSetup()
{
s_ClientSpawnedNetworkObjects.Clear();
return base.OnSetup();
}
/// <summary>
/// Clients will call this when NetworkObjects are spawned on their end
@@ -173,70 +195,32 @@ namespace Unity.Netcode.RuntimeTests
}
}
protected override bool CanStartServerAndClients()
protected override void OnServerAndClientsCreated()
{
return false;
m_PrefabToSpawn = CreateNetworkObjectPrefab("NetVarCont");
// Create the two instances of the NetVarContainer components and add them to the
// GameObject of this prefab
var netVarContainer = m_PrefabToSpawn.AddComponent<NetVarContainer>();
netVarContainer.NumberOfNetVarsToCheck = m_NetVarCombinationTypes.FirstType;
if (m_SessionModeType == SessionModeTypes.DistributedAuthority)
{
netVarContainer.SetOwnerWrite();
}
netVarContainer.ValueToSetNetVarTo = NetVarValueToSet;
netVarContainer = m_PrefabToSpawn.AddComponent<NetVarContainer>();
if (m_SessionModeType == SessionModeTypes.DistributedAuthority)
{
netVarContainer.SetOwnerWrite();
}
netVarContainer.NumberOfNetVarsToCheck = m_NetVarCombinationTypes.SecondType;
netVarContainer.ValueToSetNetVarTo = NetVarValueToSet;
base.OnServerAndClientsCreated();
}
/// <summary>
/// Creates the server and client(s) required for this particular test iteration
/// </summary>
private IEnumerator StartClientsAndServer(bool useHost, int numberOfClients, GameObject prefabObject)
{
void AddNetworkPrefab(NetworkConfig config, NetworkPrefab prefab)
{
config.Prefabs.Add(prefab);
}
// Sanity check to make sure we are not trying to create more clients than we have available to use
Assert.True(numberOfClients <= m_ClientNetworkManagers.Length);
m_ActiveClientsForCurrentTest = new List<NetworkManager>();
// Create a list of the clients to be used in this test from the available clients
for (int i = 0; i < numberOfClients; i++)
{
m_ActiveClientsForCurrentTest.Add(m_ClientNetworkManagers[i]);
}
// Add the prefab to be used for this particular test iteration
var np = new NetworkPrefab { Prefab = prefabObject };
AddNetworkPrefab(m_ServerNetworkManager.NetworkConfig, np);
m_ServerNetworkManager.NetworkConfig.TickRate = 30;
foreach (var clientManager in m_ActiveClientsForCurrentTest)
{
m_ServerNetworkManager.NetworkConfig.TickRate = 30;
AddNetworkPrefab(clientManager.NetworkConfig, np);
}
// Now spin everything up normally
var clientsAsArry = m_ActiveClientsForCurrentTest.ToArray();
Assert.True(NetcodeIntegrationTestHelpers.Start(useHost, m_ServerNetworkManager, clientsAsArry), "Failed to start server and client instances");
// Only if we have clients (not host)
if (numberOfClients > 0)
{
RegisterSceneManagerHandler();
}
// Wait for connection on client and server side
yield return WaitForClientsConnectedOrTimeOut(clientsAsArry);
}
/// <summary>
/// This list replaces the original NetworkVariable types to be checked.
/// Both NetworkVariables are of type int and the original version of this test was testing
/// the NetworkBehaviour Update when there were 1 or more (i.e two) on the same NetworkBehaviour.
/// After reviewing, we really only needed to test a much smaller combination of types and so
/// this pre-generated array represents the reduced set of combinations to test.
/// Note:
/// The original test was also testing for no NetworkVariables of type int, which there ended up
/// being no reason to do that and only added to the length of the execution time for this test.
/// </summary>
public static NetVarCombinationTypes[] NetVarCombinationTypeValues = new[]{
new NetVarCombinationTypes() { FirstType = NetVarContainer.NetVarsToCheck.One, SecondType = NetVarContainer.NetVarsToCheck.One },
new NetVarCombinationTypes() { FirstType = NetVarContainer.NetVarsToCheck.One, SecondType = NetVarContainer.NetVarsToCheck.Two },
new NetVarCombinationTypes() { FirstType = NetVarContainer.NetVarsToCheck.Two, SecondType = NetVarContainer.NetVarsToCheck.Two }};
/// <summary>
/// The updated BehaviourUpdaterAllTests was re-designed to replicate the same functionality being tested in the
/// original version of this test with additional time out handling and a re-organization in the order of operations.
@@ -246,59 +230,36 @@ namespace Unity.Netcode.RuntimeTests
/// version like the desktop tests use).
/// This update also updated how the server and clients were being constructed to help reduce the execution time.
/// </summary>
/// <param name="useHost"> whether to run the server as a host or not</param>
/// <param name="varCombinationTypes">the NetworkVariable combination types</param>
/// <param name="nbClients"> number of clients to use for the test</param>
/// <param name="hostOrServer"> whether to run the server as a host or not</param>
/// <param name="numToSpawn"> number of NetworkObjects to be spawned</param>
[UnityTest]
public IEnumerator BehaviourUpdaterAllTests([Values] bool useHost,
[ValueSource(nameof(NetVarCombinationTypeValues))] NetVarCombinationTypes varCombinationTypes,
[Values(0, 1, 2)] int nbClients, [Values(1, 2)] int numToSpawn)
public IEnumerator BehaviourUpdaterAllTests([Values(1, 2)] int numToSpawn)
{
s_ClientSpawnedNetworkObjects.Clear();
// The edge case scenario where we can exit early is when we are running
// just the server (i.e. non-host) and there are zero clients. Under this
// edge case scenario of the various combinations we do not need to run
// this test as the IsDirty flag is never cleared when no clients exist at all.
if (nbClients == 0 && !useHost)
{
yield break;
}
// Create our prefab based on the NetVarCombinationTypes
var prefabToSpawn = NetVarContainer.CreatePrefabGameObject(varCombinationTypes);
yield return StartClientsAndServer(useHost, nbClients, prefabToSpawn);
// Tracks the server-side spawned prefab instances
var spawnedPrefabs = new List<GameObject>();
var tickInterval = 1.0f / m_ServerNetworkManager.NetworkConfig.TickRate;
// Used to determine if the client-side checks of this test should be
// executed or not as well is used to make sure all clients have spawned
// the appropriate number of NetworkObjects with the NetVarContainer behaviour
var numberOfObjectsToSpawnOnClients = numToSpawn * nbClients;
var numberOfObjectsToSpawn = numToSpawn * NumberOfClients;
var authority = m_SessionModeType == SessionModeTypes.DistributedAuthority ? m_ClientNetworkManagers[0] : m_ServerNetworkManager;
// spawn the objects
for (int i = 0; i < numToSpawn; i++)
{
var spawnedObject = Object.Instantiate(prefabToSpawn);
var spawnedObject = Object.Instantiate(m_PrefabToSpawn);
spawnedPrefabs.Add(spawnedObject);
var networkSpawnedObject = spawnedObject.GetComponent<NetworkObject>();
networkSpawnedObject.NetworkManagerOwner = m_ServerNetworkManager;
networkSpawnedObject.NetworkManagerOwner = authority;
networkSpawnedObject.Spawn();
}
// When there are no clients (excluding when server is in host mode), we can skip all of this
// wait until all objects are spawned on the clients
if (numberOfObjectsToSpawnOnClients > 0)
{
// Waits for all clients to spawn the NetworkObjects
yield return WaitForConditionOrTimeOut(() => numberOfObjectsToSpawnOnClients == s_ClientSpawnedNetworkObjects.Count);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for clients to report spawning objects! " +
$"Total reported client-side spawned objects {s_ClientSpawnedNetworkObjects.Count}");
}
// Waits for all clients to spawn the NetworkObjects
yield return WaitForConditionOrTimeOut(() => numberOfObjectsToSpawn == s_ClientSpawnedNetworkObjects.Count);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for clients to report spawning objects! " +
$"Total reported client-side spawned objects {s_ClientSpawnedNetworkObjects.Count}");
// Once all clients have spawned the NetworkObjects, set the network variables for
// those NetworkObjects on the server-side.
@@ -312,39 +273,33 @@ namespace Unity.Netcode.RuntimeTests
}
// Update the NetworkBehaviours to make sure all network variables are no longer marked as dirty
m_ServerNetworkManager.BehaviourUpdater.NetworkBehaviourUpdate();
authority.BehaviourUpdater.NetworkBehaviourUpdate();
// Verify that all network variables are no longer dirty on server side only if we have clients (including host)
foreach (var serverSpawnedObject in spawnedPrefabs)
foreach (var spawnedPrefab in spawnedPrefabs)
{
var netVarContainers = serverSpawnedObject.GetComponents<NetVarContainer>();
var netVarContainers = spawnedPrefab.GetComponents<NetVarContainer>();
foreach (var netVarContainer in netVarContainers)
{
Assert.False(netVarContainer.AreNetVarsDirty(), "Some NetworkVariables were still marked dirty after NetworkBehaviourUpdate!");
}
}
// When there are no clients (excluding when server is in host mode), we can skip all of this
if (numberOfObjectsToSpawnOnClients > 0)
// Get a list of all NetVarContainer components on the client-side spawned NetworkObjects
var clientSideNetVarContainers = new List<NetVarContainer>();
foreach (var clientSpawnedObjects in s_ClientSpawnedNetworkObjects)
{
// Get a list of all NetVarContainer components on the client-side spawned NetworkObjects
var clientSideNetVarContainers = new List<NetVarContainer>();
foreach (var clientSpawnedObjects in s_ClientSpawnedNetworkObjects)
var netVarContainers = clientSpawnedObjects.GetComponents<NetVarContainer>();
foreach (var netvarContiner in netVarContainers)
{
var netVarContainers = clientSpawnedObjects.GetComponents<NetVarContainer>();
foreach (var netvarContiner in netVarContainers)
{
clientSideNetVarContainers.Add(netvarContiner);
}
clientSideNetVarContainers.Add(netvarContiner);
}
yield return WaitForConditionOrTimeOut(() =>
clientSideNetVarContainers.Where(d =>
d.HaveAllValuesChanged(NetVarValueToSet)).Count() == clientSideNetVarContainers.Count);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client side NetVarContainers to report all NetworkVariables have been updated!");
}
Object.DestroyImmediate(prefabToSpawn);
yield return WaitForConditionOrTimeOut(() =>
clientSideNetVarContainers.Where(d =>
d.HaveAllValuesChanged(NetVarValueToSet)).Count() == clientSideNetVarContainers.Count);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client side NetVarContainers to report all NetworkVariables have been updated!");
}
}
}

View File

@@ -13,16 +13,21 @@ namespace Unity.Netcode.RuntimeTests
/// - Server destroy spawned => Object gets destroyed and despawned/destroyed on all clients. Server does not run <see cref="NetworkPrefaInstanceHandler.HandleNetworkPrefabDestroy"/>. Client runs it.
/// - Client destroy spawned => throw exception.
/// </summary>
[TestFixture(SessionModeTypes.DistributedAuthority)]
[TestFixture(SessionModeTypes.ClientServer)]
public class NetworkObjectDestroyTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
protected override int NumberOfClients => 2;
public NetworkObjectDestroyTests(SessionModeTypes sessionModeType) : base(sessionModeType) { }
/// <summary>
/// Tests that a server can destroy a NetworkObject and that it gets despawned correctly.
/// </summary>
/// <returns></returns>
[UnityTest]
public IEnumerator TestNetworkObjectServerDestroy()
public IEnumerator TestNetworkObjectAuthorityDestroy()
{
// This is the *SERVER VERSION* of the *CLIENT PLAYER*
var serverClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper<NetworkObject>();
@@ -35,15 +40,25 @@ namespace Unity.Netcode.RuntimeTests
Assert.IsNotNull(serverClientPlayerResult.Result.gameObject);
Assert.IsNotNull(clientClientPlayerResult.Result.gameObject);
// destroy the server player
Object.Destroy(serverClientPlayerResult.Result.gameObject);
var targetNetworkManager = m_ClientNetworkManagers[0];
if (m_DistributedAuthority)
{
targetNetworkManager = m_ClientNetworkManagers[1];
// destroy the authoritative player (distributed authority)
Object.Destroy(clientClientPlayerResult.Result.gameObject);
}
else
{
// destroy the authoritative player (client-server)
Object.Destroy(serverClientPlayerResult.Result.gameObject);
}
yield return NetcodeIntegrationTestHelpers.WaitForMessageOfTypeHandled<DestroyObjectMessage>(m_ClientNetworkManagers[0]);
yield return NetcodeIntegrationTestHelpers.WaitForMessageOfTypeHandled<DestroyObjectMessage>(targetNetworkManager);
Assert.IsTrue(serverClientPlayerResult.Result == null); // Assert.IsNull doesn't work here
Assert.IsTrue(clientClientPlayerResult.Result == null);
// create an unspawned networkobject and destroy it
// validate that any unspawned networkobject can be destroyed
var go = new GameObject();
go.AddComponent<NetworkObject>();
Object.Destroy(go);
@@ -74,16 +89,42 @@ namespace Unity.Netcode.RuntimeTests
//destroying a NetworkObject while shutting down is allowed
if (isShuttingDown)
{
m_ClientNetworkManagers[0].Shutdown();
if (m_DistributedAuthority)
{
// Shutdown the 2nd client
m_ClientNetworkManagers[1].Shutdown();
}
else
{
// Shutdown the
m_ClientNetworkManagers[0].Shutdown();
}
}
else
{
LogAssert.ignoreFailingMessages = true;
NetworkLog.NetworkManagerOverride = m_ClientNetworkManagers[0];
}
m_ClientPlayerName = clientPlayer.gameObject.name;
m_ClientNetworkObjectId = clientPlayer.NetworkObjectId;
Object.DestroyImmediate(clientPlayer.gameObject);
if (m_DistributedAuthority)
{
m_ClientPlayerName = m_PlayerNetworkObjects[m_ClientNetworkManagers[1].LocalClientId][m_ClientNetworkManagers[0].LocalClientId].gameObject.name;
m_ClientNetworkObjectId = m_PlayerNetworkObjects[m_ClientNetworkManagers[1].LocalClientId][m_ClientNetworkManagers[0].LocalClientId].NetworkObjectId;
if (!isShuttingDown)
{
NetworkLog.NetworkManagerOverride = m_ClientNetworkManagers[1];
}
// the 2nd client attempts to destroy the 1st client's player object (if shutting down then "ok" if not then not "ok")
Object.DestroyImmediate(m_PlayerNetworkObjects[m_ClientNetworkManagers[1].LocalClientId][m_ClientNetworkManagers[0].LocalClientId].gameObject);
}
else
{
// the 1st client attempts to destroy its own player object (if shutting down then "ok" if not then not "ok")
Object.DestroyImmediate(m_ClientNetworkManagers[0].LocalClient.PlayerObject.gameObject);
}
// destroying a NetworkObject while a session is active is not allowed
if (!isShuttingDown)
@@ -95,16 +136,25 @@ namespace Unity.Netcode.RuntimeTests
private bool HaveLogsBeenReceived()
{
if (!NetcodeLogAssert.HasLogBeenReceived(LogType.Error, $"[Netcode] [Invalid Destroy][{m_ClientPlayerName}][NetworkObjectId:{m_ClientNetworkObjectId}] Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call Destroy or Despawn on the server/host instead."))
if (m_DistributedAuthority)
{
return false;
if (!NetcodeLogAssert.HasLogBeenReceived(LogType.Error, $"[Netcode] [Invalid Destroy][{m_ClientPlayerName}][NetworkObjectId:{m_ClientNetworkObjectId}] Destroy a spawned {nameof(NetworkObject)} on a non-owner client is not valid during a distributed authority session. Call Destroy or Despawn on the client-owner instead."))
{
return false;
}
}
if (!NetcodeLogAssert.HasLogBeenReceived(LogType.Error, $"[Netcode-Server Sender={m_ClientNetworkManagers[0].LocalClientId}] [Invalid Destroy][{m_ClientPlayerName}][NetworkObjectId:{m_ClientNetworkObjectId}] Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call Destroy or Despawn on the server/host instead."))
else
{
return false;
}
if (!NetcodeLogAssert.HasLogBeenReceived(LogType.Error, $"[Netcode] [Invalid Destroy][{m_ClientPlayerName}][NetworkObjectId:{m_ClientNetworkObjectId}] Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call Destroy or Despawn on the server/host instead."))
{
return false;
}
if (!NetcodeLogAssert.HasLogBeenReceived(LogType.Error, $"[Netcode-Server Sender={m_ClientNetworkManagers[0].LocalClientId}] [Invalid Destroy][{m_ClientPlayerName}][NetworkObjectId:{m_ClientNetworkObjectId}] Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call Destroy or Despawn on the server/host instead."))
{
return false;
}
}
return true;
}

View File

@@ -8,6 +8,7 @@ using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
public class NetworkObjectDontDestroyWithOwnerTests : NetcodeIntegrationTest
@@ -36,6 +37,22 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForConditionOrTimeOut(() => client.SpawnManager.GetClientOwnedObjects(clientId).Count() == k_NumberObjectsToSpawn + 1);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client to have 33 NetworkObjects spawned! Only {client.SpawnManager.GetClientOwnedObjects(clientId).Count()} were assigned!");
// Since clients spawn their objects locally in distributed authority mode, we have to rebuild the list of the client
// owned objects on the (DAHost) server-side because when the client disconnects it will destroy its local instances.
if (m_DistributedAuthority)
{
networkObjects.Clear();
var serversideClientOwnedObjects = m_ServerNetworkManager.SpawnManager.GetClientOwnedObjects(clientId);
foreach (var networkObject in serversideClientOwnedObjects)
{
if (!networkObject.IsPlayerObject)
{
networkObjects.Add(networkObject.gameObject);
}
}
}
// disconnect the client that owns all the clients
NetcodeIntegrationTestHelpers.StopOneClient(client);

View File

@@ -10,12 +10,13 @@ namespace Unity.Netcode.RuntimeTests
/// <summary>
/// Tests that check OnNetworkDespawn being invoked
/// </summary>
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
public class NetworkObjectOnNetworkDespawnTests : NetcodeIntegrationTest
{
private const string k_ObjectName = "TestDespawn";
public enum InstanceType
public enum InstanceTypes
{
Server,
Client
@@ -23,7 +24,10 @@ namespace Unity.Netcode.RuntimeTests
protected override int NumberOfClients => 1;
private GameObject m_ObjectToSpawn;
private NetworkObject m_NetworkObject;
private HostOrServer m_HostOrServer;
public NetworkObjectOnNetworkDespawnTests(HostOrServer hostOrServer) : base(hostOrServer)
{
m_HostOrServer = hostOrServer;
@@ -31,32 +35,17 @@ namespace Unity.Netcode.RuntimeTests
internal class OnNetworkDespawnTestComponent : NetworkBehaviour
{
public static bool OnServerNetworkDespawnCalled { get; internal set; }
public static bool OnClientNetworkDespawnCalled { get; internal set; }
public bool OnNetworkDespawnCalled { get; internal set; }
public override void OnNetworkSpawn()
{
if (IsServer)
{
OnServerNetworkDespawnCalled = false;
}
else
{
OnClientNetworkDespawnCalled = false;
}
OnNetworkDespawnCalled = false;
base.OnNetworkSpawn();
}
public override void OnNetworkDespawn()
{
if (IsServer)
{
OnServerNetworkDespawnCalled = true;
}
else
{
OnClientNetworkDespawnCalled = true;
}
OnNetworkDespawnCalled = true;
base.OnNetworkDespawn();
}
}
@@ -68,46 +57,67 @@ namespace Unity.Netcode.RuntimeTests
base.OnServerAndClientsCreated();
}
private bool ObjectSpawnedOnAllNetworkManagerInstances()
{
if (!s_GlobalNetworkObjects.ContainsKey(m_ServerNetworkManager.LocalClientId))
{
return false;
}
if (!s_GlobalNetworkObjects[m_ServerNetworkManager.LocalClientId].ContainsKey(m_NetworkObject.NetworkObjectId))
{
return false;
}
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
if (!s_GlobalNetworkObjects.ContainsKey(clientNetworkManager.LocalClientId))
{
return false;
}
if (!s_GlobalNetworkObjects[clientNetworkManager.LocalClientId].ContainsKey(m_NetworkObject.NetworkObjectId))
{
return false;
}
}
return true;
}
/// <summary>
/// This test validates that <see cref="NetworkBehaviour.OnNetworkDespawn"/> is invoked when the
/// <see cref="NetworkManager"/> is shutdown.
/// </summary>
[UnityTest]
public IEnumerator TestNetworkObjectDespawnOnShutdown()
public IEnumerator TestNetworkObjectDespawnOnShutdown([Values(InstanceTypes.Server, InstanceTypes.Client)] InstanceTypes despawnCheck)
{
// Spawn the test object
var spawnedObject = SpawnObject(m_ObjectToSpawn, m_ServerNetworkManager);
var spawnedNetworkObject = spawnedObject.GetComponent<NetworkObject>();
// Wait for the client to spawn the object
yield return WaitForConditionOrTimeOut(() =>
var networkManager = despawnCheck == InstanceTypes.Server ? m_ServerNetworkManager : m_ClientNetworkManagers[0];
var networkManagerOwner = m_ServerNetworkManager;
if (m_DistributedAuthority)
{
if (!s_GlobalNetworkObjects.ContainsKey(m_ClientNetworkManagers[0].LocalClientId))
{
return false;
}
if (!s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId].ContainsKey(spawnedNetworkObject.NetworkObjectId))
{
return false;
}
return true;
});
networkManagerOwner = networkManager;
}
AssertOnTimeout($"Timed out waiting for client to spawn {k_ObjectName}!");
// Spawn the test object
var spawnedObject = SpawnObject(m_ObjectToSpawn, networkManagerOwner);
m_NetworkObject = spawnedObject.GetComponent<NetworkObject>();
yield return WaitForConditionOrTimeOut(ObjectSpawnedOnAllNetworkManagerInstances);
AssertOnTimeout($"Timed out waiting for all {nameof(NetworkManager)} instances to spawn {m_NetworkObject.name}!");
// Get the spawned object relative to which NetworkManager instance we are testing.
var relativeSpawnedObject = s_GlobalNetworkObjects[networkManager.LocalClientId][m_NetworkObject.NetworkObjectId];
var onNetworkDespawnTestComponent = relativeSpawnedObject.GetComponent<OnNetworkDespawnTestComponent>();
// Confirm it is not set before shutting down the NetworkManager
Assert.IsFalse(OnNetworkDespawnTestComponent.OnClientNetworkDespawnCalled, "[Client-side] despawn state is already set (should not be set at this point)!");
Assert.IsFalse(OnNetworkDespawnTestComponent.OnServerNetworkDespawnCalled, $"[{m_HostOrServer}-side] despawn state is already set (should not be set at this point)!");
Assert.IsFalse(onNetworkDespawnTestComponent.OnNetworkDespawnCalled, $"{nameof(OnNetworkDespawnTestComponent.OnNetworkDespawnCalled)} was set prior to shutting down!");
// Shutdown the client-side first to validate the client-side instance invokes OnNetworkDespawn
m_ClientNetworkManagers[0].Shutdown();
yield return WaitForConditionOrTimeOut(() => OnNetworkDespawnTestComponent.OnClientNetworkDespawnCalled);
AssertOnTimeout($"[Client-side] Timed out waiting for {k_ObjectName}'s {nameof(NetworkBehaviour.OnNetworkDespawn)} to be invoked!");
// Shutdown the NetworkManager instance we are testing.
networkManager.Shutdown();
// Shutdown the servr-host-side second to validate servr-host-side instance invokes OnNetworkDespawn
m_ServerNetworkManager.Shutdown();
yield return WaitForConditionOrTimeOut(() => OnNetworkDespawnTestComponent.OnClientNetworkDespawnCalled);
AssertOnTimeout($"[{m_HostOrServer}-side]Timed out waiting for {k_ObjectName}'s {nameof(NetworkBehaviour.OnNetworkDespawn)} to be invoked!");
// Confirm that OnNetworkDespawn is invoked after shutdown
yield return WaitForConditionOrTimeOut(() => onNetworkDespawnTestComponent.OnNetworkDespawnCalled);
AssertOnTimeout($"Timed out waiting for {nameof(NetworkObject)} instance to despawn on the {despawnCheck} side!");
}
}
}

View File

@@ -7,6 +7,8 @@ using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(SessionModeTypes.DistributedAuthority)]
[TestFixture(SessionModeTypes.ClientServer)]
public class NetworkObjectOnSpawnTests : NetcodeIntegrationTest
{
private GameObject m_TestNetworkObjectPrefab;
@@ -27,23 +29,39 @@ namespace Unity.Netcode.RuntimeTests
private const string k_WithObserversError = "Not all clients spawned the";
private const string k_WithoutObserversError = "A client spawned the";
public NetworkObjectOnSpawnTests(SessionModeTypes sessionModeType) : base(sessionModeType) { }
protected override void OnServerAndClientsCreated()
{
m_ObserverPrefab = CreateNetworkObjectPrefab(k_ObserverTestObjName);
base.OnServerAndClientsCreated();
}
private bool CheckClientsSideObserverTestObj()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!s_GlobalNetworkObjects.ContainsKey(client.LocalClientId))
if (m_ObserverTestType == ObserverTestTypes.WithObservers)
{
// When no observers there shouldn't be any client spawned NetworkObjects
// (players are held in a different list)
return !(m_ObserverTestType == ObserverTestTypes.WithObservers);
// When validating this portion of the test and spawning with observers is true, there
// should be spawned objects on the clients.
if (!s_GlobalNetworkObjects.ContainsKey(client.LocalClientId))
{
return false;
}
}
else
{
// When validating this portion of the test and spawning with observers is false, there
// should be no spawned objects on the clients.
if (s_GlobalNetworkObjects.ContainsKey(client.LocalClientId))
{
return false;
}
// We don't need to check anything else for spawn without observers
continue;
}
var clientObjects = s_GlobalNetworkObjects[client.LocalClientId];
// Make sure they did spawn the object
if (m_ObserverTestType == ObserverTestTypes.WithObservers)
@@ -83,10 +101,16 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
/// <param name="observerTestTypes">whether to spawn with or without observers</param>
[UnityTest]
public IEnumerator ObserverSpawnTests([Values] ObserverTestTypes observerTestTypes, [Values] bool sceneManagement)
public IEnumerator ObserverSpawnTests([Values] ObserverTestTypes observerTestTypes, [Values] SceneManagementState sceneManagement)
{
if (!sceneManagement)
if (sceneManagement == SceneManagementState.SceneManagementDisabled)
{
// When scene management is disabled, we need this wait period for all clients to be up to date with
// all other clients before beginning the process of stopping all clients.
if (m_DistributedAuthority)
{
yield return new WaitForSeconds(0.5f);
}
// Disable prefabs to prevent them from being destroyed
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
{
@@ -156,8 +180,10 @@ namespace Unity.Netcode.RuntimeTests
m_ObserverTestType = ObserverTestTypes.WithoutObservers;
// Just give a little time to make sure nothing spawned
yield return s_DefaultWaitForTick;
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
AssertOnTimeout($"{(withoutObservers ? k_WithoutObserversError : k_WithObserversError)} {k_ObserverTestObjName} object!");
// This just requires a targeted check to assure the newly joined client did not spawn the NetworkObject with SpawnWithObservers set to false
var lateJoinClientId = m_ClientNetworkManagers[m_ClientNetworkManagers.Length - 1].LocalClientId;
Assert.False(s_GlobalNetworkObjects.ContainsKey(lateJoinClientId), $"[Client-{lateJoinClientId}] Spawned {instance.name} when it shouldn't have!");
// Now validate that we can make the NetworkObject visible to the newly joined client
m_ObserverTestNetworkObject.NetworkShow(m_ClientNetworkManagers[NumberOfClients].LocalClientId);

View File

@@ -0,0 +1,205 @@
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
{
[TestFixture(SessionModeTypes.DistributedAuthority)]
[TestFixture(SessionModeTypes.ClientServer)]
public class NetworkObjectOwnershipPropertiesTests : NetcodeIntegrationTest
{
private class DummyNetworkBehaviour : NetworkBehaviour
{
}
protected override int NumberOfClients => 2;
private GameObject m_PrefabToSpawn;
private NetworkObject m_OwnerSpawnedInstance;
private NetworkObject m_TargetOwnerInstance;
private NetworkManager m_InitialOwner;
private NetworkManager m_NextTargetOwner;
private ulong m_InitialOwnerId;
private ulong m_TargetOwnerId;
private bool m_SpawnedInstanceIsOwner;
private bool m_InitialOwnerOwnedBySever;
private bool m_TargetOwnerOwnedBySever;
public NetworkObjectOwnershipPropertiesTests(SessionModeTypes sessionModeType) : base(sessionModeType) { }
protected override IEnumerator OnTearDown()
{
m_OwnerSpawnedInstance = null;
m_InitialOwner = null;
m_NextTargetOwner = null;
m_PrefabToSpawn = null;
return base.OnTearDown();
}
protected override void OnServerAndClientsCreated()
{
m_PrefabToSpawn = CreateNetworkObjectPrefab("ClientOwnedObject");
m_PrefabToSpawn.gameObject.AddComponent<DummyNetworkBehaviour>();
m_PrefabToSpawn.GetComponent<NetworkObject>().SetOwnershipStatus(NetworkObject.OwnershipStatus.Distributable);
}
public enum InstanceTypes
{
Server,
Client
}
private StringBuilder m_OwnershipPropagatedFailures = new StringBuilder();
private bool OwnershipPropagated()
{
var conditionMet = true;
m_OwnershipPropagatedFailures.Clear();
// In distributed authority mode, we will check client owner to DAHost owner with InstanceTypes.Server and client owner to client
// when InstanceTypes.Client
if (m_DistributedAuthority)
{
if (!m_ClientNetworkManagers[1].SpawnManager.GetClientOwnedObjects(m_NextTargetOwner.LocalClientId).Any(x => x.NetworkObjectId == m_OwnerSpawnedInstance.NetworkObjectId))
{
conditionMet = false;
m_OwnershipPropagatedFailures.AppendLine($"Client-{m_ClientNetworkManagers[1].LocalClientId} has no ownership entry for {m_OwnerSpawnedInstance.name} ({m_OwnerSpawnedInstance.NetworkObjectId})");
}
if (!m_ClientNetworkManagers[0].SpawnManager.GetClientOwnedObjects(m_NextTargetOwner.LocalClientId).Any(x => x.NetworkObjectId == m_OwnerSpawnedInstance.NetworkObjectId))
{
conditionMet = false;
m_OwnershipPropagatedFailures.AppendLine($"Client-{m_ClientNetworkManagers[0].LocalClientId} has no ownership entry for {m_OwnerSpawnedInstance.name} ({m_OwnerSpawnedInstance.NetworkObjectId})");
}
if (!m_ServerNetworkManager.SpawnManager.GetClientOwnedObjects(m_NextTargetOwner.LocalClientId).Any(x => x.NetworkObjectId == m_OwnerSpawnedInstance.NetworkObjectId))
{
conditionMet = false;
m_OwnershipPropagatedFailures.AppendLine($"Client-{m_ServerNetworkManager.LocalClientId} has no ownership entry for {m_OwnerSpawnedInstance.name} ({m_OwnerSpawnedInstance.NetworkObjectId})");
}
}
else
{
if (m_NextTargetOwner != m_ServerNetworkManager)
{
if (!m_NextTargetOwner.SpawnManager.GetClientOwnedObjects(m_NextTargetOwner.LocalClientId).Any(x => x.NetworkObjectId == m_OwnerSpawnedInstance.NetworkObjectId))
{
conditionMet = false;
m_OwnershipPropagatedFailures.AppendLine($"Client-{m_NextTargetOwner.LocalClientId} has no ownership entry for {m_OwnerSpawnedInstance.name} ({m_OwnerSpawnedInstance.NetworkObjectId})");
}
}
if (!m_ServerNetworkManager.SpawnManager.GetClientOwnedObjects(m_NextTargetOwner.LocalClientId).Any(x => x.NetworkObjectId == m_OwnerSpawnedInstance.NetworkObjectId))
{
conditionMet = false;
m_OwnershipPropagatedFailures.AppendLine($"Client-{m_ServerNetworkManager.LocalClientId} has no ownership entry for {m_OwnerSpawnedInstance.name} ({m_OwnerSpawnedInstance.NetworkObjectId})");
}
}
return conditionMet;
}
private void ValidateOwnerShipProperties(bool targetIsOwner = false)
{
Assert.AreEqual(m_OwnerSpawnedInstance.IsOwner, m_SpawnedInstanceIsOwner);
Assert.AreEqual(m_OwnerSpawnedInstance.IsOwnedByServer, m_InitialOwnerOwnedBySever);
Assert.AreEqual(targetIsOwner ? m_TargetOwnerId : m_InitialOwnerId, m_OwnerSpawnedInstance.OwnerClientId);
var initialOwnerBehaviour = m_OwnerSpawnedInstance.GetComponent<DummyNetworkBehaviour>();
Assert.AreEqual(initialOwnerBehaviour.IsOwner, m_SpawnedInstanceIsOwner);
Assert.AreEqual(initialOwnerBehaviour.IsOwnedByServer, m_InitialOwnerOwnedBySever);
Assert.AreEqual(targetIsOwner ? m_TargetOwnerId : m_InitialOwnerId, initialOwnerBehaviour.OwnerClientId);
Assert.AreEqual(m_TargetOwnerInstance.IsOwner, targetIsOwner);
Assert.AreEqual(m_TargetOwnerInstance.IsOwnedByServer, m_TargetOwnerOwnedBySever);
Assert.AreEqual(targetIsOwner ? m_TargetOwnerId : m_InitialOwnerId, m_TargetOwnerInstance.OwnerClientId);
var targetOwnerBehaviour = m_TargetOwnerInstance.GetComponent<DummyNetworkBehaviour>();
Assert.AreEqual(targetOwnerBehaviour.IsOwner, targetIsOwner);
Assert.AreEqual(targetOwnerBehaviour.IsOwnedByServer, m_TargetOwnerOwnedBySever);
Assert.AreEqual(targetIsOwner ? m_TargetOwnerId : m_InitialOwnerId, m_TargetOwnerInstance.OwnerClientId);
}
[UnityTest]
public IEnumerator ValidatePropertiesWithOwnershipChanges([Values(InstanceTypes.Server, InstanceTypes.Client)] InstanceTypes instanceType)
{
m_NextTargetOwner = instanceType == InstanceTypes.Server ? m_ServerNetworkManager : m_ClientNetworkManagers[0];
m_InitialOwner = instanceType == InstanceTypes.Client ? m_ServerNetworkManager : m_ClientNetworkManagers[0];
// In distributed authority mode, we will check client owner to DAHost owner with InstanceTypes.Server and client owner to client
// when InstanceTypes.Client
if (m_DistributedAuthority)
{
m_InitialOwner = m_ClientNetworkManagers[0];
if (instanceType == InstanceTypes.Client)
{
m_NextTargetOwner = m_ClientNetworkManagers[1];
}
m_PrefabToSpawn.GetComponent<NetworkObject>().SetOwnershipStatus(NetworkObject.OwnershipStatus.Transferable);
}
m_InitialOwnerId = m_InitialOwner.LocalClientId;
m_TargetOwnerId = m_NextTargetOwner.LocalClientId;
m_InitialOwnerOwnedBySever = m_InitialOwner.IsServer;
m_TargetOwnerOwnedBySever = m_InitialOwner.IsServer;
var objectInstance = SpawnObject(m_PrefabToSpawn, m_InitialOwner);
m_OwnerSpawnedInstance = objectInstance.GetComponent<NetworkObject>();
m_SpawnedInstanceIsOwner = m_OwnerSpawnedInstance.NetworkManager == m_InitialOwner;
// Sanity check to verify that the next owner to target is not the owner of the spawned object
var hasEntry = m_InitialOwner.SpawnManager.GetClientOwnedObjects(m_NextTargetOwner.LocalClientId).Any(x => x.NetworkObjectId == m_OwnerSpawnedInstance.NetworkObjectId);
Assert.False(hasEntry);
// Since CreateObjectMessage gets proxied by DAHost, just wait until the next target owner has the spawned instance in the s_GlobalNetworkObjects table.
yield return WaitForConditionOrTimeOut(() => s_GlobalNetworkObjects.ContainsKey(m_NextTargetOwner.LocalClientId) && s_GlobalNetworkObjects[m_NextTargetOwner.LocalClientId].ContainsKey(m_OwnerSpawnedInstance.NetworkObjectId));
AssertOnTimeout($"Timed out waiting for Client-{m_NextTargetOwner.LocalClientId} to have an instance entry of {m_OwnerSpawnedInstance.name}-{m_OwnerSpawnedInstance.NetworkObjectId}!");
// Get the target client's instance of the spawned object
m_TargetOwnerInstance = s_GlobalNetworkObjects[m_NextTargetOwner.LocalClientId][m_OwnerSpawnedInstance.NetworkObjectId];
// Validate that NetworkObject and NetworkBehaviour ownership properties are correct
ValidateOwnerShipProperties();
// The authority always changes the ownership
// Client-Server: It will always be the host instance
// Distributed Authority: It can be either the DAHost or the client
if (m_DistributedAuthority)
{
// Use the target client's instance to change ownership
m_TargetOwnerInstance.ChangeOwnership(m_NextTargetOwner.LocalClientId);
if (instanceType == InstanceTypes.Client)
{
var networkManagersList = new System.Collections.Generic.List<NetworkManager>() { m_ServerNetworkManager, m_ClientNetworkManagers[0] };
// Provide enough time for the client to receive and process the spawned message.
yield return WaitForMessageReceived<ChangeOwnershipMessage>(networkManagersList);
}
else
{
// Provide enough time for the client to receive and process the change in ownership message.
yield return WaitForMessageReceived<ChangeOwnershipMessage>(m_ClientNetworkManagers.ToList());
}
}
else
{
m_OwnerSpawnedInstance.ChangeOwnership(m_NextTargetOwner.LocalClientId);
// Provide enough time for the client to receive and process the change in ownership message.
yield return WaitForMessageReceived<ChangeOwnershipMessage>(m_ClientNetworkManagers.ToList());
}
// Ensure it's the ownership tables are updated
yield return WaitForConditionOrTimeOut(OwnershipPropagated);
AssertOnTimeout($"Timed out waiting for ownership to propagate!\n{m_OwnershipPropagatedFailures}");
m_SpawnedInstanceIsOwner = m_OwnerSpawnedInstance.NetworkManager == m_NextTargetOwner;
if (m_SpawnedInstanceIsOwner)
{
m_InitialOwnerOwnedBySever = m_OwnerSpawnedInstance.NetworkManager.IsServer;
}
m_InitialOwnerOwnedBySever = m_NextTargetOwner.IsServer;
m_TargetOwnerOwnedBySever = m_NextTargetOwner.IsServer;
// Validate that NetworkObject and NetworkBehaviour ownership properties are correct
ValidateOwnerShipProperties(true);
}
}
}

View File

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

View File

@@ -10,6 +10,8 @@ namespace Unity.Netcode.RuntimeTests
{
public class NetworkObjectOwnershipComponent : NetworkBehaviour
{
public static Dictionary<ulong, NetworkObjectOwnershipComponent> SpawnedInstances = new Dictionary<ulong, NetworkObjectOwnershipComponent>();
public bool OnLostOwnershipFired = false;
public bool OnGainedOwnershipFired = false;
@@ -23,6 +25,15 @@ namespace Unity.Netcode.RuntimeTests
OnGainedOwnershipFired = true;
}
public override void OnNetworkSpawn()
{
if (!SpawnedInstances.ContainsKey(NetworkManager.LocalClientId))
{
SpawnedInstances.Add(NetworkManager.LocalClientId, this);
}
base.OnNetworkSpawn();
}
public void ResetFlags()
{
OnLostOwnershipFired = false;
@@ -30,6 +41,8 @@ namespace Unity.Netcode.RuntimeTests
}
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
public class NetworkObjectOwnershipTests : NetcodeIntegrationTest
@@ -48,10 +61,20 @@ namespace Unity.Netcode.RuntimeTests
Remove
}
protected override IEnumerator OnSetup()
{
NetworkObjectOwnershipComponent.SpawnedInstances.Clear();
return base.OnSetup();
}
protected override void OnServerAndClientsCreated()
{
m_OwnershipPrefab = CreateNetworkObjectPrefab("OnwershipPrefab");
m_OwnershipPrefab.AddComponent<NetworkObjectOwnershipComponent>();
if (m_DistributedAuthority)
{
m_OwnershipPrefab.GetComponent<NetworkObject>().SetOwnershipStatus(NetworkObject.OwnershipStatus.Transferable);
}
base.OnServerAndClientsCreated();
}
@@ -67,6 +90,23 @@ namespace Unity.Netcode.RuntimeTests
Assert.NotNull(clientPlayerObject, $"Client Id {m_ClientNetworkManagers[0].LocalClientId} does not have its local player marked as an owned object using local client!");
}
private bool AllObjectsSpawnedOnClients()
{
if (!NetworkObjectOwnershipComponent.SpawnedInstances.ContainsKey(m_ServerNetworkManager.LocalClientId))
{
return false;
}
foreach (var client in m_ClientNetworkManagers)
{
if (!NetworkObjectOwnershipComponent.SpawnedInstances.ContainsKey(client.LocalClientId))
{
return false;
}
}
return true;
}
[UnityTest]
public IEnumerator TestOwnershipCallbacks([Values] OwnershipChecks ownershipChecks)
{
@@ -75,6 +115,9 @@ namespace Unity.Netcode.RuntimeTests
yield return NetcodeIntegrationTestHelpers.WaitForMessageOfTypeHandled<CreateObjectMessage>(m_ClientNetworkManagers[0]);
yield return WaitForConditionOrTimeOut(AllObjectsSpawnedOnClients);
AssertOnTimeout($"Timed out waiting for all clients to spawn the ownership object!");
var ownershipNetworkObjectId = m_OwnershipNetworkObject.NetworkObjectId;
Assert.That(ownershipNetworkObjectId, Is.GreaterThan(0));
Assert.That(m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(ownershipNetworkObjectId));
@@ -123,18 +166,22 @@ namespace Unity.Netcode.RuntimeTests
else
{
// Validates that when ownership is removed the server gets an OnGainedOwnership notification
serverObject.RemoveOwnership();
// In distributed authority mode, the current owner just rolls the ownership back over to the DAHost client (i.e. host mocking CMB Service)
if (m_DistributedAuthority)
{
clientObject.ChangeOwnership(NetworkManager.ServerClientId);
}
else
{
serverObject.RemoveOwnership();
}
}
yield return s_DefaultWaitForTick;
yield return WaitForConditionOrTimeOut(() => serverComponent.OnGainedOwnershipFired && serverComponent.OwnerClientId == m_ServerNetworkManager.LocalClientId);
AssertOnTimeout($"Timed out waiting for ownership to be transfered back to the host instance!");
Assert.That(serverComponent.OnGainedOwnershipFired);
Assert.That(serverComponent.OwnerClientId, Is.EqualTo(m_ServerNetworkManager.LocalClientId));
yield return WaitForConditionOrTimeOut(() => clientComponent.OnLostOwnershipFired);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client to lose ownership!");
Assert.That(clientComponent.OnLostOwnershipFired);
Assert.That(clientComponent.OwnerClientId, Is.EqualTo(m_ServerNetworkManager.LocalClientId));
yield return WaitForConditionOrTimeOut(() => clientComponent.OnLostOwnershipFired && clientComponent.OwnerClientId == m_ServerNetworkManager.LocalClientId);
AssertOnTimeout($"Timed out waiting for client-side lose ownership event to trigger or owner identifier to be equal to the host!");
}
/// <summary>
@@ -193,11 +240,11 @@ namespace Unity.Netcode.RuntimeTests
m_ServerNetworkManager.SpawnManager.RemoveOwnership(m_OwnershipNetworkObject);
var serverObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
Assert.That(serverObject, Is.Not.Null);
var clientObject = (NetworkObject)null;
var clientObjects = new List<NetworkObject>();
for (int i = 0; i < NumberOfClients; i++)
{
var clientObject = m_ClientNetworkManagers[i].SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
clientObject = m_ClientNetworkManagers[i].SpawnManager.SpawnedObjects[ownershipNetworkObjectId];
Assert.That(clientObject, Is.Not.Null);
clientObjects.Add(clientObject);
}
@@ -217,9 +264,10 @@ namespace Unity.Netcode.RuntimeTests
// After the 1st client has been given ownership to the object, this will be used to make sure each previous owner properly received the remove ownership message
var previousClientComponent = (NetworkObjectOwnershipComponent)null;
for (int clientIndex = 0; clientIndex < NumberOfClients; clientIndex++)
{
var clientObject = clientObjects[clientIndex];
clientObject = clientObjects[clientIndex];
var clientId = m_ClientNetworkManagers[clientIndex].LocalClientId;
Assert.That(m_ServerNetworkManager.ConnectedClients.ContainsKey(clientId));
@@ -271,7 +319,15 @@ namespace Unity.Netcode.RuntimeTests
else
{
// Validates that when ownership is removed the server gets an OnGainedOwnership notification
serverObject.RemoveOwnership();
// In distributed authority mode, the current owner just rolls the ownership back over to the DAHost client (i.e. host mocking CMB Service)
if (m_DistributedAuthority)
{
clientObject.ChangeOwnership(NetworkManager.ServerClientId);
}
else
{
serverObject.RemoveOwnership();
}
}
yield return WaitForConditionOrTimeOut(ownershipMessageHooks);

View File

@@ -6,6 +6,9 @@ using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(SessionModeTypes.ClientServer)]
[TestFixture(SessionModeTypes.DistributedAuthority)]
public class NetworkObjectSpawnManyObjectsTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
@@ -15,6 +18,7 @@ namespace Unity.Netcode.RuntimeTests
private NetworkPrefab m_PrefabToSpawn;
public NetworkObjectSpawnManyObjectsTests(SessionModeTypes sessionModeType) : base(sessionModeType) { }
// Using this component assures we will know precisely how many prefabs were spawned on the client
public class SpawnObjecTrackingComponent : NetworkBehaviour
{
@@ -35,6 +39,7 @@ namespace Unity.Netcode.RuntimeTests
var gameObject = new GameObject("TestObject");
var networkObject = gameObject.AddComponent<NetworkObject>();
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
networkObject.IsSceneObject = false;
gameObject.AddComponent<SpawnObjecTrackingComponent>();
m_PrefabToSpawn = new NetworkPrefab() { Prefab = gameObject };

View File

@@ -8,6 +8,8 @@ using Random = UnityEngine.Random;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.DAHost)]
[TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Host)]
[TestFixture(VariableLengthSafety.EnabledNetVarSafety, HostOrServer.Host)]
[TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Server)]
@@ -29,17 +31,20 @@ namespace Unity.Netcode.RuntimeTests
{
DisableNetVarSafety,
EnabledNetVarSafety,
}
};
public NetworkObjectSynchronizationTests(VariableLengthSafety variableLengthSafety, HostOrServer hostOrServer)
public NetworkObjectSynchronizationTests(VariableLengthSafety variableLengthSafety, HostOrServer hostOrServer) : base(hostOrServer)
{
m_VariableLengthSafety = variableLengthSafety;
m_UseHost = hostOrServer == HostOrServer.Host;
}
protected override void OnCreatePlayerPrefab()
{
m_PlayerPrefab.AddComponent<NetworkBehaviourWithOwnerNetworkVariables>();
var component = m_PlayerPrefab.AddComponent<NetworkBehaviourWithOwnerNetworkVariables>();
if (m_DistributedAuthority)
{
component.SetWritePermissions(NetworkVariableWritePermission.Owner);
}
base.OnCreatePlayerPrefab();
}
@@ -123,40 +128,64 @@ namespace Unity.Netcode.RuntimeTests
if (m_UseHost)
{
var delayCounter = 0;
while (m_ClientNetworkManagers.Length == 0)
{
delayCounter++;
Assert.True(delayCounter < 30, "TimeOut waiting for client to spawn!");
yield return s_DefaultWaitForTick;
}
delayCounter = 0;
while (!m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId].ContainsKey(m_ClientNetworkManagers[0].LocalClientId))
{
delayCounter++;
if (delayCounter >= 30)
{
VerboseDebug("Trap!");
}
Assert.True(delayCounter < 30, "TimeOut waiting for client to spawn!");
yield return s_DefaultWaitForTick;
}
var serverSideClientPlayerComponent = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
var serverSideHostPlayerComponent = m_ServerNetworkManager.LocalClient.PlayerObject.GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
var clientSidePlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
var clientSideHostPlayerComponent = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ServerNetworkManager.LocalClientId].GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
var modeText = m_DistributedAuthority ? "owner" : "server";
// Validate that the client side player values match the server side value of the client's player
Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData1.Value == clientSidePlayerComponent.NetworkVariableData1.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})" +
$" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})!");
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Client Player-{clientSidePlayerComponent.OwnerClientId}] Client side value ({clientSidePlayerComponent.NetworkVariableData1.Value})" +
$" does not equal the {modeText} side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})!");
Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData2.Value == clientSidePlayerComponent.NetworkVariableData2.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})" +
$" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})!");
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Client Player-{clientSidePlayerComponent.OwnerClientId}] Client side value ({clientSidePlayerComponent.NetworkVariableData2.Value})" +
$" does not equal the {modeText} side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})!");
Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData3.Value == clientSidePlayerComponent.NetworkVariableData3.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})" +
$" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})!");
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Client Player-{clientSidePlayerComponent.OwnerClientId}] Client side value ({clientSidePlayerComponent.NetworkVariableData3.Value})" +
$" does not equal the {modeText} side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})!");
Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData4.Value == clientSidePlayerComponent.NetworkVariableData4.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})" +
$" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})!");
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Client Player-{clientSidePlayerComponent.OwnerClientId}] Client side value ({clientSidePlayerComponent.NetworkVariableData4.Value})" +
$" does not equal the {modeText} side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})!");
// Validate that only the 2nd and 4th NetworkVariable on the client side instance of the host's player is the same and the other two do not match
// (i.e. NetworkVariables owned by the server should not get synchronized on client)
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData1.Value != clientSideHostPlayerComponent.NetworkVariableData1.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData1.Value})" +
$" should not be equal to the server side value ({clientSideHostPlayerComponent.NetworkVariableData1.Value})!");
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData2.Value == clientSideHostPlayerComponent.NetworkVariableData2.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData2.Value})" +
$" does not equal the server side value ({clientSideHostPlayerComponent.NetworkVariableData2.Value})!");
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData3.Value != clientSideHostPlayerComponent.NetworkVariableData3.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData3.Value})" +
$" should not be equal to the server side value ({clientSideHostPlayerComponent.NetworkVariableData3.Value})!");
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData4.Value == clientSideHostPlayerComponent.NetworkVariableData4.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData4.Value})" +
$" does not equal the server side value ({clientSideHostPlayerComponent.NetworkVariableData4.Value})!");
// DANGO-TODO: This scenario is only possible to do if we add a DA-Server to mock the CMB Service or we integrate the CMB Service AND we have updated NetworkVariable permissions
// to only allow the service to write. For now, we will skip this validation for distributed authority
if (!m_DistributedAuthority)
{
// Validate that only the 2nd and 4th NetworkVariable on the client side instance of the host's player is the same and the other two do not match
// (i.e. NetworkVariables owned by the server should not get synchronized on client)
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData1.Value != clientSideHostPlayerComponent.NetworkVariableData1.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Host Player] Client side value ({clientSideHostPlayerComponent.NetworkVariableData1.Value})" +
$" should not be equal to the server side value ({serverSideHostPlayerComponent.NetworkVariableData1.Value})!");
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData2.Value == clientSideHostPlayerComponent.NetworkVariableData2.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Host Player] Client side value ({clientSideHostPlayerComponent.NetworkVariableData2.Value})" +
$" does not equal the server side value ({serverSideHostPlayerComponent.NetworkVariableData2.Value})!");
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData3.Value != clientSideHostPlayerComponent.NetworkVariableData3.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Host Player] Client side value ({clientSideHostPlayerComponent.NetworkVariableData3.Value})" +
$" should not be equal to the server side value ({serverSideHostPlayerComponent.NetworkVariableData3.Value})!");
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData4.Value == serverSideHostPlayerComponent.NetworkVariableData4.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Host Player] Client side value ({clientSideHostPlayerComponent.NetworkVariableData4.Value})" +
$" does not equal the server side value ({serverSideHostPlayerComponent.NetworkVariableData4.Value})!");
}
}
else
{
@@ -190,44 +219,53 @@ namespace Unity.Netcode.RuntimeTests
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData4.Value})" +
$" does not equal Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData4.Value})!");
// DANGO-TODO: This scenario is only possible to do if we add a DA-Server to mock the CMB Service or we integrate the CMB Service AND we have updated NetworkVariable permissions
// to only allow the service to write. For now, we will skip this validation for distributed authority
if (!m_DistributedAuthority)
{
// Validate that client two's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData1.Value != clientSide1Player2Clone.NetworkVariableData1.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData1.Value})" +
$" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData1.Value})!");
// Validate that client two's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData1.Value != clientSide1Player2Clone.NetworkVariableData1.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData1.Value})" +
$" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData1.Value})!");
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData2.Value == clientSide1Player2Clone.NetworkVariableData2.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData2.Value})" +
$" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData2.Value})!");
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData2.Value == clientSide1Player2Clone.NetworkVariableData2.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData2.Value})" +
$" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData2.Value})!");
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData3.Value != clientSide1Player2Clone.NetworkVariableData3.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData3.Value})" +
$" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData3.Value})!");
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData3.Value != clientSide1Player2Clone.NetworkVariableData3.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData3.Value})" +
$" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData3.Value})!");
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData4.Value == clientSide1Player2Clone.NetworkVariableData4.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData4.Value})" +
$" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData4.Value})!");
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData4.Value == clientSide1Player2Clone.NetworkVariableData4.Value,
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData4.Value})" +
$" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData4.Value})!");
}
}
// Now validate all of the NetworkVariable values match to assure everything synchronized properly
foreach (var spawnedObject in validSpawnedNetworkObjects)
// DANGO-TODO: This scenario is only possible to do if we add a DA-Server to mock the CMB Service or we integrate the CMB Service AND we have updated NetworkVariable permissions
// to only allow the service to write. For now, we will skip this validation for distributed authority
if (!m_DistributedAuthority)
{
foreach (var clientNetworkManager in m_ClientNetworkManagers)
// Now validate all of the NetworkVariable values match to assure everything synchronized properly
foreach (var spawnedObject in validSpawnedNetworkObjects)
{
//Validate that the connected client has spawned all of the instances that shouldn't have failed.
var clientSideNetworkObjects = s_GlobalNetworkObjects[clientNetworkManager.LocalClientId];
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
//Validate that the connected client has spawned all of the instances that shouldn't have failed.
var clientSideNetworkObjects = s_GlobalNetworkObjects[clientNetworkManager.LocalClientId];
Assert.IsTrue(NetworkBehaviourWithNetworkVariables.ClientSpawnCount[clientNetworkManager.LocalClientId] == validSpawnedNetworkObjects.Count, $"Client-{clientNetworkManager.LocalClientId} spawned " +
$"({NetworkBehaviourWithNetworkVariables.ClientSpawnCount}) {nameof(NetworkObject)}s but the expected number of {nameof(NetworkObject)}s should have been ({validSpawnedNetworkObjects.Count})!");
Assert.IsTrue(NetworkBehaviourWithNetworkVariables.ClientSpawnCount[clientNetworkManager.LocalClientId] == validSpawnedNetworkObjects.Count, $"Client-{clientNetworkManager.LocalClientId} spawned " +
$"({NetworkBehaviourWithNetworkVariables.ClientSpawnCount}) {nameof(NetworkObject)}s but the expected number of {nameof(NetworkObject)}s should have been ({validSpawnedNetworkObjects.Count})!");
var spawnedNetworkObject = spawnedObject.GetComponent<NetworkObject>();
Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedNetworkObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a " +
$"{nameof(NetworkObject.NetworkObjectId)} of {spawnedNetworkObject.NetworkObjectId}");
var spawnedNetworkObject = spawnedObject.GetComponent<NetworkObject>();
Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedNetworkObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a " +
$"{nameof(NetworkObject.NetworkObjectId)} of {spawnedNetworkObject.NetworkObjectId}");
var clientSideObject = clientSideNetworkObjects[spawnedNetworkObject.NetworkObjectId];
Assert.IsTrue(clientSideObject.NetworkManager == clientNetworkManager, $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!");
var clientSideObject = clientSideNetworkObjects[spawnedNetworkObject.NetworkObjectId];
Assert.IsTrue(clientSideObject.NetworkManager == clientNetworkManager, $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!");
ValidateNetworkBehaviourWithNetworkVariables(spawnedNetworkObject, clientSideObject);
ValidateNetworkBehaviourWithNetworkVariables(spawnedNetworkObject, clientSideObject);
}
}
}
}
@@ -259,6 +297,22 @@ namespace Unity.Netcode.RuntimeTests
$"does not match the client side instance value ({clientSideComponent.NetworkVariableData4.Value})!");
}
private bool ClientSpawnedNetworkObjects(List<GameObject> spawnedObjectList)
{
var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
foreach (var spawnedObject in spawnedObjectList)
{
var serverSideSpawnedNetworkObject = spawnedObject.GetComponent<NetworkObject>();
if (!clientSideNetworkObjects.ContainsKey(serverSideSpawnedNetworkObject.NetworkObjectId))
{
return false;
}
}
return true;
}
/// <summary>
/// This validates that when a NetworkBehaviour fails serialization or deserialization during synchronizations that other NetworkBehaviours
/// will still be initialized properly
@@ -287,6 +341,9 @@ namespace Unity.Netcode.RuntimeTests
// Validate that when a NetworkBehaviour fails to synchronize and is skipped over it does not
// impact the rest of the NetworkBehaviours.
var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
yield return WaitForConditionOrTimeOut(() => ClientSpawnedNetworkObjects(spawnedObjectList));
AssertOnTimeout($"Timed out waiting for newly joined client to spawn all NetworkObjects!");
foreach (var spawnedObject in spawnedObjectList)
{
var serverSideSpawnedNetworkObject = spawnedObject.GetComponent<NetworkObject>();
@@ -387,6 +444,24 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
public class NetworkBehaviourWithOwnerNetworkVariables : NetworkBehaviour
{
private NetworkVariableWritePermission m_NetworkVariableWritePermission = NetworkVariableWritePermission.Server;
/// <summary>
/// For distributed authority, there is no such thing as a server and only owners
/// DANGO-TODO: When NetworkVariable permissions are updated, this test might need to be updated
/// </summary>
/// <param name="networkVariableWritePermission"></param>
public void SetWritePermissions(NetworkVariableWritePermission networkVariableWritePermission)
{
m_NetworkVariableWritePermission = networkVariableWritePermission;
// Should synchronize with everyone
NetworkVariableData1 = new NetworkVariable<int>(default, NetworkVariableReadPermission.Everyone, networkVariableWritePermission);
// Should synchronize with everyone
NetworkVariableData2 = new NetworkVariable<long>(default, NetworkVariableReadPermission.Everyone, networkVariableWritePermission);
// Should synchronize with everyone
NetworkVariableData3 = new NetworkVariable<byte>(default, NetworkVariableReadPermission.Everyone, networkVariableWritePermission);
// Should synchronize with everyone
NetworkVariableData4 = new NetworkVariable<ushort>(default, NetworkVariableReadPermission.Everyone, networkVariableWritePermission);
}
// Should not synchronize on non-owners
public NetworkVariable<int> NetworkVariableData1 = new NetworkVariable<int>(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
@@ -399,7 +474,9 @@ namespace Unity.Netcode.RuntimeTests
public override void OnNetworkSpawn()
{
if (IsServer)
// Adjustment for distributed authority mode
if ((m_NetworkVariableWritePermission == NetworkVariableWritePermission.Server && IsServer && !NetworkManager.DistributedAuthorityMode) ||
(m_NetworkVariableWritePermission == NetworkVariableWritePermission.Owner && IsOwner && NetworkManager.DistributedAuthorityMode))
{
NetworkVariableData1.Value = Random.Range(1, 1000);
NetworkVariableData2.Value = Random.Range(1, 1000);

View File

@@ -1,6 +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;
@@ -16,7 +17,6 @@ namespace Unity.Netcode.RuntimeTests
public static int ValueAfterOwnershipChange = 0;
public static Dictionary<ulong, ShowHideObject> ObjectsPerClientId = new Dictionary<ulong, ShowHideObject>();
public static List<ulong> ClientIdsRpcCalledOn;
public static NetworkObject GetNetworkObjectById(ulong networkObjectId)
{
foreach (var entry in ClientTargetedNetworkObjects)
@@ -113,7 +113,10 @@ namespace Unity.Netcode.RuntimeTests
[ClientRpc]
public void SomeRandomClientRPC()
{
Debug.Log($"RPC called {NetworkManager.LocalClientId}");
if (!Silent)
{
Debug.Log($"RPC called {NetworkManager.LocalClientId}");
}
ClientIdsRpcCalledOn?.Add(NetworkManager.LocalClientId);
}
@@ -123,12 +126,15 @@ namespace Unity.Netcode.RuntimeTests
}
}
[TestFixture(SessionModeTypes.ClientServer)]
[TestFixture(SessionModeTypes.DistributedAuthority)]
public class NetworkShowHideTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 4;
private ulong m_ClientId0;
private GameObject m_PrefabToSpawn;
private GameObject m_PrefabSpawnWithoutObservers;
private NetworkObject m_NetSpawnedObject1;
private NetworkObject m_NetSpawnedObject2;
@@ -137,10 +143,15 @@ namespace Unity.Netcode.RuntimeTests
private NetworkObject m_Object2OnClient0;
private NetworkObject m_Object3OnClient0;
public NetworkShowHideTests(SessionModeTypes sessionModeType) : base(sessionModeType) { }
protected override void OnServerAndClientsCreated()
{
m_PrefabToSpawn = CreateNetworkObjectPrefab("ShowHideObject");
m_PrefabToSpawn.AddComponent<ShowHideObject>();
m_PrefabSpawnWithoutObservers = CreateNetworkObjectPrefab("ObserversObject");
m_PrefabSpawnWithoutObservers.GetComponent<NetworkObject>().SpawnWithObservers = false;
}
// Check that the first client see them, or not, as expected
@@ -377,6 +388,105 @@ namespace Unity.Netcode.RuntimeTests
LogAssert.NoUnexpectedReceived();
}
private List<ulong> m_ClientsWithVisibility = new List<ulong>();
private NetworkObject m_ObserverTestObject;
private bool CheckListedClientsVisibility()
{
if (m_ClientsWithVisibility.Contains(m_ServerNetworkManager.LocalClientId))
{
if (!m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(m_ObserverTestObject.NetworkObjectId))
{
return false;
}
}
foreach (var client in m_ClientNetworkManagers)
{
if (m_ClientsWithVisibility.Contains(client.LocalClientId))
{
if (!client.SpawnManager.SpawnedObjects.ContainsKey(m_ObserverTestObject.NetworkObjectId))
{
return false;
}
}
}
return true;
}
[UnityTest]
public IEnumerator SpawnWithoutObserversTest()
{
var spawnedObject = SpawnObject(m_PrefabSpawnWithoutObservers, m_ServerNetworkManager);
m_ObserverTestObject = spawnedObject.GetComponent<NetworkObject>();
yield return WaitForTicks(m_ServerNetworkManager, 3);
// When in client-server, the server can spawn a NetworkObject without any observers (even when running as a host the host-client should not have visibility)
// When in distributed authority mode, the owner client has to be an observer of the object
if (!m_DistributedAuthority)
{
// No observers should be assigned at this point
Assert.True(m_ObserverTestObject.Observers.Count == m_ClientsWithVisibility.Count, $"Expected the observer count to be {m_ClientsWithVisibility.Count} but it was {m_ObserverTestObject.Observers.Count}!");
m_ObserverTestObject.NetworkShow(m_ServerNetworkManager.LocalClientId);
}
m_ClientsWithVisibility.Add(m_ServerNetworkManager.LocalClientId);
Assert.True(m_ObserverTestObject.Observers.Count == m_ClientsWithVisibility.Count, $"Expected the observer count to be {m_ClientsWithVisibility.Count} but it was {m_ObserverTestObject.Observers.Count}!");
yield return WaitForConditionOrTimeOut(CheckListedClientsVisibility);
AssertOnTimeout($"[Authority-Only] Timed out waiting for only the authority to be an observer!");
foreach (var client in m_ClientNetworkManagers)
{
m_ObserverTestObject.NetworkShow(client.LocalClientId);
m_ClientsWithVisibility.Add(client.LocalClientId);
Assert.True(m_ObserverTestObject.Observers.Contains(client.LocalClientId), $"[NetworkShow] Client-{client.LocalClientId} is still not an observer!");
Assert.True(m_ObserverTestObject.Observers.Count == m_ClientsWithVisibility.Count, $"Expected the observer count to be {m_ClientsWithVisibility.Count} but it was {m_ObserverTestObject.Observers.Count}!");
yield return WaitForConditionOrTimeOut(CheckListedClientsVisibility);
AssertOnTimeout($"[Client-{client.LocalClientId}] Timed out waiting for the client to be an observer and spawn the {nameof(NetworkObject)}!");
Assert.False(client.SpawnManager.SpawnedObjects[m_ObserverTestObject.NetworkObjectId].SpawnWithObservers, $"Client-{client.LocalClientId} instance of {m_ObserverTestObject.name} has a {nameof(NetworkObject.SpawnWithObservers)} value of true!");
}
}
private bool ClientsSpawnedObject1()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!client.SpawnManager.SpawnedObjects.ContainsKey(m_NetSpawnedObject1.NetworkObjectId))
{
return false;
}
}
return true;
}
private StringBuilder m_ErrorLog = new StringBuilder();
private ulong m_ClientWithoutVisibility;
private bool Object1IsNotVisibileToClient()
{
m_ErrorLog.Clear();
foreach (var client in m_ClientNetworkManagers)
{
if (client.LocalClientId == m_ClientWithoutVisibility)
{
if (client.SpawnManager.SpawnedObjects.ContainsKey(m_NetSpawnedObject1.NetworkObjectId))
{
m_ErrorLog.AppendLine($"{m_NetSpawnedObject1.name} is still visible to Client-{m_ClientWithoutVisibility}!");
}
}
else
if (client.SpawnManager.SpawnedObjects[m_NetSpawnedObject1.NetworkObjectId].IsNetworkVisibleTo(m_ClientWithoutVisibility))
{
m_ErrorLog.AppendLine($"Local instance of {m_NetSpawnedObject1.name} on Client-{client.LocalClientId} thinks Client-{m_ClientWithoutVisibility} still has visibility!");
}
}
return m_ErrorLog.Length == 0;
}
[UnityTest]
public IEnumerator NetworkHideChangeOwnership()
{
@@ -387,12 +497,29 @@ namespace Unity.Netcode.RuntimeTests
var spawnedObject1 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
m_NetSpawnedObject1 = spawnedObject1.GetComponent<NetworkObject>();
yield return WaitForConditionOrTimeOut(ClientsSpawnedObject1);
AssertOnTimeout($"Not all clients spawned object!");
m_NetSpawnedObject1.GetComponent<ShowHideObject>().MyNetworkVariable.Value++;
// Hide an object to a client
m_NetSpawnedObject1.NetworkHide(m_ClientNetworkManagers[1].LocalClientId);
m_ClientWithoutVisibility = m_ClientNetworkManagers[1].LocalClientId;
yield return WaitForConditionOrTimeOut(Object1IsNotVisibileToClient);
AssertOnTimeout($"NetworkObject is still visible to Client-{m_ClientWithoutVisibility} or other clients think it is still visible to Client-{m_ClientWithoutVisibility}:\n {m_ErrorLog}");
yield return WaitForConditionOrTimeOut(() => ShowHideObject.ClientTargetedNetworkObjects.Count == 0);
foreach (var client in m_ClientNetworkManagers)
{
if (m_ClientNetworkManagers[1].LocalClientId == client.LocalClientId)
{
continue;
}
var clientInstance = client.SpawnManager.SpawnedObjects[m_NetSpawnedObject1.NetworkObjectId];
Assert.IsFalse(clientInstance.IsNetworkVisibleTo(m_ClientNetworkManagers[1].LocalClientId), $"Object instance on Client-{client.LocalClientId} is still visible to Client-{m_ClientNetworkManagers[1].LocalClientId}!");
}
// Change ownership while the object is hidden to some
m_NetSpawnedObject1.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
@@ -400,30 +527,49 @@ namespace Unity.Netcode.RuntimeTests
yield return new WaitForSeconds(1.25f);
LogAssert.NoUnexpectedReceived();
// Show the object again to check nothing unexpected happens
m_NetSpawnedObject1.NetworkShow(m_ClientNetworkManagers[1].LocalClientId);
if (m_DistributedAuthority)
{
Assert.True(m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects.ContainsKey(m_NetSpawnedObject1.NetworkObjectId), $"Client-{m_ClientNetworkManagers[0].LocalClientId} has no spawned object with an ID of: {m_NetSpawnedObject1.NetworkObjectId}!");
var clientInstance = m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects[m_NetSpawnedObject1.NetworkObjectId];
Assert.True(clientInstance.HasAuthority, $"Client-{m_ClientNetworkManagers[0].LocalClientId} does not have authority to hide NetworkObject ID: {m_NetSpawnedObject1.NetworkObjectId}!");
clientInstance.NetworkShow(m_ClientNetworkManagers[1].LocalClientId);
}
else
{
m_NetSpawnedObject1.NetworkShow(m_ClientNetworkManagers[1].LocalClientId);
}
yield return WaitForConditionOrTimeOut(() => ShowHideObject.ClientTargetedNetworkObjects.Count == 1);
Assert.True(ShowHideObject.ClientTargetedNetworkObjects[0].OwnerClientId == m_ClientNetworkManagers[0].LocalClientId);
}
private bool AllClientsSpawnedObject1()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!ShowHideObject.ObjectsPerClientId.ContainsKey(client.LocalClientId))
{
return false;
}
}
return true;
}
[UnityTest]
public IEnumerator NetworkHideChangeOwnershipNotHidden()
{
ShowHideObject.ClientTargetedNetworkObjects.Clear();
ShowHideObject.ObjectsPerClientId.Clear();
ShowHideObject.ClientIdToTarget = m_ClientNetworkManagers[1].LocalClientId;
ShowHideObject.Silent = true;
var spawnedObject1 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
m_NetSpawnedObject1 = spawnedObject1.GetComponent<NetworkObject>();
// wait for host to have spawned and gained ownership
while (ShowHideObject.GainOwnershipCount == 0)
{
yield return new WaitForSeconds(0.0f);
}
yield return WaitForConditionOrTimeOut(AllClientsSpawnedObject1);
AssertOnTimeout($"Timed out waiting for all clients to spawn {spawnedObject1.name}!");
// change the value
m_NetSpawnedObject1.GetComponent<ShowHideObject>().MyOwnerReadNetworkVariable.Value++;
@@ -445,10 +591,10 @@ namespace Unity.Netcode.RuntimeTests
yield return WaitForTicks(m_ClientNetworkManagers[0], 3);
// verify ownership changed
Assert.True(ShowHideObject.ClientTargetedNetworkObjects[0].OwnerClientId == m_ClientNetworkManagers[0].LocalClientId);
Assert.AreEqual(ShowHideObject.ClientTargetedNetworkObjects[0].OwnerClientId, m_ClientNetworkManagers[0].LocalClientId);
// verify the expected client got the OnValueChanged. (Only client 1 sets this value)
Assert.True(ShowHideObject.ValueAfterOwnershipChange == 1);
Assert.AreEqual(1, ShowHideObject.ValueAfterOwnershipChange);
}
private string Display(NetworkList<int> list)
@@ -487,20 +633,20 @@ namespace Unity.Netcode.RuntimeTests
private IEnumerator HideThenShowAndHideThenModifyAndShow()
{
Debug.Log("Hiding");
VerboseDebug("Hiding");
// hide
m_NetSpawnedObject1.NetworkHide(1);
yield return WaitForTicks(m_ServerNetworkManager, 3);
yield return WaitForTicks(m_ClientNetworkManagers[0], 3);
Debug.Log("Showing and Hiding");
VerboseDebug("Showing and Hiding");
// show and hide
m_NetSpawnedObject1.NetworkShow(1);
m_NetSpawnedObject1.NetworkHide(1);
yield return WaitForTicks(m_ServerNetworkManager, 3);
yield return WaitForTicks(m_ClientNetworkManagers[0], 3);
Debug.Log("Modifying and Showing");
VerboseDebug("Modifying and Showing");
// modify and show
m_NetSpawnedObject1.GetComponent<ShowHideObject>().MyList.Add(5);
m_NetSpawnedObject1.NetworkShow(1);
@@ -575,19 +721,19 @@ namespace Unity.Netcode.RuntimeTests
switch (i)
{
case 0:
Debug.Log("Running HideThenModifyAndShow");
VerboseDebug("Running HideThenModifyAndShow");
yield return HideThenModifyAndShow();
break;
case 1:
Debug.Log("Running HideThenShowAndModify");
VerboseDebug("Running HideThenShowAndModify");
yield return HideThenShowAndModify();
break;
case 2:
Debug.Log("Running HideThenShowAndHideThenModifyAndShow");
VerboseDebug("Running HideThenShowAndHideThenModifyAndShow");
yield return HideThenShowAndHideThenModifyAndShow();
break;
case 3:
Debug.Log("Running HideThenShowAndRPC");
VerboseDebug("Running HideThenShowAndRPC");
ShowHideObject.ClientIdsRpcCalledOn = new List<ulong>();
yield return HideThenShowAndRPC();
// Provide enough time for slower systems or VM systems possibly under a heavy load could fail on this test

View File

@@ -1,11 +1,12 @@
using System.Collections;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
public class NetworkSpawnManagerTests : NetcodeIntegrationTest
{
private ulong serverSideClientId => NetworkManager.ServerClientId;
@@ -14,6 +15,8 @@ namespace Unity.Netcode.RuntimeTests
protected override int NumberOfClients => 2;
public NetworkSpawnManagerTests(HostOrServer hostOrServer) : base(hostOrServer) { }
[Test]
public void TestServerCanAccessItsOwnPlayer()
{
@@ -23,9 +26,16 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(serverSideClientId, serverSideServerPlayerObject.OwnerClientId);
}
[Test]
public void TestServerCanAccessOtherPlayers()
/// <summary>
/// Test was converted from a Test to UnityTest so distributed authority mode will pass this test.
/// In distributed authority mode, client-side player spawning is enabled by default which requires
/// all client (including DAHost) instances to wait for all players to be spawned.
/// </summary>
[UnityTest]
public IEnumerator TestServerCanAccessOtherPlayers()
{
yield return null;
// server can access other players
var serverSideClientPlayerObject = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(clientSideClientId);
Assert.NotNull(serverSideClientPlayerObject);
@@ -34,11 +44,17 @@ namespace Unity.Netcode.RuntimeTests
var serverSideOtherClientPlayerObject = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(otherClientSideClientId);
Assert.NotNull(serverSideOtherClientPlayerObject);
Assert.AreEqual(otherClientSideClientId, serverSideOtherClientPlayerObject.OwnerClientId);
}
[Test]
public void TestClientCantAccessServerPlayer()
{
if (m_DistributedAuthority)
{
VerboseDebug($"Ignoring test: Clients have access to other player objects in {m_SessionModeType} mode.");
return;
}
// client can't access server player
Assert.Throws<NotServerException>(() =>
{
@@ -55,9 +71,29 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(clientSideClientId, clientSideClientPlayerObject.OwnerClientId);
}
[Test]
public void TestClientCanAccessOtherPlayer()
{
if (!m_DistributedAuthority)
{
VerboseDebug($"Ignoring test: Clients do not have access to other player objects in {m_SessionModeType} mode.");
return;
}
var otherClientPlayer = m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(otherClientSideClientId);
Assert.NotNull(otherClientPlayer, $"Failed to obtain Client{otherClientSideClientId}'s player object!");
}
[Test]
public void TestClientCantAccessOtherPlayer()
{
if (m_DistributedAuthority)
{
VerboseDebug($"Ignoring test: Clients have access to other player objects in {m_SessionModeType} mode.");
return;
}
// client can't access other player
Assert.Throws<NotServerException>(() =>
{
@@ -97,18 +133,8 @@ namespace Unity.Netcode.RuntimeTests
public IEnumerator TestConnectAndDisconnect()
{
// test when client connects, player object is now available
// connect new client
if (!NetcodeIntegrationTestHelpers.CreateNewClients(1, out NetworkManager[] clients))
{
Debug.LogError("Failed to create instances");
Assert.Fail("Failed to create instances");
}
var newClientNetworkManager = clients[0];
newClientNetworkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
newClientNetworkManager.StartClient();
yield return NetcodeIntegrationTestHelpers.WaitForClientConnected(newClientNetworkManager);
yield return WaitForConditionOrTimeOut(() => m_ServerNetworkManager.ConnectedClients.ContainsKey(newClientNetworkManager.LocalClientId));
yield return CreateAndStartNewClient();
var newClientNetworkManager = m_ClientNetworkManagers[NumberOfClients];
var newClientLocalClientId = newClientNetworkManager.LocalClientId;
// test new client can get that itself locally

View File

@@ -12,12 +12,12 @@ namespace Unity.Netcode.RuntimeTests
public class NetworkTransformBase : IntegrationTestWithApproximation
{
// The number of iterations to change position, rotation, and scale for NetworkTransformMultipleChangesOverTime
// The number of iterations to change position, rotation, and scale for NetworkTransformMultipleChangesOverTime
protected const int k_PositionRotationScaleIterations = 3;
protected const int k_PositionRotationScaleIterations3Axis = 8;
protected float m_CurrentHalfPrecision = 0.0f;
protected const float k_HalfPrecisionPosScale = 0.115f;
protected const float k_HalfPrecisionPosScale = 0.1256f;
protected const float k_HalfPrecisionRot = 0.725f;
@@ -126,7 +126,7 @@ namespace Unity.Netcode.RuntimeTests
{
return m_CurrentHalfPrecision;
}
return 0.045f;
return 0.055f;
}
/// <summary>
@@ -473,12 +473,12 @@ namespace Unity.Netcode.RuntimeTests
}
if (!Approximately(childLocalPosition, authorityObjectLocalPosition))
{
m_InfoMessage.AppendLine($"[{childParentName}][{childInstance.name}] Child's Local Position ({childLocalPosition}) | Authority Local Position ({authorityObjectLocalPosition})");
m_InfoMessage.AppendLine($"[{childParentName}][{childInstance.name}] Child's Local Position ({GetVector3Values(childLocalPosition)}) | Authority Local Position ({GetVector3Values(authorityObjectLocalPosition)})");
success = false;
}
if (!Approximately(childLocalScale, authorityObjectLocalScale))
{
m_InfoMessage.AppendLine($"[{childParentName}][{childInstance.name}] Child's Local Scale ({childLocalScale}) | Authority Local Scale ({authorityObjectLocalScale})");
m_InfoMessage.AppendLine($"[{childParentName}][{childInstance.name}] Child's Local Scale ({GetVector3Values(childLocalScale)}) | Authority Local Scale ({GetVector3Values(authorityObjectLocalScale)})");
success = false;
}
@@ -489,7 +489,7 @@ namespace Unity.Netcode.RuntimeTests
}
if (!ApproximatelyEuler(childLocalRotation, authorityObjectLocalRotation))
{
m_InfoMessage.AppendLine($"[{childParentName}][{childInstance.name}] Child's Local Rotation ({childLocalRotation}) | Authority Local Rotation ({authorityObjectLocalRotation})");
m_InfoMessage.AppendLine($"[{childParentName}][{childInstance.name}] Child's Local Rotation ({GetVector3Values(childLocalRotation)}) | Authority Local Rotation ({GetVector3Values(authorityObjectLocalRotation)})");
success = false;
}
}

View File

@@ -9,13 +9,28 @@ using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(HostOrServer.DAHost, MotionModels.UseTransform)]
[TestFixture(HostOrServer.DAHost, MotionModels.UseRigidbody)]
[TestFixture(HostOrServer.Host, MotionModels.UseTransform)]
public class NetworkTransformOwnershipTests : IntegrationTestWithApproximation
{
public enum MotionModels
{
UseRigidbody,
UseTransform
}
protected override int NumberOfClients => 1;
private GameObject m_ClientNetworkTransformPrefab;
private GameObject m_NetworkTransformPrefab;
private MotionModels m_MotionModel;
public NetworkTransformOwnershipTests(HostOrServer hostOrServer, MotionModels motionModel) : base(hostOrServer)
{
m_MotionModel = motionModel;
}
protected override void OnServerAndClientsCreated()
{
VerifyObjectIsSpawnedOnClient.ResetObjectTable();
@@ -25,20 +40,26 @@ namespace Unity.Netcode.RuntimeTests
clientNetworkTransform.UseHalfFloatPrecision = false;
var rigidBody = m_ClientNetworkTransformPrefab.AddComponent<Rigidbody>();
rigidBody.useGravity = false;
rigidBody.interpolation = RigidbodyInterpolation.None;
rigidBody.maxLinearVelocity = 0;
// 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
m_ClientNetworkTransformPrefab.AddComponent<NetworkRigidbody>();
var networkRigidbody = m_ClientNetworkTransformPrefab.AddComponent<NetworkRigidbody>();
networkRigidbody.UseRigidBodyForMotion = m_MotionModel == MotionModels.UseRigidbody;
m_ClientNetworkTransformPrefab.AddComponent<VerifyObjectIsSpawnedOnClient>();
m_NetworkTransformPrefab = CreateNetworkObjectPrefab("ServerAuthorityTest");
var networkTransform = m_NetworkTransformPrefab.AddComponent<NetworkTransform>();
rigidBody = m_NetworkTransformPrefab.AddComponent<Rigidbody>();
rigidBody.useGravity = false;
rigidBody.interpolation = RigidbodyInterpolation.None;
rigidBody.maxLinearVelocity = 0;
// 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
m_NetworkTransformPrefab.AddComponent<NetworkRigidbody>();
networkRigidbody = m_NetworkTransformPrefab.AddComponent<NetworkRigidbody>();
networkRigidbody.UseRigidBodyForMotion = m_MotionModel == MotionModels.UseRigidbody;
m_NetworkTransformPrefab.AddComponent<VerifyObjectIsSpawnedOnClient>();
networkTransform.Interpolate = false;
networkTransform.UseHalfFloatPrecision = false;
@@ -99,10 +120,14 @@ namespace Unity.Netcode.RuntimeTests
// Wait until the client gains ownership
yield return WaitForConditionOrTimeOut(ClientIsOwner);
AssertOnTimeout($"Timed out waiting for the {nameof(ClientIsOwner)} condition to be met!");
// Spawn a new client
yield return CreateAndStartNewClient();
yield return WaitForConditionOrTimeOut(() => VerifyObjectIsSpawnedOnClient.NetworkManagerRelativeSpawnedObjects.ContainsKey(m_ClientNetworkManagers[1].LocalClientId));
AssertOnTimeout($"Timed out waiting for late joing client VerifyObjectIsSpawnedOnClient entry to be created!");
// Get the instance of the object relative to the newly joined client
var newClientObjectInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(m_ClientNetworkManagers[1].LocalClientId);
@@ -117,7 +142,16 @@ namespace Unity.Netcode.RuntimeTests
// Wait one frame so the NetworkTransform can apply the owner's last state received on the late joining client side
// (i.e. prevent the non-owner from changing the transform)
yield return null;
if (m_MotionModel == MotionModels.UseRigidbody)
{
// Allow fixed update to run twice for values to propogate to Unity transform
yield return new WaitForFixedUpdate();
yield return new WaitForFixedUpdate();
}
else
{
yield return null;
}
// Get the owner instance
var ownerInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(m_ClientNetworkManagers[0].LocalClientId);
@@ -139,6 +173,23 @@ namespace Unity.Netcode.RuntimeTests
ClientStartsAsOwner,
}
private bool ClientAndServerSpawnedInstance()
{
return VerifyObjectIsSpawnedOnClient.NetworkManagerRelativeSpawnedObjects.ContainsKey(m_ServerNetworkManager.LocalClientId) && VerifyObjectIsSpawnedOnClient.NetworkManagerRelativeSpawnedObjects.ContainsKey(m_ClientNetworkManagers[0].LocalClientId);
}
private bool m_UseAdjustedVariance;
private const float k_AdjustedVariance = 0.025f;
protected override float GetDeltaVarianceThreshold()
{
if (m_UseAdjustedVariance)
{
return k_AdjustedVariance;
}
return base.GetDeltaVarianceThreshold();
}
/// <summary>
/// This verifies that when authority is owner authoritative the owner's
/// Rigidbody is kinematic and the non-owner's is not.
@@ -155,28 +206,90 @@ namespace Unity.Netcode.RuntimeTests
// Spawn the m_ClientNetworkTransformPrefab and wait for the client-side to spawn the object
var serverSideInstance = SpawnObject(m_ClientNetworkTransformPrefab, networkManagerOwner);
yield return WaitForConditionOrTimeOut(() => VerifyObjectIsSpawnedOnClient.GetClientsThatSpawnedThisPrefab().Contains(m_ClientNetworkManagers[0].LocalClientId));
yield return WaitForConditionOrTimeOut(ClientAndServerSpawnedInstance);
AssertOnTimeout($"Timed out waiting for all object instances to be spawned!");
// Get owner relative instances
var ownerInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(networkManagerOwner.LocalClientId);
var nonOwnerInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(networkManagerNonOwner.LocalClientId);
Assert.NotNull(ownerInstance);
Assert.NotNull(nonOwnerInstance);
Assert.True(networkManagerOwner.LocalClientId != networkManagerNonOwner.LocalClientId);
Assert.True(nonOwnerInstance.OwnerClientId != networkManagerNonOwner.LocalClientId);
Assert.True(nonOwnerInstance.NetworkManager.LocalClientId == networkManagerNonOwner.LocalClientId);
Vector3 GetNonOwnerPosition()
{
if (m_MotionModel == MotionModels.UseRigidbody)
{
return nonOwnerInstance.GetComponent<Rigidbody>().position;
}
else
{
return nonOwnerInstance.transform.position;
}
}
Quaternion GetNonOwnerRotation()
{
if (m_MotionModel == MotionModels.UseRigidbody)
{
return nonOwnerInstance.GetComponent<Rigidbody>().rotation;
}
else
{
return nonOwnerInstance.transform.rotation;
}
}
void LogNonOwnerRigidBody(int stage)
{
if (m_MotionModel == MotionModels.UseRigidbody && m_EnableVerboseDebug)
{
var rigidbody = nonOwnerInstance.GetComponent<Rigidbody>();
Debug.Log($"[{stage}][Rigidbody-NonOwner][Owner:{nonOwnerInstance.OwnerClientId} [Client-{nonOwnerInstance.NetworkManager.LocalClientId}][Gravity: {rigidbody.useGravity}][Kinematic: {rigidbody.isKinematic}][RB-Pos: {rigidbody.position}][RB-Rotation: {rigidbody.rotation}]");
}
}
void LogOwnerRigidBody(int stage)
{
if (m_MotionModel == MotionModels.UseRigidbody && m_EnableVerboseDebug)
{
var rigidbody = ownerInstance.GetComponent<Rigidbody>();
Debug.Log($"[{stage}][Rigidbody-Owner][Owner:{ownerInstance.OwnerClientId} [Client-{ownerInstance.NetworkManager.LocalClientId}][Gravity: {rigidbody.useGravity}][Kinematic: {rigidbody.isKinematic}][RB-Pos: {rigidbody.position}][RB-Rotation: {rigidbody.rotation}]");
}
}
// Make sure the owner is not kinematic and the non-owner(s) are kinematic
Assert.True(nonOwnerInstance.GetComponent<Rigidbody>().isKinematic, $"{networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} is not kinematic when it should be!");
Assert.False(ownerInstance.GetComponent<Rigidbody>().isKinematic, $"{networkManagerOwner.name}'s object instance {ownerInstance.name} is kinematic when it should not be!");
if (m_MotionModel == MotionModels.UseRigidbody)
{
nonOwnerInstance.GetComponent<NetworkTransform>().LogStateUpdate = m_EnableVerboseDebug;
}
// Owner changes transform values
var valueSetByOwner = Vector3.one * 2;
ownerInstance.transform.position = valueSetByOwner;
ownerInstance.transform.localScale = valueSetByOwner;
var rotation = new Quaternion
{
eulerAngles = valueSetByOwner
};
ownerInstance.transform.rotation = rotation;
if (m_MotionModel == MotionModels.UseRigidbody)
{
var ownerRigidbody = ownerInstance.GetComponent<Rigidbody>();
ownerRigidbody.Move(valueSetByOwner, rotation);
ownerRigidbody.linearVelocity = Vector3.zero;
yield return s_DefaultWaitForTick;
ownerInstance.transform.localScale = valueSetByOwner;
}
else
{
ownerInstance.transform.position = valueSetByOwner;
ownerInstance.transform.rotation = rotation;
ownerInstance.transform.localScale = valueSetByOwner;
}
var transformToTest = nonOwnerInstance.transform;
LogNonOwnerRigidBody(1);
yield return WaitForConditionOrTimeOut(() => Approximately(transformToTest.position, valueSetByOwner) && Approximately(transformToTest.localScale, valueSetByOwner) && Approximately(transformToTest.rotation, rotation));
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" +
@@ -186,14 +299,29 @@ namespace Unity.Netcode.RuntimeTests
// Verify non-owners cannot change transform values
nonOwnerInstance.transform.position = Vector3.zero;
yield return s_DefaultWaitForTick;
Assert.True(Approximately(nonOwnerInstance.transform.position, valueSetByOwner), $"{networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} was allowed to change its position! Expected: {valueSetByOwner} Is Currently:{nonOwnerInstance.transform.position}");
if (m_MotionModel == MotionModels.UseRigidbody)
{
yield return new WaitForFixedUpdate();
}
LogNonOwnerRigidBody(2);
Assert.True(Approximately(GetNonOwnerPosition(), valueSetByOwner), $"{networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} was allowed to change its position! Expected: {valueSetByOwner} Is Currently:{GetNonOwnerPosition()}");
// Change ownership and wait for the non-owner to reflect the change
VerifyObjectIsSpawnedOnClient.ResetObjectTable();
m_ServerNetworkManager.SpawnManager.ChangeOwnership(serverSideInstance.GetComponent<NetworkObject>(), networkManagerNonOwner.LocalClientId);
if (m_DistributedAuthority)
{
ownerInstance.NetworkObject.ChangeOwnership(networkManagerNonOwner.LocalClientId);
}
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(4);
// Re-assign the ownership references and wait for the non-owner instance to be notified of ownership change
networkManagerOwner = startingOwnership == StartingOwnership.HostStartsAsOwner ? m_ClientNetworkManagers[0] : m_ServerNetworkManager;
networkManagerNonOwner = startingOwnership == StartingOwnership.HostStartsAsOwner ? m_ServerNetworkManager : m_ClientNetworkManagers[0];
@@ -206,15 +334,48 @@ namespace Unity.Netcode.RuntimeTests
// 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!");
Assert.True(nonOwnerInstance.GetComponent<Rigidbody>().isKinematic, $"{networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} is not kinematic when it should be!");
transformToTest = nonOwnerInstance.transform;
Assert.True(networkManagerOwner.LocalClientId != networkManagerNonOwner.LocalClientId);
Assert.True(nonOwnerInstance.OwnerClientId != networkManagerNonOwner.LocalClientId);
Assert.True(nonOwnerInstance.NetworkManager.LocalClientId == networkManagerNonOwner.LocalClientId);
yield return WaitForConditionOrTimeOut(() => Approximately(transformToTest.position, valueSetByOwner) && Approximately(transformToTest.localScale, valueSetByOwner) && Approximately(transformToTest.rotation, rotation));
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}");
LogNonOwnerRigidBody(5);
// Have the new owner change transform values and wait for those values to be applied on the non-owner side.
valueSetByOwner = Vector3.one * 10;
ownerInstance.transform.position = valueSetByOwner;
ownerInstance.transform.localScale = valueSetByOwner;
rotation.eulerAngles = valueSetByOwner;
ownerInstance.transform.rotation = rotation;
transformToTest = nonOwnerInstance.transform;
yield return WaitForConditionOrTimeOut(() => Approximately(transformToTest.position, valueSetByOwner) && Approximately(transformToTest.localScale, valueSetByOwner) && Approximately(transformToTest.rotation, rotation));
LogOwnerRigidBody(1);
if (m_MotionModel == MotionModels.UseRigidbody)
{
m_UseAdjustedVariance = true;
var ownerRigidbody = ownerInstance.GetComponent<Rigidbody>();
ownerRigidbody.Move(valueSetByOwner, rotation);
LogOwnerRigidBody(2);
ownerInstance.GetComponent<NetworkTransform>().LogMotion = m_EnableVerboseDebug;
nonOwnerInstance.GetComponent<NetworkTransform>().LogMotion = m_EnableVerboseDebug;
ownerRigidbody.linearVelocity = Vector3.zero;
}
else
{
m_UseAdjustedVariance = false;
ownerInstance.transform.position = valueSetByOwner;
ownerInstance.transform.rotation = rotation;
}
LogOwnerRigidBody(3);
LogNonOwnerRigidBody(6);
yield return WaitForConditionOrTimeOut(() => Approximately(GetNonOwnerPosition(), valueSetByOwner) && Approximately(transformToTest.localScale, valueSetByOwner) && Approximately(GetNonOwnerRotation(), rotation));
if (s_GlobalTimeoutHelper.TimedOut)
{
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" +
@@ -223,7 +384,11 @@ namespace Unity.Netcode.RuntimeTests
// The last check is to verify non-owners cannot change transform values after ownership has changed
nonOwnerInstance.transform.position = Vector3.zero;
yield return s_DefaultWaitForTick;
Assert.True(Approximately(nonOwnerInstance.transform.position, valueSetByOwner), $"{networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} was allowed to change its position! Expected: {Vector3.one} Is Currently:{nonOwnerInstance.transform.position}");
if (m_MotionModel == MotionModels.UseRigidbody)
{
yield return new WaitForFixedUpdate();
}
Assert.True(Approximately(GetNonOwnerPosition(), valueSetByOwner), $"{networkManagerNonOwner.name}'s object instance {nonOwnerInstance.name} was allowed to change its position! Expected: {valueSetByOwner} Is Currently:{GetNonOwnerPosition()}");
}
/// <summary>
@@ -255,6 +420,12 @@ namespace Unity.Netcode.RuntimeTests
eulerAngles = valueSetByOwner
};
ownerInstance.transform.rotation = rotation;
// Allow scale to update first when using rigid body motion
if (m_MotionModel == MotionModels.UseRigidbody)
{
yield return new WaitForFixedUpdate();
}
var transformToTest = nonOwnerInstance.transform;
yield return WaitForConditionOrTimeOut(() => transformToTest.position == valueSetByOwner && transformToTest.localScale == valueSetByOwner && transformToTest.rotation == rotation);
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for {m_ClientNetworkManagers[0].name}'s object instance {nonOwnerInstance.name} to change its transform!\n" +
@@ -265,6 +436,11 @@ namespace Unity.Netcode.RuntimeTests
// The last check is to verify clients cannot change transform values
nonOwnerInstance.transform.position = Vector3.zero;
yield return s_DefaultWaitForTick;
// Allow scale to update first when using rigid body motion
if (m_MotionModel == MotionModels.UseRigidbody)
{
yield return new WaitForFixedUpdate();
}
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}");
}
@@ -273,59 +449,59 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
public class VerifyObjectIsSpawnedOnClient : NetworkBehaviour
{
private static Dictionary<ulong, VerifyObjectIsSpawnedOnClient> s_NetworkManagerRelativeSpawnedObjects = new Dictionary<ulong, VerifyObjectIsSpawnedOnClient>();
public static Dictionary<ulong, VerifyObjectIsSpawnedOnClient> NetworkManagerRelativeSpawnedObjects = new Dictionary<ulong, VerifyObjectIsSpawnedOnClient>();
public static void ResetObjectTable()
{
s_NetworkManagerRelativeSpawnedObjects.Clear();
NetworkManagerRelativeSpawnedObjects.Clear();
}
public override void OnGainedOwnership()
{
if (!s_NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
if (!NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
{
s_NetworkManagerRelativeSpawnedObjects.Add(NetworkManager.LocalClientId, this);
NetworkManagerRelativeSpawnedObjects.Add(NetworkManager.LocalClientId, this);
}
base.OnGainedOwnership();
}
public override void OnLostOwnership()
{
if (!s_NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
if (!NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
{
s_NetworkManagerRelativeSpawnedObjects.Add(NetworkManager.LocalClientId, this);
NetworkManagerRelativeSpawnedObjects.Add(NetworkManager.LocalClientId, this);
}
base.OnLostOwnership();
}
public static List<ulong> GetClientsThatSpawnedThisPrefab()
{
return s_NetworkManagerRelativeSpawnedObjects.Keys.ToList();
return NetworkManagerRelativeSpawnedObjects.Keys.ToList();
}
public static VerifyObjectIsSpawnedOnClient GetClientInstance(ulong clientId)
{
if (s_NetworkManagerRelativeSpawnedObjects.ContainsKey(clientId))
if (NetworkManagerRelativeSpawnedObjects.ContainsKey(clientId))
{
return s_NetworkManagerRelativeSpawnedObjects[clientId];
return NetworkManagerRelativeSpawnedObjects[clientId];
}
return null;
}
public override void OnNetworkSpawn()
{
if (!s_NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
if (!NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
{
s_NetworkManagerRelativeSpawnedObjects.Add(NetworkManager.LocalClientId, this);
NetworkManagerRelativeSpawnedObjects.Add(NetworkManager.LocalClientId, this);
}
base.OnNetworkSpawn();
}
public override void OnNetworkDespawn()
{
if (s_NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
if (NetworkManagerRelativeSpawnedObjects.ContainsKey(NetworkManager.LocalClientId))
{
s_NetworkManagerRelativeSpawnedObjects.Remove(NetworkManager.LocalClientId);
NetworkManagerRelativeSpawnedObjects.Remove(NetworkManager.LocalClientId);
}
base.OnNetworkDespawn();
}
@@ -338,24 +514,24 @@ namespace Unity.Netcode.RuntimeTests
[DisallowMultipleComponent]
public class TestClientNetworkTransform : NetworkTransform
{
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
CanCommitToTransform = IsOwner;
}
//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 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()
{

View File

@@ -14,6 +14,12 @@ namespace Unity.Netcode.RuntimeTests
/// models for each operating mode when packet loss and latency is
/// present.
/// </summary>
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half)]
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full)]
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half)]
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full)]
@@ -54,13 +60,18 @@ namespace Unity.Netcode.RuntimeTests
// We don't assert on timeout here because we want to log this information during PostAllChildrenLocalTransformValuesMatch
yield return WaitForConditionOrTimeOut(() => AllInstancesKeptLocalTransformValues(useSubChild));
var success = true;
m_InfoMessage.AppendLine($"[{checkType}][{useSubChild}] Timed out waiting for all children to have the correct local space values:\n");
if (s_GlobalTimeoutHelper.TimedOut)
{
var waitForMs = new WaitForSeconds(0.001f);
var waitForMs = new WaitForSeconds(0.0025f);
if (m_Precision == Precision.Half)
{
m_CurrentHalfPrecision = 0.2156f;
}
// If we timed out, then wait for a full range of ticks to assure all data has been synchronized before declaring this a failed test.
for (int j = 0; j < m_ServerNetworkManager.NetworkConfig.TickRate; j++)
{
m_InfoMessage.Clear();
m_InfoMessage.AppendLine($"[{checkType}][{useSubChild}] Timed out waiting for all children to have the correct local space values:\n");
var instances = useSubChild ? ChildObjectComponent.SubInstances : ChildObjectComponent.Instances;
success = PostAllChildrenLocalTransformValuesMatch(useSubChild);
yield return waitForMs;
@@ -100,6 +111,11 @@ namespace Unity.Netcode.RuntimeTests
var serverSideChild = SpawnObject(m_ChildObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
var serverSideSubChild = SpawnObject(m_SubChildObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
yield return s_DefaultWaitForTick;
yield return s_DefaultWaitForTick;
yield return s_DefaultWaitForTick;
yield return s_DefaultWaitForTick;
// Assure all of the child object instances are spawned before proceeding to parenting
yield return WaitForConditionOrTimeOut(AllChildObjectInstancesAreSpawned);
AssertOnTimeout("Timed out waiting for all child instances to be spawned!");

View File

@@ -1,3 +1,4 @@
#if !MULTIPLAYER_TOOLS
using NUnit.Framework;
using Unity.Netcode.Components;
using UnityEngine;
@@ -233,6 +234,8 @@ namespace Unity.Netcode.RuntimeTests
var manager = new GameObject($"Test-{nameof(NetworkManager)}.{nameof(TestSyncAxes)}");
var networkManager = manager.AddComponent<NetworkManager>();
networkManager.NetworkConfig = new NetworkConfig();
networkObject.NetworkManagerOwner = networkManager;
networkTransform.enabled = false; // do not tick `FixedUpdate()` or `Update()`
@@ -905,3 +908,4 @@ namespace Unity.Netcode.RuntimeTests
}
}
}
#endif

View File

@@ -1,3 +1,4 @@
using System.Collections;
using NUnit.Framework;
using UnityEngine;
@@ -8,25 +9,37 @@ namespace Unity.Netcode.RuntimeTests
/// server and host operating modes and will test both authoritative
/// models for each operating mode.
/// </summary>
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full)]
#if !MULTIPLAYER_TOOLS
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full)]
[TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half)]
#endif
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full)]
#if !MULTIPLAYER_TOOLS
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half)]
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full)]
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half)]
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full)]
[TestFixture(HostOrServer.Host, Authority.ServerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half)]
#endif
[TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full)]
#if !MULTIPLAYER_TOOLS
[TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half)]
[TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full)]
[TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half)]
[TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full)]
[TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half)]
#endif
public class NetworkTransformTests : NetworkTransformBase
{
protected const int k_TickRate = 60;
/// <summary>
/// Constructor
/// </summary>
/// <param name="testWithHost">Determines if we are running as a server or host</param>
/// <param name="hostOrServer">Determines if we are running as a server or host</param>
/// <param name="authority">Determines if we are using server or owner authority</param>
public NetworkTransformTests(HostOrServer testWithHost, Authority authority, RotationCompression rotationCompression, Rotation rotation, Precision precision) :
base(testWithHost, authority, rotationCompression, rotation, precision)
@@ -41,6 +54,24 @@ namespace Unity.Netcode.RuntimeTests
return k_TickRate;
}
private bool m_UseParentingThreshold;
private const float k_ParentingThreshold = 0.25f;
protected override float GetDeltaVarianceThreshold()
{
if (m_UseParentingThreshold)
{
return k_ParentingThreshold;
}
return base.GetDeltaVarianceThreshold();
}
protected override IEnumerator OnSetup()
{
m_UseParentingThreshold = false;
return base.OnSetup();
}
/// <summary>
/// Handles validating the local space values match the original local space values.
/// If not, it generates a message containing the axial values that did not match
@@ -69,6 +100,7 @@ namespace Unity.Netcode.RuntimeTests
}
}
#if !MULTIPLAYER_TOOLS
/// <summary>
/// Validates that transform values remain the same when a NetworkTransform is
/// parented under another NetworkTransform under all of the possible axial conditions
@@ -77,6 +109,7 @@ namespace Unity.Netcode.RuntimeTests
[Test]
public void ParentedNetworkTransformTest([Values] Interpolation interpolation, [Values] bool worldPositionStays, [Values(0.5f, 1.0f, 5.0f)] float scale)
{
m_UseParentingThreshold = true;
// Get the NetworkManager that will have authority in order to spawn with the correct authority
var isServerAuthority = m_Authority == Authority.ServerAuthority;
var authorityNetworkManager = m_ServerNetworkManager;
@@ -134,6 +167,10 @@ namespace Unity.Netcode.RuntimeTests
Assert.True(success, "All transform values did not match prior to parenting!");
success = WaitForConditionOrTimeOutWithTimeTravel(PositionRotationScaleMatches);
Assert.True(success, "All transform values did not match prior to parenting!");
// Parent the child under the parent with the current world position stays setting
Assert.True(serverSideChild.TrySetParent(serverSideParent.transform, worldPositionStays), "[Server-Side Child] Failed to set child's parent!");
@@ -349,6 +386,7 @@ namespace Unity.Netcode.RuntimeTests
}
}
}
#endif
/// <summary>
/// Tests changing all axial values one at a time.

View File

@@ -6,6 +6,8 @@ using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
public class NetworkVarBufferCopyTest : NetcodeIntegrationTest
{
public class DummyNetVar : NetworkVariableBase
@@ -67,15 +69,32 @@ namespace Unity.Netcode.RuntimeTests
DeltaRead = true;
}
public DummyNetVar(
NetworkVariableReadPermission readPerm = DefaultReadPerm,
NetworkVariableWritePermission writePerm = DefaultWritePerm) : base(readPerm, writePerm) { }
}
public class DummyNetBehaviour : NetworkBehaviour
{
public DummyNetVar NetVar = new DummyNetVar();
public static bool DistributedAuthority;
public DummyNetVar NetVar;
private void Awake()
{
if (DistributedAuthority)
{
NetVar = new DummyNetVar(writePerm: NetworkVariableWritePermission.Owner);
}
else
{
NetVar = new DummyNetVar();
}
}
public override void OnNetworkSpawn()
{
if (!IsServer)
if ((NetworkManager.DistributedAuthorityMode && !IsOwner) || (!NetworkManager.DistributedAuthorityMode && !IsServer))
{
ClientDummyNetBehaviourSpawned(this);
}
@@ -84,6 +103,8 @@ namespace Unity.Netcode.RuntimeTests
}
protected override int NumberOfClients => 1;
public NetworkVarBufferCopyTest(HostOrServer hostOrServer) : base(hostOrServer) { }
private static List<DummyNetBehaviour> s_ClientDummyNetBehavioursSpawned = new List<DummyNetBehaviour>();
public static void ClientDummyNetBehaviourSpawned(DummyNetBehaviour dummyNetBehaviour)
{
@@ -98,6 +119,7 @@ namespace Unity.Netcode.RuntimeTests
protected override void OnCreatePlayerPrefab()
{
DummyNetBehaviour.DistributedAuthority = m_DistributedAuthority;
m_PlayerPrefab.AddComponent<DummyNetBehaviour>();
}
@@ -127,27 +149,29 @@ namespace Unity.Netcode.RuntimeTests
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client side DummyNetBehaviour to register it was spawned!");
// Check that FieldWritten is written when dirty
serverComponent.NetVar.SetDirty(true);
var authorityComponent = m_DistributedAuthority ? clientComponent : serverComponent;
var nonAuthorityComponent = m_DistributedAuthority ? serverComponent : clientComponent;
authorityComponent.NetVar.SetDirty(true);
yield return s_DefaultWaitForTick;
Assert.True(serverComponent.NetVar.FieldWritten);
Assert.True(authorityComponent.NetVar.FieldWritten);
// Check that DeltaWritten is written when dirty
serverComponent.NetVar.SetDirty(true);
authorityComponent.NetVar.SetDirty(true);
yield return s_DefaultWaitForTick;
Assert.True(serverComponent.NetVar.DeltaWritten);
Assert.True(authorityComponent.NetVar.DeltaWritten);
// Check that both FieldRead and DeltaRead were invoked on the client side
yield return WaitForConditionOrTimeOut(() => clientComponent.NetVar.FieldRead == true && clientComponent.NetVar.DeltaRead == true);
yield return WaitForConditionOrTimeOut(() => nonAuthorityComponent.NetVar.FieldRead == true && nonAuthorityComponent.NetVar.DeltaRead == true);
var timedOutMessage = "Timed out waiting for client reads: ";
if (s_GlobalTimeoutHelper.TimedOut)
{
if (!clientComponent.NetVar.FieldRead)
if (!nonAuthorityComponent.NetVar.FieldRead)
{
timedOutMessage += "[FieldRead]";
}
if (!clientComponent.NetVar.DeltaRead)
if (!nonAuthorityComponent.NetVar.DeltaRead)
{
timedOutMessage += "[DeltaRead]";
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,937 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
namespace Unity.Netcode.RuntimeTests
{
public class NetVarPermTestComp : NetworkBehaviour
{
public NetworkVariable<Vector3> OwnerWritable_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableBase.DefaultReadPerm, NetworkVariableWritePermission.Owner);
public NetworkVariable<Vector3> ServerWritable_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableBase.DefaultReadPerm, NetworkVariableWritePermission.Server);
public NetworkVariable<Vector3> OwnerReadWrite_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Owner);
}
public class NetworkVariableMiddleclass<TMiddleclassName> : NetworkVariable<TMiddleclassName>
{
}
public class NetworkVariableSubclass<TSubclassName> : NetworkVariableMiddleclass<TSubclassName>
{
}
public class NetworkBehaviourWithNetVarArray : NetworkBehaviour
{
public NetworkVariable<int> Int0 = new NetworkVariable<int>();
public NetworkVariable<int> Int1 = new NetworkVariable<int>();
public NetworkVariable<int> Int2 = new NetworkVariable<int>();
public NetworkVariable<int> Int3 = new NetworkVariable<int>();
public NetworkVariable<int> Int4 = new NetworkVariable<int>();
public NetworkVariable<int>[] AllInts = new NetworkVariable<int>[5];
public int InitializedFieldCount => NetworkVariableFields.Count;
private void Awake()
{
AllInts[0] = Int0;
AllInts[1] = Int1;
AllInts[2] = Int2;
AllInts[3] = Int3;
AllInts[4] = Int4;
}
}
internal struct TypeReferencedOnlyInCustomSerialization1 : INetworkSerializeByMemcpy
{
public int I;
}
internal struct TypeReferencedOnlyInCustomSerialization2 : INetworkSerializeByMemcpy
{
public int I;
}
internal struct TypeReferencedOnlyInCustomSerialization3 : INetworkSerializeByMemcpy
{
public int I;
}
internal struct TypeReferencedOnlyInCustomSerialization4 : INetworkSerializeByMemcpy
{
public int I;
}
internal struct TypeReferencedOnlyInCustomSerialization5 : INetworkSerializeByMemcpy
{
public int I;
}
internal struct TypeReferencedOnlyInCustomSerialization6 : INetworkSerializeByMemcpy
{
public int I;
}
// Both T and U are serializable
[GenerateSerializationForGenericParameter(0)]
[GenerateSerializationForGenericParameter(1)]
internal class CustomSerializableClass<TSerializableType1, TSerializableType2>
{
}
// Only U is serializable
[GenerateSerializationForGenericParameter(1)]
internal class CustomSerializableBaseClass<TUnserializableType, TSerializableType>
{
}
// T is serializable, passes TypeReferencedOnlyInCustomSerialization3 as U to the subclass, making it serializable
[GenerateSerializationForGenericParameter(0)]
internal class CustomSerializableSubclass<TSerializableType> : CustomSerializableBaseClass<TSerializableType, TypeReferencedOnlyInCustomSerialization3>
{
}
// T is serializable, passes TypeReferencedOnlyInCustomSerialization3 as U to the subclass, making it serializable
[GenerateSerializationForGenericParameter(0)]
internal class CustomSerializableSubclassWithNativeArray<TSerializableType> : CustomSerializableBaseClass<TSerializableType, NativeArray<TypeReferencedOnlyInCustomSerialization3>>
{
}
internal class CustomGenericSerializationTestBehaviour : NetworkBehaviour
{
public CustomSerializableClass<TypeReferencedOnlyInCustomSerialization1, TypeReferencedOnlyInCustomSerialization2> Value1;
public CustomSerializableClass<NativeArray<TypeReferencedOnlyInCustomSerialization1>, NativeArray<TypeReferencedOnlyInCustomSerialization2>> Value2;
public CustomSerializableSubclass<TypeReferencedOnlyInCustomSerialization4> Value3;
public CustomSerializableSubclassWithNativeArray<NativeArray<TypeReferencedOnlyInCustomSerialization4>> Value4;
}
[GenerateSerializationForType(typeof(TypeReferencedOnlyInCustomSerialization5))]
[GenerateSerializationForType(typeof(NativeArray<TypeReferencedOnlyInCustomSerialization5>))]
internal struct SomeRandomStruct
{
[GenerateSerializationForType(typeof(TypeReferencedOnlyInCustomSerialization6))]
[GenerateSerializationForType(typeof(NativeArray<TypeReferencedOnlyInCustomSerialization6>))]
public void Foo()
{
}
}
public struct TemplatedValueOnlyReferencedByNetworkVariableSubclass<T> : INetworkSerializeByMemcpy
where T : unmanaged
{
public T Value;
}
public enum ByteEnum : byte
{
A,
B,
C = byte.MaxValue
}
public enum SByteEnum : sbyte
{
A,
B,
C = sbyte.MaxValue
}
public enum ShortEnum : short
{
A,
B,
C = short.MaxValue
}
public enum UShortEnum : ushort
{
A,
B,
C = ushort.MaxValue
}
public enum IntEnum : int
{
A,
B,
C = int.MaxValue
}
public enum UIntEnum : uint
{
A,
B,
C = uint.MaxValue
}
public enum LongEnum : long
{
A,
B,
C = long.MaxValue
}
public enum ULongEnum : ulong
{
A,
B,
C = ulong.MaxValue
}
public struct HashableNetworkVariableTestStruct : INetworkSerializeByMemcpy, IEquatable<HashableNetworkVariableTestStruct>
{
public byte A;
public short B;
public ushort C;
public int D;
public uint E;
public long F;
public ulong G;
public bool H;
public char I;
public float J;
public double K;
public bool Equals(HashableNetworkVariableTestStruct other)
{
return A == other.A && B == other.B && C == other.C && D == other.D && E == other.E && F == other.F && G == other.G && H == other.H && I == other.I && J.Equals(other.J) && K.Equals(other.K);
}
public override bool Equals(object obj)
{
return obj is HashableNetworkVariableTestStruct other && Equals(other);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(A);
hashCode.Add(B);
hashCode.Add(C);
hashCode.Add(D);
hashCode.Add(E);
hashCode.Add(F);
hashCode.Add(G);
hashCode.Add(H);
hashCode.Add(I);
hashCode.Add(J);
hashCode.Add(K);
return hashCode.ToHashCode();
}
}
public struct HashMapKeyStruct : INetworkSerializeByMemcpy, IEquatable<HashMapKeyStruct>
{
public byte A;
public short B;
public ushort C;
public int D;
public uint E;
public long F;
public ulong G;
public bool H;
public char I;
public float J;
public double K;
public bool Equals(HashMapKeyStruct other)
{
return A == other.A && B == other.B && C == other.C && D == other.D && E == other.E && F == other.F && G == other.G && H == other.H && I == other.I && J.Equals(other.J) && K.Equals(other.K);
}
public override bool Equals(object obj)
{
return obj is HashMapKeyStruct other && Equals(other);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(A);
hashCode.Add(B);
hashCode.Add(C);
hashCode.Add(D);
hashCode.Add(E);
hashCode.Add(F);
hashCode.Add(G);
hashCode.Add(H);
hashCode.Add(I);
hashCode.Add(J);
hashCode.Add(K);
return hashCode.ToHashCode();
}
}
public struct HashMapValStruct : INetworkSerializeByMemcpy, IEquatable<HashMapValStruct>
{
public byte A;
public short B;
public ushort C;
public int D;
public uint E;
public long F;
public ulong G;
public bool H;
public char I;
public float J;
public double K;
public bool Equals(HashMapValStruct other)
{
return A == other.A && B == other.B && C == other.C && D == other.D && E == other.E && F == other.F && G == other.G && H == other.H && I == other.I && J.Equals(other.J) && K.Equals(other.K);
}
public override bool Equals(object obj)
{
return obj is HashMapValStruct other && Equals(other);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(A);
hashCode.Add(B);
hashCode.Add(C);
hashCode.Add(D);
hashCode.Add(E);
hashCode.Add(F);
hashCode.Add(G);
hashCode.Add(H);
hashCode.Add(I);
hashCode.Add(J);
hashCode.Add(K);
return hashCode.ToHashCode();
}
}
public struct NetworkVariableTestStruct : INetworkSerializeByMemcpy
{
public byte A;
public short B;
public ushort C;
public int D;
public uint E;
public long F;
public ulong G;
public bool H;
public char I;
public float J;
public double K;
private static System.Random s_Random = new System.Random();
public static NetworkVariableTestStruct GetTestStruct()
{
var testStruct = new NetworkVariableTestStruct
{
A = (byte)s_Random.Next(),
B = (short)s_Random.Next(),
C = (ushort)s_Random.Next(),
D = s_Random.Next(),
E = (uint)s_Random.Next(),
F = ((long)s_Random.Next() << 32) + s_Random.Next(),
G = ((ulong)s_Random.Next() << 32) + (ulong)s_Random.Next(),
H = true,
I = '\u263a',
J = (float)s_Random.NextDouble(),
K = s_Random.NextDouble(),
};
return testStruct;
}
}
public class HashableNetworkVariableTestClass : INetworkSerializable, IEquatable<HashableNetworkVariableTestClass>
{
public HashableNetworkVariableTestStruct Data;
public bool Equals(HashableNetworkVariableTestClass other)
{
return Data.Equals(other.Data);
}
public override bool Equals(object obj)
{
return obj is HashableNetworkVariableTestClass other && Equals(other);
}
public override int GetHashCode()
{
return Data.GetHashCode();
}
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref Data);
}
}
public class HashMapKeyClass : INetworkSerializable, IEquatable<HashMapKeyClass>
{
public HashMapKeyStruct Data;
public bool Equals(HashMapKeyClass other)
{
return Data.Equals(other.Data);
}
public override bool Equals(object obj)
{
return obj is HashMapKeyClass other && Equals(other);
}
public override int GetHashCode()
{
return Data.GetHashCode();
}
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref Data);
}
}
public class HashMapValClass : INetworkSerializable, IEquatable<HashMapValClass>
{
public HashMapValStruct Data;
public bool Equals(HashMapValClass other)
{
return Data.Equals(other.Data);
}
public override bool Equals(object obj)
{
return obj is HashMapValClass other && Equals(other);
}
public override int GetHashCode()
{
return Data.GetHashCode();
}
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref Data);
}
}
public class NetworkVariableTestClass : INetworkSerializable, IEquatable<NetworkVariableTestClass>
{
public NetworkVariableTestStruct Data;
public bool Equals(NetworkVariableTestClass other)
{
return NetworkVariableSerialization<NetworkVariableTestStruct>.AreEqual(ref Data, ref other.Data);
}
public override bool Equals(object obj)
{
return obj is NetworkVariableTestClass other && Equals(other);
}
// This type is not used for hashing, we just need to implement IEquatable to verify lists match.
public override int GetHashCode()
{
return 0;
}
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref Data);
}
}
// The ILPP code for NetworkVariables to determine how to serialize them relies on them existing as fields of a NetworkBehaviour to find them.
// Some of the tests below create NetworkVariables on the stack, so this class is here just to make sure the relevant types are all accounted for.
public class NetVarILPPClassForTests : NetworkBehaviour
{
public NetworkVariable<byte> ByteVar;
public NetworkVariable<NativeArray<byte>> ByteArrayVar;
public NetworkVariable<List<byte>> ByteManagedListVar;
public NetworkVariable<HashSet<byte>> ByteManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<byte>> ByteListVar;
public NetworkVariable<NativeHashSet<byte>> ByteHashSetVar;
public NetworkVariable<NativeHashMap<byte, byte>> ByteByteHashMapVar;
public NetworkVariable<NativeHashMap<ulong, byte>> ULongByteHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, byte>> Vector2ByteHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, byte>> HashMapKeyStructByteHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, byte>> ByteByteDictionaryVar;
public NetworkVariable<Dictionary<ulong, byte>> ULongByteDictionaryVar;
public NetworkVariable<Dictionary<Vector2, byte>> Vector2ByteDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, byte>> HashMapKeyClassByteDictionaryVar;
public NetworkVariable<sbyte> SbyteVar;
public NetworkVariable<NativeArray<sbyte>> SbyteArrayVar;
public NetworkVariable<List<sbyte>> SbyteManagedListVar;
public NetworkVariable<HashSet<sbyte>> SbyteManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<sbyte>> SbyteListVar;
public NetworkVariable<NativeHashSet<sbyte>> SbyteHashSetVar;
public NetworkVariable<NativeHashMap<byte, sbyte>> ByteSbyteHashMapVar;
public NetworkVariable<NativeHashMap<ulong, sbyte>> ULongSbyteHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, sbyte>> Vector2SbyteHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, sbyte>> HashMapKeyStructSbyteHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, sbyte>> ByteSbyteDictionaryVar;
public NetworkVariable<Dictionary<ulong, sbyte>> ULongSbyteDictionaryVar;
public NetworkVariable<Dictionary<Vector2, sbyte>> Vector2SbyteDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, sbyte>> HashMapKeyClassSbyteDictionaryVar;
public NetworkVariable<short> ShortVar;
public NetworkVariable<NativeArray<short>> ShortArrayVar;
public NetworkVariable<List<short>> ShortManagedListVar;
public NetworkVariable<HashSet<short>> ShortManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<short>> ShortListVar;
public NetworkVariable<NativeHashSet<short>> ShortHashSetVar;
public NetworkVariable<NativeHashMap<byte, short>> ByteShortHashMapVar;
public NetworkVariable<NativeHashMap<ulong, short>> ULongShortHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, short>> Vector2ShortHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, short>> HashMapKeyStructShortHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, short>> ByteShortDictionaryVar;
public NetworkVariable<Dictionary<ulong, short>> ULongShortDictionaryVar;
public NetworkVariable<Dictionary<Vector2, short>> Vector2ShortDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, short>> HashMapKeyClassShortDictionaryVar;
public NetworkVariable<ushort> UshortVar;
public NetworkVariable<NativeArray<ushort>> UshortArrayVar;
public NetworkVariable<List<ushort>> UshortManagedListVar;
public NetworkVariable<HashSet<ushort>> UshortManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<ushort>> UshortListVar;
public NetworkVariable<NativeHashSet<ushort>> UshortHashSetVar;
public NetworkVariable<NativeHashMap<byte, ushort>> ByteUshortHashMapVar;
public NetworkVariable<NativeHashMap<ulong, ushort>> ULongUshortHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, ushort>> Vector2UshortHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, ushort>> HashMapKeyStructUshortHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, ushort>> ByteUshortDictionaryVar;
public NetworkVariable<Dictionary<ulong, ushort>> ULongUshortDictionaryVar;
public NetworkVariable<Dictionary<Vector2, ushort>> Vector2UshortDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, ushort>> HashMapKeyClassUshortDictionaryVar;
public NetworkVariable<int> IntVar;
public NetworkVariable<NativeArray<int>> IntArrayVar;
public NetworkVariable<List<int>> IntManagedListVar;
public NetworkVariable<HashSet<int>> IntManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<int>> IntListVar;
public NetworkVariable<NativeHashSet<int>> IntHashSetVar;
public NetworkVariable<NativeHashMap<byte, int>> ByteIntHashMapVar;
public NetworkVariable<NativeHashMap<ulong, int>> ULongIntHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, int>> Vector2IntHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, int>> HashMapKeyStructIntHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, int>> ByteIntDictionaryVar;
public NetworkVariable<Dictionary<ulong, int>> ULongIntDictionaryVar;
public NetworkVariable<Dictionary<Vector2, int>> Vector2IntDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, int>> HashMapKeyClassIntDictionaryVar;
public NetworkVariable<uint> UintVar;
public NetworkVariable<NativeArray<uint>> UintArrayVar;
public NetworkVariable<List<uint>> UintManagedListVar;
public NetworkVariable<HashSet<uint>> UintManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<uint>> UintListVar;
public NetworkVariable<NativeHashSet<uint>> UintHashSetVar;
public NetworkVariable<NativeHashMap<byte, uint>> ByteUintHashMapVar;
public NetworkVariable<NativeHashMap<ulong, uint>> ULongUintHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, uint>> Vector2UintHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, uint>> HashMapKeyStructUintHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, uint>> ByteUintDictionaryVar;
public NetworkVariable<Dictionary<ulong, uint>> ULongUintDictionaryVar;
public NetworkVariable<Dictionary<Vector2, uint>> Vector2UintDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, uint>> HashMapKeyClassUintDictionaryVar;
public NetworkVariable<long> LongVar;
public NetworkVariable<NativeArray<long>> LongArrayVar;
public NetworkVariable<List<long>> LongManagedListVar;
public NetworkVariable<HashSet<long>> LongManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<long>> LongListVar;
public NetworkVariable<NativeHashSet<long>> LongHashSetVar;
public NetworkVariable<NativeHashMap<byte, long>> ByteLongHashMapVar;
public NetworkVariable<NativeHashMap<ulong, long>> ULongLongHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, long>> Vector2LongHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, long>> HashMapKeyStructLongHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, long>> ByteLongDictionaryVar;
public NetworkVariable<Dictionary<ulong, long>> ULongLongDictionaryVar;
public NetworkVariable<Dictionary<Vector2, long>> Vector2LongDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, long>> HashMapKeyClassLongDictionaryVar;
public NetworkVariable<ulong> UlongVar;
public NetworkVariable<NativeArray<ulong>> UlongArrayVar;
public NetworkVariable<List<ulong>> UlongManagedListVar;
public NetworkVariable<HashSet<ulong>> UlongManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<ulong>> UlongListVar;
public NetworkVariable<NativeHashSet<ulong>> UlongHashSetVar;
public NetworkVariable<NativeHashMap<byte, ulong>> ByteUlongHashMapVar;
public NetworkVariable<NativeHashMap<ulong, ulong>> ULongUlongHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, ulong>> Vector2UlongHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, ulong>> HashMapKeyStructUlongHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, ulong>> ByteUlongDictionaryVar;
public NetworkVariable<Dictionary<ulong, ulong>> ULongUlongDictionaryVar;
public NetworkVariable<Dictionary<Vector2, ulong>> Vector2UlongDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, ulong>> HashMapKeyClassUlongDictionaryVar;
public NetworkVariable<bool> BoolVar;
public NetworkVariable<NativeArray<bool>> BoolArrayVar;
public NetworkVariable<List<bool>> BoolManagedListVar;
public NetworkVariable<HashSet<bool>> BoolManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<bool>> BoolListVar;
public NetworkVariable<NativeHashSet<bool>> BoolHashSetVar;
public NetworkVariable<NativeHashMap<byte, bool>> ByteBoolHashMapVar;
public NetworkVariable<NativeHashMap<ulong, bool>> ULongBoolHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, bool>> Vector2BoolHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, bool>> HashMapKeyStructBoolHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, bool>> ByteBoolDictionaryVar;
public NetworkVariable<Dictionary<ulong, bool>> ULongBoolDictionaryVar;
public NetworkVariable<Dictionary<Vector2, bool>> Vector2BoolDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, bool>> HashMapKeyClassBoolDictionaryVar;
public NetworkVariable<char> CharVar;
public NetworkVariable<NativeArray<char>> CharArrayVar;
public NetworkVariable<List<char>> CharManagedListVar;
public NetworkVariable<HashSet<char>> CharManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<char>> CharListVar;
public NetworkVariable<NativeHashSet<char>> CharHashSetVar;
public NetworkVariable<NativeHashMap<byte, char>> ByteCharHashMapVar;
public NetworkVariable<NativeHashMap<ulong, char>> ULongCharHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, char>> Vector2CharHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, char>> HashMapKeyStructCharHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, char>> ByteCharDictionaryVar;
public NetworkVariable<Dictionary<ulong, char>> ULongCharDictionaryVar;
public NetworkVariable<Dictionary<Vector2, char>> Vector2CharDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, char>> HashMapKeyClassCharDictionaryVar;
public NetworkVariable<float> FloatVar;
public NetworkVariable<NativeArray<float>> FloatArrayVar;
public NetworkVariable<List<float>> FloatManagedListVar;
public NetworkVariable<HashSet<float>> FloatManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<float>> FloatListVar;
public NetworkVariable<NativeHashSet<float>> FloatHashSetVar;
public NetworkVariable<NativeHashMap<byte, float>> ByteFloatHashMapVar;
public NetworkVariable<NativeHashMap<ulong, float>> ULongFloatHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, float>> Vector2FloatHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, float>> HashMapKeyStructFloatHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, float>> ByteFloatDictionaryVar;
public NetworkVariable<Dictionary<ulong, float>> ULongFloatDictionaryVar;
public NetworkVariable<Dictionary<Vector2, float>> Vector2FloatDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, float>> HashMapKeyClassFloatDictionaryVar;
public NetworkVariable<double> DoubleVar;
public NetworkVariable<NativeArray<double>> DoubleArrayVar;
public NetworkVariable<List<double>> DoubleManagedListVar;
public NetworkVariable<HashSet<double>> DoubleManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<double>> DoubleListVar;
public NetworkVariable<NativeHashSet<double>> DoubleHashSetVar;
public NetworkVariable<NativeHashMap<byte, double>> ByteDoubleHashMapVar;
public NetworkVariable<NativeHashMap<ulong, double>> ULongDoubleHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, double>> Vector2DoubleHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, double>> HashMapKeyStructDoubleHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, double>> ByteDoubleDictionaryVar;
public NetworkVariable<Dictionary<ulong, double>> ULongDoubleDictionaryVar;
public NetworkVariable<Dictionary<Vector2, double>> Vector2DoubleDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, double>> HashMapKeyClassDoubleDictionaryVar;
public NetworkVariable<ByteEnum> ByteEnumVar;
public NetworkVariable<NativeArray<ByteEnum>> ByteEnumArrayVar;
public NetworkVariable<List<ByteEnum>> ByteEnumManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<ByteEnum>> ByteEnumListVar;
#endif
public NetworkVariable<SByteEnum> SByteEnumVar;
public NetworkVariable<NativeArray<SByteEnum>> SByteEnumArrayVar;
public NetworkVariable<List<SByteEnum>> SByteEnumManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<SByteEnum>> SByteEnumListVar;
#endif
public NetworkVariable<ShortEnum> ShortEnumVar;
public NetworkVariable<NativeArray<ShortEnum>> ShortEnumArrayVar;
public NetworkVariable<List<ShortEnum>> ShortEnumManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<ShortEnum>> ShortEnumListVar;
#endif
public NetworkVariable<UShortEnum> UShortEnumVar;
public NetworkVariable<NativeArray<UShortEnum>> UShortEnumArrayVar;
public NetworkVariable<List<UShortEnum>> UShortEnumManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<UShortEnum>> UShortEnumListVar;
#endif
public NetworkVariable<IntEnum> IntEnumVar;
public NetworkVariable<NativeArray<IntEnum>> IntEnumArrayVar;
public NetworkVariable<List<IntEnum>> IntEnumManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<IntEnum>> IntEnumListVar;
#endif
public NetworkVariable<UIntEnum> UIntEnumVar;
public NetworkVariable<NativeArray<UIntEnum>> UIntEnumArrayVar;
public NetworkVariable<List<UIntEnum>> UIntEnumManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<UIntEnum>> UIntEnumListVar;
#endif
public NetworkVariable<LongEnum> LongEnumVar;
public NetworkVariable<NativeArray<LongEnum>> LongEnumArrayVar;
public NetworkVariable<List<LongEnum>> LongEnumManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<LongEnum>> LongEnumListVar;
#endif
public NetworkVariable<ULongEnum> ULongEnumVar;
public NetworkVariable<NativeArray<ULongEnum>> ULongEnumArrayVar;
public NetworkVariable<List<ULongEnum>> ULongEnumManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<ULongEnum>> ULongEnumListVar;
#endif
public NetworkVariable<Vector2> Vector2Var;
public NetworkVariable<NativeArray<Vector2>> Vector2ArrayVar;
public NetworkVariable<List<Vector2>> Vector2ManagedListVar;
public NetworkVariable<HashSet<Vector2>> Vector2ManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Vector2>> Vector2ListVar;
public NetworkVariable<NativeHashSet<Vector2>> Vector2HashSetVar;
public NetworkVariable<NativeHashMap<byte, Vector2>> ByteVector2HashMapVar;
public NetworkVariable<NativeHashMap<ulong, Vector2>> ULongVector2HashMapVar;
public NetworkVariable<NativeHashMap<Vector2, Vector2>> Vector2Vector2HashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, Vector2>> HashMapKeyStructVector2HashMapVar;
#endif
public NetworkVariable<Dictionary<byte, Vector2>> ByteVector2DictionaryVar;
public NetworkVariable<Dictionary<ulong, Vector2>> ULongVector2DictionaryVar;
public NetworkVariable<Dictionary<Vector2, Vector2>> Vector2Vector2DictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, Vector2>> HashMapKeyClassVector2DictionaryVar;
public NetworkVariable<Vector3> Vector3Var;
public NetworkVariable<NativeArray<Vector3>> Vector3ArrayVar;
public NetworkVariable<List<Vector3>> Vector3ManagedListVar;
public NetworkVariable<HashSet<Vector3>> Vector3ManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Vector3>> Vector3ListVar;
public NetworkVariable<NativeHashSet<Vector3>> Vector3HashSetVar;
public NetworkVariable<NativeHashMap<byte, Vector3>> ByteVector3HashMapVar;
public NetworkVariable<NativeHashMap<ulong, Vector3>> ULongVector3HashMapVar;
public NetworkVariable<NativeHashMap<Vector2, Vector3>> Vector2Vector3HashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, Vector3>> HashMapKeyStructVector3HashMapVar;
#endif
public NetworkVariable<Dictionary<byte, Vector3>> ByteVector3DictionaryVar;
public NetworkVariable<Dictionary<ulong, Vector3>> ULongVector3DictionaryVar;
public NetworkVariable<Dictionary<Vector2, Vector3>> Vector2Vector3DictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, Vector3>> HashMapKeyClassVector3DictionaryVar;
public NetworkVariable<Vector2Int> Vector2IntVar;
public NetworkVariable<NativeArray<Vector2Int>> Vector2IntArrayVar;
public NetworkVariable<List<Vector2Int>> Vector2IntManagedListVar;
public NetworkVariable<HashSet<Vector2Int>> Vector2IntManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Vector2Int>> Vector2IntListVar;
public NetworkVariable<NativeHashSet<Vector2Int>> Vector2IntHashSetVar;
public NetworkVariable<NativeHashMap<byte, Vector2Int>> ByteVector2IntHashMapVar;
public NetworkVariable<NativeHashMap<ulong, Vector2Int>> ULongVector2IntHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, Vector2Int>> Vector2Vector2IntHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, Vector2Int>> HashMapKeyStructVector2IntHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, Vector2Int>> ByteVector2IntDictionaryVar;
public NetworkVariable<Dictionary<ulong, Vector2Int>> ULongVector2IntDictionaryVar;
public NetworkVariable<Dictionary<Vector2, Vector2Int>> Vector2Vector2IntDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, Vector2Int>> HashMapKeyClassVector2IntDictionaryVar;
public NetworkVariable<Vector3Int> Vector3IntVar;
public NetworkVariable<NativeArray<Vector3Int>> Vector3IntArrayVar;
public NetworkVariable<List<Vector3Int>> Vector3IntManagedListVar;
public NetworkVariable<HashSet<Vector3Int>> Vector3IntManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Vector3Int>> Vector3IntListVar;
public NetworkVariable<NativeHashSet<Vector3Int>> Vector3IntHashSetVar;
public NetworkVariable<NativeHashMap<byte, Vector3Int>> ByteVector3IntHashMapVar;
public NetworkVariable<NativeHashMap<ulong, Vector3Int>> ULongVector3IntHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, Vector3Int>> Vector2Vector3IntHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, Vector3Int>> HashMapKeyStructVector3IntHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, Vector3Int>> ByteVector3IntDictionaryVar;
public NetworkVariable<Dictionary<ulong, Vector3Int>> ULongVector3IntDictionaryVar;
public NetworkVariable<Dictionary<Vector2, Vector3Int>> Vector2Vector3IntDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, Vector3Int>> HashMapKeyClassVector3IntDictionaryVar;
public NetworkVariable<Vector4> Vector4Var;
public NetworkVariable<NativeArray<Vector4>> Vector4ArrayVar;
public NetworkVariable<List<Vector4>> Vector4ManagedListVar;
public NetworkVariable<HashSet<Vector4>> Vector4ManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Vector4>> Vector4ListVar;
public NetworkVariable<NativeHashSet<Vector4>> Vector4HashSetVar;
public NetworkVariable<NativeHashMap<byte, Vector4>> ByteVector4HashMapVar;
public NetworkVariable<NativeHashMap<ulong, Vector4>> ULongVector4HashMapVar;
public NetworkVariable<NativeHashMap<Vector2, Vector4>> Vector2Vector4HashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, Vector4>> HashMapKeyStructVector4HashMapVar;
#endif
public NetworkVariable<Dictionary<byte, Vector4>> ByteVector4DictionaryVar;
public NetworkVariable<Dictionary<ulong, Vector4>> ULongVector4DictionaryVar;
public NetworkVariable<Dictionary<Vector2, Vector4>> Vector2Vector4DictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, Vector4>> HashMapKeyClassVector4DictionaryVar;
public NetworkVariable<Quaternion> QuaternionVar;
public NetworkVariable<NativeArray<Quaternion>> QuaternionArrayVar;
public NetworkVariable<List<Quaternion>> QuaternionManagedListVar;
public NetworkVariable<HashSet<Quaternion>> QuaternionManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Quaternion>> QuaternionListVar;
public NetworkVariable<NativeHashSet<Quaternion>> QuaternionHashSetVar;
public NetworkVariable<NativeHashMap<byte, Quaternion>> ByteQuaternionHashMapVar;
public NetworkVariable<NativeHashMap<ulong, Quaternion>> ULongQuaternionHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, Quaternion>> Vector2QuaternionHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, Quaternion>> HashMapKeyStructQuaternionHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, Quaternion>> ByteQuaternionDictionaryVar;
public NetworkVariable<Dictionary<ulong, Quaternion>> ULongQuaternionDictionaryVar;
public NetworkVariable<Dictionary<Vector2, Quaternion>> Vector2QuaternionDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, Quaternion>> HashMapKeyClassQuaternionDictionaryVar;
public NetworkVariable<Color> ColorVar;
public NetworkVariable<NativeArray<Color>> ColorArrayVar;
public NetworkVariable<List<Color>> ColorManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Color>> ColorListVar;
#endif
public NetworkVariable<Color32> Color32Var;
public NetworkVariable<NativeArray<Color32>> Color32ArrayVar;
public NetworkVariable<List<Color32>> Color32ManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Color32>> Color32ListVar;
#endif
public NetworkVariable<Ray> RayVar;
public NetworkVariable<NativeArray<Ray>> RayArrayVar;
public NetworkVariable<List<Ray>> RayManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Ray>> RayListVar;
#endif
public NetworkVariable<Ray2D> Ray2DVar;
public NetworkVariable<NativeArray<Ray2D>> Ray2DArrayVar;
public NetworkVariable<List<Ray2D>> Ray2DManagedListVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<Ray2D>> Ray2DListVar;
#endif
public NetworkVariable<NetworkVariableTestStruct> TestStructVar;
public NetworkVariable<NativeArray<NetworkVariableTestStruct>> TestStructArrayVar;
public NetworkVariable<List<NetworkVariableTestClass>> TestStructManagedListVar;
public NetworkVariable<HashSet<HashableNetworkVariableTestClass>> TestStructManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<NetworkVariableTestStruct>> TestStructListVar;
public NetworkVariable<NativeHashSet<HashableNetworkVariableTestStruct>> TestStructHashSetVar;
public NetworkVariable<NativeHashMap<byte, HashMapValStruct>> ByteTestStructHashMapVar;
public NetworkVariable<NativeHashMap<ulong, HashMapValStruct>> ULongTestStructHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, HashMapValStruct>> Vector2TestStructHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, HashMapValStruct>> HashMapKeyStructTestStructHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, HashMapValClass>> ByteTestStructDictionaryVar;
public NetworkVariable<Dictionary<ulong, HashMapValClass>> ULongTestStructDictionaryVar;
public NetworkVariable<Dictionary<Vector2, HashMapValClass>> Vector2TestStructDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, HashMapValClass>> HashMapKeyClassTestStructDictionaryVar;
public NetworkVariable<FixedString32Bytes> FixedStringVar;
public NetworkVariable<NativeArray<FixedString32Bytes>> FixedStringArrayVar;
public NetworkVariable<List<FixedString32Bytes>> FixedStringManagedListVar;
public NetworkVariable<HashSet<FixedString32Bytes>> FixedStringManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<FixedString32Bytes>> FixedStringListVar;
public NetworkVariable<NativeHashSet<FixedString32Bytes>> FixedStringHashSetVar;
public NetworkVariable<NativeHashMap<byte, FixedString32Bytes>> ByteFixedStringHashMapVar;
public NetworkVariable<NativeHashMap<ulong, FixedString32Bytes>> ULongFixedStringHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, FixedString32Bytes>> Vector2FixedStringHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, FixedString32Bytes>> HashMapKeyStructFixedStringHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, FixedString32Bytes>> ByteFixedStringDictionaryVar;
public NetworkVariable<Dictionary<ulong, FixedString32Bytes>> ULongFixedStringDictionaryVar;
public NetworkVariable<Dictionary<Vector2, FixedString32Bytes>> Vector2FixedStringDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, FixedString32Bytes>> HashMapKeyClassFixedStringDictionaryVar;
public NetworkVariable<UnmanagedNetworkSerializableType> UnmanagedNetworkSerializableTypeVar;
public NetworkVariable<HashSet<UnmanagedNetworkSerializableType>> UnmanagedNetworkSerializableManagedHashSetVar;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
public NetworkVariable<NativeList<UnmanagedNetworkSerializableType>> UnmanagedNetworkSerializableListVar;
public NetworkVariable<NativeHashSet<UnmanagedNetworkSerializableType>> UnmanagedNetworkSerializableHashSetVar;
public NetworkVariable<NativeHashMap<byte, UnmanagedNetworkSerializableType>> ByteUnmanagedNetworkSerializableHashMapVar;
public NetworkVariable<NativeHashMap<ulong, UnmanagedNetworkSerializableType>> ULongUnmanagedNetworkSerializableHashMapVar;
public NetworkVariable<NativeHashMap<Vector2, UnmanagedNetworkSerializableType>> Vector2UnmanagedNetworkSerializableHashMapVar;
public NetworkVariable<NativeHashMap<HashMapKeyStruct, UnmanagedNetworkSerializableType>> HashMapKeyStructUnmanagedNetworkSerializableHashMapVar;
#endif
public NetworkVariable<Dictionary<byte, UnmanagedNetworkSerializableType>> ByteUnmanagedNetworkSerializableDictionaryVar;
public NetworkVariable<Dictionary<ulong, UnmanagedNetworkSerializableType>> ULongUnmanagedNetworkSerializableDictionaryVar;
public NetworkVariable<Dictionary<Vector2, UnmanagedNetworkSerializableType>> Vector2UnmanagedNetworkSerializableDictionaryVar;
public NetworkVariable<Dictionary<HashMapKeyClass, UnmanagedNetworkSerializableType>> HashMapKeyClassUnmanagedNetworkSerializableDictionaryVar;
public NetworkVariable<NativeArray<UnmanagedNetworkSerializableType>> UnmanagedNetworkSerializableArrayVar;
public NetworkVariable<List<UnmanagedNetworkSerializableType>> UnmanagedNetworkSerializableManagedListVar;
public NetworkVariable<ManagedNetworkSerializableType> ManagedNetworkSerializableTypeVar;
public NetworkVariable<string> StringVar;
public NetworkVariable<Guid> GuidVar;
public NetworkVariableSubclass<TemplatedValueOnlyReferencedByNetworkVariableSubclass<int>> SubclassVar;
}
public class TemplateNetworkBehaviourType<T> : NetworkBehaviour
{
public NetworkVariable<T> TheVar;
}
public class IntermediateNetworkBehavior<T> : TemplateNetworkBehaviourType<T>
{
public NetworkVariable<T> TheVar2;
}
#if !NGO_MINIMALPROJECT
public class ClassHavingNetworkBehaviour : IntermediateNetworkBehavior<TestClass>
{
}
// Please do not reference TestClass_ReferencedOnlyByTemplateNetworkBehavourType anywhere other than here!
public class ClassHavingNetworkBehaviour2 : TemplateNetworkBehaviourType<TestClass_ReferencedOnlyByTemplateNetworkBehavourType>
{
}
public class StructHavingNetworkBehaviour : TemplateNetworkBehaviourType<TestStruct>
{
}
#endif
public struct StructUsedOnlyInNetworkList : IEquatable<StructUsedOnlyInNetworkList>, INetworkSerializeByMemcpy
{
public int Value;
public bool Equals(StructUsedOnlyInNetworkList other)
{
return Value == other.Value;
}
public override bool Equals(object obj)
{
return obj is StructUsedOnlyInNetworkList other && Equals(other);
}
public override int GetHashCode()
{
return Value;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a580aaafc247486193f372174a99fae4
timeCreated: 1705098783

View File

@@ -7,20 +7,18 @@ using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(SceneManagementState.SceneManagementEnabled)]
[TestFixture(SceneManagementState.SceneManagementDisabled)]
[TestFixture(SceneManagementState.SceneManagementEnabled, SessionModeTypes.DistributedAuthority)]
[TestFixture(SceneManagementState.SceneManagementDisabled, SessionModeTypes.DistributedAuthority)]
[TestFixture(SceneManagementState.SceneManagementEnabled, SessionModeTypes.ClientServer)]
[TestFixture(SceneManagementState.SceneManagementDisabled, SessionModeTypes.ClientServer)]
public class NetworkVisibilityTests : NetcodeIntegrationTest
{
public enum SceneManagementState
{
SceneManagementEnabled,
SceneManagementDisabled
}
protected override int NumberOfClients => 1;
private GameObject m_TestNetworkPrefab;
private bool m_SceneManagementEnabled;
public NetworkVisibilityTests(SceneManagementState sceneManagementState)
public NetworkVisibilityTests(SceneManagementState sceneManagementState, SessionModeTypes sessionModeType) : base(sessionModeType)
{
m_SceneManagementEnabled = sceneManagementState == SceneManagementState.SceneManagementEnabled;
}

View File

@@ -18,6 +18,8 @@ namespace Unity.Netcode.RuntimeTests
internal static int Updates = 0;
public static bool EnableVerbose;
private void Awake()
{
MyNetworkList = new NetworkList<int>(new List<int>(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
@@ -34,7 +36,11 @@ namespace Unity.Netcode.RuntimeTests
expected++;
listString += i.ToString();
}
Debug.Log($"[{NetworkManager.LocalClientId}] Value changed to {listString}");
if (EnableVerbose)
{
Debug.Log($"[{NetworkManager.LocalClientId}] Value changed to {listString}");
}
Updates++;
}
@@ -68,10 +74,14 @@ namespace Unity.Netcode.RuntimeTests
}
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
public class OwnerModifiedTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 2;
public OwnerModifiedTests(HostOrServer hostOrServer) : base(hostOrServer) { }
protected override void OnCreatePlayerPrefab()
{
m_PlayerPrefab.AddComponent<OwnerModifiedObject>();
@@ -80,6 +90,7 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator OwnerModifiedTest()
{
OwnerModifiedObject.EnableVerbose = m_EnableVerboseDebug;
// We use this to assure we are the "last client" connected.
yield return CreateAndStartNewClient();
var ownerModLastClient = m_ClientNetworkManagers[2].LocalClient.PlayerObject.GetComponent<OwnerModifiedObject>();
@@ -89,7 +100,7 @@ namespace Unity.Netcode.RuntimeTests
foreach (var updateLoopType in System.Enum.GetValues(typeof(NetworkUpdateStage)))
{
ownerModLastClient.NetworkUpdateStageToCheck = (NetworkUpdateStage)updateLoopType;
Debug.Log($"Testing Update Stage: {ownerModLastClient.NetworkUpdateStageToCheck}");
VerboseDebug($"Testing Update Stage: {ownerModLastClient.NetworkUpdateStageToCheck}");
ownerModLastClient.AddValues = true;
yield return WaitForTicks(m_ServerNetworkManager, 5);
}

View File

@@ -50,7 +50,7 @@ namespace Unity.Netcode.RuntimeTests
public override void OnNetworkSpawn()
{
Objects[CurrentlySpawning, NetworkManager.LocalClientId] = GetComponent<OwnerPermissionObject>();
Debug.Log($"Object index ({CurrentlySpawning}) spawned on client {NetworkManager.LocalClientId}");
//Debug.Log($"Object index ({CurrentlySpawning}) spawned on client {NetworkManager.LocalClientId}");
}
private void Awake()
@@ -131,7 +131,7 @@ namespace Unity.Netcode.RuntimeTests
{
// ==== Server-writable NetworkVariable ====
var gotException = false;
Debug.Log($"Writing to server-write variable on object {objectIndex} on client {clientWriting}");
VerboseDebug($"Writing to server-write variable on object {objectIndex} on client {clientWriting}");
try
{
@@ -148,7 +148,7 @@ namespace Unity.Netcode.RuntimeTests
// ==== Owner-writable NetworkVariable ====
gotException = false;
Debug.Log($"Writing to owner-write variable on object {objectIndex} on client {clientWriting}");
VerboseDebug($"Writing to owner-write variable on object {objectIndex} on client {clientWriting}");
try
{
@@ -165,7 +165,6 @@ namespace Unity.Netcode.RuntimeTests
// ==== Server-writable NetworkList ====
gotException = false;
Debug.Log($"Writing to server-write list on object {objectIndex} on client {clientWriting}");
try
{
@@ -182,7 +181,6 @@ namespace Unity.Netcode.RuntimeTests
// ==== Owner-writable NetworkList ====
gotException = false;
Debug.Log($"Writing to owner-write list on object {objectIndex} on client {clientWriting}");
try
{

View File

@@ -133,6 +133,7 @@ namespace Unity.Netcode.RuntimeTests
}
yield return WaitForConditionOrTimeOut(hooks);
Assert.False(s_GlobalTimeoutHelper.TimedOut);
foreach (var client in m_ClientNetworkManagers)
@@ -144,18 +145,18 @@ namespace Unity.Netcode.RuntimeTests
}
if (m_UseHost)
{
Assert.IsTrue(client.ConnectedClientsIds.Contains(0ul));
Assert.IsTrue(client.ConnectedClientsIds.Contains(0ul), $"[Client-{client.LocalClientId}][Connected ({client.IsConnectedClient})] Still has client identifier 0!");
}
for (var i = 1ul; i < 3ul; ++i)
{
if (i == disconnectedClient)
{
Assert.IsFalse(client.ConnectedClientsIds.Contains(i));
Assert.IsFalse(client.ConnectedClientsIds.Contains(i), $"[Client-{client.LocalClientId}][Connected ({client.IsConnectedClient})] Still has client identifier {i}!");
}
else
{
Assert.IsTrue(client.ConnectedClientsIds.Contains(i));
Assert.IsTrue(client.ConnectedClientsIds.Contains(i), $"[Client-{client.LocalClientId}][Connected ({client.IsConnectedClient})] Still has client identifier {i}!");
}
}
}

View File

@@ -8,16 +8,33 @@ using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(RigidbodyInterpolation.Interpolate, true, true)] // This should be allowed under all condistions when using Rigidbody motion
[TestFixture(RigidbodyInterpolation.Extrapolate, true, true)] // This should not allow extrapolation on non-auth instances when using Rigidbody motion & NT interpolation
[TestFixture(RigidbodyInterpolation.Extrapolate, false, true)] // This should allow extrapolation on non-auth instances when using Rigidbody & NT has no interpolation
[TestFixture(RigidbodyInterpolation.Interpolate, true, false)] // This should not allow kinematic instances to have Rigidbody interpolation enabled
[TestFixture(RigidbodyInterpolation.Interpolate, false, false)] // Testing that rigid body interpolation remains the same if NT interpolate is disabled
public class NetworkRigidbodyTest : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
private bool m_NetworkTransformInterpolate;
private bool m_UseRigidBodyForMotion;
private RigidbodyInterpolation m_RigidbodyInterpolation;
public NetworkRigidbodyTest(RigidbodyInterpolation rigidbodyInterpolation, bool networkTransformInterpolate, bool useRigidbodyForMotion)
{
m_RigidbodyInterpolation = rigidbodyInterpolation;
m_NetworkTransformInterpolate = networkTransformInterpolate;
m_UseRigidBodyForMotion = useRigidbodyForMotion;
}
protected override void OnCreatePlayerPrefab()
{
m_PlayerPrefab.AddComponent<NetworkTransform>();
m_PlayerPrefab.AddComponent<Rigidbody>();
m_PlayerPrefab.AddComponent<NetworkRigidbody>();
m_PlayerPrefab.GetComponent<Rigidbody>().interpolation = RigidbodyInterpolation.Interpolate;
var networkTransform = m_PlayerPrefab.AddComponent<NetworkTransform>();
networkTransform.Interpolate = m_NetworkTransformInterpolate;
var rigidbody = m_PlayerPrefab.AddComponent<Rigidbody>();
rigidbody.interpolation = m_RigidbodyInterpolation;
var networkRigidbody = m_PlayerPrefab.AddComponent<NetworkRigidbody>();
networkRigidbody.UseRigidBodyForMotion = m_UseRigidBodyForMotion;
}
/// <summary>
@@ -28,39 +45,67 @@ namespace Unity.Netcode.RuntimeTests
public IEnumerator TestRigidbodyKinematicEnableDisable()
{
// This is the *SERVER VERSION* of the *CLIENT PLAYER*
var serverClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper<NetworkObject>();
yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult);
var serverPlayer = serverClientPlayerResult.Result.gameObject;
var serverClientPlayerInstance = m_ServerNetworkManager.ConnectedClients[m_ClientNetworkManagers[0].LocalClientId].PlayerObject;
// This is the *CLIENT VERSION* of the *CLIENT PLAYER*
var clientClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper<NetworkObject>();
yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult);
var clientPlayer = clientClientPlayerResult.Result.gameObject;
var clientPlayerInstance = m_ClientNetworkManagers[0].LocalClient.PlayerObject;
Assert.IsNotNull(serverPlayer, "serverPlayer is not null");
Assert.IsNotNull(clientPlayer, "clientPlayer is not null");
Assert.IsNotNull(serverClientPlayerInstance, $"{nameof(serverClientPlayerInstance)} is null!");
Assert.IsNotNull(clientPlayerInstance, $"{nameof(clientPlayerInstance)} is null!");
yield return WaitForTicks(m_ServerNetworkManager, 3);
var serverClientInstanceRigidBody = serverClientPlayerInstance.GetComponent<Rigidbody>();
var clientRigidBody = clientPlayerInstance.GetComponent<Rigidbody>();
// server rigidbody has authority and should not be kinematic
Assert.True(serverPlayer.GetComponent<Rigidbody>().isKinematic == false, "serverPlayer kinematic");
Assert.AreEqual(RigidbodyInterpolation.Interpolate, serverPlayer.GetComponent<Rigidbody>().interpolation, "server equal interpolate");
if (m_UseRigidBodyForMotion)
{
var interpolateCompareNonAuthoritative = m_NetworkTransformInterpolate ? RigidbodyInterpolation.Interpolate : m_RigidbodyInterpolation;
// client rigidbody has no authority and should have a kinematic mode of true
Assert.True(clientPlayer.GetComponent<Rigidbody>().isKinematic, "clientPlayer kinematic");
Assert.AreEqual(RigidbodyInterpolation.None, clientPlayer.GetComponent<Rigidbody>().interpolation, "client equal interpolate");
// Server authoritative NT should yield non-kinematic mode for the server-side player instance
Assert.False(serverClientInstanceRigidBody.isKinematic, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is kinematic!");
// The authoritative instance can be None, Interpolate, or Extrapolate for the Rigidbody interpolation settings.
Assert.AreEqual(m_RigidbodyInterpolation, serverClientInstanceRigidBody.interpolation, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " +
$"player's {nameof(Rigidbody)}'s interpolation is {serverClientInstanceRigidBody.interpolation} and not {m_RigidbodyInterpolation}!");
// Server authoritative NT should yield kinematic mode for the client-side player instance
Assert.True(clientRigidBody.isKinematic, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic!");
// When using Rigidbody motion, authoritative and non-authoritative Rigidbody interpolation settings should be preserved (except when extrapolation is used
Assert.AreEqual(interpolateCompareNonAuthoritative, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " +
$"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {interpolateCompareNonAuthoritative}!");
}
else
{
// server rigidbody has authority and should not be kinematic
Assert.False(serverClientInstanceRigidBody.isKinematic, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is kinematic!");
Assert.AreEqual(RigidbodyInterpolation.Interpolate, serverClientInstanceRigidBody.interpolation, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " +
$"player's {nameof(Rigidbody)}'s interpolation is {serverClientInstanceRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.Interpolate)}!");
// Server authoritative NT should yield kinematic mode for the client-side player instance
Assert.True(clientRigidBody.isKinematic, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic!");
// client rigidbody has no authority with NT interpolation disabled should allow Rigidbody interpolation
if (!m_NetworkTransformInterpolate)
{
Assert.AreEqual(RigidbodyInterpolation.Interpolate, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " +
$"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.Interpolate)}!");
}
else
{
Assert.AreEqual(RigidbodyInterpolation.None, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " +
$"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.None)}!");
}
}
// despawn the server player (but keep it around on the server)
serverPlayer.GetComponent<NetworkObject>().Despawn(false);
serverClientPlayerInstance.Despawn(false);
yield return WaitForTicks(m_ServerNetworkManager, 3);
yield return WaitForConditionOrTimeOut(() => !serverClientPlayerInstance.IsSpawned && !clientPlayerInstance.IsSpawned);
AssertOnTimeout("Timed out waiting for client player to despawn on both server and client!");
// When despawned, we should always be kinematic (i.e. don't apply physics when despawned)
Assert.IsTrue(serverPlayer.GetComponent<Rigidbody>().isKinematic == true, "serverPlayer second kinematic");
yield return WaitForTicks(m_ServerNetworkManager, 3);
Assert.IsTrue(clientPlayer == null, "clientPlayer being null"); // safety check that object is actually despawned.
Assert.True(serverClientInstanceRigidBody.isKinematic, $"[Server-Side][Despawned] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic when despawned!");
Assert.IsTrue(clientPlayerInstance == null, $"[Client-Side] Player {nameof(NetworkObject)} is not null!");
}
}
}

View File

@@ -6,6 +6,7 @@ using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
public class PlayerObjectTests : NetcodeIntegrationTest
@@ -25,6 +26,8 @@ namespace Unity.Netcode.RuntimeTests
[UnityTest]
public IEnumerator SpawnAndReplaceExistingPlayerObject()
{
yield return WaitForConditionOrTimeOut(() => m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId].ContainsKey(m_ClientNetworkManagers[0].LocalClientId));
AssertOnTimeout("Timed out waiting for client-side player object to spawn!");
// Get the server-side player NetworkObject
var originalPlayer = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId];
// Get the client-side player NetworkObject
@@ -33,7 +36,8 @@ namespace Unity.Netcode.RuntimeTests
// Create a new player prefab instance
var newPlayer = Object.Instantiate(m_NewPlayerToSpawn);
var newPlayerNetworkObject = newPlayer.GetComponent<NetworkObject>();
newPlayerNetworkObject.NetworkManagerOwner = m_ServerNetworkManager;
// In distributed authority mode, the client owner spawns its new player
newPlayerNetworkObject.NetworkManagerOwner = m_DistributedAuthority ? m_ClientNetworkManagers[0] : m_ServerNetworkManager;
// Spawn this instance as a new player object for the client who already has an assigned player object
newPlayerNetworkObject.SpawnAsPlayerObject(m_ClientNetworkManagers[0].LocalClientId);

View File

@@ -5,7 +5,6 @@ using NUnit.Framework;
using Unity.Collections;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine.TestTools;
using Debug = UnityEngine.Debug;
using Vector3 = UnityEngine.Vector3;
namespace Unity.Netcode.RuntimeTests
@@ -121,7 +120,7 @@ namespace Unity.Netcode.RuntimeTests
localClienRpcTestNB.OnClient_Rpc += () =>
{
Debug.Log("ClientRpc received on client object");
VerboseDebug("ClientRpc received on client object");
hasReceivedClientRpcRemotely = true;
};
@@ -139,14 +138,14 @@ namespace Unity.Netcode.RuntimeTests
serverClientRpcTestNB.OnServer_Rpc += (clientId, param) =>
{
Debug.Log("ServerRpc received on server object");
VerboseDebug("ServerRpc received on server object");
Assert.True(param.Receive.SenderClientId == clientId);
hasReceivedServerRpc = true;
};
serverClientRpcTestNBFloat.OnServer_Rpc += (clientId, param) =>
{
Debug.Log("ServerRpc (float) received on server object");
VerboseDebug("ServerRpc (float) received on server object");
Assert.True(param.Receive.SenderClientId == clientId);
hasReceivedFloatServerRpc = true;
};
@@ -154,7 +153,7 @@ namespace Unity.Netcode.RuntimeTests
serverClientRpcTestNB.OnClient_Rpc += () =>
{
// The RPC invoked locally. (Weaver failure?)
Debug.Log("ClientRpc received on server object");
VerboseDebug("ClientRpc received on server object");
hasReceivedClientRpcLocally = true;
};
@@ -167,7 +166,7 @@ namespace Unity.Netcode.RuntimeTests
#endif
param4) =>
{
Debug.Log("TypedServerRpc received on server object");
VerboseDebug("TypedServerRpc received on server object");
Assert.AreEqual(param1, vector3);
Assert.AreEqual(param2.Length, vector3s.Length);
Assert.AreEqual(param2[0], vector3s[0]);

View File

@@ -889,9 +889,9 @@ namespace Unity.Netcode.RuntimeTests
clientObject.OnReceived = o =>
{
receivedValue = o;
Debug.Log($"Received value {o}");
VerboseDebug($"Received value {o}");
};
Debug.Log($"Sending first RPC with {firstTest}");
VerboseDebug($"Sending first RPC with {firstTest}");
method.Invoke(serverObject, new object[] { firstTest });
yield return WaitForMessageReceived<ClientRpcMessage>(m_ClientNetworkManagers.ToList());
@@ -904,7 +904,7 @@ namespace Unity.Netcode.RuntimeTests
receivedValue = null;
Debug.Log($"Sending second RPC with {secondTest}");
VerboseDebug($"Sending second RPC with {secondTest}");
method.Invoke(serverObject, new object[] { secondTest });
yield return WaitForMessageReceived<ClientRpcMessage>(m_ClientNetworkManagers.ToList());
@@ -943,9 +943,9 @@ namespace Unity.Netcode.RuntimeTests
clientObject.OnReceived = o =>
{
receivedValue = o;
Debug.Log($"Received value {o}");
VerboseDebug($"Received value {o}");
};
Debug.Log($"Sending first RPC with {firstTest}");
VerboseDebug($"Sending first RPC with {firstTest}");
method.Invoke(serverObject, new object[] { firstTest });
yield return WaitForMessageReceived<ClientRpcMessage>(m_ClientNetworkManagers.ToList());
@@ -958,7 +958,7 @@ namespace Unity.Netcode.RuntimeTests
receivedValue = null;
Debug.Log($"Sending second RPC with {secondTest}");
VerboseDebug($"Sending second RPC with {secondTest}");
method.Invoke(serverObject, new object[] { secondTest });
yield return WaitForMessageReceived<ClientRpcMessage>(m_ClientNetworkManagers.ToList());
@@ -1005,9 +1005,9 @@ namespace Unity.Netcode.RuntimeTests
Assert.AreEqual(o.GetType(), typeof(NativeArray<T>));
var oAsArray = (NativeArray<T>)o;
receivedValue = new NativeArray<T>(oAsArray, Allocator.Persistent);
Debug.Log($"Received value {o}");
VerboseDebug($"Received value {o}");
};
Debug.Log($"Sending first RPC with {firstTest}");
VerboseDebug($"Sending first RPC with {firstTest}");
method.Invoke(serverObject, new object[] { firstTest });
yield return WaitForMessageReceived<ClientRpcMessage>(m_ClientNetworkManagers.ToList());
@@ -1020,7 +1020,7 @@ namespace Unity.Netcode.RuntimeTests
receivedValue = new NativeArray<T>();
Debug.Log($"Sending second RPC with {secondTest}");
VerboseDebug($"Sending second RPC with {secondTest}");
method.Invoke(serverObject, new object[] { secondTest });
yield return WaitForMessageReceived<ClientRpcMessage>(m_ClientNetworkManagers.ToList());
@@ -1066,9 +1066,9 @@ namespace Unity.Netcode.RuntimeTests
{
receivedValue.Add(item);
}
Debug.Log($"Received value {o}");
VerboseDebug($"Received value {o}");
};
Debug.Log($"Sending first RPC with {firstTest}");
VerboseDebug($"Sending first RPC with {firstTest}");
method.Invoke(serverObject, new object[] { firstTest });
yield return WaitForMessageReceived<ClientRpcMessage>(m_ClientNetworkManagers.ToList());
@@ -1081,7 +1081,7 @@ namespace Unity.Netcode.RuntimeTests
receivedValue = new NativeList<T>();
Debug.Log($"Sending second RPC with {secondTest}");
VerboseDebug($"Sending second RPC with {secondTest}");
method.Invoke(serverObject, new object[] { secondTest });
yield return WaitForMessageReceived<ClientRpcMessage>(m_ClientNetworkManagers.ToList());

View File

@@ -80,7 +80,7 @@ namespace Unity.Netcode.RuntimeTests
private void NetworkTickSystemOnTick()
{
Debug.Log(m_Client.NetworkTickSystem.ServerTime.Tick);
//Debug.Log(m_Client.NetworkTickSystem.ServerTime.Tick);
m_ClientTickCounter++;
}
@@ -88,7 +88,7 @@ namespace Unity.Netcode.RuntimeTests
{
// client connected to server
m_ConnectedTick = m_Client.NetworkTickSystem.ServerTime.Tick;
Debug.Log($"Connected tick: {m_ConnectedTick}");
//Debug.Log($"Connected tick: {m_ConnectedTick}");
}
[UnityTearDown]

View File

@@ -1,6 +1,6 @@
#if !MULTIPLAYER_TOOLS
using System;
using System.Collections;
using System.Linq;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
@@ -13,7 +13,7 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
public class TimeIntegrationTest : NetcodeIntegrationTest
{
private const double k_AdditionalTimeTolerance = 0.3d; // magic number and in theory not needed but without this mac os test fail in Yamato because it looks like we get random framerate drops during unit test.
private const double k_AdditionalTimeTolerance = 0.3333d; // magic number and in theory not needed but without this mac os test fail in Yamato because it looks like we get random framerate drops during unit test.
private NetworkTimeState m_ServerState;
private NetworkTimeState m_Client1State;
@@ -26,17 +26,11 @@ namespace Unity.Netcode.RuntimeTests
return NetworkManagerInstatiationMode.DoNotCreate;
}
private void UpdateTimeStates(NetworkManager[] networkManagers)
private void UpdateTimeStates()
{
var server = networkManagers.First(t => t.IsServer);
var firstClient = networkManagers.First(t => t.IsClient);
var secondClient = networkManagers.Last(t => t.IsClient);
Assert.AreNotEqual(firstClient, secondClient);
m_ServerState = new NetworkTimeState(server);
m_Client1State = new NetworkTimeState(firstClient);
m_Client2State = new NetworkTimeState(secondClient);
m_ServerState = new NetworkTimeState(m_ServerNetworkManager);
m_Client1State = new NetworkTimeState(m_ClientNetworkManagers[0]);
m_Client2State = new NetworkTimeState(m_ClientNetworkManagers[1]);
}
[UnityTest]
@@ -61,26 +55,21 @@ namespace Unity.Netcode.RuntimeTests
double frameInterval = 1d / targetFrameRate;
double tickInterval = 1d / tickRate;
var networkManagers = NetcodeIntegrationTestHelpers.NetworkManagerInstances.ToArray();
var server = networkManagers.First(t => t.IsServer);
var firstClient = networkManagers.First(t => !t.IsServer);
var secondClient = networkManagers.Last(t => !t.IsServer);
Assert.AreNotEqual(firstClient, secondClient);
var server = m_ServerNetworkManager;
var firstClient = m_ClientNetworkManagers[0];
var secondClient = m_ClientNetworkManagers[1];
// increase the buffer time of client 2 // the values for client 1 are 0.0333/0.0333 here
secondClient.NetworkTimeSystem.LocalBufferSec = 0.2;
secondClient.NetworkTimeSystem.ServerBufferSec = 0.1;
UpdateTimeStates(networkManagers);
// wait for at least one tick to pass
yield return new WaitUntil(() => m_ServerState.LocalTime.Tick != server.NetworkTickSystem.LocalTime.Tick);
yield return new WaitUntil(() => m_Client1State.LocalTime.Tick != firstClient.NetworkTickSystem.LocalTime.Tick);
yield return new WaitUntil(() => m_Client2State.LocalTime.Tick != secondClient.NetworkTickSystem.LocalTime.Tick);
UpdateTimeStates();
// wait for a few ticks to pass
for (int i = 0; i < 4; i++)
{
yield return s_DefaultWaitForTick;
}
var framesToRun = 3d / frameInterval;
@@ -103,7 +92,7 @@ namespace Unity.Netcode.RuntimeTests
currentAdjustment += additionalTimeTolerance * fpsAdjustment;
}
UpdateTimeStates(networkManagers);
UpdateTimeStates();
// compares whether client times have the correct offset to server
m_ServerState.AssertCheckDifference(m_Client1State, tickInterval, tickInterval, tickInterval * 2 + frameInterval * 2 + currentAdjustment);
@@ -211,3 +200,4 @@ namespace Unity.Netcode.RuntimeTests
}
}
}
#endif

View File

@@ -1,3 +1,4 @@
#if !MULTIPLAYER_TOOLS
using System.Collections;
using NUnit.Framework;
using Unity.Netcode.Components;
@@ -221,8 +222,9 @@ namespace Unity.Netcode.RuntimeTests
// and how they move
var timeOutHelper = new TimeoutFrameCountHelper(10);
yield return WaitForConditionOrTimeOut(spawnedObjectNetworkTransform.ReachedTargetLocalSpaceTransitionCount, timeOutHelper);
Debug.Log($"[TransformInterpolationTest] Wait condition reached or timed out. Frame Count ({timeOutHelper.GetFrameCount()}) | Time Elapsed ({timeOutHelper.GetTimeElapsed()})");
VerboseDebug($"[TransformInterpolationTest] Wait condition reached or timed out. Frame Count ({timeOutHelper.GetFrameCount()}) | Time Elapsed ({timeOutHelper.GetTimeElapsed()})");
AssertOnTimeout($"Failed to reach desired local to world space transitions in the given time!", timeOutHelper);
}
}
}
#endif

View File

@@ -1,3 +1,4 @@
#if !MULTIPLAYER_TOOLS && !NGO_MINIMALPROJECT
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -24,6 +25,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
public string Received = string.Empty;
public Tuple<int, bool, float, string> ReceivedParams = null;
public ulong ReceivedFrom = ulong.MaxValue;
public int ReceivedCount;
public void OnRpcReceived()
{
@@ -32,6 +34,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
var currentMethod = sf.GetMethod();
Received = currentMethod.Name;
ReceivedCount++;
}
public void OnRpcReceivedWithParams(int a, bool b, float f, string s)
{
@@ -40,6 +43,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
var currentMethod = sf.GetMethod();
Received = currentMethod.Name;
ReceivedCount++;
ReceivedParams = new Tuple<int, bool, float, string>(a, b, f, s);
}
@@ -93,6 +97,18 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
OnRpcReceived();
}
[Rpc(SendTo.Authority)]
public void DefaultToAuthorityRpc()
{
OnRpcReceived();
}
[Rpc(SendTo.NotAuthority)]
public void DefaultToNotAuthorityRpc()
{
OnRpcReceived();
}
// RPCs with parameters
[Rpc(SendTo.Everyone)]
@@ -143,6 +159,18 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
OnRpcReceivedWithParams(i, b, f, s);
}
[Rpc(SendTo.Authority)]
public void DefaultToAuthorityWithParamsRpc(int i, bool b, float f, string s)
{
OnRpcReceivedWithParams(i, b, f, s);
}
[Rpc(SendTo.NotAuthority)]
public void DefaultToNotAuthorityWithParamsRpc(int i, bool b, float f, string s)
{
OnRpcReceivedWithParams(i, b, f, s);
}
// RPCs with RPC parameters
[Rpc(SendTo.Everyone)]
@@ -201,6 +229,19 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
ReceivedFrom = rpcParams.Receive.SenderClientId;
}
[Rpc(SendTo.Authority)]
public void DefaultToAuthorityWithRpcParamsRpc(RpcParams rpcParams)
{
OnRpcReceived();
ReceivedFrom = rpcParams.Receive.SenderClientId;
}
[Rpc(SendTo.NotAuthority)]
public void DefaultToNotAuthorityWithRpcParamsRpc(RpcParams rpcParams)
{
OnRpcReceived();
ReceivedFrom = rpcParams.Receive.SenderClientId;
}
// RPCs with parameters and RPC parameters
@@ -252,6 +293,18 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
OnRpcReceivedWithParams(i, b, f, s);
}
[Rpc(SendTo.Authority)]
public void DefaultToAuthorityWithParamsAndRpcParamsRpc(int i, bool b, float f, string s, RpcParams rpcParams)
{
OnRpcReceivedWithParams(i, b, f, s);
}
[Rpc(SendTo.NotAuthority)]
public void DefaultToNotAuthorityWithParamsAndRpcParamsRpc(int i, bool b, float f, string s, RpcParams rpcParams)
{
OnRpcReceivedWithParams(i, b, f, s);
}
// RPCs with AllowTargetOverride = true
// AllowTargetOverried is implied with SpecifiedInParams and does not need to be stated
@@ -310,6 +363,18 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
OnRpcReceived();
}
[Rpc(SendTo.Authority, AllowTargetOverride = true)]
public void DefaultToAuthorityAllowOverrideRpc(RpcParams rpcParams)
{
OnRpcReceived();
}
[Rpc(SendTo.NotAuthority, AllowTargetOverride = true)]
public void DefaultToNotAuthorityAllowOverrideRpc(RpcParams rpcParams)
{
OnRpcReceived();
}
// RPCs with DeferLocal = true
[Rpc(SendTo.Everyone, DeferLocal = true)]
@@ -354,6 +419,18 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
OnRpcReceived();
}
[Rpc(SendTo.Authority, DeferLocal = true)]
public void DefaultToAuthorityDeferLocalRpc(RpcParams rpcParams)
{
OnRpcReceived();
}
[Rpc(SendTo.NotAuthority, DeferLocal = true)]
public void DefaultToNotAuthorityDeferLocalRpc(RpcParams rpcParams)
{
OnRpcReceived();
}
// RPCs with RequireOwnership = true
[Rpc(SendTo.Everyone, RequireOwnership = true)]
@@ -410,6 +487,17 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
OnRpcReceived();
}
[Rpc(SendTo.Authority, RequireOwnership = true)]
public void DefaultToAuthorityRequireOwnershipRpc()
{
OnRpcReceived();
}
[Rpc(SendTo.NotAuthority, RequireOwnership = true)]
public void DefaultToNotAuthorityRequireOwnershipRpc()
{
OnRpcReceived();
}
// Mutual RPC Recursion
@@ -496,6 +584,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
foreach (var obj in Object.FindObjectsByType<UniversalRpcNetworkBehaviour>(FindObjectsSortMode.None))
{
obj.Received = string.Empty;
obj.ReceivedCount = 0;
obj.ReceivedParams = null;
obj.ReceivedFrom = ulong.MaxValue;
}
@@ -528,10 +617,11 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
return m_PlayerNetworkObjects[onClient][ownerClientId].GetComponent<UniversalRpcNetworkBehaviour>();
}
protected void VerifyLocalReceived(ulong objectOwner, ulong sender, string name, bool verifyReceivedFrom)
protected void VerifyLocalReceived(ulong objectOwner, ulong sender, string name, bool verifyReceivedFrom, int expectedReceived = 1)
{
var obj = GetPlayerObject(objectOwner, sender);
Assert.AreEqual(name, obj.Received);
Assert.That(obj.ReceivedCount, Is.EqualTo(expectedReceived));
Assert.IsNull(obj.ReceivedParams);
if (verifyReceivedFrom)
{
@@ -543,6 +633,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
{
var obj = GetPlayerObject(objectOwner, sender);
Assert.AreEqual(name, obj.Received);
Assert.That(obj.ReceivedCount, Is.EqualTo(1));
Assert.IsNotNull(obj.ReceivedParams);
Assert.AreEqual(i, obj.ReceivedParams.Item1);
Assert.AreEqual(b, obj.ReceivedParams.Item2);
@@ -556,17 +647,18 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
{
UniversalRpcNetworkBehaviour playerObject = GetPlayerObject(objectOwner, client);
Assert.AreEqual(string.Empty, playerObject.Received);
Assert.That(playerObject.ReceivedCount, Is.EqualTo(0));
Assert.IsNull(playerObject.ReceivedParams);
}
}
protected void VerifyRemoteReceived(ulong objectOwner, ulong sender, string message, ulong[] receivedBy, bool verifyReceivedFrom, bool waitForMessages = true)
protected void VerifyRemoteReceived(ulong objectOwner, ulong sender, string message, ulong[] receivedBy, bool verifyReceivedFrom, bool waitForMessages = true, int expectedReceived = 1)
{
foreach (var client in receivedBy)
{
if (client == sender)
{
VerifyLocalReceived(objectOwner, sender, message, verifyReceivedFrom);
VerifyLocalReceived(objectOwner, sender, message, verifyReceivedFrom, expectedReceived);
break;
}
@@ -628,6 +720,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
{
UniversalRpcNetworkBehaviour playerObject = GetPlayerObject(objectOwner, client);
Assert.AreEqual(message, playerObject.Received);
Assert.That(playerObject.ReceivedCount, Is.EqualTo(expectedReceived));
Assert.IsNull(playerObject.ReceivedParams);
if (verifyReceivedFrom)
{
@@ -701,6 +794,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
{
UniversalRpcNetworkBehaviour playerObject = GetPlayerObject(objectOwner, client);
Assert.AreEqual(message, playerObject.Received);
Assert.That(playerObject.ReceivedCount, Is.EqualTo(1));
Assert.IsNotNull(playerObject.ReceivedParams);
Assert.AreEqual(i, playerObject.ReceivedParams.Item1);
@@ -805,6 +899,26 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
VerifySentToNotId(objectOwner, sender, sender, methodName, false);
}
public void VerifySentToAuthority(ulong objectOwner, ulong sender, string methodName)
{
var receiver = objectOwner;
if (!m_DistributedAuthority)
{
receiver = NetworkManager.ServerClientId;
}
VerifySentToId(objectOwner, sender, receiver, methodName, false);
}
public void VerifySentToNotAuthority(ulong objectOwner, ulong sender, string methodName)
{
var receiver = objectOwner;
if (!m_DistributedAuthority)
{
receiver = NetworkManager.ServerClientId;
}
VerifySentToNotId(objectOwner, sender, receiver, methodName, false);
}
public void VerifySentToOwnerWithReceivedFrom(ulong objectOwner, ulong sender, string methodName)
{
VerifySentToId(objectOwner, sender, objectOwner, methodName, true);
@@ -847,6 +961,26 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
VerifySentToNotId(objectOwner, sender, sender, methodName, true);
}
public void VerifySentToAuthorityWithReceivedFrom(ulong objectOwner, ulong sender, string methodName)
{
var receiver = objectOwner;
if (!m_DistributedAuthority)
{
receiver = NetworkManager.ServerClientId;
}
VerifySentToId(objectOwner, sender, receiver, methodName, true);
}
public void VerifySentToNotAuthorityWithReceivedFrom(ulong objectOwner, ulong sender, string methodName)
{
var receiver = objectOwner;
if (!m_DistributedAuthority)
{
receiver = NetworkManager.ServerClientId;
}
VerifySentToNotId(objectOwner, sender, receiver, methodName, true);
}
public void VerifySentToOwnerWithParams(ulong objectOwner, ulong sender, string methodName, int i, bool b, float f, string s)
{
VerifySentToIdWithParams(objectOwner, sender, objectOwner, methodName, i, b, f, s);
@@ -889,6 +1023,26 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
VerifySentToNotIdWithParams(objectOwner, sender, sender, methodName, i, b, f, s);
}
public void VerifySentToAuthorityWithParams(ulong objectOwner, ulong sender, string methodName, int i, bool b, float f, string s)
{
var receiver = objectOwner;
if (!m_DistributedAuthority)
{
receiver = NetworkManager.ServerClientId;
}
VerifySentToIdWithParams(objectOwner, sender, receiver, methodName, i, b, f, s);
}
public void VerifySentToNotAuthorityWithParams(ulong objectOwner, ulong sender, string methodName, int i, bool b, float f, string s)
{
var receiver = objectOwner;
if (!m_DistributedAuthority)
{
receiver = NetworkManager.ServerClientId;
}
VerifySentToNotIdWithParams(objectOwner, sender, receiver, methodName, i, b, f, s);
}
public void RethrowTargetInvocationException(Action action)
{
try
@@ -902,6 +1056,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
internal class UniversalRpcTestSendingNoOverride : UniversalRpcTestsBase
@@ -914,7 +1069,8 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
[Test]
public void TestSendingNoOverride(
// Excludes SendTo.SpecifiedInParams
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost)] SendTo sendTo,
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer,
SendTo.ClientsAndHost, SendTo.Authority, SendTo.NotAuthority)] SendTo sendTo,
[Values(0u, 1u, 2u)] ulong objectOwner,
[Values(0u, 1u, 2u)] ulong sender
)
@@ -932,6 +1088,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
internal class UniversalRpcTestSenderClientId : UniversalRpcTestsBase
@@ -944,7 +1101,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
[Test]
public void TestSenderClientId(
// Excludes SendTo.SpecifiedInParams
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost)] SendTo sendTo,
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost, SendTo.Authority, SendTo.NotAuthority)] SendTo sendTo,
[Values(0u, 1u, 2u)] ulong objectOwner,
[Values(0u, 1u, 2u)] ulong sender
)
@@ -962,6 +1119,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
internal class UniversalRpcTestSendingNoOverrideWithParams : UniversalRpcTestsBase
@@ -974,7 +1132,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
[Test]
public void TestSendingNoOverrideWithParams(
// Excludes SendTo.SpecifiedInParams
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost)] SendTo sendTo,
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost, SendTo.Authority, SendTo.NotAuthority)] SendTo sendTo,
[Values(0u, 1u, 2u)] ulong objectOwner,
[Values(0u, 1u, 2u)] ulong sender
)
@@ -1004,6 +1162,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
internal class UniversalRpcTestSendingNoOverrideWithParamsAndRpcParams : UniversalRpcTestsBase
@@ -1016,7 +1175,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
[Test]
public void TestSendingNoOverrideWithParamsAndRpcParams(
// Excludes SendTo.SpecifiedInParams
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost)] SendTo sendTo,
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost, SendTo.Authority, SendTo.NotAuthority)] SendTo sendTo,
[Values(0u, 1u, 2u)] ulong objectOwner,
[Values(0u, 1u, 2u)] ulong sender
)
@@ -1046,6 +1205,8 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
internal class UniversalRpcTestRequireOwnership : UniversalRpcTestsBase
@@ -1058,7 +1219,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
[Test]
public void TestRequireOwnership(
// Excludes SendTo.SpecifiedInParams
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost)] SendTo sendTo,
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost, SendTo.Authority, SendTo.NotAuthority)] SendTo sendTo,
[Values(0u, 1u, 2u)] ulong objectOwner,
[Values(0u, 1u, 2u)] ulong sender
)
@@ -1082,6 +1243,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
internal class UniversalRpcTestDisallowedOverride : UniversalRpcTestsBase
@@ -1091,10 +1253,11 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
// Add both authority and nonauthority
[Test]
public void TestDisallowedOverride(
// Excludes SendTo.SpecifiedInParams
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost)] SendTo sendTo,
[Values(SendTo.Everyone, SendTo.Me, SendTo.Owner, SendTo.Server, SendTo.NotMe, SendTo.NotOwner, SendTo.NotServer, SendTo.ClientsAndHost, SendTo.Authority, SendTo.NotAuthority)] SendTo sendTo,
[Values(0u, 1u, 2u)] ulong objectOwner,
[Values(0u, 1u, 2u)] ulong sender)
{
@@ -1126,6 +1289,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
// Look at the implementations and add both
[Test]
public void TestSendingWithTargetOverride(
[Values] SendTo defaultSendTo,
@@ -1382,6 +1546,7 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
}
[TestFixture(HostOrServer.DAHost)]
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
internal class UniversalRpcTestDeferLocal : UniversalRpcTestsBase
@@ -1438,6 +1603,15 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
[TestCase(SendTo.ClientsAndHost, 2u, 0u)]
[TestCase(SendTo.ClientsAndHost, 2u, 1u)]
[TestCase(SendTo.ClientsAndHost, 2u, 2u)]
[TestCase(SendTo.Authority, 0u, 0u)]
[TestCase(SendTo.Authority, 1u, 1u)]
[TestCase(SendTo.Authority, 2u, 2u)]
[TestCase(SendTo.NotAuthority, 0u, 1u)]
[TestCase(SendTo.NotAuthority, 0u, 2u)]
[TestCase(SendTo.NotAuthority, 1u, 0u)]
[TestCase(SendTo.NotAuthority, 1u, 2u)]
[TestCase(SendTo.NotAuthority, 2u, 0u)]
[TestCase(SendTo.NotAuthority, 2u, 1u)]
public void TestDeferLocal(
SendTo defaultSendTo,
ulong objectOwner,
@@ -1450,6 +1624,13 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
// Just consider this case a success...
return;
}
// Similar to above, since Server and NotServer are already tested we can consider this a success
if (!m_DistributedAuthority && defaultSendTo == SendTo.Authority || defaultSendTo == SendTo.NotAuthority)
{
return;
}
var sendMethodName = $"DefaultTo{defaultSendTo}DeferLocalRpc";
var verifyMethodName = $"VerifySentTo{defaultSendTo}";
var senderObject = GetPlayerObject(objectOwner, sender);
@@ -1512,6 +1693,15 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
[TestCase(SendTo.ClientsAndHost, 2u, 0u)]
[TestCase(SendTo.ClientsAndHost, 2u, 1u)]
[TestCase(SendTo.ClientsAndHost, 2u, 2u)]
[TestCase(SendTo.Authority, 0u, 0u)]
[TestCase(SendTo.Authority, 1u, 1u)]
[TestCase(SendTo.Authority, 2u, 2u)]
[TestCase(SendTo.NotAuthority, 0u, 1u)]
[TestCase(SendTo.NotAuthority, 0u, 2u)]
[TestCase(SendTo.NotAuthority, 1u, 0u)]
[TestCase(SendTo.NotAuthority, 1u, 2u)]
[TestCase(SendTo.NotAuthority, 2u, 0u)]
[TestCase(SendTo.NotAuthority, 2u, 1u)]
public void TestDeferLocalOverrideToTrue(
SendTo defaultSendTo,
ulong objectOwner,
@@ -1524,6 +1714,12 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
// Just consider this case a success...
return;
}
// Similar to above, since Server and NotServer are already tested we can consider this a success
if (!m_DistributedAuthority && defaultSendTo == SendTo.Authority || defaultSendTo == SendTo.NotAuthority)
{
return;
}
var sendMethodName = $"DefaultTo{defaultSendTo}WithRpcParamsRpc";
var verifyMethodName = $"VerifySentTo{defaultSendTo}";
var senderObject = GetPlayerObject(objectOwner, sender);
@@ -1586,6 +1782,15 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
[TestCase(SendTo.ClientsAndHost, 2u, 0u)]
[TestCase(SendTo.ClientsAndHost, 2u, 1u)]
[TestCase(SendTo.ClientsAndHost, 2u, 2u)]
[TestCase(SendTo.Authority, 0u, 0u)]
[TestCase(SendTo.Authority, 1u, 1u)]
[TestCase(SendTo.Authority, 2u, 2u)]
[TestCase(SendTo.NotAuthority, 0u, 1u)]
[TestCase(SendTo.NotAuthority, 0u, 2u)]
[TestCase(SendTo.NotAuthority, 1u, 0u)]
[TestCase(SendTo.NotAuthority, 1u, 2u)]
[TestCase(SendTo.NotAuthority, 2u, 0u)]
[TestCase(SendTo.NotAuthority, 2u, 1u)]
public void TestDeferLocalOverrideToFalse(
SendTo defaultSendTo,
ulong objectOwner,
@@ -1598,6 +1803,12 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
// Just consider this case a success...
return;
}
// Similar to above, since Server and NotServer are already tested we can consider this a success
if (!m_DistributedAuthority && defaultSendTo == SendTo.Authority || defaultSendTo == SendTo.NotAuthority)
{
return;
}
var sendMethodName = $"DefaultTo{defaultSendTo}DeferLocalRpc";
var verifyMethodName = $"VerifySentTo{defaultSendTo}";
var senderObject = GetPlayerObject(objectOwner, sender);
@@ -1636,17 +1847,20 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
VerifyNotReceived(NetworkManager.ServerClientId, s_ClientIds);
for (var i = 0; i < 10; ++i)
var clientListExpected = 1;
var serverListExpected = 2;
for (var i = 1; i <= 10; ++i)
{
WaitForMessageReceivedWithTimeTravel<RpcMessage>(clientList);
VerifyRemoteReceived(NetworkManager.ServerClientId, NetworkManager.ServerClientId, nameof(UniversalRpcNetworkBehaviour.MutualRecursionClientRpc), clientIdArray, false, false);
VerifyRemoteReceived(NetworkManager.ServerClientId, NetworkManager.ServerClientId, nameof(UniversalRpcNetworkBehaviour.MutualRecursionClientRpc), clientIdArray, false, false, clientListExpected);
VerifyNotReceived(NetworkManager.ServerClientId, serverIdArray);
clientListExpected *= 2;
Clear();
WaitForMessageReceivedWithTimeTravel<RpcMessage>(serverList);
VerifyRemoteReceived(NetworkManager.ServerClientId, NetworkManager.ServerClientId, nameof(UniversalRpcNetworkBehaviour.MutualRecursionServerRpc), serverIdArray, false, false);
VerifyRemoteReceived(NetworkManager.ServerClientId, NetworkManager.ServerClientId, nameof(UniversalRpcNetworkBehaviour.MutualRecursionServerRpc), serverIdArray, false, false, serverListExpected);
VerifyNotReceived(NetworkManager.ServerClientId, clientIdArray);
serverListExpected *= 2;
Clear();
}
@@ -1915,3 +2129,4 @@ namespace Unity.Netcode.RuntimeTests.UniversalRpcTests
}
}
#endif

View File

@@ -12,12 +12,20 @@
"Unity.Networking.Transport",
"ClientNetworkTransform",
"Unity.Netcode.TestHelpers.Runtime",
"Unity.Mathematics"
"Unity.Mathematics",
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"optionalUnityReferences": [
"TestAssemblies"
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS",
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [
@@ -46,5 +54,6 @@
"expression": "2.0.0-exp",
"define": "UTP_TRANSPORT_2_0_ABOVE"
}
]
}
],
"noEngineReferences": false
}