com.unity.netcode.gameobjects@1.1.0

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

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

## [1.1.0] - 2022-10-21

### Added

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

### Changed

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

### Fixed

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

View File

@@ -48,7 +48,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
public JobTypes JobType;
public string SceneName;
public Scene Scene;
public ISceneManagerHandler.SceneEventAction SceneAction;
public SceneEventProgress SceneEventProgress;
public IntegrationTestSceneHandler IntegrationTestSceneHandler;
}
@@ -106,7 +106,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
// We always load additively for all scenes during integration tests
SceneManager.LoadSceneAsync(queuedSceneJob.SceneName, LoadSceneMode.Additive);
var asyncOperation = SceneManager.LoadSceneAsync(queuedSceneJob.SceneName, LoadSceneMode.Additive);
queuedSceneJob.SceneEventProgress.SetAsyncOperation(asyncOperation);
// Wait for it to finish
while (queuedSceneJob.JobType != QueuedSceneJob.JobTypes.Completed)
@@ -114,7 +115,6 @@ namespace Unity.Netcode.TestHelpers.Runtime
yield return s_WaitForSeconds;
}
yield return s_WaitForSeconds;
CurrentQueuedSceneJob.SceneAction.Invoke();
}
/// <summary>
@@ -176,7 +176,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
SceneManager.sceneUnloaded += SceneManager_sceneUnloaded;
if (queuedSceneJob.Scene.IsValid() && queuedSceneJob.Scene.isLoaded && !queuedSceneJob.Scene.name.Contains(NetcodeIntegrationTestHelpers.FirstPartOfTestRunnerSceneName))
{
SceneManager.UnloadSceneAsync(queuedSceneJob.Scene);
var asyncOperation = SceneManager.UnloadSceneAsync(queuedSceneJob.Scene);
queuedSceneJob.SceneEventProgress.SetAsyncOperation(asyncOperation);
}
else
{
@@ -188,7 +189,6 @@ namespace Unity.Netcode.TestHelpers.Runtime
{
yield return s_WaitForSeconds;
}
CurrentQueuedSceneJob.SceneAction.Invoke();
}
/// <summary>
@@ -246,7 +246,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
/// <summary>
/// Server always loads like it normally would
/// </summary>
public AsyncOperation GenericLoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, ISceneManagerHandler.SceneEventAction sceneEventAction)
public AsyncOperation GenericLoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventProgress sceneEventProgress)
{
m_ServerSceneBeingLoaded = sceneName;
if (NetcodeIntegrationTest.IsRunning)
@@ -254,8 +254,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
SceneManager.sceneLoaded += Sever_SceneLoaded;
}
var operation = SceneManager.LoadSceneAsync(sceneName, loadSceneMode);
operation.completed += new Action<AsyncOperation>(asyncOp2 => { sceneEventAction.Invoke(); });
sceneEventProgress.SetAsyncOperation(operation);
return operation;
}
@@ -271,39 +270,39 @@ namespace Unity.Netcode.TestHelpers.Runtime
/// <summary>
/// Server always unloads like it normally would
/// </summary>
public AsyncOperation GenericUnloadSceneAsync(Scene scene, ISceneManagerHandler.SceneEventAction sceneEventAction)
public AsyncOperation GenericUnloadSceneAsync(Scene scene, SceneEventProgress sceneEventProgress)
{
var operation = SceneManager.UnloadSceneAsync(scene);
operation.completed += new Action<AsyncOperation>(asyncOp2 => { sceneEventAction.Invoke(); });
sceneEventProgress.SetAsyncOperation(operation);
return operation;
}
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, ISceneManagerHandler.SceneEventAction sceneEventAction)
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventProgress sceneEventProgress)
{
// Server and non NetcodeIntegrationTest tests use the generic load scene method
if (!NetcodeIntegrationTest.IsRunning)
{
return GenericLoadSceneAsync(sceneName, loadSceneMode, sceneEventAction);
return GenericLoadSceneAsync(sceneName, loadSceneMode, sceneEventProgress);
}
else // NetcodeIntegrationTest Clients always get added to the jobs queue
{
AddJobToQueue(new QueuedSceneJob() { IntegrationTestSceneHandler = this, SceneName = sceneName, SceneAction = sceneEventAction, JobType = QueuedSceneJob.JobTypes.Loading });
AddJobToQueue(new QueuedSceneJob() { IntegrationTestSceneHandler = this, SceneName = sceneName, SceneEventProgress = sceneEventProgress, JobType = QueuedSceneJob.JobTypes.Loading });
}
return null;
}
public AsyncOperation UnloadSceneAsync(Scene scene, ISceneManagerHandler.SceneEventAction sceneEventAction)
public AsyncOperation UnloadSceneAsync(Scene scene, SceneEventProgress sceneEventProgress)
{
// Server and non NetcodeIntegrationTest tests use the generic unload scene method
if (!NetcodeIntegrationTest.IsRunning)
{
return GenericUnloadSceneAsync(scene, sceneEventAction);
return GenericUnloadSceneAsync(scene, sceneEventProgress);
}
else // NetcodeIntegrationTest Clients always get added to the jobs queue
{
AddJobToQueue(new QueuedSceneJob() { IntegrationTestSceneHandler = this, Scene = scene, SceneAction = sceneEventAction, JobType = QueuedSceneJob.JobTypes.Unloading });
AddJobToQueue(new QueuedSceneJob() { IntegrationTestSceneHandler = this, Scene = scene, SceneEventProgress = sceneEventProgress, JobType = QueuedSceneJob.JobTypes.Unloading });
}
// This is OK to return a "nothing" AsyncOperation since we are simulating client loading
return null;

View File

@@ -4,14 +4,22 @@ namespace Unity.Netcode.TestHelpers.Runtime
{
internal class MessageHooks : INetworkHooks
{
public bool IsWaiting;
public delegate bool MessageReceiptCheck(object receivedMessage);
public bool IsWaiting = true;
public delegate bool MessageReceiptCheck(Type receivedMessageType);
public MessageReceiptCheck ReceiptCheck;
public delegate bool MessageHandleCheck(object receivedMessage);
public MessageHandleCheck HandleCheck;
public static bool CheckForMessageOfType<T>(object receivedMessage) where T : INetworkMessage
public static bool CurrentMessageHasTriggerdAHook = false;
public static bool CheckForMessageOfTypeHandled<T>(object receivedMessage) where T : INetworkMessage
{
return receivedMessage is T;
}
public static bool CheckForMessageOfTypeReceived<T>(Type receivedMessageType) where T : INetworkMessage
{
return receivedMessageType == typeof(T);
}
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
{
@@ -23,10 +31,24 @@ namespace Unity.Netcode.TestHelpers.Runtime
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
// The way the system works, it goes through all hooks and calls OnBeforeHandleMessage, then handles the message,
// then goes thorugh all hooks and calls OnAfterHandleMessage.
// This ensures each message only manages to activate a single message hook - because we know that only
// one message will ever be handled between OnBeforeHandleMessage and OnAfterHandleMessage,
// we can reset the flag here, and then in OnAfterHandleMessage, the moment the message matches a hook,
// it'll flip this flag back on, and then other hooks will stop checking that message.
// Without this flag, waiting for 10 messages of the same type isn't possible - all 10 hooks would get
// tripped by the first message.
CurrentMessageHasTriggerdAHook = false;
}
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
{
if (!CurrentMessageHasTriggerdAHook && IsWaiting && (HandleCheck == null || HandleCheck.Invoke(messageType)))
{
IsWaiting = false;
CurrentMessageHasTriggerdAHook = true;
}
}
public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
@@ -57,13 +79,23 @@ namespace Unity.Netcode.TestHelpers.Runtime
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
// The way the system works, it goes through all hooks and calls OnBeforeHandleMessage, then handles the message,
// then goes thorugh all hooks and calls OnAfterHandleMessage.
// This ensures each message only manages to activate a single message hook - because we know that only
// one message will ever be handled between OnBeforeHandleMessage and OnAfterHandleMessage,
// we can reset the flag here, and then in OnAfterHandleMessage, the moment the message matches a hook,
// it'll flip this flag back on, and then other hooks will stop checking that message.
// Without this flag, waiting for 10 messages of the same type isn't possible - all 10 hooks would get
// tripped by the first message.
CurrentMessageHasTriggerdAHook = false;
}
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
{
if (IsWaiting && (ReceiptCheck == null || ReceiptCheck.Invoke(message)))
if (!CurrentMessageHasTriggerdAHook && IsWaiting && (HandleCheck == null || HandleCheck.Invoke(message)))
{
IsWaiting = false;
CurrentMessageHasTriggerdAHook = true;
}
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
@@ -63,18 +64,34 @@ namespace Unity.Netcode.TestHelpers.Runtime
}
}
public enum ReceiptType
{
Received,
Handled
}
public class MessageHookEntry
{
internal MessageHooks MessageHooks;
protected NetworkManager m_NetworkManager;
private MessageHooks.MessageReceiptCheck m_MessageReceiptCheck;
private MessageHooks.MessageHandleCheck m_MessageHandleCheck;
internal string MessageType;
private ReceiptType m_ReceiptType;
public void Initialize()
{
Assert.IsNotNull(m_MessageReceiptCheck, $"{nameof(m_MessageReceiptCheck)} is null, did you forget to initialize?");
MessageHooks = new MessageHooks();
MessageHooks.ReceiptCheck = m_MessageReceiptCheck;
if (m_ReceiptType == ReceiptType.Handled)
{
Assert.IsNotNull(m_MessageHandleCheck, $"{nameof(m_MessageHandleCheck)} is null, did you forget to initialize?");
MessageHooks.HandleCheck = m_MessageHandleCheck;
}
else
{
Assert.IsNotNull(m_MessageReceiptCheck, $"{nameof(m_MessageReceiptCheck)} is null, did you forget to initialize?");
MessageHooks.ReceiptCheck = m_MessageReceiptCheck;
}
Assert.IsNotNull(m_NetworkManager.MessagingSystem, $"{nameof(NetworkManager.MessagingSystem)} is null! Did you forget to start first?");
m_NetworkManager.MessagingSystem.Hook(MessageHooks);
}
@@ -82,14 +99,41 @@ namespace Unity.Netcode.TestHelpers.Runtime
internal void AssignMessageType<T>() where T : INetworkMessage
{
MessageType = typeof(T).Name;
m_MessageReceiptCheck = MessageHooks.CheckForMessageOfType<T>;
if (m_ReceiptType == ReceiptType.Handled)
{
m_MessageHandleCheck = MessageHooks.CheckForMessageOfTypeHandled<T>;
}
else
{
m_MessageReceiptCheck = MessageHooks.CheckForMessageOfTypeReceived<T>;
}
Initialize();
}
public MessageHookEntry(NetworkManager networkManager)
internal void AssignMessageType(Type type)
{
MessageType = type.Name;
if (m_ReceiptType == ReceiptType.Handled)
{
m_MessageHandleCheck = (message) =>
{
return message.GetType() == type;
};
}
else
{
m_MessageReceiptCheck = (messageType) =>
{
return messageType == type;
};
}
Initialize();
}
public MessageHookEntry(NetworkManager networkManager, ReceiptType type = ReceiptType.Handled)
{
m_NetworkManager = networkManager;
m_ReceiptType = type;
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Unity.Netcode.TestHelpers.Runtime.Metrics
}
[ClientRpc]
public void MyClientRpc()
public void MyClientRpc(ClientRpcParams rpcParams = default)
{
OnClientRpcAction?.Invoke();
}

View File

@@ -1,10 +1,6 @@
#if MULTIPLAYER_TOOLS
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NUnit.Framework;
using Unity.Multiplayer.Tools.MetricTypes;
using Unity.Multiplayer.Tools.NetStats;
@@ -12,10 +8,11 @@ namespace Unity.Netcode.TestHelpers.Runtime.Metrics
{
internal class WaitForEventMetricValues<TMetric> : WaitForMetricValues<TMetric>
{
IReadOnlyCollection<TMetric> m_EventValues;
private IReadOnlyCollection<TMetric> m_EventValues;
public delegate bool EventFilter(TMetric metric);
EventFilter m_EventFilterDelegate;
private EventFilter m_EventFilterDelegate;
public WaitForEventMetricValues(IMetricDispatcher dispatcher, DirectionalMetricInfo directionalMetricName)
: base(dispatcher, directionalMetricName)

View File

@@ -7,7 +7,7 @@ using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
using System.Runtime.CompilerServices;
using Unity.Netcode.RuntimeTests;
using Object = UnityEngine.Object;
namespace Unity.Netcode.TestHelpers.Runtime
@@ -23,7 +23,9 @@ namespace Unity.Netcode.TestHelpers.Runtime
/// </summary>
internal static bool IsRunning { get; private set; }
protected static TimeoutHelper s_GlobalTimeoutHelper = new TimeoutHelper(8.0f);
protected static WaitForSeconds s_DefaultWaitForTick = new WaitForSeconds(1.0f / k_DefaultTickRate);
protected static WaitForSecondsRealtime s_DefaultWaitForTick = new WaitForSecondsRealtime(1.0f / k_DefaultTickRate);
public NetcodeLogAssert NetcodeLogAssert;
/// <summary>
/// Registered list of all NetworkObjects spawned.
@@ -129,6 +131,18 @@ namespace Unity.Netcode.TestHelpers.Runtime
protected bool m_EnableVerboseDebug { get; set; }
/// <summary>
/// When set to true, this will bypass the entire
/// wait for clients to connect process.
/// </summary>
/// <remarks>
/// CAUTION:
/// Setting this to true will bypass other helper
/// identification related code, so this should only
/// be used for connection failure oriented testing
/// </remarks>
protected bool m_BypassConnectionTimeout { get; set; }
/// <summary>
/// Used to display the various integration test
/// stages and can be used to log verbose information
@@ -207,6 +221,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
{
VerboseDebug($"Entering {nameof(SetUp)}");
NetcodeLogAssert = new NetcodeLogAssert();
yield return OnSetup();
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests && m_ServerNetworkManager == null ||
m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.PerTest)
@@ -336,7 +351,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
if (m_ServerNetworkManager != null)
{
s_DefaultWaitForTick = new WaitForSeconds(1.0f / m_ServerNetworkManager.NetworkConfig.TickRate);
s_DefaultWaitForTick = new WaitForSecondsRealtime(1.0f / m_ServerNetworkManager.NetworkConfig.TickRate);
}
// Set the player prefab for the server and clients
@@ -452,31 +467,36 @@ namespace Unity.Netcode.TestHelpers.Runtime
// Notification that the server and clients have been started
yield return OnStartedServerAndClients();
// Wait for all clients to connect
yield return WaitForClientsConnectedOrTimeOut();
AssertOnTimeout($"{nameof(StartServerAndClients)} timed out waiting for all clients to be connected!");
if (m_UseHost || m_ServerNetworkManager.IsHost)
// When true, we skip everything else (most likely a connection oriented test)
if (!m_BypassConnectionTimeout)
{
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
var serverPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
foreach (var playerNetworkObject in serverPlayerClones)
// Wait for all clients to connect
yield return WaitForClientsConnectedOrTimeOut();
AssertOnTimeout($"{nameof(StartServerAndClients)} timed out waiting for all clients to be connected!");
if (m_UseHost || m_ServerNetworkManager.IsHost)
{
if (!m_PlayerNetworkObjects.ContainsKey(playerNetworkObject.NetworkManager.LocalClientId))
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
var serverPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
foreach (var playerNetworkObject in serverPlayerClones)
{
m_PlayerNetworkObjects.Add(playerNetworkObject.NetworkManager.LocalClientId, new Dictionary<ulong, NetworkObject>());
if (!m_PlayerNetworkObjects.ContainsKey(playerNetworkObject.NetworkManager.LocalClientId))
{
m_PlayerNetworkObjects.Add(playerNetworkObject.NetworkManager.LocalClientId, new Dictionary<ulong, NetworkObject>());
}
m_PlayerNetworkObjects[playerNetworkObject.NetworkManager.LocalClientId].Add(m_ServerNetworkManager.LocalClientId, playerNetworkObject);
}
m_PlayerNetworkObjects[playerNetworkObject.NetworkManager.LocalClientId].Add(m_ServerNetworkManager.LocalClientId, playerNetworkObject);
}
ClientNetworkManagerPostStartInit();
// Notification that at this time the server and client(s) are instantiated,
// started, and connected on both sides.
yield return OnServerAndClientsConnected();
VerboseDebug($"Exiting {nameof(StartServerAndClients)}");
}
ClientNetworkManagerPostStartInit();
// Notification that at this time the server and client(s) are instantiated,
// started, and connected on both sides.
yield return OnServerAndClientsConnected();
VerboseDebug($"Exiting {nameof(StartServerAndClients)}");
}
}
@@ -571,7 +591,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
UnloadRemainingScenes();
// reset the m_ServerWaitForTick for the next test to initialize
s_DefaultWaitForTick = new WaitForSeconds(1.0f / k_DefaultTickRate);
s_DefaultWaitForTick = new WaitForSecondsRealtime(1.0f / k_DefaultTickRate);
VerboseDebug($"Exiting {nameof(ShutdownAndCleanUp)}");
}
@@ -596,6 +616,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
}
VerboseDebug($"Exiting {nameof(TearDown)}");
NetcodeLogAssert.Dispose();
}
/// <summary>
@@ -758,6 +779,41 @@ namespace Unity.Netcode.TestHelpers.Runtime
yield return WaitForClientsConnectedOrTimeOut(m_ClientNetworkManagers);
}
internal IEnumerator WaitForMessageReceived<T>(List<NetworkManager> wiatForReceivedBy, ReceiptType type = ReceiptType.Handled) where T : INetworkMessage
{
// Build our message hook entries tables so we can determine if all clients received spawn or ownership messages
var messageHookEntriesForSpawn = new List<MessageHookEntry>();
foreach (var clientNetworkManager in wiatForReceivedBy)
{
var messageHook = new MessageHookEntry(clientNetworkManager, type);
messageHook.AssignMessageType<T>();
messageHookEntriesForSpawn.Add(messageHook);
}
// Used to determine if all clients received the CreateObjectMessage
var hooks = new MessageHooksConditional(messageHookEntriesForSpawn);
yield return WaitForConditionOrTimeOut(hooks);
Assert.False(s_GlobalTimeoutHelper.TimedOut);
}
internal IEnumerator WaitForMessagesReceived(List<Type> messagesInOrder, List<NetworkManager> wiatForReceivedBy, ReceiptType type = ReceiptType.Handled)
{
// Build our message hook entries tables so we can determine if all clients received spawn or ownership messages
var messageHookEntriesForSpawn = new List<MessageHookEntry>();
foreach (var clientNetworkManager in wiatForReceivedBy)
{
foreach (var message in messagesInOrder)
{
var messageHook = new MessageHookEntry(clientNetworkManager, type);
messageHook.AssignMessageType(message);
messageHookEntriesForSpawn.Add(messageHook);
}
}
// Used to determine if all clients received the CreateObjectMessage
var hooks = new MessageHooksConditional(messageHookEntriesForSpawn);
yield return WaitForConditionOrTimeOut(hooks);
Assert.False(s_GlobalTimeoutHelper.TimedOut);
}
/// <summary>
/// Creates a basic NetworkObject test prefab, assigns it to a new
/// NetworkPrefab entry, and then adds it to the server and client(s)

View File

@@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NUnit.Framework;
using UnityEngine;
namespace Unity.Netcode.RuntimeTests
{
public class NetcodeLogAssert
{
private struct LogData
{
public LogType LogType;
public string Message;
public string StackTrace;
}
private readonly object m_Lock = new object();
private bool m_Disposed;
private List<LogData> AllLogs { get; }
public NetcodeLogAssert()
{
AllLogs = new List<LogData>();
Activate();
}
private void Activate()
{
Application.logMessageReceivedThreaded += AddLog;
}
private void Deactivate()
{
Application.logMessageReceivedThreaded -= AddLog;
}
public void AddLog(string message, string stacktrace, LogType type)
{
lock (m_Lock)
{
var log = new LogData
{
LogType = type,
Message = message,
StackTrace = stacktrace,
};
AllLogs.Add(log);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (m_Disposed)
{
return;
}
m_Disposed = true;
if (disposing)
{
Deactivate();
}
}
public void LogWasNotReceived(LogType type, string message)
{
lock (m_Lock)
{
foreach (var logEvent in AllLogs)
{
if (logEvent.LogType == type && message.Equals(logEvent.Message))
{
Assert.Fail($"Unexpected log: [{logEvent.LogType}] {logEvent.Message}");
}
}
}
}
public void LogWasNotReceived(LogType type, Regex messageRegex)
{
lock (m_Lock)
{
foreach (var logEvent in AllLogs)
{
if (logEvent.LogType == type && messageRegex.IsMatch(logEvent.Message))
{
Assert.Fail($"Unexpected log: [{logEvent.LogType}] {logEvent.Message}");
}
}
}
}
public void LogWasReceived(LogType type, string message)
{
lock (m_Lock)
{
var found = false;
foreach (var logEvent in AllLogs)
{
if (logEvent.LogType == type && message.Equals(logEvent.Message))
{
found = true;
break;
}
}
if (!found)
{
Assert.Fail($"Expected log was not received: [{type}] {message}");
}
}
}
public void LogWasReceived(LogType type, Regex messageRegex)
{
lock (m_Lock)
{
var found = false;
foreach (var logEvent in AllLogs)
{
if (logEvent.LogType == type && messageRegex.IsMatch(logEvent.Message))
{
found = true;
}
}
if (!found)
{
Assert.Fail($"Expected log was not received: [{type}] {messageRegex}");
}
}
}
public void Reset()
{
lock (m_Lock)
{
AllLogs.Clear();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 61774c54cd14423ca4de6d56c9fd0fe8
timeCreated: 1661800793

View File

@@ -13,7 +13,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
/// From both we can then at least determine if the value indeed changed
/// </summary>
/// <typeparam name="T"></typeparam>
public class NetworkVariableHelper<T> : NetworkVariableBaseHelper where T : unmanaged
public class NetworkVariableHelper<T> : NetworkVariableBaseHelper
{
private readonly NetworkVariable<T> m_NetworkVariable;
public delegate void OnMyValueChangedDelegateHandler(T previous, T next);
@@ -50,7 +50,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
{
if (previous is ValueType testValueType)
{
CheckVariableChanged(previous, next);
CheckVariableChanged(previous as ValueType, next as ValueType);
}
else
{