2 Commits

Author SHA1 Message Date
Unity Technologies
eab996f3ac com.unity.netcode.gameobjects@2.0.0-pre.4
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-pre.4] - 2024-08-21

### Added

- Added `NetworkVariable.CheckDirtyState` that is to be used in tandem with collections in order to detect whether the collection or an item within the collection has changed. (#3004)

### Fixed

- Fixed issue where nested `NetworkTransform` components were not getting updated. (#3016)
- Fixed issue by adding null checks in `NetworkVariableBase.CanClientRead` and `NetworkVariableBase.CanClientWrite` methods to ensure safe access to `NetworkBehaviour`. (#3012)
- Fixed issue where `FixedStringSerializer<T>` was using `NetworkVariableSerialization<byte>.AreEqual` to determine if two bytes were equal causes an exception to be thrown due to no byte serializer having been defined. (#3009)
- Fixed Issue where a state with dual triggers, inbound and outbound, could cause a false layer to layer state transition message to be sent to non-authority `NetworkAnimator` instances and cause a warning message to be logged. (#3008)
- Fixed issue using collections within `NetworkVariable` where the collection would not detect changes to items or nested items. (#3004)
- Fixed issue where `List`, `Dictionary`, and `HashSet` collections would not uniquely duplicate nested collections. (#3004)
- Fixed issue where `NotAuthorityTarget` would include the service observer in the list of targets to send the RPC to as opposed to excluding the service observer as it should. (#3000)
- Fixed issue where `ProxyRpcTargetGroup` could attempt to send a message if there were no targets to send to. (#3000)

### Changed

- Changed `NetworkAnimator` to automatically switch to owner authoritative mode when using a distributed authority network topology. (#3021)
- Changed permissions exception thrown in `NetworkList` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3004)
- Changed permissions exception thrown in `NetworkVariable.Value` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3004)
2024-08-21 00:00:00 +00:00
Unity Technologies
a813ba0dd6 com.unity.netcode.gameobjects@2.0.0-pre.3
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-pre.3] - 2024-07-23

### Added
- Added: `UnityTransport.GetNetworkDriver` and `UnityTransport.GetLocalEndpoint` methods to expose the driver and local endpoint being used. (#2978)

### Fixed

- Fixed issue where deferred despawn was causing GC allocations when converting an `IEnumerable` to a list. (#2983)
- Fixed issue where the realtime network stats monitor was not able to display RPC traffic in release builds due to those stats being only available in development builds or the editor. (#2979)
- Fixed issue where `NetworkManager.ScenesLoaded` was not being updated if `PostSynchronizationSceneUnloading` was set and any loaded scenes not used during synchronization were unloaded. (#2971)
- Fixed issue where `Rigidbody2d` under Unity 6000.0.11f1 has breaking changes where `velocity` is now `linearVelocity` and `isKinematic` is replaced by `bodyType`. (#2971)
- Fixed issue where `NetworkSpawnManager.InstantiateAndSpawn` and `NetworkObject.InstantiateAndSpawn` were not honoring the ownerClientId parameter when using a client-server network topology. (#2968)
- Fixed issue where internal delta serialization could not have a byte serializer defined when serializing deltas for other types. Added `[GenerateSerializationForType(typeof(byte))]` to both the `NetworkVariable` and `AnticipatedNetworkVariable` classes to assure a byte serializer is defined.(#2962)
- Fixed issue when scene management was disabled and the session owner would still try to synchronize a late joining client. (#2962)
- Fixed issue when using a distributed authority network topology where it would allow a session owner to spawn a `NetworkObject` prior to being approved. Now, an error message is logged and the `NetworkObject` will not be spawned prior to the client being approved.  (#2962)
- Fixed issue where attempting to spawn during `NetworkBehaviour.OnInSceneObjectsSpawned` and `NetworkBehaviour.OnNetworkSessionSynchronized` notifications would throw a collection modified exception.  (#2962)

### Changed

- Changed logic where clients can now set the `NetworkSceneManager` client synchronization mode when using a distributed authority network topology. (#2985)
2024-07-23 00:00:00 +00:00
68 changed files with 4948 additions and 1132 deletions

View File

@@ -6,6 +6,52 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
## [2.0.0-pre.4] - 2024-08-21
### Added
- Added `NetworkVariable.CheckDirtyState` that is to be used in tandem with collections in order to detect whether the collection or an item within the collection has changed. (#3004)
### Fixed
- Fixed issue where nested `NetworkTransform` components were not getting updated. (#3016)
- Fixed issue by adding null checks in `NetworkVariableBase.CanClientRead` and `NetworkVariableBase.CanClientWrite` methods to ensure safe access to `NetworkBehaviour`. (#3012)
- Fixed issue where `FixedStringSerializer<T>` was using `NetworkVariableSerialization<byte>.AreEqual` to determine if two bytes were equal causes an exception to be thrown due to no byte serializer having been defined. (#3009)
- Fixed Issue where a state with dual triggers, inbound and outbound, could cause a false layer to layer state transition message to be sent to non-authority `NetworkAnimator` instances and cause a warning message to be logged. (#3008)
- Fixed issue using collections within `NetworkVariable` where the collection would not detect changes to items or nested items. (#3004)
- Fixed issue where `List`, `Dictionary`, and `HashSet` collections would not uniquely duplicate nested collections. (#3004)
- Fixed issue where `NotAuthorityTarget` would include the service observer in the list of targets to send the RPC to as opposed to excluding the service observer as it should. (#3000)
- Fixed issue where `ProxyRpcTargetGroup` could attempt to send a message if there were no targets to send to. (#3000)
### Changed
- Changed `NetworkAnimator` to automatically switch to owner authoritative mode when using a distributed authority network topology. (#3021)
- Changed permissions exception thrown in `NetworkList` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3004)
- Changed permissions exception thrown in `NetworkVariable.Value` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3004)
## [2.0.0-pre.3] - 2024-07-23
### Added
- Added: `UnityTransport.GetNetworkDriver` and `UnityTransport.GetLocalEndpoint` methods to expose the driver and local endpoint being used. (#2978)
### Fixed
- Fixed issue where deferred despawn was causing GC allocations when converting an `IEnumerable` to a list. (#2983)
- Fixed issue where the realtime network stats monitor was not able to display RPC traffic in release builds due to those stats being only available in development builds or the editor. (#2979)
- Fixed issue where `NetworkManager.ScenesLoaded` was not being updated if `PostSynchronizationSceneUnloading` was set and any loaded scenes not used during synchronization were unloaded. (#2971)
- Fixed issue where `Rigidbody2d` under Unity 6000.0.11f1 has breaking changes where `velocity` is now `linearVelocity` and `isKinematic` is replaced by `bodyType`. (#2971)
- Fixed issue where `NetworkSpawnManager.InstantiateAndSpawn` and `NetworkObject.InstantiateAndSpawn` were not honoring the ownerClientId parameter when using a client-server network topology. (#2968)
- Fixed issue where internal delta serialization could not have a byte serializer defined when serializing deltas for other types. Added `[GenerateSerializationForType(typeof(byte))]` to both the `NetworkVariable` and `AnticipatedNetworkVariable` classes to assure a byte serializer is defined.(#2962)
- Fixed issue when scene management was disabled and the session owner would still try to synchronize a late joining client. (#2962)
- Fixed issue when using a distributed authority network topology where it would allow a session owner to spawn a `NetworkObject` prior to being approved. Now, an error message is logged and the `NetworkObject` will not be spawned prior to the client being approved. (#2962)
- Fixed issue where attempting to spawn during `NetworkBehaviour.OnInSceneObjectsSpawned` and `NetworkBehaviour.OnNetworkSessionSynchronized` notifications would throw a collection modified exception. (#2962)
### Changed
- Changed logic where clients can now set the `NetworkSceneManager` client synchronization mode when using a distributed authority network topology. (#2985)
## [2.0.0-pre.2] - 2024-06-17 ## [2.0.0-pre.2] - 2024-06-17
### Added ### Added

View File

@@ -360,6 +360,10 @@ namespace Unity.Netcode.Components
public override void OnNetworkSpawn() public override void OnNetworkSpawn()
{ {
if (NetworkManager.DistributedAuthorityMode)
{
Debug.LogWarning($"This component is not currently supported in distributed authority.");
}
base.OnNetworkSpawn(); base.OnNetworkSpawn();
m_OutstandingAuthorityChange = true; m_OutstandingAuthorityChange = true;
ApplyAuthoritativeState(); ApplyAuthoritativeState();

View File

@@ -8,7 +8,7 @@ namespace Unity.Netcode.Components
/// Half float precision <see cref="Vector3"/>. /// Half float precision <see cref="Vector3"/>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The Vector3T<ushort> values are half float values returned by <see cref="Mathf.FloatToHalf(float)"/> for each /// The Vector3T&lt;ushort&gt; values are half float values returned by <see cref="Mathf.FloatToHalf(float)"/> for each
/// individual axis and the 16 bits of the half float are stored as <see cref="ushort"/> values since C# does not have /// individual axis and the 16 bits of the half float are stored as <see cref="ushort"/> values since C# does not have
/// a half float type. /// a half float type.
/// </remarks> /// </remarks>

View File

@@ -8,7 +8,7 @@ namespace Unity.Netcode.Components
/// Half Precision <see cref="Vector4"/> that can also be used to convert a <see cref="Quaternion"/> to half precision. /// Half Precision <see cref="Vector4"/> that can also be used to convert a <see cref="Quaternion"/> to half precision.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The Vector4T<ushort> values are half float values returned by <see cref="Mathf.FloatToHalf(float)"/> for each /// The Vector4T&lt;ushort&gt; values are half float values returned by <see cref="Mathf.FloatToHalf(float)"/> for each
/// individual axis and the 16 bits of the half float are stored as <see cref="ushort"/> values since C# does not have /// individual axis and the 16 bits of the half float are stored as <see cref="ushort"/> values since C# does not have
/// a half float type. /// a half float type.
/// </remarks> /// </remarks>

View File

@@ -5,7 +5,7 @@ using UnityEngine;
namespace Unity.Netcode namespace Unity.Netcode
{ {
/// <summary> /// <summary>
/// Solves for incoming values that are jittered /// Solves for incoming values that are jittered.
/// Partially solves for message loss. Unclamped lerping helps hide this, but not completely /// Partially solves for message loss. Unclamped lerping helps hide this, but not completely
/// </summary> /// </summary>
/// <typeparam name="T">The type of interpolated value</typeparam> /// <typeparam name="T">The type of interpolated value</typeparam>

View File

@@ -498,9 +498,13 @@ namespace Unity.Netcode.Components
/// <summary> /// <summary>
/// Override this method and return false to switch to owner authoritative mode /// Override this method and return false to switch to owner authoritative mode
/// </summary> /// </summary>
/// <remarks>
/// When using a distributed authority network topology, this will default to
/// owner authoritative.
/// </remarks>
protected virtual bool OnIsServerAuthoritative() protected virtual bool OnIsServerAuthoritative()
{ {
return true; return NetworkManager ? !NetworkManager.DistributedAuthorityMode : true;
} }
// Animators only support up to 32 parameters // Animators only support up to 32 parameters
@@ -851,7 +855,12 @@ namespace Unity.Netcode.Components
stateChangeDetected = true; stateChangeDetected = true;
//Debug.Log($"[Cross-Fade] To-Hash: {nt.fullPathHash} | TI-Duration: ({tt.duration}) | TI-Norm: ({tt.normalizedTime}) | From-Hash: ({m_AnimationHash[layer]}) | SI-FPHash: ({st.fullPathHash}) | SI-Norm: ({st.normalizedTime})"); //Debug.Log($"[Cross-Fade] To-Hash: {nt.fullPathHash} | TI-Duration: ({tt.duration}) | TI-Norm: ({tt.normalizedTime}) | From-Hash: ({m_AnimationHash[layer]}) | SI-FPHash: ({st.fullPathHash}) | SI-Norm: ({st.normalizedTime})");
} }
else if (!tt.anyState && tt.fullPathHash != m_TransitionHash[layer]) // If we are not transitioned into the "any state" and the animator transition isn't a full path hash (layer to layer) and our pre-built destination state to transition does not contain the
// current layer (i.e. transitioning into a state from another layer) =or= we do contain the layer and the layer contains state to transition to is contained within our pre-built destination
// state then we can handle this transition as a non-cross fade state transition between layers.
// Otherwise, if we don't enter into this then this is a "trigger transition to some state that is now being transitioned back to the Idle state via trigger" or "Dual Triggers" IDLE<-->State.
else if (!tt.anyState && tt.fullPathHash != m_TransitionHash[layer] && (!m_DestinationStateToTransitioninfo.ContainsKey(layer) ||
(m_DestinationStateToTransitioninfo.ContainsKey(layer) && m_DestinationStateToTransitioninfo[layer].ContainsKey(nt.fullPathHash))))
{ {
// first time in this transition for this layer // first time in this transition for this layer
m_TransitionHash[layer] = tt.fullPathHash; m_TransitionHash[layer] = tt.fullPathHash;
@@ -860,6 +869,10 @@ namespace Unity.Netcode.Components
animState.CrossFade = false; animState.CrossFade = false;
animState.Transition = true; animState.Transition = true;
animState.NormalizedTime = tt.normalizedTime; animState.NormalizedTime = tt.normalizedTime;
if (m_DestinationStateToTransitioninfo.ContainsKey(layer) && m_DestinationStateToTransitioninfo[layer].ContainsKey(nt.fullPathHash))
{
animState.DestinationStateHash = nt.fullPathHash;
}
stateChangeDetected = true; stateChangeDetected = true;
//Debug.Log($"[Transition] TI-Duration: ({tt.duration}) | TI-Norm: ({tt.normalizedTime}) | From-Hash: ({m_AnimationHash[layer]}) |SI-FPHash: ({st.fullPathHash}) | SI-Norm: ({st.normalizedTime})"); //Debug.Log($"[Transition] TI-Duration: ({tt.duration}) | TI-Norm: ({tt.normalizedTime}) | From-Hash: ({m_AnimationHash[layer]}) |SI-FPHash: ({st.fullPathHash}) | SI-Norm: ({st.normalizedTime})");
} }

View File

@@ -177,7 +177,11 @@ namespace Unity.Netcode.Components
{ {
if (m_IsRigidbody2D) if (m_IsRigidbody2D)
{ {
#if COM_UNITY_MODULES_PHYSICS2D_LINEAR
m_Rigidbody2D.linearVelocity = linearVelocity;
#else
m_Rigidbody2D.velocity = linearVelocity; m_Rigidbody2D.velocity = linearVelocity;
#endif
} }
else else
{ {
@@ -197,7 +201,11 @@ namespace Unity.Netcode.Components
{ {
if (m_IsRigidbody2D) if (m_IsRigidbody2D)
{ {
#if COM_UNITY_MODULES_PHYSICS2D_LINEAR
return m_Rigidbody2D.linearVelocity;
#else
return m_Rigidbody2D.velocity; return m_Rigidbody2D.velocity;
#endif
} }
else else
{ {
@@ -238,7 +246,7 @@ namespace Unity.Netcode.Components
{ {
if (m_IsRigidbody2D) if (m_IsRigidbody2D)
{ {
return Vector3.forward * m_Rigidbody2D.velocity; return Vector3.forward * m_Rigidbody2D.angularVelocity;
} }
else else
{ {
@@ -481,7 +489,7 @@ namespace Unity.Netcode.Components
{ {
if (m_IsRigidbody2D) if (m_IsRigidbody2D)
{ {
return m_Rigidbody2D.isKinematic; return m_Rigidbody2D.bodyType == RigidbodyType2D.Kinematic;
} }
else else
{ {
@@ -510,7 +518,7 @@ namespace Unity.Netcode.Components
{ {
if (m_IsRigidbody2D) if (m_IsRigidbody2D)
{ {
m_Rigidbody2D.isKinematic = isKinematic; m_Rigidbody2D.bodyType = isKinematic ? RigidbodyType2D.Kinematic : RigidbodyType2D.Dynamic;
} }
else else
{ {
@@ -715,7 +723,11 @@ namespace Unity.Netcode.Components
if (zeroVelocity) if (zeroVelocity)
{ {
#if COM_UNITY_MODULES_PHYSICS2D_LINEAR
m_Rigidbody2D.linearVelocity = Vector2.zero;
#else
m_Rigidbody2D.velocity = Vector2.zero; m_Rigidbody2D.velocity = Vector2.zero;
#endif
m_Rigidbody2D.angularVelocity = 0.0f; m_Rigidbody2D.angularVelocity = 0.0f;
} }

View File

@@ -2960,7 +2960,10 @@ namespace Unity.Netcode.Components
#else #else
var forUpdate = true; var forUpdate = true;
#endif #endif
NetworkManager?.NetworkTransformRegistration(this, forUpdate, false); if (m_CachedNetworkObject != null)
{
NetworkManager?.NetworkTransformRegistration(m_CachedNetworkObject, forUpdate, false);
}
DeregisterForTickUpdate(this); DeregisterForTickUpdate(this);
CanCommitToTransform = false; CanCommitToTransform = false;
} }
@@ -3069,7 +3072,7 @@ namespace Unity.Netcode.Components
if (CanCommitToTransform) if (CanCommitToTransform)
{ {
// Make sure authority doesn't get added to updates (no need to do this on the authority side) // Make sure authority doesn't get added to updates (no need to do this on the authority side)
m_CachedNetworkManager.NetworkTransformRegistration(this, forUpdate, false); m_CachedNetworkManager.NetworkTransformRegistration(NetworkObject, forUpdate, false);
if (UseHalfFloatPrecision) if (UseHalfFloatPrecision)
{ {
m_HalfPositionState = new NetworkDeltaPosition(currentPosition, m_CachedNetworkManager.ServerTime.Tick, math.bool3(SyncPositionX, SyncPositionY, SyncPositionZ)); m_HalfPositionState = new NetworkDeltaPosition(currentPosition, m_CachedNetworkManager.ServerTime.Tick, math.bool3(SyncPositionX, SyncPositionY, SyncPositionZ));
@@ -3090,7 +3093,7 @@ namespace Unity.Netcode.Components
else else
{ {
// Non-authority needs to be added to updates for interpolation and applying state purposes // Non-authority needs to be added to updates for interpolation and applying state purposes
m_CachedNetworkManager.NetworkTransformRegistration(this, forUpdate, true); m_CachedNetworkManager.NetworkTransformRegistration(NetworkObject, forUpdate, true);
// Remove this instance from the tick update // Remove this instance from the tick update
DeregisterForTickUpdate(this); DeregisterForTickUpdate(this);
ResetInterpolatedStateToCurrentAuthoritativeState(); ResetInterpolatedStateToCurrentAuthoritativeState();
@@ -3535,7 +3538,7 @@ namespace Unity.Netcode.Components
/// </summary> /// </summary>
private void UpdateTransformState() private void UpdateTransformState()
{ {
if (m_CachedNetworkManager.ShutdownInProgress || (m_CachedNetworkManager.DistributedAuthorityMode && m_CachedNetworkObject.Observers.Count - 1 == 0)) if (m_CachedNetworkManager.ShutdownInProgress || (m_CachedNetworkManager.DistributedAuthorityMode && !m_CachedNetworkManager.CMBServiceConnection && m_CachedNetworkObject.Observers.Count - 1 == 0))
{ {
return; return;
} }

View File

@@ -159,9 +159,21 @@ namespace Unity.Netcode
public bool AutoSpawnPlayerPrefabClientSide = true; public bool AutoSpawnPlayerPrefabClientSide = true;
#if MULTIPLAYER_TOOLS #if MULTIPLAYER_TOOLS
/// <summary>
/// Controls whether network messaging metrics will be gathered. (defaults to true)
/// There is a slight performance cost to having this enabled, and can increase in processing time based on network message traffic.
/// </summary>
/// <remarks>
/// The Realtime Network Stats Monitoring tool requires this to be enabled.
/// </remarks>
[Tooltip("Enable (default) if you want to gather messaging metrics. Realtime Network Stats Monitor requires this to be enabled. Disabling this can improve performance in release builds.")]
public bool NetworkMessageMetrics = true; public bool NetworkMessageMetrics = true;
#endif #endif
/// <summary>
/// When enabled (default, this enables network profiling information. This does come with a per message processing cost.
/// Network profiling information is automatically disabled in release builds.
/// </summary>
[Tooltip("Enable (default) if you want to profile network messages with development builds and defaults to being disabled in release builds. When disabled, network messaging profiling will be disabled in development builds.")]
public bool NetworkProfilingMetrics = true; public bool NetworkProfilingMetrics = true;
/// <summary> /// <summary>

View File

@@ -15,7 +15,7 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// The base class to override to write network code. Inherits MonoBehaviour /// The base class to override to write network code. Inherits MonoBehaviour.
/// </summary> /// </summary>
public abstract class NetworkBehaviour : MonoBehaviour public abstract class NetworkBehaviour : MonoBehaviour
{ {
@@ -27,7 +27,7 @@ namespace Unity.Netcode
// RuntimeAccessModifiersILPP will make this `public` // RuntimeAccessModifiersILPP will make this `public`
internal static readonly Dictionary<Type, Dictionary<uint, RpcReceiveHandler>> __rpc_func_table = new Dictionary<Type, Dictionary<uint, RpcReceiveHandler>>(); internal static readonly Dictionary<Type, Dictionary<uint, RpcReceiveHandler>> __rpc_func_table = new Dictionary<Type, Dictionary<uint, RpcReceiveHandler>>();
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
// RuntimeAccessModifiersILPP will make this `public` // RuntimeAccessModifiersILPP will make this `public`
internal static readonly Dictionary<Type, Dictionary<uint, string>> __rpc_name_table = new Dictionary<Type, Dictionary<uint, string>>(); internal static readonly Dictionary<Type, Dictionary<uint, string>> __rpc_name_table = new Dictionary<Type, Dictionary<uint, string>>();
#endif #endif
@@ -124,7 +124,7 @@ namespace Unity.Netcode
} }
bufferWriter.Dispose(); bufferWriter.Dispose();
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName)) if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName))
{ {
NetworkManager.NetworkMetrics.TrackRpcSent( NetworkManager.NetworkMetrics.TrackRpcSent(
@@ -252,7 +252,7 @@ namespace Unity.Netcode
} }
bufferWriter.Dispose(); bufferWriter.Dispose();
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName)) if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName))
{ {
if (clientRpcParams.Send.TargetClientIds != null) if (clientRpcParams.Send.TargetClientIds != null)
@@ -410,8 +410,8 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Gets the NetworkManager that owns this NetworkBehaviour instance /// Gets the NetworkManager that owns this NetworkBehaviour instance.
/// See note around `NetworkObject` for how there is a chicken / egg problem when we are not initialized /// See `NetworkObject` note for how there is a chicken/egg problem when not initialized.
/// </summary> /// </summary>
public NetworkManager NetworkManager public NetworkManager NetworkManager
{ {
@@ -439,38 +439,40 @@ namespace Unity.Netcode
/// <see cref="Unity.Netcode.RpcTarget.Not(NativeArray{ulong})"/>, /// <see cref="Unity.Netcode.RpcTarget.Not(NativeArray{ulong})"/>,
/// <see cref="Unity.Netcode.RpcTarget.Not(NativeList{ulong})"/>, /// <see cref="Unity.Netcode.RpcTarget.Not(NativeList{ulong})"/>,
/// <see cref="Unity.Netcode.RpcTarget.Not(ulong[])"/>, and /// <see cref="Unity.Netcode.RpcTarget.Not(ulong[])"/>, and
/// <see cref="Unity.Netcode.RpcTarget.Not{T}(T)"/> /// <see cref="Unity.Netcode.RpcTarget.Not{T}(T)"/>.
/// </summary> /// </summary>
#pragma warning restore IDE0001 #pragma warning restore IDE0001
public RpcTarget RpcTarget => NetworkManager.RpcTarget; public RpcTarget RpcTarget => NetworkManager.RpcTarget;
/// <summary> /// <summary>
/// If a NetworkObject is assigned, it will return whether or not this NetworkObject /// If a NetworkObject is assigned, returns whether the NetworkObject
/// is the local player object. If no NetworkObject is assigned it will always return false. /// is the local player object. If no NetworkObject is assigned, returns false.
/// </summary> /// </summary>
public bool IsLocalPlayer { get; private set; } public bool IsLocalPlayer { get; private set; }
/// <summary> /// <summary>
/// Gets if the object is owned by the local player or if the object is the local player object /// Gets whether the object is owned by the local player or if the object is the local player object.
/// </summary> /// </summary>
public bool IsOwner { get; internal set; } public bool IsOwner { get; internal set; }
/// <summary> /// <summary>
/// Gets if we are executing as server /// Gets whether executing as a server.
/// </summary> /// </summary>
public bool IsServer { get; private set; } public bool IsServer { get; private set; }
/// <summary> /// <summary>
/// Determines if the local client has authority over the associated NetworkObject /// Determines if the local client has authority over the associated NetworkObject.
/// Client-Server: This will return true if IsServer or IsHost /// <list type="bullet">
/// Distributed Authority: This will return true if IsOwner /// <item>In client-server contexts: returns true if `IsServer` or `IsHost`.</item>
/// <item>In distributed authority contexts: returns true if `IsOwner`.</item>
/// </list>
/// </summary> /// </summary>
public bool HasAuthority { get; internal set; } public bool HasAuthority { get; internal set; }
internal NetworkClient LocalClient { get; private set; } internal NetworkClient LocalClient { get; private set; }
/// <summary> /// <summary>
/// Gets if the client is the distributed authority mode session owner /// Gets whether the client is the distributed authority mode session owner.
/// </summary> /// </summary>
public bool IsSessionOwner public bool IsSessionOwner
{ {
@@ -486,29 +488,29 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Gets if the server (local or remote) is a host - i.e., also a client /// Gets whether the server (local or remote) is a host.
/// </summary> /// </summary>
public bool ServerIsHost { get; private set; } public bool ServerIsHost { get; private set; }
/// <summary> /// <summary>
/// Gets if we are executing as client /// Gets whether executing as a client.
/// </summary> /// </summary>
public bool IsClient { get; private set; } public bool IsClient { get; private set; }
/// <summary> /// <summary>
/// Gets if we are executing as Host, I.E Server and Client /// Gets whether executing as a host (both server and client).
/// </summary> /// </summary>
public bool IsHost { get; private set; } public bool IsHost { get; private set; }
/// <summary> /// <summary>
/// Gets Whether or not the object has a owner /// Gets whether the object has an owner.
/// </summary> /// </summary>
public bool IsOwnedByServer { get; internal set; } public bool IsOwnedByServer { get; internal set; }
/// <summary> /// <summary>
/// Used to determine if it is safe to access NetworkObject and NetworkManager from within a NetworkBehaviour component /// Determines whether it's safe to access a NetworkObject and NetworkManager from within a NetworkBehaviour component.
/// Primarily useful when checking NetworkObject/NetworkManager properties within FixedUpate /// Primarily useful when checking NetworkObject or NetworkManager properties within FixedUpate.
/// </summary> /// </summary>
public bool IsSpawned { get; internal set; } public bool IsSpawned { get; internal set; }
@@ -528,7 +530,7 @@ namespace Unity.Netcode
/// the warning below. This is why IsBehaviourEditable had to be created. Matt was going to re-do /// the warning below. This is why IsBehaviourEditable had to be created. Matt was going to re-do
/// how NetworkObject works but it was close to the release and too risky to change /// how NetworkObject works but it was close to the release and too risky to change
/// <summary> /// <summary>
/// Gets the NetworkObject that owns this NetworkBehaviour instance /// Gets the NetworkObject that owns this NetworkBehaviour instance.
/// </summary> /// </summary>
public NetworkObject NetworkObject public NetworkObject NetworkObject
{ {
@@ -567,19 +569,19 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Gets whether or not this NetworkBehaviour instance has a NetworkObject owner. /// Gets whether this NetworkBehaviour instance has a NetworkObject owner.
/// </summary> /// </summary>
public bool HasNetworkObject => NetworkObject != null; public bool HasNetworkObject => NetworkObject != null;
private NetworkObject m_NetworkObject = null; private NetworkObject m_NetworkObject = null;
/// <summary> /// <summary>
/// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour /// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour instance.
/// </summary> /// </summary>
public ulong NetworkObjectId { get; internal set; } public ulong NetworkObjectId { get; internal set; }
/// <summary> /// <summary>
/// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject /// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject.
/// </summary> /// </summary>
public ushort NetworkBehaviourId { get; internal set; } public ushort NetworkBehaviourId { get; internal set; }
@@ -589,7 +591,7 @@ namespace Unity.Netcode
internal ushort NetworkBehaviourIdCache = 0; internal ushort NetworkBehaviourIdCache = 0;
/// <summary> /// <summary>
/// Returns a the NetworkBehaviour with a given BehaviourId for the current NetworkObject /// Returns the NetworkBehaviour with a given BehaviourId for the current NetworkObject.
/// </summary> /// </summary>
/// <param name="behaviourId">The behaviourId to return</param> /// <param name="behaviourId">The behaviourId to return</param>
/// <returns>Returns NetworkBehaviour with given behaviourId</returns> /// <returns>Returns NetworkBehaviour with given behaviourId</returns>
@@ -599,7 +601,7 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Gets the ClientId that owns the NetworkObject /// Gets the ClientId that owns this NetworkObject.
/// </summary> /// </summary>
public ulong OwnerClientId { get; internal set; } public ulong OwnerClientId { get; internal set; }
@@ -651,28 +653,29 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Distributed Authority Mode Only /// Only for use in distributed authority mode.
/// Invoked only on the authority instance when a <see cref="NetworkObject"/> is deferring its despawn on non-authoritative instances. /// Invoked only on the authority instance when a <see cref="NetworkObject"/> is deferring its despawn on non-authoritative instances.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// See also: <see cref="NetworkObject.DeferDespawn(int, bool)"/> /// See also: <see cref="NetworkObject.DeferDespawn(int, bool)"/>
/// </remarks> /// </remarks>
/// <param name="despawnTick">the future network tick that the <see cref="NetworkObject"/> will be despawned on non-authoritative instances</param> /// <param name="despawnTick">The future network tick that the <see cref="NetworkObject"/> will be despawned on non-authoritative instances</param>
public virtual void OnDeferringDespawn(int despawnTick) { } public virtual void OnDeferringDespawn(int despawnTick) { }
/// <summary>
/// Gets called after the <see cref="NetworkObject"/> is spawned. No NetworkBehaviours associated with the NetworkObject will have had <see cref="OnNetworkSpawn"/> invoked yet. /// Gets called after the <see cref="NetworkObject"/> is spawned. No NetworkBehaviours associated with the NetworkObject will have had <see cref="OnNetworkSpawn"/> invoked yet.
/// A reference to <see cref="NetworkManager"/> is passed in as a parameter to determine the context of execution (IsServer/IsClient) /// A reference to <see cref="NetworkManager"/> is passed in as a parameter to determine the context of execution (`IsServer` or `IsClient`).
/// </summary> /// </summary>
/// <remarks>
/// <param name="networkManager">a ref to the <see cref="NetworkManager"/> since this is not yet set on the <see cref="NetworkBehaviour"/></param> /// <param name="networkManager">a ref to the <see cref="NetworkManager"/> since this is not yet set on the <see cref="NetworkBehaviour"/></param>
/// <remarks>
/// The <see cref="NetworkBehaviour"/> will not have anything assigned to it at this point in time. /// The <see cref="NetworkBehaviour"/> will not have anything assigned to it at this point in time.
/// Settings like ownership, NetworkBehaviourId, NetworkManager, and most other spawn related properties will not be set. /// Settings like ownership, NetworkBehaviourId, NetworkManager, and most other spawn-related properties will not be set.
/// This can be used to handle things like initializing/instantiating a NetworkVariable or the like. /// This can be used to handle things like initializing a NetworkVariable.
/// </remarks> /// </remarks>
protected virtual void OnNetworkPreSpawn(ref NetworkManager networkManager) { } protected virtual void OnNetworkPreSpawn(ref NetworkManager networkManager) { }
/// <summary> /// <summary>
/// Gets called when the <see cref="NetworkObject"/> gets spawned, message handlers are ready to be registered and the network is setup. /// Gets called when the <see cref="NetworkObject"/> gets spawned, message handlers are ready to be registered, and the network is set up.
/// </summary> /// </summary>
public virtual void OnNetworkSpawn() { } public virtual void OnNetworkSpawn() { }
@@ -686,28 +689,28 @@ namespace Unity.Netcode
protected virtual void OnNetworkPostSpawn() { } protected virtual void OnNetworkPostSpawn() { }
/// <summary> /// <summary>
/// [Client-Side Only] /// This method is only available client-side.
/// When a new client joins it is synchronized with all spawned NetworkObjects and scenes loaded for the session joined. At the end of the synchronization process, when all /// When a new client joins it's synchronized with all spawned NetworkObjects and scenes loaded for the session joined. At the end of the synchronization process, when all
/// <see cref="NetworkObject"/>s and scenes (if scene management is enabled) have finished synchronizing, all NetworkBehaviour components associated with spawned <see cref="NetworkObject"/>s /// <see cref="NetworkObject"/>s and scenes (if scene management is enabled) have finished synchronizing, all NetworkBehaviour components associated with spawned <see cref="NetworkObject"/>s
/// will have this method invoked. /// will have this method invoked.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This can be used to handle post synchronization actions where you might need to access a different NetworkObject and/or NetworkBehaviour not local to the current NetworkObject context. /// This can be used to handle post-synchronization actions where you might need to access a different NetworkObject and/or NetworkBehaviour not local to the current NetworkObject context.
/// This is only invoked on clients during a client-server network topology session. /// This is only invoked on clients during a client-server network topology session.
/// </remarks> /// </remarks>
protected virtual void OnNetworkSessionSynchronized() { } protected virtual void OnNetworkSessionSynchronized() { }
/// <summary> /// <summary>
/// [Client & Server Side] /// When a scene is loaded and in-scene placed NetworkObjects are finished spawning, this method is invoked on all of the newly spawned in-scene placed NetworkObjects.
/// When a scene is loaded an in-scene placed NetworkObjects are all spawned, this method is invoked on all of the newly spawned in-scene placed NetworkObjects. /// This method runs both client and server side.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This can be used to handle post scene loaded actions for in-scene placed NetworkObjcts where you might need to access a different NetworkObject and/or NetworkBehaviour not local to the current NetworkObject context. /// This method can be used to handle post-scene loaded actions for in-scene placed NetworkObjcts where you might need to access a different NetworkObject and/or NetworkBehaviour not local to the current NetworkObject context.
/// </remarks> /// </remarks>
protected virtual void OnInSceneObjectsSpawned() { } protected virtual void OnInSceneObjectsSpawned() { }
/// <summary> /// <summary>
/// Gets called when the <see cref="NetworkObject"/> gets despawned. Is called both on the server and clients. /// Gets called when the <see cref="NetworkObject"/> gets despawned. This method runs both client and server side.
/// </summary> /// </summary>
public virtual void OnNetworkDespawn() { } public virtual void OnNetworkDespawn() { }
@@ -803,7 +806,8 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Gets called when the local client gains ownership of this object /// In client-server contexts, this method is invoked on both the server and the local client of the owner when <see cref="Netcode.NetworkObject"/> ownership is assigned.
/// <para>In distributed authority contexts, this method is only invoked on the local client that has been assigned ownership of the associated <see cref="Netcode.NetworkObject"/>.</para>
/// </summary> /// </summary>
public virtual void OnGainedOwnership() { } public virtual void OnGainedOwnership() { }
@@ -814,8 +818,8 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Invoked on all clients, override this method to be notified of any /// Invoked on all clients. Override this method to be notified of any
/// ownership changes (even if the instance was niether the previous or /// ownership changes (even if the instance was neither the previous or
/// newly assigned current owner). /// newly assigned current owner).
/// </summary> /// </summary>
/// <param name="previous">the previous owner</param> /// <param name="previous">the previous owner</param>
@@ -831,7 +835,9 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Gets called when we loose ownership of this object /// In client-server contexts, this method is invoked on the local client when it loses ownership of the associated <see cref="Netcode.NetworkObject"/>
/// and on the server when any client loses ownership.
/// <para>In distributed authority contexts, this method is only invoked on the local client that has lost ownership of the associated <see cref="Netcode.NetworkObject"/>.</para>
/// </summary> /// </summary>
public virtual void OnLostOwnership() { } public virtual void OnLostOwnership() { }
@@ -842,7 +848,7 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed /// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed.
/// </summary> /// </summary>
/// <param name="parentNetworkObject">the new <see cref="NetworkObject"/> parent</param> /// <param name="parentNetworkObject">the new <see cref="NetworkObject"/> parent</param>
public virtual void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject) { } public virtual void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject) { }
@@ -877,7 +883,7 @@ namespace Unity.Netcode
#pragma warning restore IDE1006 // restore naming rule violation check #pragma warning restore IDE1006 // restore naming rule violation check
{ {
__rpc_func_table[GetType()][hash] = handler; __rpc_func_table[GetType()][hash] = handler;
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
__rpc_name_table[GetType()][hash] = rpcMethodName; __rpc_name_table[GetType()][hash] = rpcMethodName;
#endif #endif
} }
@@ -903,7 +909,7 @@ namespace Unity.Netcode
if (!__rpc_func_table.ContainsKey(GetType())) if (!__rpc_func_table.ContainsKey(GetType()))
{ {
__rpc_func_table[GetType()] = new Dictionary<uint, RpcReceiveHandler>(); __rpc_func_table[GetType()] = new Dictionary<uint, RpcReceiveHandler>();
#if UNITY_EDITOR || DEVELOPMENT_BUILD #if UNITY_EDITOR || DEVELOPMENT_BUILD || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
__rpc_name_table[GetType()] = new Dictionary<uint, string>(); __rpc_name_table[GetType()] = new Dictionary<uint, string>();
#endif #endif
__initializeRpcs(); __initializeRpcs();
@@ -1112,31 +1118,38 @@ namespace Unity.Netcode
/// </remarks> /// </remarks>
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId) internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
{ {
// Create any values that require accessing the NetworkManager locally (it is expensive to access it in NetworkBehaviour)
var networkManager = NetworkManager; var networkManager = NetworkManager;
if (networkManager.DistributedAuthorityMode) var distributedAuthority = networkManager.DistributedAuthorityMode;
var ensureLengthSafety = networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety;
// Always write the NetworkVariable count even if zero for distributed authority (used by comb server)
if (distributedAuthority)
{ {
writer.WriteValueSafe((ushort)NetworkVariableFields.Count); writer.WriteValueSafe((ushort)NetworkVariableFields.Count);
} }
// Exit early if there are no NetworkVariables
if (NetworkVariableFields.Count == 0) if (NetworkVariableFields.Count == 0)
{ {
return; return;
} }
// DANGO-TODO: Made some modifications here that overlap/won't play nice with EnsureNetworkVariableLenghtSafety.
// Worth either merging or more cleanly separating these codepaths.
for (int j = 0; j < NetworkVariableFields.Count; j++) for (int j = 0; j < NetworkVariableFields.Count; j++)
{ {
// Note: In distributed authority mode, all clients can read // Client-Server: Try to write values only for clients that have read permissions.
// Distributed Authority: All clients have read permissions, always try to write the value.
if (NetworkVariableFields[j].CanClientRead(targetClientId)) if (NetworkVariableFields[j].CanClientRead(targetClientId))
{ {
if (networkManager.DistributedAuthorityMode) // Write additional NetworkVariable information when length safety is enabled or when in distributed authority mode
if (ensureLengthSafety || distributedAuthority)
{ {
writer.WriteValueSafe(NetworkVariableFields[j].Type); // Write the type being serialized for distributed authority (only for comb-server)
} if (distributedAuthority)
{
writer.WriteValueSafe(NetworkVariableFields[j].Type);
}
if (networkManager.DistributedAuthorityMode || networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
{
var writePos = writer.Position; var writePos = writer.Position;
// Note: This value can't be packed because we don't know how large it will be in advance // Note: This value can't be packed because we don't know how large it will be in advance
// we reserve space for it, then write the data, then come back and fill in the space // we reserve space for it, then write the data, then come back and fill in the space
@@ -1148,18 +1161,19 @@ namespace Unity.Netcode
NetworkVariableFields[j].WriteField(writer); NetworkVariableFields[j].WriteField(writer);
var size = writer.Position - startPos; var size = writer.Position - startPos;
writer.Seek(writePos); writer.Seek(writePos);
// Write the NetworkVariable value
writer.WriteValueSafe((ushort)size); writer.WriteValueSafe((ushort)size);
writer.Seek(startPos + size); writer.Seek(startPos + size);
} }
else else // Client-Server Only: Should only ever be invoked when using a client-server NetworkTopology
{ {
// Write the NetworkVariable value
NetworkVariableFields[j].WriteField(writer); NetworkVariableFields[j].WriteField(writer);
} }
} }
else else if (ensureLengthSafety)
{ {
// Only if EnsureNetworkVariableLengthSafety, otherwise just skip // Client-Server Only: If the client cannot read this field, then skip it but write a 0 for this NetworkVariable's position
if (networkManager.DistributedAuthorityMode || networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
{ {
writer.WriteValueSafe((ushort)0); writer.WriteValueSafe((ushort)0);
} }
@@ -1177,75 +1191,78 @@ namespace Unity.Netcode
/// </remarks> /// </remarks>
internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId) internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
{ {
// Stack cache any values that requires accessing the NetworkManager (it is expensive to access it in NetworkBehaviour)
var networkManager = NetworkManager; var networkManager = NetworkManager;
if (networkManager.DistributedAuthorityMode) var distributedAuthority = networkManager.DistributedAuthorityMode;
var ensureLengthSafety = networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety;
// Always read the NetworkVariable count when in distributed authority (sanity check if comb-server matches what client has locally)
if (distributedAuthority)
{ {
reader.ReadValueSafe(out ushort variableCount); reader.ReadValueSafe(out ushort variableCount);
if (variableCount != NetworkVariableFields.Count) if (variableCount != NetworkVariableFields.Count)
{ {
Debug.LogError("NetworkVariable count mismatch."); Debug.LogError($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}] NetworkVariable count mismatch! (Read: {variableCount} vs. Expected: {NetworkVariableFields.Count})");
return; return;
} }
} }
// Exit early if nothing else to read
if (NetworkVariableFields.Count == 0) if (NetworkVariableFields.Count == 0)
{ {
return; return;
} }
// DANGO-TODO: Made some modifications here that overlap/won't play nice with EnsureNetworkVariableLenghtSafety.
// Worth either merging or more cleanly separating these codepaths.
for (int j = 0; j < NetworkVariableFields.Count; j++) for (int j = 0; j < NetworkVariableFields.Count; j++)
{ {
var varSize = (ushort)0; var varSize = (ushort)0;
var readStartPos = 0; var readStartPos = 0;
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) // Client-Server: Clients that only have read permissions will try to read the value
// Distributed Authority: All clients have read permissions, always try to read the value
if (NetworkVariableFields[j].CanClientRead(clientId))
{ {
reader.ReadValueSafe(out varSize); if (ensureLengthSafety || distributedAuthority)
if (varSize == 0)
{ {
continue; // Read the type being serialized and discard it (for now) when in a distributed authority network topology (only used by comb-server)
if (distributedAuthority)
{
reader.ReadValueSafe(out NetworkVariableType _);
}
reader.ReadValueSafe(out varSize);
if (varSize == 0)
{
Debug.LogError($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{NetworkVariableFields[j].Name}] Expected non-zero size readable NetworkVariable! (Skipping)");
continue;
}
readStartPos = reader.Position;
} }
readStartPos = reader.Position;
} }
else // If the client cannot read this field, then skip it else // Client-Server Only: If the client cannot read this field, then skip it
if (!NetworkVariableFields[j].CanClientRead(clientId))
{ {
if (networkManager.DistributedAuthorityMode) // If skipping and length safety, then fill in a 0 size for this one spot
if (ensureLengthSafety)
{ {
reader.ReadValueSafe(out ushort size); reader.ReadValueSafe(out ushort size);
if (size != 0) if (size != 0)
{ {
Debug.LogError("Expected zero size"); Debug.LogError($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{NetworkVariableFields[j].Name}] Expected zero size for non-readable NetworkVariable when EnsureNetworkVariableLengthSafety is enabled! (Skipping)");
} }
} }
continue; continue;
} }
if (networkManager.DistributedAuthorityMode) // Read the NetworkVarible value
{ NetworkVariableFields[j].ReadField(reader);
// Explicit setting of the NetworkVariableType is only needed for CMB Runtime
reader.ReadValueSafe(out NetworkVariableType _);
reader.ReadValueSafe(out ushort size);
var start_marker = reader.Position;
NetworkVariableFields[j].ReadField(reader);
if (reader.Position - start_marker != size)
{
Debug.LogError("Mismatched network variable size");
}
}
else
{
NetworkVariableFields[j].ReadField(reader);
}
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) // When EnsureNetworkVariableLengthSafety or DistributedAuthorityMode always do a bounds check
if (ensureLengthSafety || distributedAuthority)
{ {
if (reader.Position > (readStartPos + varSize)) if (reader.Position > (readStartPos + varSize))
{ {
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{ {
NetworkLog.LogWarning($"Var data read too far. {reader.Position - (readStartPos + varSize)} bytes."); NetworkLog.LogWarning($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{NetworkVariableFields[j].Name}] NetworkVariable data read too big. {reader.Position - (readStartPos + varSize)} bytes.");
} }
reader.Seek(readStartPos + varSize); reader.Seek(readStartPos + varSize);
@@ -1254,7 +1271,7 @@ namespace Unity.Netcode
{ {
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{ {
NetworkLog.LogWarning($"Var data read too little. {(readStartPos + varSize) - reader.Position} bytes."); NetworkLog.LogWarning($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{NetworkVariableFields[j].Name}] NetworkVariable data read too small. {(readStartPos + varSize) - reader.Position} bytes.");
} }
reader.Seek(readStartPos + varSize); reader.Seek(readStartPos + varSize);
@@ -1264,7 +1281,7 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// Gets the local instance of a object with a given NetworkId /// Gets the local instance of a NetworkObject with a given NetworkId.
/// </summary> /// </summary>
/// <param name="networkId"></param> /// <param name="networkId"></param>
/// <returns></returns> /// <returns></returns>
@@ -1275,14 +1292,14 @@ namespace Unity.Netcode
/// <summary> /// <summary>
/// Override this method if your derived NetworkBehaviour requires custom synchronization data. /// Override this method if your derived NetworkBehaviour requires custom synchronization data.
/// Note: Use of this method is only for the initial client synchronization of NetworkBehaviours /// Use of this method is only for the initial client synchronization of NetworkBehaviours
/// and will increase the payload size for client synchronization and dynamically spawned /// and will increase the payload size for client synchronization and dynamically spawned
/// <see cref="NetworkObject"/>s. /// <see cref="NetworkObject"/>s.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// When serializing (writing) this will be invoked during the client synchronization period and /// When serializing (writing), this method is invoked during the client synchronization period and
/// when spawning new NetworkObjects. /// when spawning new NetworkObjects.
/// When deserializing (reading), this will be invoked prior to the NetworkBehaviour's associated /// When deserializing (reading), this method is invoked prior to the NetworkBehaviour's associated
/// NetworkObject being spawned. /// NetworkObject being spawned.
/// </remarks> /// </remarks>
/// <param name="serializer">The serializer to use to read and write the data.</param> /// <param name="serializer">The serializer to use to read and write the data.</param>
@@ -1305,10 +1322,10 @@ namespace Unity.Netcode
/// The relative client identifier targeted for the serialization of this <see cref="NetworkBehaviour"/> instance. /// The relative client identifier targeted for the serialization of this <see cref="NetworkBehaviour"/> instance.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This value will be set prior to <see cref="OnSynchronize{T}(ref BufferSerializer{T})"/> being invoked. /// This value is set prior to <see cref="OnSynchronize{T}(ref BufferSerializer{T})"/> being invoked.
/// For writing (server-side), this is useful to know which client will receive the serialized data. /// For writing (server-side), this is useful to know which client will receive the serialized data.
/// For reading (client-side), this will be the <see cref="NetworkManager.LocalClientId"/>. /// For reading (client-side), this will be the <see cref="NetworkManager.LocalClientId"/>.
/// When synchronization of this instance is complete, this value will be reset to 0 /// When synchronization of this instance is complete, this value is reset to 0.
/// </remarks> /// </remarks>
protected ulong m_TargetIdBeingSynchronized { get; private set; } protected ulong m_TargetIdBeingSynchronized { get; private set; }
@@ -1431,9 +1448,8 @@ namespace Unity.Netcode
/// <summary> /// <summary>
/// Invoked when the <see cref="GameObject"/> the <see cref="NetworkBehaviour"/> is attached to. /// Invoked when the <see cref="GameObject"/> the <see cref="NetworkBehaviour"/> is attached to is destroyed.
/// NOTE: If you override this, you will want to always invoke this base class version of this /// If you override this, you must always invoke the base class version of this <see cref="OnDestroy"/> method.
/// <see cref="OnDestroy"/> method!!
/// </summary> /// </summary>
public virtual void OnDestroy() public virtual void OnDestroy()
{ {

View File

@@ -8,7 +8,6 @@ using UnityEditor;
#endif #endif
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using Debug = UnityEngine.Debug; using Debug = UnityEngine.Debug;
using Unity.Netcode.Components;
namespace Unity.Netcode namespace Unity.Netcode
{ {
@@ -29,13 +28,40 @@ namespace Unity.Netcode
// RuntimeAccessModifiersILPP will make this `public` // RuntimeAccessModifiersILPP will make this `public`
internal static readonly Dictionary<uint, RpcReceiveHandler> __rpc_func_table = new Dictionary<uint, RpcReceiveHandler>(); internal static readonly Dictionary<uint, RpcReceiveHandler> __rpc_func_table = new Dictionary<uint, RpcReceiveHandler>();
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
// RuntimeAccessModifiersILPP will make this `public` // RuntimeAccessModifiersILPP will make this `public`
internal static readonly Dictionary<uint, string> __rpc_name_table = new Dictionary<uint, string>(); internal static readonly Dictionary<uint, string> __rpc_name_table = new Dictionary<uint, string>();
#endif #endif
#pragma warning restore IDE1006 // restore naming rule violation check #pragma warning restore IDE1006 // restore naming rule violation check
#if DEVELOPMENT_BUILD || UNITY_EDITOR
private static List<Type> s_SerializedType = new List<Type>();
// This is used to control the serialized type not optimized messaging for integration test purposes
internal static bool DisableNotOptimizedSerializedType;
/// <summary>
/// Until all serialized types are optimized for the distributed authority network topology,
/// this will handle the notification to the user that the type being serialized is not yet
/// optimized but will only log the message once to prevent log spamming.
/// </summary>
internal static void LogSerializedTypeNotOptimized<T>()
{
if (DisableNotOptimizedSerializedType)
{
return;
}
var type = typeof(T);
if (!s_SerializedType.Contains(type))
{
s_SerializedType.Add(type);
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
{
Debug.LogWarning($"[{type.Name}] Serialized type has not been optimized for use with Distributed Authority!");
}
}
}
#endif
internal static bool IsDistributedAuthority; internal static bool IsDistributedAuthority;
/// <summary> /// <summary>
@@ -188,25 +214,25 @@ namespace Unity.Netcode
} }
} }
internal Dictionary<ulong, NetworkTransform> NetworkTransformUpdate = new Dictionary<ulong, NetworkTransform>(); internal Dictionary<ulong, NetworkObject> NetworkTransformUpdate = new Dictionary<ulong, NetworkObject>();
#if COM_UNITY_MODULES_PHYSICS #if COM_UNITY_MODULES_PHYSICS
internal Dictionary<ulong, NetworkTransform> NetworkTransformFixedUpdate = new Dictionary<ulong, NetworkTransform>(); internal Dictionary<ulong, NetworkObject> NetworkTransformFixedUpdate = new Dictionary<ulong, NetworkObject>();
#endif #endif
internal void NetworkTransformRegistration(NetworkTransform networkTransform, bool forUpdate = true, bool register = true) internal void NetworkTransformRegistration(NetworkObject networkObject, bool onUpdate = true, bool register = true)
{ {
if (forUpdate) if (onUpdate)
{ {
if (register) if (register)
{ {
if (!NetworkTransformUpdate.ContainsKey(networkTransform.NetworkObjectId)) if (!NetworkTransformUpdate.ContainsKey(networkObject.NetworkObjectId))
{ {
NetworkTransformUpdate.Add(networkTransform.NetworkObjectId, networkTransform); NetworkTransformUpdate.Add(networkObject.NetworkObjectId, networkObject);
} }
} }
else else
{ {
NetworkTransformUpdate.Remove(networkTransform.NetworkObjectId); NetworkTransformUpdate.Remove(networkObject.NetworkObjectId);
} }
} }
#if COM_UNITY_MODULES_PHYSICS #if COM_UNITY_MODULES_PHYSICS
@@ -214,14 +240,14 @@ namespace Unity.Netcode
{ {
if (register) if (register)
{ {
if (!NetworkTransformFixedUpdate.ContainsKey(networkTransform.NetworkObjectId)) if (!NetworkTransformFixedUpdate.ContainsKey(networkObject.NetworkObjectId))
{ {
NetworkTransformFixedUpdate.Add(networkTransform.NetworkObjectId, networkTransform); NetworkTransformFixedUpdate.Add(networkObject.NetworkObjectId, networkObject);
} }
} }
else else
{ {
NetworkTransformFixedUpdate.Remove(networkTransform.NetworkObjectId); NetworkTransformFixedUpdate.Remove(networkObject.NetworkObjectId);
} }
} }
#endif #endif
@@ -262,11 +288,21 @@ namespace Unity.Netcode
#if COM_UNITY_MODULES_PHYSICS #if COM_UNITY_MODULES_PHYSICS
case NetworkUpdateStage.FixedUpdate: case NetworkUpdateStage.FixedUpdate:
{ {
foreach (var networkTransformEntry in NetworkTransformFixedUpdate) foreach (var networkObjectEntry in NetworkTransformFixedUpdate)
{ {
if (networkTransformEntry.Value.gameObject.activeInHierarchy && networkTransformEntry.Value.IsSpawned) // if not active or not spawned then skip
if (!networkObjectEntry.Value.gameObject.activeInHierarchy || !networkObjectEntry.Value.IsSpawned)
{ {
networkTransformEntry.Value.OnFixedUpdate(); continue;
}
foreach (var networkTransformEntry in networkObjectEntry.Value.NetworkTransforms)
{
// only update if enabled
if (networkTransformEntry.enabled)
{
networkTransformEntry.OnFixedUpdate();
}
} }
} }
} }
@@ -281,11 +317,21 @@ namespace Unity.Netcode
case NetworkUpdateStage.PreLateUpdate: case NetworkUpdateStage.PreLateUpdate:
{ {
// Non-physics based non-authority NetworkTransforms update their states after all other components // Non-physics based non-authority NetworkTransforms update their states after all other components
foreach (var networkTransformEntry in NetworkTransformUpdate) foreach (var networkObjectEntry in NetworkTransformUpdate)
{ {
if (networkTransformEntry.Value.gameObject.activeInHierarchy && networkTransformEntry.Value.IsSpawned) // if not active or not spawned then skip
if (!networkObjectEntry.Value.gameObject.activeInHierarchy || !networkObjectEntry.Value.IsSpawned)
{ {
networkTransformEntry.Value.OnUpdate(); continue;
}
foreach (var networkTransformEntry in networkObjectEntry.Value.NetworkTransforms)
{
// only update if enabled
if (networkTransformEntry.enabled)
{
networkTransformEntry.OnUpdate();
}
} }
} }
} }
@@ -1062,6 +1108,13 @@ namespace Unity.Netcode
internal void Initialize(bool server) internal void Initialize(bool server)
{ {
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (!DisableNotOptimizedSerializedType)
{
s_SerializedType.Clear();
}
#endif
#if COM_UNITY_MODULES_PHYSICS #if COM_UNITY_MODULES_PHYSICS
NetworkTransformFixedUpdate.Clear(); NetworkTransformFixedUpdate.Clear();
#endif #endif
@@ -1250,17 +1303,8 @@ namespace Unity.Netcode
{ {
SpawnManager.ServerSpawnSceneObjectsOnStartSweep(); SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
// Notify the server that all in-scnee placed NetworkObjects are spawned at this time.
foreach (var networkObject in SpawnManager.SpawnedObjectsList)
{
networkObject.InternalInSceneNetworkObjectsSpawned();
}
// Notify the server that everything should be synchronized/spawned at this time. // Notify the server that everything should be synchronized/spawned at this time.
foreach (var networkObject in SpawnManager.SpawnedObjectsList) SpawnManager.NotifyNetworkObjectsSynchronized();
{
networkObject.InternalNetworkSessionSynchronized();
}
OnServerStarted?.Invoke(); OnServerStarted?.Invoke();
ConnectionManager.LocalClient.IsApproved = true; ConnectionManager.LocalClient.IsApproved = true;
return true; return true;
@@ -1407,17 +1451,9 @@ namespace Unity.Netcode
} }
SpawnManager.ServerSpawnSceneObjectsOnStartSweep(); SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
// Notify the host that all in-scnee placed NetworkObjects are spawned at this time.
foreach (var networkObject in SpawnManager.SpawnedObjectsList)
{
networkObject.InternalInSceneNetworkObjectsSpawned();
}
// Notify the host that everything should be synchronized/spawned at this time. // Notify the host that everything should be synchronized/spawned at this time.
foreach (var networkObject in SpawnManager.SpawnedObjectsList) SpawnManager.NotifyNetworkObjectsSynchronized();
{
networkObject.InternalNetworkSessionSynchronized();
}
OnServerStarted?.Invoke(); OnServerStarted?.Invoke();
OnClientStarted?.Invoke(); OnClientStarted?.Invoke();

View File

@@ -37,7 +37,7 @@ namespace Unity.Netcode
internal uint PrefabGlobalObjectIdHash; internal uint PrefabGlobalObjectIdHash;
/// <summary> /// <summary>
/// This is the source prefab of an in-scene placed NetworkObject. This is not set for in-scene /// This is the source prefab of an in-scene placed NetworkObject. This is not set for in-scene
/// placd NetworkObjects that are not prefab instances, dynamically spawned prefab instances, /// placd NetworkObjects that are not prefab instances, dynamically spawned prefab instances,
/// or for network prefab assets. /// or for network prefab assets.
/// </summary> /// </summary>
@@ -207,8 +207,8 @@ namespace Unity.Netcode
} }
/// <summary> /// <summary>
/// This checks to see if this NetworkObject is an in-scene placed prefab instance. If so it will /// This checks to see if this NetworkObject is an in-scene placed prefab instance. If so it will
/// automatically find the source prefab asset's GlobalObjectIdHash value, assign it to /// automatically find the source prefab asset's GlobalObjectIdHash value, assign it to
/// InScenePlacedSourceGlobalObjectIdHash and mark this as being in-scene placed. /// InScenePlacedSourceGlobalObjectIdHash and mark this as being in-scene placed.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
@@ -540,8 +540,8 @@ namespace Unity.Netcode
/// permission failure status codes will be returned via <see cref="OnOwnershipPermissionsFailure"/>. /// permission failure status codes will be returned via <see cref="OnOwnershipPermissionsFailure"/>.
/// <see cref="Locked"/>: The <see cref="NetworkObject"/> is locked and ownership cannot be acquired. /// <see cref="Locked"/>: The <see cref="NetworkObject"/> is locked and ownership cannot be acquired.
/// <see cref="RequestRequired"/>: The <see cref="NetworkObject"/> requires an ownership request via <see cref="RequestOwnership"/>. /// <see cref="RequestRequired"/>: The <see cref="NetworkObject"/> requires an ownership request via <see cref="RequestOwnership"/>.
/// <see cref="RequestInProgress"/>: The <see cref="NetworkObject"/> already is processing an ownership request and ownership cannot be acquired at this time. /// <see cref="RequestInProgress"/>: The <see cref="NetworkObject"/> is already processing an ownership request and ownership cannot be acquired at this time.
/// <see cref="NotTransferrable": The <see cref="NetworkObject"/> does not have the <see cref="OwnershipStatus.Transferable"/> flag set and ownership cannot be acquired. /// <see cref="NotTransferrable"/>: The <see cref="NetworkObject"/> does not have the <see cref="OwnershipStatus.Transferable"/> flag set and ownership cannot be acquired.
/// </summary> /// </summary>
public enum OwnershipPermissionsFailureStatus public enum OwnershipPermissionsFailureStatus
{ {
@@ -1547,6 +1547,11 @@ namespace Unity.Netcode
if (NetworkManager.DistributedAuthorityMode) if (NetworkManager.DistributedAuthorityMode)
{ {
if (NetworkManager.LocalClient == null || !NetworkManager.IsConnectedClient || !NetworkManager.ConnectionManager.LocalClient.IsApproved)
{
Debug.LogError($"Cannot spawn {name} until the client is fully connected to the session!");
return;
}
if (NetworkManager.NetworkConfig.EnableSceneManagement) if (NetworkManager.NetworkConfig.EnableSceneManagement)
{ {
NetworkSceneHandle = NetworkManager.SceneManager.ClientSceneHandleToServerSceneHandle[gameObject.scene.handle]; NetworkSceneHandle = NetworkManager.SceneManager.ClientSceneHandleToServerSceneHandle[gameObject.scene.handle];
@@ -1638,7 +1643,7 @@ namespace Unity.Netcode
return null; return null;
} }
ownerClientId = networkManager.DistributedAuthorityMode ? networkManager.LocalClientId : NetworkManager.ServerClientId; ownerClientId = networkManager.DistributedAuthorityMode ? networkManager.LocalClientId : ownerClientId;
// We only need to check for authority when running in client-server mode // We only need to check for authority when running in client-server mode
if (!networkManager.IsServer && !networkManager.DistributedAuthorityMode) if (!networkManager.IsServer && !networkManager.DistributedAuthorityMode)
{ {
@@ -2354,7 +2359,7 @@ namespace Unity.Netcode
{ {
m_ChildNetworkBehaviours.Add(networkBehaviours[i]); m_ChildNetworkBehaviours.Add(networkBehaviours[i]);
var type = networkBehaviours[i].GetType(); var type = networkBehaviours[i].GetType();
if (type.IsInstanceOfType(typeof(NetworkTransform)) || type.IsSubclassOf(typeof(NetworkTransform))) if (type == typeof(NetworkTransform) || type.IsInstanceOfType(typeof(NetworkTransform)) || type.IsSubclassOf(typeof(NetworkTransform)))
{ {
if (NetworkTransforms == null) if (NetworkTransforms == null)
{ {
@@ -2436,10 +2441,10 @@ namespace Unity.Netcode
if (NetworkManager.DistributedAuthorityMode) if (NetworkManager.DistributedAuthorityMode)
{ {
var readerPosition = reader.Position; var readerPosition = reader.Position;
reader.ReadValueSafe(out ushort behaviorCount); reader.ReadValueSafe(out ushort behaviourCount);
if (behaviorCount != ChildNetworkBehaviours.Count) if (behaviourCount != ChildNetworkBehaviours.Count)
{ {
Debug.LogError($"Network Behavior Count Mismatch! [{readerPosition}][{reader.Position}]"); Debug.LogError($"[{name}] Network Behavior Count Mismatch! [In: {behaviourCount} vs Local: {ChildNetworkBehaviours.Count}][StartReaderPos: {readerPosition}] CurrentReaderPos: {reader.Position}]");
return false; return false;
} }
} }

View File

@@ -31,7 +31,7 @@ namespace Unity.Netcode
public void Handle(ref NetworkContext context) public void Handle(ref NetworkContext context)
{ {
var networkManager = (NetworkManager)context.SystemOwner; var networkManager = (NetworkManager)context.SystemOwner;
if ((ShouldSynchronize || networkManager.CMBServiceConnection) && networkManager.DistributedAuthorityMode && networkManager.LocalClient.IsSessionOwner) if (ShouldSynchronize && networkManager.NetworkConfig.EnableSceneManagement && networkManager.DistributedAuthorityMode && networkManager.LocalClient.IsSessionOwner)
{ {
networkManager.SceneManager.SynchronizeNetworkObjects(ClientId); networkManager.SceneManager.SynchronizeNetworkObjects(ClientId);
} }

View File

@@ -224,10 +224,7 @@ namespace Unity.Netcode
networkManager.ConnectionManager.InvokeOnClientConnectedCallback(context.SenderId); networkManager.ConnectionManager.InvokeOnClientConnectedCallback(context.SenderId);
// For convenience, notify all NetworkBehaviours that synchronization is complete. // For convenience, notify all NetworkBehaviours that synchronization is complete.
foreach (var networkObject in networkManager.SpawnManager.SpawnedObjectsList) networkManager.SpawnManager.NotifyNetworkObjectsSynchronized();
{
networkObject.InternalNetworkSessionSynchronized();
}
} }
else else
{ {
@@ -240,16 +237,12 @@ namespace Unity.Netcode
if (!IsRestoredSession) if (!IsRestoredSession)
{ {
// Synchronize the service with the initial session owner's loaded scenes and spawned objects
networkManager.SceneManager.SynchronizeNetworkObjects(NetworkManager.ServerClientId);
// Spawn any in-scene placed NetworkObjects // Spawn any in-scene placed NetworkObjects
networkManager.SpawnManager.ServerSpawnSceneObjectsOnStartSweep(); networkManager.SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
// With scene management enabled and since the session owner doesn't send a Synchronize scene event synchronize itself,
// we need to notify the session owner that all in-scnee placed NetworkObjects are spawned at this time.
foreach (var networkObject in networkManager.SpawnManager.SpawnedObjectsList)
{
networkObject.InternalInSceneNetworkObjectsSpawned();
}
// Spawn the local player of the session owner // Spawn the local player of the session owner
if (networkManager.AutoSpawnPlayerPrefabClientSide) if (networkManager.AutoSpawnPlayerPrefabClientSide)
{ {
@@ -261,10 +254,7 @@ namespace Unity.Netcode
// With scene management enabled and since the session owner doesn't send a Synchronize scene event synchronize itself, // With scene management enabled and since the session owner doesn't send a Synchronize scene event synchronize itself,
// we need to notify the session owner that everything should be synchronized/spawned at this time. // we need to notify the session owner that everything should be synchronized/spawned at this time.
foreach (var networkObject in networkManager.SpawnManager.SpawnedObjectsList) networkManager.SpawnManager.NotifyNetworkObjectsSynchronized();
{
networkObject.InternalNetworkSessionSynchronized();
}
// When scene management is enabled and since the session owner is synchronizing the service (i.e. acting like host), // When scene management is enabled and since the session owner is synchronizing the service (i.e. acting like host),
// we need to locallyh invoke the OnClientConnected callback at this point in time. // we need to locallyh invoke the OnClientConnected callback at this point in time.

View File

@@ -25,8 +25,6 @@ namespace Unity.Netcode
private const string k_Name = "NetworkVariableDeltaMessage"; private const string k_Name = "NetworkVariableDeltaMessage";
// DANGO-TODO: Made some modifications here that overlap/won't play nice with EnsureNetworkVariableLenghtSafety.
// Worth either merging or more cleanly separating these codepaths.
public void Serialize(FastBufferWriter writer, int targetVersion) public void Serialize(FastBufferWriter writer, int targetVersion)
{ {
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex))) if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
@@ -126,10 +124,6 @@ namespace Unity.Netcode
} }
else else
{ {
// DANGO-TODO:
// Complex types with custom type serialization (either registered custom types or INetworkSerializable implementations) will be problematic
// Non-complex types always provide a full state update per delta
// DANGO-TODO: Add NetworkListEvent<T>.EventType awareness to the cloud-state server
if (networkManager.DistributedAuthorityMode) if (networkManager.DistributedAuthorityMode)
{ {
var size_marker = writer.Position; var size_marker = writer.Position;
@@ -167,8 +161,6 @@ namespace Unity.Netcode
return true; return true;
} }
// DANGO-TODO: Made some modifications here that overlap/won't play nice with EnsureNetworkVariableLenghtSafety.
// Worth either merging or more cleanly separating these codepaths.
public void Handle(ref NetworkContext context) public void Handle(ref NetworkContext context)
{ {
var networkManager = (NetworkManager)context.SystemOwner; var networkManager = (NetworkManager)context.SystemOwner;

View File

@@ -41,7 +41,7 @@ namespace Unity.Netcode
payload = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.None, reader.Length - reader.Position); payload = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.None, reader.Length - reader.Position);
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
if (NetworkBehaviour.__rpc_name_table[networkBehaviour.GetType()].TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName)) if (NetworkBehaviour.__rpc_name_table[networkBehaviour.GetType()].TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName))
{ {
networkManager.NetworkMetrics.TrackRpcReceived( networkManager.NetworkMetrics.TrackRpcReceived(

View File

@@ -36,12 +36,12 @@ namespace Unity.Netcode
private protected void SendMessageToClient(NetworkBehaviour behaviour, ulong clientId, ref RpcMessage message, NetworkDelivery delivery) private protected void SendMessageToClient(NetworkBehaviour behaviour, ulong clientId, ref RpcMessage message, NetworkDelivery delivery)
{ {
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
var size = var size =
#endif #endif
behaviour.NetworkManager.MessageManager.SendMessage(ref message, delivery, clientId); behaviour.NetworkManager.MessageManager.SendMessage(ref message, delivery, clientId);
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
if (NetworkBehaviour.__rpc_name_table[behaviour.GetType()].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName)) if (NetworkBehaviour.__rpc_name_table[behaviour.GetType()].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName))
{ {
behaviour.NetworkManager.NetworkMetrics.TrackRpcSent( behaviour.NetworkManager.NetworkMetrics.TrackRpcSent(

View File

@@ -46,7 +46,7 @@ namespace Unity.Netcode
message.Handle(ref context); message.Handle(ref context);
length = tempBuffer.Length; length = tempBuffer.Length;
} }
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
if (NetworkBehaviour.__rpc_name_table[behaviour.GetType()].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName)) if (NetworkBehaviour.__rpc_name_table[behaviour.GetType()].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName))
{ {
networkManager.NetworkMetrics.TrackRpcSent( networkManager.NetworkMetrics.TrackRpcSent(

View File

@@ -29,6 +29,12 @@ namespace Unity.Netcode
{ {
continue; continue;
} }
// The CMB-Service holds ID 0 and should not be added to the targets
if (clientId == NetworkManager.ServerClientId && m_NetworkManager.CMBServiceConnection)
{
continue;
}
m_GroupSendTarget.Add(clientId); m_GroupSendTarget.Add(clientId);
} }
} }
@@ -41,6 +47,12 @@ namespace Unity.Netcode
continue; continue;
} }
// The CMB-Service holds ID 0 and should not be added to the targets
if (clientId == NetworkManager.ServerClientId && m_NetworkManager.CMBServiceConnection)
{
continue;
}
if (clientId == m_NetworkManager.LocalClientId) if (clientId == m_NetworkManager.LocalClientId)
{ {
m_LocalSendRpcTarget.Send(behaviour, ref message, delivery, rpcParams); m_LocalSendRpcTarget.Send(behaviour, ref message, delivery, rpcParams);

View File

@@ -17,13 +17,18 @@ namespace Unity.Netcode
internal override void Send(NetworkBehaviour behaviour, ref RpcMessage message, NetworkDelivery delivery, RpcParams rpcParams) internal override void Send(NetworkBehaviour behaviour, ref RpcMessage message, NetworkDelivery delivery, RpcParams rpcParams)
{ {
// If there are no targets then don't attempt to send anything.
if (TargetClientIds.Length == 0 && Ids.Count == 0)
{
return;
}
var proxyMessage = new ProxyMessage { Delivery = delivery, TargetClientIds = TargetClientIds.AsArray(), WrappedMessage = message }; var proxyMessage = new ProxyMessage { Delivery = delivery, TargetClientIds = TargetClientIds.AsArray(), WrappedMessage = message };
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
var size = var size =
#endif #endif
behaviour.NetworkManager.MessageManager.SendMessage(ref proxyMessage, delivery, NetworkManager.ServerClientId); behaviour.NetworkManager.MessageManager.SendMessage(ref proxyMessage, delivery, NetworkManager.ServerClientId);
#if DEVELOPMENT_BUILD || UNITY_EDITOR #if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
if (NetworkBehaviour.__rpc_name_table[behaviour.GetType()].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName)) if (NetworkBehaviour.__rpc_name_table[behaviour.GetType()].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName))
{ {
foreach (var clientId in TargetClientIds) foreach (var clientId in TargetClientIds)

View File

@@ -94,18 +94,18 @@ namespace Unity.Netcode
{ {
case NetworkListEvent<T>.EventType.Add: case NetworkListEvent<T>.EventType.Add:
{ {
NetworkVariableSerialization<T>.Write(writer, ref element.Value); NetworkVariableSerialization<T>.Serializer.Write(writer, ref element.Value);
} }
break; break;
case NetworkListEvent<T>.EventType.Insert: case NetworkListEvent<T>.EventType.Insert:
{ {
BytePacker.WriteValueBitPacked(writer, element.Index); BytePacker.WriteValueBitPacked(writer, element.Index);
NetworkVariableSerialization<T>.Write(writer, ref element.Value); NetworkVariableSerialization<T>.Serializer.Write(writer, ref element.Value);
} }
break; break;
case NetworkListEvent<T>.EventType.Remove: case NetworkListEvent<T>.EventType.Remove:
{ {
NetworkVariableSerialization<T>.Write(writer, ref element.Value); NetworkVariableSerialization<T>.Serializer.Write(writer, ref element.Value);
} }
break; break;
case NetworkListEvent<T>.EventType.RemoveAt: case NetworkListEvent<T>.EventType.RemoveAt:
@@ -116,7 +116,7 @@ namespace Unity.Netcode
case NetworkListEvent<T>.EventType.Value: case NetworkListEvent<T>.EventType.Value:
{ {
BytePacker.WriteValueBitPacked(writer, element.Index); BytePacker.WriteValueBitPacked(writer, element.Index);
NetworkVariableSerialization<T>.Write(writer, ref element.Value); NetworkVariableSerialization<T>.Serializer.Write(writer, ref element.Value);
} }
break; break;
case NetworkListEvent<T>.EventType.Clear: case NetworkListEvent<T>.EventType.Clear:
@@ -133,13 +133,13 @@ namespace Unity.Netcode
{ {
if (m_NetworkManager.DistributedAuthorityMode) if (m_NetworkManager.DistributedAuthorityMode)
{ {
writer.WriteValueSafe(NetworkVariableSerialization<T>.Type); writer.WriteValueSafe(NetworkVariableSerialization<T>.Serializer.Type);
if (NetworkVariableSerialization<T>.Type == CollectionItemType.Unmanaged) if (NetworkVariableSerialization<T>.Serializer.Type == NetworkVariableType.Unmanaged)
{ {
// Write the size of the unmanaged serialized type as it has a fixed size. This allows the CMB runtime to correctly read the unmanged type. // Write the size of the unmanaged serialized type as it has a fixed size. This allows the CMB runtime to correctly read the unmanged type.
var placeholder = new T(); var placeholder = new T();
var startPos = writer.Position; var startPos = writer.Position;
NetworkVariableSerialization<T>.Write(writer, ref placeholder); NetworkVariableSerialization<T>.Serializer.Write(writer, ref placeholder);
var size = writer.Position - startPos; var size = writer.Position - startPos;
writer.Seek(startPos); writer.Seek(startPos);
BytePacker.WriteValueBitPacked(writer, size); BytePacker.WriteValueBitPacked(writer, size);
@@ -148,7 +148,7 @@ namespace Unity.Netcode
writer.WriteValueSafe((ushort)m_List.Length); writer.WriteValueSafe((ushort)m_List.Length);
for (int i = 0; i < m_List.Length; i++) for (int i = 0; i < m_List.Length; i++)
{ {
NetworkVariableSerialization<T>.Write(writer, ref m_List.ElementAt(i)); NetworkVariableSerialization<T>.Serializer.Write(writer, ref m_List.ElementAt(i));
} }
} }
@@ -158,9 +158,9 @@ namespace Unity.Netcode
m_List.Clear(); m_List.Clear();
if (m_NetworkManager.DistributedAuthorityMode) if (m_NetworkManager.DistributedAuthorityMode)
{ {
// Collection item type is used by the CMB rust service, drop value here. SerializationTools.ReadType(reader, NetworkVariableSerialization<T>.Serializer);
reader.ReadValueSafe(out CollectionItemType type); // Collection item type is used by the DA server, drop value here.
if (type == CollectionItemType.Unmanaged) if (NetworkVariableSerialization<T>.Serializer.Type == NetworkVariableType.Unmanaged)
{ {
ByteUnpacker.ReadValueBitPacked(reader, out int _); ByteUnpacker.ReadValueBitPacked(reader, out int _);
} }
@@ -169,7 +169,7 @@ namespace Unity.Netcode
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
var value = new T(); var value = new T();
NetworkVariableSerialization<T>.Read(reader, ref value); NetworkVariableSerialization<T>.Serializer.Read(reader, ref value);
m_List.Add(value); m_List.Add(value);
} }
} }
@@ -186,7 +186,7 @@ namespace Unity.Netcode
case NetworkListEvent<T>.EventType.Add: case NetworkListEvent<T>.EventType.Add:
{ {
var value = new T(); var value = new T();
NetworkVariableSerialization<T>.Read(reader, ref value); NetworkVariableSerialization<T>.Serializer.Read(reader, ref value);
m_List.Add(value); m_List.Add(value);
if (OnListChanged != null) if (OnListChanged != null)
@@ -215,7 +215,7 @@ namespace Unity.Netcode
{ {
ByteUnpacker.ReadValueBitPacked(reader, out int index); ByteUnpacker.ReadValueBitPacked(reader, out int index);
var value = new T(); var value = new T();
NetworkVariableSerialization<T>.Read(reader, ref value); NetworkVariableSerialization<T>.Serializer.Read(reader, ref value);
if (index < m_List.Length) if (index < m_List.Length)
{ {
@@ -252,7 +252,7 @@ namespace Unity.Netcode
case NetworkListEvent<T>.EventType.Remove: case NetworkListEvent<T>.EventType.Remove:
{ {
var value = new T(); var value = new T();
NetworkVariableSerialization<T>.Read(reader, ref value); NetworkVariableSerialization<T>.Serializer.Read(reader, ref value);
int index = m_List.IndexOf(value); int index = m_List.IndexOf(value);
if (index == -1) if (index == -1)
{ {
@@ -315,7 +315,7 @@ namespace Unity.Netcode
{ {
ByteUnpacker.ReadValueBitPacked(reader, out int index); ByteUnpacker.ReadValueBitPacked(reader, out int index);
var value = new T(); var value = new T();
NetworkVariableSerialization<T>.Read(reader, ref value); NetworkVariableSerialization<T>.Serializer.Read(reader, ref value);
if (index >= m_List.Length) if (index >= m_List.Length)
{ {
throw new Exception("Shouldn't be here, index is higher than list length"); throw new Exception("Shouldn't be here, index is higher than list length");
@@ -393,7 +393,8 @@ namespace Unity.Netcode
// check write permissions // check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId)) if (!CanClientWrite(m_NetworkManager.LocalClientId))
{ {
throw new InvalidOperationException("Client is not allowed to write to this NetworkList"); LogWritePermissionError();
return;
} }
m_List.Add(item); m_List.Add(item);
@@ -414,7 +415,8 @@ namespace Unity.Netcode
// check write permissions // check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId)) if (!CanClientWrite(m_NetworkManager.LocalClientId))
{ {
throw new InvalidOperationException("Client is not allowed to write to this NetworkList"); LogWritePermissionError();
return;
} }
m_List.Clear(); m_List.Clear();
@@ -440,7 +442,8 @@ namespace Unity.Netcode
// check write permissions // check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId)) if (!CanClientWrite(m_NetworkManager.LocalClientId))
{ {
throw new InvalidOperationException("Client is not allowed to write to this NetworkList"); LogWritePermissionError();
return false;
} }
int index = m_List.IndexOf(item); int index = m_List.IndexOf(item);
@@ -475,7 +478,8 @@ namespace Unity.Netcode
// check write permissions // check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId)) if (!CanClientWrite(m_NetworkManager.LocalClientId))
{ {
throw new InvalidOperationException("Client is not allowed to write to this NetworkList"); LogWritePermissionError();
return;
} }
if (index < m_List.Length) if (index < m_List.Length)
@@ -520,6 +524,8 @@ namespace Unity.Netcode
HandleAddListEvent(listEvent); HandleAddListEvent(listEvent);
} }
/// <inheritdoc /> /// <inheritdoc />
public T this[int index] public T this[int index]
{ {
@@ -529,7 +535,8 @@ namespace Unity.Netcode
// check write permissions // check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId)) if (!CanClientWrite(m_NetworkManager.LocalClientId))
{ {
throw new InvalidOperationException("Client is not allowed to write to this NetworkList"); LogWritePermissionError();
return;
} }
var previousValue = m_List[index]; var previousValue = m_List[index];

View File

@@ -94,27 +94,59 @@ namespace Unity.Netcode
/// <summary> /// <summary>
/// The value of the NetworkVariable container /// The value of the NetworkVariable container
/// </summary> /// </summary>
/// <remarks>
/// When assigning collections to <see cref="Value"/>, unless it is a completely new collection this will not
/// detect any deltas with most managed collection classes since assignment of one collection value to another
/// is actually just a reference to the collection itself. <br />
/// To detect deltas in a collection, you should invoke <see cref="CheckDirtyState"/> after making modifications to the collection.
/// </remarks>
public virtual T Value public virtual T Value
{ {
get => m_InternalValue; get => m_InternalValue;
set set
{ {
// Compare bitwise if (m_NetworkManager && !CanClientWrite(m_NetworkManager.LocalClientId))
if (NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref value))
{ {
LogWritePermissionError();
return; return;
} }
if (m_NetworkManager && !CanClientWrite(m_NetworkManager.LocalClientId)) // Compare the Value being applied to the current value
if (!NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref value))
{ {
throw new InvalidOperationException($"[Client-{m_NetworkManager.LocalClientId}][{m_NetworkBehaviour.name}][{Name}] Write permissions ({WritePerm}) for this client instance is not allowed!"); T previousValue = m_InternalValue;
m_InternalValue = value;
SetDirty(true);
m_IsDisposed = false;
OnValueChanged?.Invoke(previousValue, m_InternalValue);
} }
Set(value);
m_IsDisposed = false;
} }
} }
/// <summary>
/// Invoke this method to check if a collection's items are dirty.
/// The default behavior is to exit early if the <see cref="NetworkVariable{T}"/> is already dirty.
/// </summary>
/// <param name="forceCheck"> when true, this check will force a full item collection check even if the NetworkVariable is already dirty</param>
/// <remarks>
/// This is to be used as a way to check if a <see cref="NetworkVariable{T}"/> containing a managed collection has any changees to the collection items.<br />
/// If you invoked this when a collection is dirty, it will not trigger the <see cref="OnValueChanged"/> unless you set <param name="forceCheck"/> to true. <br />
/// </remarks>
public bool CheckDirtyState(bool forceCheck = false)
{
var isDirty = base.IsDirty();
// Compare the previous with the current if not dirty or forcing a check.
if ((!isDirty || forceCheck) && !NetworkVariableSerialization<T>.AreEqual(ref m_PreviousValue, ref m_InternalValue))
{
SetDirty(true);
OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue);
m_IsDisposed = false;
isDirty = true;
}
return isDirty;
}
internal ref T RefValue() internal ref T RefValue()
{ {
return ref m_InternalValue; return ref m_InternalValue;
@@ -193,19 +225,6 @@ namespace Unity.Netcode
base.ResetDirty(); base.ResetDirty();
} }
/// <summary>
/// Sets the <see cref="Value"/>, marks the <see cref="NetworkVariable{T}"/> dirty, and invokes the <see cref="OnValueChanged"/> callback
/// if there are subscribers to that event.
/// </summary>
/// <param name="value">the new value of type `T` to be set/></param>
private protected void Set(T value)
{
SetDirty(true);
T previousValue = m_InternalValue;
m_InternalValue = value;
OnValueChanged?.Invoke(previousValue, m_InternalValue);
}
/// <summary> /// <summary>
/// Writes the variable to the writer /// Writes the variable to the writer
/// </summary> /// </summary>
@@ -222,20 +241,22 @@ namespace Unity.Netcode
/// <param name="keepDirtyDelta">Whether or not the container should keep the dirty delta, or mark the delta as consumed</param> /// <param name="keepDirtyDelta">Whether or not the container should keep the dirty delta, or mark the delta as consumed</param>
public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta) public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)
{ {
// In order to get managed collections to properly have a previous and current value, we have to
// duplicate the collection at this point before making any modifications to the current.
m_HasPreviousValue = true;
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
NetworkVariableSerialization<T>.ReadDelta(reader, ref m_InternalValue);
// todo: // todo:
// keepDirtyDelta marks a variable received as dirty and causes the server to send the value to clients // keepDirtyDelta marks a variable received as dirty and causes the server to send the value to clients
// In a prefect world, whether a variable was A) modified locally or B) received and needs retransmit // In a prefect world, whether a variable was A) modified locally or B) received and needs retransmit
// would be stored in different fields // would be stored in different fields
T previousValue = m_InternalValue;
NetworkVariableSerialization<T>.ReadDelta(reader, ref m_InternalValue);
if (keepDirtyDelta) if (keepDirtyDelta)
{ {
SetDirty(true); SetDirty(true);
} }
OnValueChanged?.Invoke(previousValue, m_InternalValue); OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -35,7 +35,17 @@ namespace Unity.Netcode
private NetworkManager m_InternalNetworkManager; private NetworkManager m_InternalNetworkManager;
internal virtual NetworkVariableType Type => NetworkVariableType.Custom; internal virtual NetworkVariableType Type => NetworkVariableType.Unknown;
internal string GetWritePermissionError()
{
return $"|Client-{m_NetworkManager.LocalClientId}|{m_NetworkBehaviour.name}|{Name}| Write permissions ({WritePerm}) for this client instance is not allowed!";
}
internal void LogWritePermissionError()
{
Debug.LogError(GetWritePermissionError());
}
private protected NetworkManager m_NetworkManager private protected NetworkManager m_NetworkManager
{ {
@@ -254,6 +264,11 @@ namespace Unity.Netcode
/// <returns>Whether or not the client has permission to read</returns> /// <returns>Whether or not the client has permission to read</returns>
public bool CanClientRead(ulong clientId) public bool CanClientRead(ulong clientId)
{ {
if (!m_NetworkBehaviour)
{
return false;
}
// When in distributed authority mode, everyone can read (but only the owner can write) // When in distributed authority mode, everyone can read (but only the owner can write)
if (m_NetworkManager != null && m_NetworkManager.DistributedAuthorityMode) if (m_NetworkManager != null && m_NetworkManager.DistributedAuthorityMode)
{ {
@@ -276,6 +291,11 @@ namespace Unity.Netcode
/// <returns>Whether or not the client has permission to write</returns> /// <returns>Whether or not the client has permission to write</returns>
public bool CanClientWrite(ulong clientId) public bool CanClientWrite(ulong clientId)
{ {
if (!m_NetworkBehaviour)
{
return false;
}
switch (WritePerm) switch (WritePerm)
{ {
default: default:
@@ -311,7 +331,6 @@ namespace Unity.Netcode
/// </summary> /// </summary>
/// <param name="reader">The stream to read the state from</param> /// <param name="reader">The stream to read the state from</param>
public abstract void ReadField(FastBufferReader reader); public abstract void ReadField(FastBufferReader reader);
/// <summary> /// <summary>
/// Reads delta from the reader and applies them to the internal value /// Reads delta from the reader and applies them to the internal value
/// </summary> /// </summary>
@@ -327,47 +346,4 @@ namespace Unity.Netcode
m_InternalNetworkManager = null; m_InternalNetworkManager = null;
} }
} }
/// <summary>
/// Enum representing the different types of Network Variables.
/// </summary>
public enum NetworkVariableType : byte
{
/// <summary>
/// Value
/// Used for all of the basic NetworkVariables that contain a single value
/// </summary>
Value = 0,
/// <summary>
/// Custom
/// For any custom implemented extension of the NetworkVariableBase
/// </summary>
Custom = 1,
/// <summary>
/// NetworkList
/// </summary>
NetworkList = 2
}
public enum CollectionItemType : byte
{
/// <summary>
/// For any type that is not valid inside a NetworkVariable collection
/// </summary>
Unknown = 0,
/// <summary>
/// The following types are valid types inside of NetworkVariable collections
/// </summary>
Short = 1,
UShort = 2,
Int = 3,
UInt = 4,
Long = 5,
ULong = 6,
Unmanaged = 7,
}
} }

View File

@@ -0,0 +1,40 @@
#if UNITY_EDITOR
#endif
namespace Unity.Netcode
{
/// <summary>
/// Enum representing the different types of Network Variables that can be sent over the network.
/// The values cannot be changed, as they are used to serialize and deserialize variables on the DA server.
/// Adding new variables should be done by adding new values to the end of the enum
/// using the next free value.
/// </summary>
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/// Add any new Variable types to this table at the END with incremented index value
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
internal enum NetworkVariableType : byte
{
/// <summary>
/// Value
/// Used for all of the basic NetworkVariables that contain a single value
/// </summary>
Value = 0,
/// <summary>
/// For any type that is not known at runtime
/// </summary>
Unknown = 1,
/// <summary>
/// NetworkList
/// </summary>
NetworkList = 2,
// The following types are valid types inside of NetworkVariable collections
Short = 11,
UShort = 12,
Int = 13,
UInt = 14,
Long = 15,
ULong = 16,
Unmanaged = 17,
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: df4a4005f1c842669f94a404019400ed
timeCreated: 1718292058

View File

@@ -14,6 +14,9 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class FallbackSerializer<T> : INetworkVariableSerializer<T> internal class FallbackSerializer<T> : INetworkVariableSerializer<T>
{ {
public NetworkVariableType Type => NetworkVariableType.Unknown;
public bool IsDistributedAuthorityOptimized => true;
private void ThrowArgumentError() private void ThrowArgumentError()
{ {
throw new ArgumentException($"Serialization has not been generated for type {typeof(T).FullName}. This can be addressed by adding a [{nameof(GenerateSerializationForGenericParameterAttribute)}] to your generic class that serializes this value (if you are using one), adding [{nameof(GenerateSerializationForTypeAttribute)}(typeof({typeof(T).FullName})] to the class or method that is attempting to serialize it, or creating a field on a {nameof(NetworkBehaviour)} of type {nameof(NetworkVariable<T>)}. If this error continues to appear after doing one of those things and this is a type you can change, then either implement {nameof(INetworkSerializable)} or mark it as serializable by memcpy by adding {nameof(INetworkSerializeByMemcpy)} to its interface list to enable automatic serialization generation. If not, assign serialization code to {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.WriteValue)}, {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.ReadValue)}, and {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.DuplicateValue)}, or if it's serializable by memcpy (contains no pointers), wrap it in {typeof(ForceNetworkSerializeByMemcpy<>).Name}."); throw new ArgumentException($"Serialization has not been generated for type {typeof(T).FullName}. This can be addressed by adding a [{nameof(GenerateSerializationForGenericParameterAttribute)}] to your generic class that serializes this value (if you are using one), adding [{nameof(GenerateSerializationForTypeAttribute)}(typeof({typeof(T).FullName})] to the class or method that is attempting to serialize it, or creating a field on a {nameof(NetworkBehaviour)} of type {nameof(NetworkVariable<T>)}. If this error continues to appear after doing one of those things and this is a type you can change, then either implement {nameof(INetworkSerializable)} or mark it as serializable by memcpy by adding {nameof(INetworkSerializeByMemcpy)} to its interface list to enable automatic serialization generation. If not, assign serialization code to {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.WriteValue)}, {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.ReadValue)}, and {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.DuplicateValue)}, or if it's serializable by memcpy (contains no pointers), wrap it in {typeof(ForceNetworkSerializeByMemcpy<>).Name}.");
@@ -79,6 +82,11 @@ namespace Unity.Netcode
} }
UserNetworkVariableSerialization<T>.DuplicateValue(value, ref duplicatedValue); UserNetworkVariableSerialization<T>.DuplicateValue(value, ref duplicatedValue);
} }
public void WriteDistributedAuthority(FastBufferWriter writer, ref T value) => ThrowArgumentError();
public void ReadDistributedAuthority(FastBufferReader reader, ref T value) => ThrowArgumentError();
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref T value, ref T previousValue) => ThrowArgumentError();
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref T value) => ThrowArgumentError();
} }
// RuntimeAccessModifiersILPP will make this `public` // RuntimeAccessModifiersILPP will make this `public`

View File

@@ -3,11 +3,27 @@ using Unity.Collections;
namespace Unity.Netcode namespace Unity.Netcode
{ {
/// <summary> /// <summary>
/// Interface used by NetworkVariables to serialize them /// Interface used by NetworkVariables to serialize them with additional information for the DA runtime
/// </summary> /// </summary>
/// ///
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal interface INetworkVariableSerializer<T> internal interface IDistributedAuthoritySerializer<T>
{
/// <summary>
/// The Type tells the DA server how to parse this type.
/// The user should never be able to override this value, as it is meaningful for the DA server
/// </summary>
public NetworkVariableType Type { get; }
public bool IsDistributedAuthorityOptimized { get; }
public void WriteDistributedAuthority(FastBufferWriter writer, ref T value);
public void ReadDistributedAuthority(FastBufferReader reader, ref T value);
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref T value, ref T previousValue);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref T value);
}
/// <typeparam name="T"></typeparam>
internal interface INetworkVariableSerializer<T> : IDistributedAuthoritySerializer<T>
{ {
// Write has to be taken by ref here because of INetworkSerializable // Write has to be taken by ref here because of INetworkSerializable
// Open Instance Delegates (pointers to methods without an instance attached to them) // Open Instance Delegates (pointers to methods without an instance attached to them)

View File

@@ -16,12 +16,6 @@ namespace Unity.Netcode
internal static bool IsDistributedAuthority => NetworkManager.IsDistributedAuthority; internal static bool IsDistributedAuthority => NetworkManager.IsDistributedAuthority;
/// <summary>
/// The collection item type tells the CMB server how to read the bytes of each item in the collection
/// </summary>
/// DANGO-EXP TODO: Determine if this is distributed authority only and impacts of this in client-server
internal static CollectionItemType Type = CollectionItemType.Unknown;
/// <summary> /// <summary>
/// A callback to check if two values are equal. /// A callback to check if two values are equal.
/// </summary> /// </summary>
@@ -59,7 +53,20 @@ namespace Unity.Netcode
/// <param name="value"></param> /// <param name="value"></param>
public static void Write(FastBufferWriter writer, ref T value) public static void Write(FastBufferWriter writer, ref T value)
{ {
Serializer.Write(writer, ref value); if (IsDistributedAuthority)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (!NetworkManager.DisableNotOptimizedSerializedType && !Serializer.IsDistributedAuthorityOptimized)
{
NetworkManager.LogSerializedTypeNotOptimized<T>();
}
#endif
Serializer.WriteDistributedAuthority(writer, ref value);
}
else
{
Serializer.Write(writer, ref value);
}
} }
/// <summary> /// <summary>
@@ -84,7 +91,14 @@ namespace Unity.Netcode
/// <param name="value"></param> /// <param name="value"></param>
public static void Read(FastBufferReader reader, ref T value) public static void Read(FastBufferReader reader, ref T value)
{ {
Serializer.Read(reader, ref value); if (IsDistributedAuthority)
{
Serializer.ReadDistributedAuthority(reader, ref value);
}
else
{
Serializer.Read(reader, ref value);
}
} }
/// <summary> /// <summary>
@@ -106,7 +120,20 @@ namespace Unity.Netcode
/// <param name="value"></param> /// <param name="value"></param>
public static void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue) public static void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue)
{ {
Serializer.WriteDelta(writer, ref value, ref previousValue); if (IsDistributedAuthority)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (!NetworkManager.DisableNotOptimizedSerializedType && !Serializer.IsDistributedAuthorityOptimized)
{
NetworkManager.LogSerializedTypeNotOptimized<T>();
}
#endif
Serializer.WriteDeltaDistributedAuthority(writer, ref value, ref previousValue);
}
else
{
Serializer.WriteDelta(writer, ref value, ref previousValue);
}
} }
/// <summary> /// <summary>
@@ -131,7 +158,14 @@ namespace Unity.Netcode
/// <param name="value"></param> /// <param name="value"></param>
public static void ReadDelta(FastBufferReader reader, ref T value) public static void ReadDelta(FastBufferReader reader, ref T value)
{ {
Serializer.ReadDelta(reader, ref value); if (IsDistributedAuthority)
{
Serializer.ReadDeltaDistributedAuthority(reader, ref value);
}
else
{
Serializer.ReadDelta(reader, ref value);
}
} }
/// <summary> /// <summary>

View File

@@ -38,14 +38,6 @@ namespace Unity.Netcode
NetworkVariableSerialization<long>.AreEqual = NetworkVariableEquality<long>.ValueEquals; NetworkVariableSerialization<long>.AreEqual = NetworkVariableEquality<long>.ValueEquals;
NetworkVariableSerialization<ulong>.Serializer = new UlongSerializer(); NetworkVariableSerialization<ulong>.Serializer = new UlongSerializer();
NetworkVariableSerialization<ulong>.AreEqual = NetworkVariableEquality<ulong>.ValueEquals; NetworkVariableSerialization<ulong>.AreEqual = NetworkVariableEquality<ulong>.ValueEquals;
// DANGO-EXP TODO: Determine if this is distributed authority only and impacts of this in client-server
NetworkVariableSerialization<short>.Type = CollectionItemType.Short;
NetworkVariableSerialization<ushort>.Type = CollectionItemType.UShort;
NetworkVariableSerialization<int>.Type = CollectionItemType.Int;
NetworkVariableSerialization<uint>.Type = CollectionItemType.UInt;
NetworkVariableSerialization<long>.Type = CollectionItemType.Long;
NetworkVariableSerialization<ulong>.Type = CollectionItemType.ULong;
} }
/// <summary> /// <summary>
@@ -55,8 +47,6 @@ namespace Unity.Netcode
public static void InitializeSerializer_UnmanagedByMemcpy<T>() where T : unmanaged public static void InitializeSerializer_UnmanagedByMemcpy<T>() where T : unmanaged
{ {
NetworkVariableSerialization<T>.Serializer = new UnmanagedTypeSerializer<T>(); NetworkVariableSerialization<T>.Serializer = new UnmanagedTypeSerializer<T>();
// DANGO-EXP TODO: Determine if this is distributed authority only and impacts of this in client-server
NetworkVariableSerialization<T>.Type = CollectionItemType.Unmanaged;
} }
/// <summary> /// <summary>

View File

@@ -10,6 +10,21 @@ namespace Unity.Netcode
/// </summary> /// </summary>
internal class ShortSerializer : INetworkVariableSerializer<short> internal class ShortSerializer : INetworkVariableSerializer<short>
{ {
public NetworkVariableType Type => NetworkVariableType.Short;
public bool IsDistributedAuthorityOptimized => true;
public void WriteDistributedAuthority(FastBufferWriter writer, ref short value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref short value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref short value, ref short previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref short value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref short value) public void Write(FastBufferWriter writer, ref short value)
{ {
BytePacker.WriteValueBitPacked(writer, value); BytePacker.WriteValueBitPacked(writer, value);
@@ -46,6 +61,20 @@ namespace Unity.Netcode
/// </summary> /// </summary>
internal class UshortSerializer : INetworkVariableSerializer<ushort> internal class UshortSerializer : INetworkVariableSerializer<ushort>
{ {
public NetworkVariableType Type => NetworkVariableType.UShort;
public bool IsDistributedAuthorityOptimized => true;
public void WriteDistributedAuthority(FastBufferWriter writer, ref ushort value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref ushort value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref ushort value, ref ushort previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref ushort value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref ushort value) public void Write(FastBufferWriter writer, ref ushort value)
{ {
BytePacker.WriteValueBitPacked(writer, value); BytePacker.WriteValueBitPacked(writer, value);
@@ -82,6 +111,20 @@ namespace Unity.Netcode
/// </summary> /// </summary>
internal class IntSerializer : INetworkVariableSerializer<int> internal class IntSerializer : INetworkVariableSerializer<int>
{ {
public NetworkVariableType Type => NetworkVariableType.Int;
public bool IsDistributedAuthorityOptimized => true;
public void WriteDistributedAuthority(FastBufferWriter writer, ref int value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref int value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref int value, ref int previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref int value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref int value) public void Write(FastBufferWriter writer, ref int value)
{ {
BytePacker.WriteValueBitPacked(writer, value); BytePacker.WriteValueBitPacked(writer, value);
@@ -118,6 +161,20 @@ namespace Unity.Netcode
/// </summary> /// </summary>
internal class UintSerializer : INetworkVariableSerializer<uint> internal class UintSerializer : INetworkVariableSerializer<uint>
{ {
public NetworkVariableType Type => NetworkVariableType.UInt;
public bool IsDistributedAuthorityOptimized => true;
public void WriteDistributedAuthority(FastBufferWriter writer, ref uint value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref uint value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref uint value, ref uint previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref uint value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref uint value) public void Write(FastBufferWriter writer, ref uint value)
{ {
BytePacker.WriteValueBitPacked(writer, value); BytePacker.WriteValueBitPacked(writer, value);
@@ -154,6 +211,20 @@ namespace Unity.Netcode
/// </summary> /// </summary>
internal class LongSerializer : INetworkVariableSerializer<long> internal class LongSerializer : INetworkVariableSerializer<long>
{ {
public NetworkVariableType Type => NetworkVariableType.Long;
public bool IsDistributedAuthorityOptimized => true;
public void WriteDistributedAuthority(FastBufferWriter writer, ref long value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref long value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref long value, ref long previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref long value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref long value) public void Write(FastBufferWriter writer, ref long value)
{ {
BytePacker.WriteValueBitPacked(writer, value); BytePacker.WriteValueBitPacked(writer, value);
@@ -190,6 +261,21 @@ namespace Unity.Netcode
/// </summary> /// </summary>
internal class UlongSerializer : INetworkVariableSerializer<ulong> internal class UlongSerializer : INetworkVariableSerializer<ulong>
{ {
public NetworkVariableType Type => NetworkVariableType.ULong;
public bool IsDistributedAuthorityOptimized => true;
public void WriteDistributedAuthority(FastBufferWriter writer, ref ulong value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref ulong value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref ulong value, ref ulong previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref ulong value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref ulong value) public void Write(FastBufferWriter writer, ref ulong value)
{ {
BytePacker.WriteValueBitPacked(writer, value); BytePacker.WriteValueBitPacked(writer, value);
@@ -231,6 +317,21 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class UnmanagedTypeSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged internal class UnmanagedTypeSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged
{ {
public NetworkVariableType Type => NetworkVariableType.Unmanaged;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref T value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref T value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref T value, ref T previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref T value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref T value) public void Write(FastBufferWriter writer, ref T value)
{ {
writer.WriteUnmanagedSafe(value); writer.WriteUnmanagedSafe(value);
@@ -264,6 +365,20 @@ namespace Unity.Netcode
internal class ListSerializer<T> : INetworkVariableSerializer<List<T>> internal class ListSerializer<T> : INetworkVariableSerializer<List<T>>
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref List<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref List<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref List<T> value, ref List<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref List<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref List<T> value) public void Write(FastBufferWriter writer, ref List<T> value)
{ {
var isNull = value == null; var isNull = value == null;
@@ -343,13 +458,30 @@ namespace Unity.Netcode
duplicatedValue.Clear(); duplicatedValue.Clear();
foreach (var item in value) foreach (var item in value)
{ {
duplicatedValue.Add(item); // This handles the nested list scenario List<List<T>>
T subValue = default;
NetworkVariableSerialization<T>.Duplicate(item, ref subValue);
duplicatedValue.Add(subValue);
} }
} }
} }
internal class HashSetSerializer<T> : INetworkVariableSerializer<HashSet<T>> where T : IEquatable<T> internal class HashSetSerializer<T> : INetworkVariableSerializer<HashSet<T>> where T : IEquatable<T>
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref HashSet<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref HashSet<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref HashSet<T> value, ref HashSet<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref HashSet<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref HashSet<T> value) public void Write(FastBufferWriter writer, ref HashSet<T> value)
{ {
var isNull = value == null; var isNull = value == null;
@@ -419,6 +551,9 @@ namespace Unity.Netcode
duplicatedValue.Clear(); duplicatedValue.Clear();
foreach (var item in value) foreach (var item in value)
{ {
// Handles nested HashSets
T subValue = default;
NetworkVariableSerialization<T>.Duplicate(item, ref subValue);
duplicatedValue.Add(item); duplicatedValue.Add(item);
} }
} }
@@ -428,6 +563,20 @@ namespace Unity.Netcode
internal class DictionarySerializer<TKey, TVal> : INetworkVariableSerializer<Dictionary<TKey, TVal>> internal class DictionarySerializer<TKey, TVal> : INetworkVariableSerializer<Dictionary<TKey, TVal>>
where TKey : IEquatable<TKey> where TKey : IEquatable<TKey>
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref Dictionary<TKey, TVal> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref Dictionary<TKey, TVal> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref Dictionary<TKey, TVal> value, ref Dictionary<TKey, TVal> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref Dictionary<TKey, TVal> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref Dictionary<TKey, TVal> value) public void Write(FastBufferWriter writer, ref Dictionary<TKey, TVal> value)
{ {
var isNull = value == null; var isNull = value == null;
@@ -498,13 +647,32 @@ namespace Unity.Netcode
duplicatedValue.Clear(); duplicatedValue.Clear();
foreach (var item in value) foreach (var item in value)
{ {
duplicatedValue.Add(item.Key, item.Value); // Handles nested dictionaries
TKey subKey = default;
TVal subValue = default;
NetworkVariableSerialization<TKey>.Duplicate(item.Key, ref subKey);
NetworkVariableSerialization<TVal>.Duplicate(item.Value, ref subValue);
duplicatedValue.Add(subKey, subValue);
} }
} }
} }
internal class UnmanagedArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged internal class UnmanagedArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref NativeArray<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref NativeArray<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref NativeArray<T> value, ref NativeArray<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref NativeArray<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref NativeArray<T> value) public void Write(FastBufferWriter writer, ref NativeArray<T> value)
{ {
writer.WriteUnmanagedSafe(value); writer.WriteUnmanagedSafe(value);
@@ -550,6 +718,20 @@ namespace Unity.Netcode
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT #if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
internal class UnmanagedListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged internal class UnmanagedListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref NativeList<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref NativeList<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref NativeList<T> value, ref NativeList<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref NativeList<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref NativeList<T> value) public void Write(FastBufferWriter writer, ref NativeList<T> value)
{ {
writer.WriteUnmanagedSafe(value); writer.WriteUnmanagedSafe(value);
@@ -593,6 +775,21 @@ namespace Unity.Netcode
internal class NativeHashSetSerializer<T> : INetworkVariableSerializer<NativeHashSet<T>> where T : unmanaged, IEquatable<T> internal class NativeHashSetSerializer<T> : INetworkVariableSerializer<NativeHashSet<T>> where T : unmanaged, IEquatable<T>
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref NativeHashSet<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref NativeHashSet<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref NativeHashSet<T> value, ref NativeHashSet<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref NativeHashSet<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref NativeHashSet<T> value) public void Write(FastBufferWriter writer, ref NativeHashSet<T> value)
{ {
writer.WriteValueSafe(value); writer.WriteValueSafe(value);
@@ -638,6 +835,21 @@ namespace Unity.Netcode
where TKey : unmanaged, IEquatable<TKey> where TKey : unmanaged, IEquatable<TKey>
where TVal : unmanaged where TVal : unmanaged
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref NativeHashMap<TKey, TVal> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref NativeHashMap<TKey, TVal> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref NativeHashMap<TKey, TVal> value, ref NativeHashMap<TKey, TVal> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref NativeHashMap<TKey, TVal> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref NativeHashMap<TKey, TVal> value) public void Write(FastBufferWriter writer, ref NativeHashMap<TKey, TVal> value)
{ {
writer.WriteValueSafe(value); writer.WriteValueSafe(value);
@@ -680,12 +892,25 @@ namespace Unity.Netcode
#endif #endif
/// <summary> /// <summary>
/// Serializer for FixedStrings /// Serializer for FixedStrings
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class FixedStringSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged, INativeList<byte>, IUTF8Bytes internal class FixedStringSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged, INativeList<byte>, IUTF8Bytes
{ {
// The item type can only be bytes for fixedStrings, so the DA runtime doesn't need details on it public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref T value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref T value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref T value, ref T previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref T value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref T value) public void Write(FastBufferWriter writer, ref T value)
{ {
@@ -710,7 +935,7 @@ namespace Unity.Netcode
{ {
var val = value[i]; var val = value[i];
var prevVal = previousValue[i]; var prevVal = previousValue[i];
if (!NetworkVariableSerialization<byte>.AreEqual(ref val, ref prevVal)) if (val != prevVal)
{ {
++numChanges; ++numChanges;
changes.Set(i); changes.Set(i);
@@ -731,23 +956,15 @@ namespace Unity.Netcode
return; return;
} }
writer.WriteByte(0); // Flag that we're sending a delta writer.WriteByteSafe(0); // Flag that we're sending a delta
BytePacker.WriteValuePacked(writer, value.Length); BytePacker.WriteValuePacked(writer, value.Length);
writer.WriteValueSafe(changes); writer.WriteValueSafe(changes);
var ptr = value.GetUnsafePtr(); var ptr = value.GetUnsafePtr();
var prevPtr = previousValue.GetUnsafePtr();
for (var i = 0; i < value.Length; ++i) for (var i = 0; i < value.Length; ++i)
{ {
if (changes.IsSet(i)) if (changes.IsSet(i))
{ {
if (i < previousValue.Length) writer.WriteByteSafe(ptr[i]);
{
NetworkVariableSerialization<byte>.WriteDelta(writer, ref ptr[i], ref prevPtr[i]);
}
else
{
NetworkVariableSerialization<byte>.Write(writer, ref ptr[i]);
}
} }
} }
} }
@@ -779,7 +996,7 @@ namespace Unity.Netcode
{ {
if (changes.IsSet(i)) if (changes.IsSet(i))
{ {
reader.ReadByte(out ptr[i]); reader.ReadByteSafe(out ptr[i]);
} }
} }
} }
@@ -802,6 +1019,20 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class FixedStringArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged, INativeList<byte>, IUTF8Bytes internal class FixedStringArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged, INativeList<byte>, IUTF8Bytes
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref NativeArray<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref NativeArray<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref NativeArray<T> value, ref NativeArray<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref NativeArray<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref NativeArray<T> value) public void Write(FastBufferWriter writer, ref NativeArray<T> value)
{ {
writer.WriteValueSafe(value); writer.WriteValueSafe(value);
@@ -852,6 +1083,21 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class FixedStringListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged, INativeList<byte>, IUTF8Bytes internal class FixedStringListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged, INativeList<byte>, IUTF8Bytes
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref NativeList<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref NativeList<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref NativeList<T> value, ref NativeList<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref NativeList<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref NativeList<T> value) public void Write(FastBufferWriter writer, ref NativeList<T> value)
{ {
writer.WriteValueSafe(value); writer.WriteValueSafe(value);
@@ -899,6 +1145,21 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class UnmanagedNetworkSerializableSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged, INetworkSerializable internal class UnmanagedNetworkSerializableSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged, INetworkSerializable
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref T value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref T value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref T value, ref T previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref T value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref T value) public void Write(FastBufferWriter writer, ref T value)
{ {
var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer)); var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
@@ -951,6 +1212,20 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class UnmanagedNetworkSerializableArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged, INetworkSerializable internal class UnmanagedNetworkSerializableArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged, INetworkSerializable
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref NativeArray<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref NativeArray<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref NativeArray<T> value, ref NativeArray<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref NativeArray<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref NativeArray<T> value) public void Write(FastBufferWriter writer, ref NativeArray<T> value)
{ {
writer.WriteNetworkSerializable(value); writer.WriteNetworkSerializable(value);
@@ -1001,6 +1276,21 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class UnmanagedNetworkSerializableListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged, INetworkSerializable internal class UnmanagedNetworkSerializableListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged, INetworkSerializable
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref NativeList<T> value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref NativeList<T> value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref NativeList<T> value, ref NativeList<T> previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref NativeList<T> value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref NativeList<T> value) public void Write(FastBufferWriter writer, ref NativeList<T> value)
{ {
writer.WriteNetworkSerializable(value); writer.WriteNetworkSerializable(value);
@@ -1048,6 +1338,20 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class ManagedNetworkSerializableSerializer<T> : INetworkVariableSerializer<T> where T : class, INetworkSerializable, new() internal class ManagedNetworkSerializableSerializer<T> : INetworkVariableSerializer<T> where T : class, INetworkSerializable, new()
{ {
public NetworkVariableType Type => NetworkVariableType.Value;
public bool IsDistributedAuthorityOptimized => false;
public void WriteDistributedAuthority(FastBufferWriter writer, ref T value)
{
Write(writer, ref value);
}
public void ReadDistributedAuthority(FastBufferReader reader, ref T value)
{
Read(reader, ref value);
}
public void WriteDeltaDistributedAuthority(FastBufferWriter writer, ref T value, ref T previousValue) => Write(writer, ref value);
public void ReadDeltaDistributedAuthority(FastBufferReader reader, ref T value) => Read(reader, ref value);
public void Write(FastBufferWriter writer, ref T value) public void Write(FastBufferWriter writer, ref T value)
{ {
var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer)); var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));

View File

@@ -0,0 +1,54 @@
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
namespace Unity.Netcode
{
internal static class SerializationTools
{
public delegate void WriteDelegate<T>(FastBufferWriter writer, ref T value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteWithSize<T>(WriteDelegate<T> writeMethod, FastBufferWriter writer, ref T value)
{
var writePos = writer.Position;
// Note: This value can't be packed because we don't know how large it will be in advance
// we reserve space for it, then write the data, then come back and fill in the space
// to pack here, we'd have to write data to a temporary buffer and copy it in - which
// isn't worth possibly saving one byte if and only if the data is less than 63 bytes long...
// The way we do packing, any value > 63 in a ushort will use the full 2 bytes to represent.
writer.WriteValueSafe((ushort)0);
var startPos = writer.Position;
writeMethod(writer, ref value);
var size = writer.Position - startPos;
writer.Seek(writePos);
writer.WriteValueSafe((ushort)size);
writer.Seek(startPos + size);
}
public delegate void ReadDelegate<T>(FastBufferReader writer, ref T value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReadWithSize<T>(ReadDelegate<T> readMethod, FastBufferReader reader, ref T value)
{
reader.ReadValueSafe(out ushort _);
readMethod(reader, ref value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteType(FastBufferWriter writer, NetworkVariableType type) => writer.WriteValueSafe(type);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReadType<T>(FastBufferReader reader, INetworkVariableSerializer<T> serializer)
{
reader.ReadValueSafe(out NetworkVariableType type);
if (type != serializer.Type)
{
throw new SerializationException();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 52a4ce368df54b0a8887c08f3402bcd3
timeCreated: 1718300602

View File

@@ -227,6 +227,11 @@ namespace Unity.Netcode
foreach (var sceneToUnload in m_ScenesToUnload) foreach (var sceneToUnload in m_ScenesToUnload)
{ {
SceneManager.UnloadSceneAsync(sceneToUnload); SceneManager.UnloadSceneAsync(sceneToUnload);
// Update the ScenesLoaded when we unload scenes
if (sceneManager.ScenesLoaded.ContainsKey(sceneToUnload.handle))
{
sceneManager.ScenesLoaded.Remove(sceneToUnload.handle);
}
} }
} }
@@ -328,8 +333,9 @@ namespace Unity.Netcode
public void SetClientSynchronizationMode(ref NetworkManager networkManager, LoadSceneMode mode) public void SetClientSynchronizationMode(ref NetworkManager networkManager, LoadSceneMode mode)
{ {
var sceneManager = networkManager.SceneManager; var sceneManager = networkManager.SceneManager;
// Don't let non-authority set this value // In client-server, we don't let client's set this value.
if ((!networkManager.DistributedAuthorityMode && !networkManager.IsServer) || (networkManager.DistributedAuthorityMode && !networkManager.LocalClient.IsSessionOwner)) // In distributed authority, since session owner can be promoted clients can set this value
if (!networkManager.DistributedAuthorityMode && !networkManager.IsServer)
{ {
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{ {
@@ -338,7 +344,7 @@ namespace Unity.Netcode
return; return;
} }
else // Warn users if they are changing this after there are clients already connected and synchronized else // Warn users if they are changing this after there are clients already connected and synchronized
if (networkManager.ConnectedClientsIds.Count > (networkManager.IsHost ? 1 : 0) && sceneManager.ClientSynchronizationMode != mode) if (!networkManager.DistributedAuthorityMode && networkManager.ConnectedClientsIds.Count > (networkManager.IsHost ? 1 : 0) && sceneManager.ClientSynchronizationMode != mode)
{ {
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{ {

View File

@@ -774,6 +774,7 @@ namespace Unity.Netcode
/// <summary> /// <summary>
/// This setting changes how clients handle scene loading when initially synchronizing with the server.<br /> /// This setting changes how clients handle scene loading when initially synchronizing with the server.<br />
/// The server or host should set this value as clients will automatically be synchronized with the server (or host) side. /// The server or host should set this value as clients will automatically be synchronized with the server (or host) side.
/// </summary>
/// <remarks> /// <remarks>
/// <b>LoadSceneMode.Single:</b> All currently loaded scenes on the client will be unloaded and the /// <b>LoadSceneMode.Single:</b> All currently loaded scenes on the client will be unloaded and the
/// server's currently active scene will be loaded in single mode on the client unless it was already /// server's currently active scene will be loaded in single mode on the client unless it was already
@@ -2406,16 +2407,6 @@ namespace Unity.Netcode
NetworkManager.ConnectionManager.CreateAndSpawnPlayer(NetworkManager.LocalClientId); NetworkManager.ConnectionManager.CreateAndSpawnPlayer(NetworkManager.LocalClientId);
} }
// Client is now synchronized and fully "connected". This also means the client can send "RPCs" at this time
NetworkManager.ConnectionManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId);
// Notify the client that they have finished synchronizing
OnSceneEvent?.Invoke(new SceneEvent()
{
SceneEventType = sceneEventData.SceneEventType,
ClientId = NetworkManager.LocalClientId, // Client sent this to the server
});
// Process any SceneEventType.ObjectSceneChanged messages that // Process any SceneEventType.ObjectSceneChanged messages that
// were deferred while synchronizing and migrate the associated // were deferred while synchronizing and migrate the associated
// NetworkObjects to their newly assigned scenes. // NetworkObjects to their newly assigned scenes.
@@ -2429,6 +2420,16 @@ namespace Unity.Netcode
SceneManagerHandler.UnloadUnassignedScenes(NetworkManager); SceneManagerHandler.UnloadUnassignedScenes(NetworkManager);
} }
// Client is now synchronized and fully "connected". This also means the client can send "RPCs" at this time
NetworkManager.ConnectionManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId);
// Notify the client that they have finished synchronizing
OnSceneEvent?.Invoke(new SceneEvent()
{
SceneEventType = sceneEventData.SceneEventType,
ClientId = NetworkManager.LocalClientId, // Client sent this to the server
});
OnSynchronizeComplete?.Invoke(NetworkManager.LocalClientId); OnSynchronizeComplete?.Invoke(NetworkManager.LocalClientId);
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
@@ -2436,10 +2437,7 @@ namespace Unity.Netcode
NetworkLog.LogInfo($"[Client-{NetworkManager.LocalClientId}][Scene Management Enabled] Synchronization complete!"); NetworkLog.LogInfo($"[Client-{NetworkManager.LocalClientId}][Scene Management Enabled] Synchronization complete!");
} }
// For convenience, notify all NetworkBehaviours that synchronization is complete. // For convenience, notify all NetworkBehaviours that synchronization is complete.
foreach (var networkObject in NetworkManager.SpawnManager.SpawnedObjectsList) NetworkManager.SpawnManager.NotifyNetworkObjectsSynchronized();
{
networkObject.InternalNetworkSessionSynchronized();
}
if (NetworkManager.DistributedAuthorityMode && HasSceneAuthority() && IsRestoringSession) if (NetworkManager.DistributedAuthorityMode && HasSceneAuthority() && IsRestoringSession)
{ {

View File

@@ -643,7 +643,7 @@ namespace Unity.Netcode
return null; return null;
} }
ownerClientId = NetworkManager.DistributedAuthorityMode ? NetworkManager.LocalClientId : NetworkManager.ServerClientId; ownerClientId = NetworkManager.DistributedAuthorityMode ? NetworkManager.LocalClientId : ownerClientId;
// We only need to check for authority when running in client-server mode // We only need to check for authority when running in client-server mode
if (!NetworkManager.IsServer && !NetworkManager.DistributedAuthorityMode) if (!NetworkManager.IsServer && !NetworkManager.DistributedAuthorityMode)
{ {
@@ -1820,11 +1820,14 @@ namespace Unity.Netcode
return; return;
} }
var currentTick = serverTime.Tick; var currentTick = serverTime.Tick;
var deferredCallbackObjects = DeferredDespawnObjects.Where((c) => c.HasDeferredDespawnCheck); var deferredCallbackCount = DeferredDespawnObjects.Count();
var deferredCallbackCount = deferredCallbackObjects.Count(); for (int i = 0; i < deferredCallbackCount; i++)
for (int i = 0; i < deferredCallbackCount - 1; i++)
{ {
var deferredObjectEntry = deferredCallbackObjects.ElementAt(i); var deferredObjectEntry = DeferredDespawnObjects[i];
if (!deferredObjectEntry.HasDeferredDespawnCheck)
{
continue;
}
var networkObject = SpawnedObjects[deferredObjectEntry.NetworkObjectId]; var networkObject = SpawnedObjects[deferredObjectEntry.NetworkObjectId];
// Double check to make sure user did not remove the callback // Double check to make sure user did not remove the callback
if (networkObject.OnDeferredDespawnComplete != null) if (networkObject.OnDeferredDespawnComplete != null)
@@ -1849,9 +1852,15 @@ namespace Unity.Netcode
} }
} }
var despawnObjects = DeferredDespawnObjects.Where((c) => c.TickToDespawn < currentTick).ToList(); // Parse backwards so we can remove objects as we parse through them
foreach (var deferredObjectEntry in despawnObjects) for (int i = DeferredDespawnObjects.Count - 1; i >= 0; i--)
{ {
var deferredObjectEntry = DeferredDespawnObjects[i];
if (deferredObjectEntry.TickToDespawn >= currentTick)
{
continue;
}
if (!SpawnedObjects.ContainsKey(deferredObjectEntry.NetworkObjectId)) if (!SpawnedObjects.ContainsKey(deferredObjectEntry.NetworkObjectId))
{ {
DeferredDespawnObjects.Remove(deferredObjectEntry); DeferredDespawnObjects.Remove(deferredObjectEntry);
@@ -1863,5 +1872,16 @@ namespace Unity.Netcode
DeferredDespawnObjects.Remove(deferredObjectEntry); DeferredDespawnObjects.Remove(deferredObjectEntry);
} }
} }
internal void NotifyNetworkObjectsSynchronized()
{
// Users could spawn NetworkObjects during these notifications.
// Create a separate list from the hashset to avoid list modification errors.
var spawnedObjects = SpawnedObjectsList.ToList();
foreach (var networkObject in spawnedObjects)
{
networkObject.InternalNetworkSessionSynchronized();
}
}
} }
} }

View File

@@ -428,6 +428,31 @@ namespace Unity.Netcode.Transports.UTP
protected NetworkDriver m_Driver; protected NetworkDriver m_Driver;
/// <summary>
/// Gets a reference to the <see cref="Networking.Transport.NetworkDriver"/>.
/// </summary>
/// <returns>ref <see cref="Networking.Transport.NetworkDriver"/></returns>
public ref NetworkDriver GetNetworkDriver()
{
return ref m_Driver;
}
/// <summary>
/// Gets the local sytem's <see cref="NetworkEndpoint"/> that is assigned for the current network session.
/// </summary>
/// <remarks>
/// If the driver is not created it will return an invalid <see cref="NetworkEndpoint"/>.
/// </remarks>
/// <returns><see cref="NetworkEndpoint"/></returns>
public NetworkEndpoint GetLocalEndpoint()
{
if (m_Driver.IsCreated)
{
return m_Driver.GetLocalEndpoint();
}
return new NetworkEndpoint();
}
private PacketLossCache m_PacketLossCache = new PacketLossCache(); private PacketLossCache m_PacketLossCache = new PacketLossCache();
private State m_State = State.Disconnected; private State m_State = State.Disconnected;
@@ -450,7 +475,10 @@ namespace Unity.Netcode.Transports.UTP
private RelayServerData m_RelayServerData; private RelayServerData m_RelayServerData;
internal NetworkManager NetworkManager; /// <summary>
/// NetworkManager associated to this transport instance
/// </summary>
protected NetworkManager m_NetworkManager;
private IRealTimeProvider m_RealTimeProvider; private IRealTimeProvider m_RealTimeProvider;
@@ -795,10 +823,10 @@ namespace Unity.Netcode.Transports.UTP
} }
var mtu = 0; var mtu = 0;
if (NetworkManager) if (m_NetworkManager)
{ {
var ngoClientId = NetworkManager.ConnectionManager.TransportIdToClientId(sendTarget.ClientId); var ngoClientId = m_NetworkManager.ConnectionManager.TransportIdToClientId(sendTarget.ClientId);
mtu = NetworkManager.GetPeerMTU(ngoClientId); mtu = m_NetworkManager.GetPeerMTU(ngoClientId);
} }
new SendBatchedMessagesJob new SendBatchedMessagesJob
@@ -947,7 +975,7 @@ namespace Unity.Netcode.Transports.UTP
} }
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7 #if MULTIPLAYER_TOOLS_1_0_0_PRE_7
if (NetworkManager) if (m_NetworkManager)
{ {
ExtractNetworkMetrics(); ExtractNetworkMetrics();
} }
@@ -963,16 +991,16 @@ namespace Unity.Netcode.Transports.UTP
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7 #if MULTIPLAYER_TOOLS_1_0_0_PRE_7
private void ExtractNetworkMetrics() private void ExtractNetworkMetrics()
{ {
if (NetworkManager.IsServer) if (m_NetworkManager.IsServer)
{ {
var ngoConnectionIds = NetworkManager.ConnectedClients.Keys; var ngoConnectionIds = m_NetworkManager.ConnectedClients.Keys;
foreach (var ngoConnectionId in ngoConnectionIds) foreach (var ngoConnectionId in ngoConnectionIds)
{ {
if (ngoConnectionId == 0 && NetworkManager.IsHost) if (ngoConnectionId == 0 && m_NetworkManager.IsHost)
{ {
continue; continue;
} }
var transportClientId = NetworkManager.ConnectionManager.ClientIdToTransportId(ngoConnectionId); var transportClientId = m_NetworkManager.ConnectionManager.ClientIdToTransportId(ngoConnectionId);
ExtractNetworkMetricsForClient(transportClientId); ExtractNetworkMetricsForClient(transportClientId);
} }
} }
@@ -992,10 +1020,10 @@ namespace Unity.Netcode.Transports.UTP
ExtractNetworkMetricsFromPipeline(m_UnreliableSequencedFragmentedPipeline, networkConnection); ExtractNetworkMetricsFromPipeline(m_UnreliableSequencedFragmentedPipeline, networkConnection);
ExtractNetworkMetricsFromPipeline(m_ReliableSequencedPipeline, networkConnection); ExtractNetworkMetricsFromPipeline(m_ReliableSequencedPipeline, networkConnection);
var rttValue = NetworkManager.IsServer ? 0 : ExtractRtt(networkConnection); var rttValue = m_NetworkManager.IsServer ? 0 : ExtractRtt(networkConnection);
NetworkMetrics.UpdateRttToServer(rttValue); NetworkMetrics.UpdateRttToServer(rttValue);
var packetLoss = NetworkManager.IsServer ? 0 : ExtractPacketLoss(networkConnection); var packetLoss = m_NetworkManager.IsServer ? 0 : ExtractPacketLoss(networkConnection);
NetworkMetrics.UpdatePacketLoss(packetLoss); NetworkMetrics.UpdatePacketLoss(packetLoss);
} }
@@ -1199,9 +1227,9 @@ namespace Unity.Netcode.Transports.UTP
// use the transport client ID) or from a user (which will be using the NGO client ID). // use the transport client ID) or from a user (which will be using the NGO client ID).
// So we just try both cases (ExtractRtt returns 0 for invalid connections). // So we just try both cases (ExtractRtt returns 0 for invalid connections).
if (NetworkManager != null) if (m_NetworkManager != null)
{ {
var transportId = NetworkManager.ConnectionManager.ClientIdToTransportId(clientId); var transportId = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
var rtt = ExtractRtt(ParseClientId(transportId)); var rtt = ExtractRtt(ParseClientId(transportId));
if (rtt > 0) if (rtt > 0)
@@ -1221,14 +1249,14 @@ namespace Unity.Netcode.Transports.UTP
{ {
Debug.Assert(sizeof(ulong) == UnsafeUtility.SizeOf<NetworkConnection>(), "Netcode connection id size does not match UTP connection id size"); Debug.Assert(sizeof(ulong) == UnsafeUtility.SizeOf<NetworkConnection>(), "Netcode connection id size does not match UTP connection id size");
NetworkManager = networkManager; m_NetworkManager = networkManager;
if (NetworkManager && NetworkManager.PortOverride.Overidden) if (m_NetworkManager && m_NetworkManager.PortOverride.Overidden)
{ {
ConnectionData.Port = NetworkManager.PortOverride.Value; ConnectionData.Port = m_NetworkManager.PortOverride.Value;
} }
m_RealTimeProvider = NetworkManager ? NetworkManager.RealTimeProvider : new RealTimeProvider(); m_RealTimeProvider = m_NetworkManager ? m_NetworkManager.RealTimeProvider : new RealTimeProvider();
m_NetworkSettings = new NetworkSettings(Allocator.Persistent); m_NetworkSettings = new NetworkSettings(Allocator.Persistent);
@@ -1322,7 +1350,7 @@ namespace Unity.Netcode.Transports.UTP
// provide any reliability guarantees anymore. Disconnect the client since at // provide any reliability guarantees anymore. Disconnect the client since at
// this point they're bound to become desynchronized. // this point they're bound to become desynchronized.
var ngoClientId = NetworkManager?.ConnectionManager.TransportIdToClientId(clientId) ?? clientId; var ngoClientId = m_NetworkManager?.ConnectionManager.TransportIdToClientId(clientId) ?? clientId;
Debug.LogError($"Couldn't add payload of size {payload.Count} to reliable send queue. " + Debug.LogError($"Couldn't add payload of size {payload.Count} to reliable send queue. " +
$"Closing connection {ngoClientId} as reliability guarantees can't be maintained."); $"Closing connection {ngoClientId} as reliability guarantees can't be maintained.");
@@ -1479,7 +1507,7 @@ namespace Unity.Netcode.Transports.UTP
protected override NetworkTopologyTypes OnCurrentTopology() protected override NetworkTopologyTypes OnCurrentTopology()
{ {
return NetworkManager != null ? NetworkManager.NetworkConfig.NetworkTopology : NetworkTopologyTypes.ClientServer; return m_NetworkManager != null ? m_NetworkManager.NetworkConfig.NetworkTopology : NetworkTopologyTypes.ClientServer;
} }
private string m_ServerPrivateKey; private string m_ServerPrivateKey;
@@ -1555,7 +1583,7 @@ namespace Unity.Netcode.Transports.UTP
heartbeatTimeoutMS: transport.m_HeartbeatTimeoutMS); heartbeatTimeoutMS: transport.m_HeartbeatTimeoutMS);
#if UNITY_WEBGL && !UNITY_EDITOR #if UNITY_WEBGL && !UNITY_EDITOR
if (NetworkManager.IsServer && m_ProtocolType != ProtocolType.RelayUnityTransport) if (m_NetworkManager.IsServer && m_ProtocolType != ProtocolType.RelayUnityTransport)
{ {
throw new Exception("WebGL as a server is not supported by Unity Transport, outside the Editor."); throw new Exception("WebGL as a server is not supported by Unity Transport, outside the Editor.");
} }
@@ -1577,7 +1605,7 @@ namespace Unity.Netcode.Transports.UTP
} }
else else
{ {
if (NetworkManager.IsServer) if (m_NetworkManager.IsServer)
{ {
if (string.IsNullOrEmpty(m_ServerCertificate) || string.IsNullOrEmpty(m_ServerPrivateKey)) if (string.IsNullOrEmpty(m_ServerCertificate) || string.IsNullOrEmpty(m_ServerPrivateKey))
{ {

View File

@@ -72,6 +72,11 @@
"name": "com.unity.services.multiplayer", "name": "com.unity.services.multiplayer",
"expression": "0.2.0", "expression": "0.2.0",
"define": "MULTIPLAYER_SERVICES_SDK_INSTALLED" "define": "MULTIPLAYER_SERVICES_SDK_INSTALLED"
},
{
"name": "Unity",
"expression": "6000.0.11f1",
"define": "COM_UNITY_MODULES_PHYSICS2D_LINEAR"
} }
], ],
"noEngineReferences": false "noEngineReferences": false

View File

@@ -677,6 +677,11 @@ namespace Unity.Netcode.TestHelpers.Runtime
foreach (var sceneToUnload in m_ScenesToUnload) foreach (var sceneToUnload in m_ScenesToUnload)
{ {
SceneManager.UnloadSceneAsync(sceneToUnload.Key); SceneManager.UnloadSceneAsync(sceneToUnload.Key);
// Update the ScenesLoaded when we unload scenes
if (sceneManager.ScenesLoaded.ContainsKey(sceneToUnload.Key.handle))
{
sceneManager.ScenesLoaded.Remove(sceneToUnload.Key.handle);
}
} }
} }
@@ -795,8 +800,9 @@ namespace Unity.Netcode.TestHelpers.Runtime
var sceneManager = networkManager.SceneManager; var sceneManager = networkManager.SceneManager;
// Don't let client's set this value // In client-server, we don't let client's set this value.
if (!networkManager.IsServer) // In dsitributed authority, since session owner can be promoted clients can set this value
if (!networkManager.DistributedAuthorityMode && !networkManager.IsServer)
{ {
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{ {
@@ -804,7 +810,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
} }
return; return;
} }
else if (networkManager.ConnectedClientsIds.Count > (networkManager.IsHost ? 1 : 0) && sceneManager.ClientSynchronizationMode != mode) else if (!networkManager.DistributedAuthorityMode && networkManager.ConnectedClientsIds.Count > (networkManager.IsHost ? 1 : 0) && sceneManager.ClientSynchronizationMode != mode)
{ {
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{ {

View File

@@ -303,6 +303,11 @@ namespace Unity.Netcode.TestHelpers.Runtime
OnOneTimeSetup(); OnOneTimeSetup();
VerboseDebug($"Exiting {nameof(OneTimeSetup)}"); VerboseDebug($"Exiting {nameof(OneTimeSetup)}");
#if DEVELOPMENT_BUILD || UNITY_EDITOR
// Default to not log the serialized type not optimized warning message when testing.
NetworkManager.DisableNotOptimizedSerializedType = true;
#endif
} }
/// <summary> /// <summary>

View File

@@ -620,7 +620,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
/// Similar to WaitForClientConnected, this waits for multiple clients to be connected. /// Similar to WaitForClientConnected, this waits for multiple clients to be connected.
/// </summary> /// </summary>
/// <param name="clients">The clients to be connected</param> /// <param name="clients">The clients to be connected</param>
/// <param name="result">The result. If null, it will automatically assert<</param> /// <param name="result">The result. If null, it will automatically assert</param>
/// <param name="maxFrames">The max frames to wait for</param> /// <param name="maxFrames">The max frames to wait for</param>
/// <returns></returns> /// <returns></returns>
public static IEnumerator WaitForClientsConnected(NetworkManager[] clients, ResultWrapper<bool> result = null, float timeout = DefaultTimeout) public static IEnumerator WaitForClientsConnected(NetworkManager[] clients, ResultWrapper<bool> result = null, float timeout = DefaultTimeout)

View File

@@ -5,11 +5,13 @@ namespace Unity.Netcode.TestHelpers.Runtime
{ {
/// <summary> /// <summary>
/// Will automatically register for the NetworkVariable OnValueChanged /// Will automatically register for the NetworkVariable OnValueChanged
/// delegate handler. It then will expose that single delegate invocation /// delegate handler. It then will expose that single delegate invocation
/// to anything that registers for this NetworkVariableHelper's instance's OnValueChanged event. /// to anything that registers for this NetworkVariableHelper's instance's OnValueChanged event.
/// This allows us to register any NetworkVariable type as well as there are basically two "types of types": /// This allows us to register any NetworkVariable type as well as there are basically two "types of types":
/// IEquatable<T> /// <list type="bullet">
/// ValueType /// <item>IEquatable&lt;T&gt;</item>
/// <item>ValueType</item>
/// </list>
/// From both we can then at least determine if the value indeed changed /// From both we can then at least determine if the value indeed changed
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
@@ -20,7 +22,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
public event OnMyValueChangedDelegateHandler OnValueChanged; public event OnMyValueChangedDelegateHandler OnValueChanged;
/// <summary> /// <summary>
/// IEquatable<T> Equals Check /// IEquatable&lt;T&gt; Equals Check
/// </summary> /// </summary>
private void CheckVariableChanged(IEquatable<T> previous, IEquatable<T> next) private void CheckVariableChanged(IEquatable<T> previous, IEquatable<T> next)
{ {

View File

@@ -38,11 +38,7 @@ namespace Unity.Netcode.RuntimeTests
internal class TestNetworkComponent : NetworkBehaviour internal class TestNetworkComponent : NetworkBehaviour
{ {
public NetworkList<int> MyNetworkList = new NetworkList<int>(new List<int> { 1, 2, 3 }); public NetworkList<int> MyNetworkList = new NetworkList<int>(new List<int> { 1, 2, 3 });
public NetworkVariable<int> MyNetworkVar = new NetworkVariable<int>(3);
[Rpc(SendTo.NotAuthority)]
public void TestNotAuthorityRpc(byte[] _)
{
}
[Rpc(SendTo.Authority)] [Rpc(SendTo.Authority)]
public void TestAuthorityRpc(byte[] _) public void TestAuthorityRpc(byte[] _)
@@ -87,7 +83,7 @@ namespace Unity.Netcode.RuntimeTests
Client.LogLevel = LogLevel.Developer; Client.LogLevel = LogLevel.Developer;
// Validate we are in distributed authority mode with client side spawning and using CMB Service // 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.NetworkConfig.NetworkTopology == NetworkTopologyTypes.DistributedAuthority, "Distributed authority topology is not set!");
Assert.True(Client.AutoSpawnPlayerPrefabClientSide, "Client side spawning is not set!"); Assert.True(Client.AutoSpawnPlayerPrefabClientSide, "Client side spawning is not set!");
Assert.True(Client.CMBServiceConnection, "CMBServiceConnection is not set!"); Assert.True(Client.CMBServiceConnection, "CMBServiceConnection is not set!");
@@ -101,6 +97,9 @@ namespace Unity.Netcode.RuntimeTests
protected override IEnumerator OnStartedServerAndClients() protected override IEnumerator OnStartedServerAndClients()
{ {
// Validate the NetworkManager are in distributed authority mode
Assert.True(Client.DistributedAuthorityMode, "Distributed authority is not set!");
// Register hooks after starting clients and server (in this case just the one client) // 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) // 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(); m_ClientCodecHook = new CodecTestHooks();
@@ -219,12 +218,44 @@ namespace Unity.Netcode.RuntimeTests
} }
[UnityTest] [UnityTest]
public IEnumerator NotAuthorityRpc() public IEnumerator NetworkVariableDelta_WithValueUpdate()
{ {
Client.LocalClient.PlayerObject.GetComponent<TestNetworkComponent>().TestNotAuthorityRpc(new byte[] { 1, 2, 3, 4 }); var networkObj = CreateNetworkObjectPrefab("TestObject");
networkObj.AddComponent<TestNetworkComponent>();
var instance = SpawnObject(networkObj, Client);
yield return m_ClientCodecHook.WaitForMessageReceived<CreateObjectMessage>();
var component = instance.GetComponent<TestNetworkComponent>();
// Universal Rpcs are sent as a ProxyMessage (which contains an RpcMessage) var newValue = 5;
yield return m_ClientCodecHook.WaitForMessageReceived<ProxyMessage>(); component.MyNetworkVar.Value = newValue;
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
Assert.AreEqual(newValue, component.MyNetworkVar.Value);
}
[UnityTest]
public IEnumerator NetworkListDelta_WithValueUpdate()
{
var networkObj = CreateNetworkObjectPrefab("TestObject");
networkObj.AddComponent<TestNetworkComponent>();
var instance = SpawnObject(networkObj, Client);
yield return m_ClientCodecHook.WaitForMessageReceived<CreateObjectMessage>();
var component = instance.GetComponent<TestNetworkComponent>();
component.MyNetworkList.Add(5);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Add(6);
component.MyNetworkList.Add(7);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Insert(1, 8);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Insert(8, 11);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Remove(6);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.RemoveAt(2);
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
component.MyNetworkList.Clear();
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
} }
[UnityTest] [UnityTest]

View File

@@ -0,0 +1,97 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
namespace Unity.Netcode.RuntimeTests
{
/// <summary>
/// This test validates PR-3000 where it would invoke
/// TODO:
/// We really need to get the service running during tests
/// so we can validate these issues. While this test does
/// partially validate it we still need to manually validate
/// with a service connection.
/// </summary>
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.DAHost)]
public class RpcProxyMessageTesting : NetcodeIntegrationTest
{
protected override int NumberOfClients => 2;
private List<RpcProxyText> m_ProxyTestInstances = new List<RpcProxyText>();
private StringBuilder m_ValidationLogger = new StringBuilder();
public RpcProxyMessageTesting(HostOrServer hostOrServer) : base(hostOrServer) { }
protected override IEnumerator OnSetup()
{
m_ProxyTestInstances.Clear();
return base.OnSetup();
}
protected override void OnCreatePlayerPrefab()
{
m_PlayerPrefab.AddComponent<RpcProxyText>();
base.OnCreatePlayerPrefab();
}
private bool ValidateRpcProxyRpcs()
{
m_ValidationLogger.Clear();
foreach (var proxy in m_ProxyTestInstances)
{
if (proxy.ReceivedRpc.Count < NumberOfClients)
{
m_ValidationLogger.AppendLine($"Not all clients received RPC from Client-{proxy.OwnerClientId}!");
}
foreach (var clientId in proxy.ReceivedRpc)
{
if (clientId == proxy.OwnerClientId)
{
m_ValidationLogger.AppendLine($"Client-{proxy.OwnerClientId} sent itself an Rpc!");
}
}
}
return m_ValidationLogger.Length == 0;
}
public IEnumerator ProxyDoesNotInvokeOnSender()
{
m_ProxyTestInstances.Add(m_ServerNetworkManager.LocalClient.PlayerObject.GetComponent<RpcProxyText>());
foreach (var client in m_ClientNetworkManagers)
{
m_ProxyTestInstances.Add(client.LocalClient.PlayerObject.GetComponent<RpcProxyText>());
}
foreach (var clientProxyTest in m_ProxyTestInstances)
{
clientProxyTest.SendToEveryOneButMe();
}
yield return WaitForConditionOrTimeOut(ValidateRpcProxyRpcs);
AssertOnTimeout(m_ValidationLogger.ToString());
}
public class RpcProxyText : NetworkBehaviour
{
public List<ulong> ReceivedRpc = new List<ulong>();
public void SendToEveryOneButMe()
{
var baseTarget = NetworkManager.DistributedAuthorityMode ? RpcTarget.NotAuthority : RpcTarget.NotMe;
TestRpc(baseTarget);
}
[Rpc(SendTo.SpecifiedInParams)]
private void TestRpc(RpcParams rpcParams = default)
{
ReceivedRpc.Add(rpcParams.Receive.SenderClientId);
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 22d1d751fe245f7419f8393090c27106

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7d01969a8bbbd7146a24490a5190ea7e

View File

@@ -0,0 +1,165 @@
#if !NGO_MINIMALPROJECT
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
[TestFixtureSource(nameof(TestDataSource))]
internal class NetworkVariableInheritanceTests : NetcodeIntegrationTest
{
public NetworkVariableInheritanceTests(HostOrServer hostOrServer)
: base(hostOrServer)
{
}
protected override int NumberOfClients => 2;
public static IEnumerable<TestFixtureData> TestDataSource() =>
Enum.GetValues(typeof(HostOrServer)).OfType<HostOrServer>().Select(x => new TestFixtureData(x));
internal class ComponentA : NetworkBehaviour
{
public NetworkVariable<int> PublicFieldA = new NetworkVariable<int>(1);
protected NetworkVariable<int> m_ProtectedFieldA = new NetworkVariable<int>(2);
private NetworkVariable<int> m_PrivateFieldA = new NetworkVariable<int>(3);
public void ChangeValuesA(int pub, int pro, int pri)
{
PublicFieldA.Value = pub;
m_ProtectedFieldA.Value = pro;
m_PrivateFieldA.Value = pri;
}
public bool CompareValuesA(ComponentA other)
{
return PublicFieldA.Value == other.PublicFieldA.Value &&
m_ProtectedFieldA.Value == other.m_ProtectedFieldA.Value &&
m_PrivateFieldA.Value == other.m_PrivateFieldA.Value;
}
}
internal class ComponentB : ComponentA
{
public NetworkVariable<int> PublicFieldB = new NetworkVariable<int>(11);
protected NetworkVariable<int> m_ProtectedFieldB = new NetworkVariable<int>(22);
private NetworkVariable<int> m_PrivateFieldB = new NetworkVariable<int>(33);
public void ChangeValuesB(int pub, int pro, int pri)
{
PublicFieldB.Value = pub;
m_ProtectedFieldB.Value = pro;
m_PrivateFieldB.Value = pri;
}
public bool CompareValuesB(ComponentB other)
{
return PublicFieldB.Value == other.PublicFieldB.Value &&
m_ProtectedFieldB.Value == other.m_ProtectedFieldB.Value &&
m_PrivateFieldB.Value == other.m_PrivateFieldB.Value;
}
}
internal class ComponentC : ComponentB
{
public NetworkVariable<int> PublicFieldC = new NetworkVariable<int>(111);
protected NetworkVariable<int> m_ProtectedFieldC = new NetworkVariable<int>(222);
private NetworkVariable<int> m_PrivateFieldC = new NetworkVariable<int>(333);
public void ChangeValuesC(int pub, int pro, int pri)
{
PublicFieldC.Value = pub;
m_ProtectedFieldA.Value = pro;
m_PrivateFieldC.Value = pri;
}
public bool CompareValuesC(ComponentC other)
{
return PublicFieldC.Value == other.PublicFieldC.Value &&
m_ProtectedFieldC.Value == other.m_ProtectedFieldC.Value &&
m_PrivateFieldC.Value == other.m_PrivateFieldC.Value;
}
}
private GameObject m_TestObjectPrefab;
private ulong m_TestObjectId = 0;
protected override void OnOneTimeSetup()
{
NetworkVariableBase.IgnoreInitializeWarning = true;
base.OnOneTimeSetup();
}
protected override void OnOneTimeTearDown()
{
NetworkVariableBase.IgnoreInitializeWarning = false;
base.OnOneTimeTearDown();
}
protected override void OnServerAndClientsCreated()
{
m_TestObjectPrefab = CreateNetworkObjectPrefab($"[{nameof(NetworkVariableInheritanceTests)}.{nameof(m_TestObjectPrefab)}]");
m_TestObjectPrefab.AddComponent<ComponentA>();
m_TestObjectPrefab.AddComponent<ComponentB>();
m_TestObjectPrefab.AddComponent<ComponentC>();
}
protected override IEnumerator OnServerAndClientsConnected()
{
var serverTestObject = SpawnObject(m_TestObjectPrefab, m_ServerNetworkManager).GetComponent<NetworkObject>();
m_TestObjectId = serverTestObject.NetworkObjectId;
var serverTestComponentA = serverTestObject.GetComponent<ComponentA>();
var serverTestComponentB = serverTestObject.GetComponent<ComponentB>();
var serverTestComponentC = serverTestObject.GetComponent<ComponentC>();
serverTestComponentA.ChangeValuesA(1000, 2000, 3000);
serverTestComponentB.ChangeValuesA(1000, 2000, 3000);
serverTestComponentB.ChangeValuesB(1100, 2200, 3300);
serverTestComponentC.ChangeValuesA(1000, 2000, 3000);
serverTestComponentC.ChangeValuesB(1100, 2200, 3300);
serverTestComponentC.ChangeValuesC(1110, 2220, 3330);
yield return WaitForTicks(m_ServerNetworkManager, 2);
}
private bool CheckTestObjectComponentValuesOnAll()
{
var serverTestObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjectId];
var serverTestComponentA = serverTestObject.GetComponent<ComponentA>();
var serverTestComponentB = serverTestObject.GetComponent<ComponentB>();
var serverTestComponentC = serverTestObject.GetComponent<ComponentC>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var clientTestObject = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjectId];
var clientTestComponentA = clientTestObject.GetComponent<ComponentA>();
var clientTestComponentB = clientTestObject.GetComponent<ComponentB>();
var clientTestComponentC = clientTestObject.GetComponent<ComponentC>();
if (!serverTestComponentA.CompareValuesA(clientTestComponentA) ||
!serverTestComponentB.CompareValuesA(clientTestComponentB) ||
!serverTestComponentB.CompareValuesB(clientTestComponentB) ||
!serverTestComponentC.CompareValuesA(clientTestComponentC) ||
!serverTestComponentC.CompareValuesB(clientTestComponentC) ||
!serverTestComponentC.CompareValuesC(clientTestComponentC))
{
return false;
}
}
return true;
}
[UnityTest]
public IEnumerator TestInheritedFields()
{
yield return WaitForConditionOrTimeOut(CheckTestObjectComponentValuesOnAll);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, nameof(CheckTestObjectComponentValuesOnAll));
}
}
}
#endif

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 41d4aef8f33a8eb4e87879075f868e66

View File

@@ -0,0 +1,306 @@
#if !NGO_MINIMALPROJECT
using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
using UnityEngine.TestTools;
using Random = UnityEngine.Random;
namespace Unity.Netcode.RuntimeTests
{
[TestFixtureSource(nameof(TestDataSource))]
internal class NetworkVariablePermissionTests : NetcodeIntegrationTest
{
public static IEnumerable<TestFixtureData> TestDataSource()
{
NetworkVariableBase.IgnoreInitializeWarning = true;
foreach (HostOrServer hostOrServer in Enum.GetValues(typeof(HostOrServer)))
{
// DANGO-EXP TODO: Add support for distributed authority mode
if (hostOrServer == HostOrServer.DAHost)
{
continue;
}
yield return new TestFixtureData(hostOrServer);
}
NetworkVariableBase.IgnoreInitializeWarning = false;
}
protected override int NumberOfClients => 3;
public NetworkVariablePermissionTests(HostOrServer hostOrServer) : base(hostOrServer) { }
private GameObject m_TestObjPrefab;
private ulong m_TestObjId = 0;
protected override void OnServerAndClientsCreated()
{
m_TestObjPrefab = CreateNetworkObjectPrefab($"[{nameof(NetworkVariablePermissionTests)}.{nameof(m_TestObjPrefab)}]");
var testComp = m_TestObjPrefab.AddComponent<NetVarPermTestComp>();
}
protected override IEnumerator OnServerAndClientsConnected()
{
m_TestObjId = SpawnObject(m_TestObjPrefab, m_ServerNetworkManager).GetComponent<NetworkObject>().NetworkObjectId;
yield return null;
}
private IEnumerator WaitForPositionsAreEqual(NetworkVariable<Vector3> netvar, Vector3 expected)
{
yield return WaitForConditionOrTimeOut(() => netvar.Value == expected);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private IEnumerator WaitForOwnerWritableAreEqualOnAll()
{
yield return WaitForConditionOrTimeOut(CheckOwnerWritableAreEqualOnAll);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private bool CheckOwnerWritableAreEqualOnAll()
{
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testObjServer.OwnerClientId != testObjClient.OwnerClientId ||
testCompServer.OwnerWritable_Position.Value != testCompClient.OwnerWritable_Position.Value ||
testCompServer.OwnerWritable_Position.ReadPerm != testCompClient.OwnerWritable_Position.ReadPerm ||
testCompServer.OwnerWritable_Position.WritePerm != testCompClient.OwnerWritable_Position.WritePerm)
{
return false;
}
}
return true;
}
private IEnumerator WaitForServerWritableAreEqualOnAll()
{
yield return WaitForConditionOrTimeOut(CheckServerWritableAreEqualOnAll);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private bool CheckServerWritableAreEqualOnAll()
{
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testCompServer.ServerWritable_Position.Value != testCompClient.ServerWritable_Position.Value ||
testCompServer.ServerWritable_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm ||
testCompServer.ServerWritable_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm)
{
return false;
}
}
return true;
}
private bool CheckOwnerReadWriteAreEqualOnOwnerAndServer()
{
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testObjServer.OwnerClientId == testObjClient.OwnerClientId &&
testCompServer.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value &&
testCompServer.OwnerReadWrite_Position.ReadPerm == testCompClient.ServerWritable_Position.ReadPerm &&
testCompServer.OwnerReadWrite_Position.WritePerm == testCompClient.ServerWritable_Position.WritePerm)
{
return true;
}
}
return false;
}
private bool CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(NetVarPermTestComp ownerReadWriteObject)
{
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testObjClient.OwnerClientId != ownerReadWriteObject.OwnerClientId ||
ownerReadWriteObject.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value ||
ownerReadWriteObject.OwnerReadWrite_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm ||
ownerReadWriteObject.OwnerReadWrite_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm)
{
return false;
}
}
return true;
}
[UnityTest]
public IEnumerator ServerChangesOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
var oldValue = testCompServer.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompServer.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ServerChangesServerWritableNetVar()
{
yield return WaitForServerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
var oldValue = testCompServer.ServerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompServer.ServerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, newValue);
yield return WaitForServerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ClientChangesOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
var oldValue = testCompClient.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompClient.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
/// <summary>
/// This tests the scenario where a client owner has both read and write
/// permissions set. The server should be the only instance that can read
/// the NetworkVariable. ServerCannotChangeOwnerWritableNetVar performs
/// the same check to make sure the server cannot write to a client owner
/// NetworkVariable with owner write permissions.
/// </summary>
[UnityTest]
public IEnumerator ClientOwnerWithReadWriteChangesNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
var oldValue = testCompClient.OwnerReadWrite_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompClient.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
// Verify the client owner and server match
yield return CheckOwnerReadWriteAreEqualOnOwnerAndServer();
// Verify the non-owner clients do not have the same Value but do have the same permissions
yield return CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(testCompClient);
}
[UnityTest]
public IEnumerator ClientCannotChangeServerWritableNetVar()
{
yield return WaitForServerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForServerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
var oldValue = testCompClient.ServerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
LogAssert.Expect(LogType.Error, testCompClient.ServerWritable_Position.GetWritePermissionError());
testCompClient.ServerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, oldValue);
yield return WaitForServerWritableAreEqualOnAll();
testCompServer.ServerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, newValue);
yield return WaitForServerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ServerCannotChangeOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return WaitForTicks(m_ServerNetworkManager, 4);
yield return WaitForOwnerWritableAreEqualOnAll();
var oldValue = testCompServer.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
LogAssert.Expect(LogType.Error, testCompServer.OwnerWritable_Position.GetWritePermissionError());
testCompServer.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.OwnerWritable_Position, oldValue);
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
testCompClient.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
}
}
#endif

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 60d49d322bef8ff4ebb8c4abf57e18e3

View File

@@ -12,298 +12,6 @@ using Random = UnityEngine.Random;
namespace Unity.Netcode.RuntimeTests namespace Unity.Netcode.RuntimeTests
{ {
[TestFixtureSource(nameof(TestDataSource))]
internal class NetworkVariablePermissionTests : NetcodeIntegrationTest
{
public static IEnumerable<TestFixtureData> TestDataSource()
{
NetworkVariableBase.IgnoreInitializeWarning = true;
foreach (HostOrServer hostOrServer in Enum.GetValues(typeof(HostOrServer)))
{
// DANGO-EXP TODO: Add support for distributed authority mode
if (hostOrServer == HostOrServer.DAHost)
{
continue;
}
yield return new TestFixtureData(hostOrServer);
}
NetworkVariableBase.IgnoreInitializeWarning = false;
}
protected override int NumberOfClients => 3;
public NetworkVariablePermissionTests(HostOrServer hostOrServer) : base(hostOrServer) { }
private GameObject m_TestObjPrefab;
private ulong m_TestObjId = 0;
protected override void OnServerAndClientsCreated()
{
m_TestObjPrefab = CreateNetworkObjectPrefab($"[{nameof(NetworkVariablePermissionTests)}.{nameof(m_TestObjPrefab)}]");
var testComp = m_TestObjPrefab.AddComponent<NetVarPermTestComp>();
}
protected override IEnumerator OnServerAndClientsConnected()
{
m_TestObjId = SpawnObject(m_TestObjPrefab, m_ServerNetworkManager).GetComponent<NetworkObject>().NetworkObjectId;
yield return null;
}
private IEnumerator WaitForPositionsAreEqual(NetworkVariable<Vector3> netvar, Vector3 expected)
{
yield return WaitForConditionOrTimeOut(() => netvar.Value == expected);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private IEnumerator WaitForOwnerWritableAreEqualOnAll()
{
yield return WaitForConditionOrTimeOut(CheckOwnerWritableAreEqualOnAll);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private bool CheckOwnerWritableAreEqualOnAll()
{
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testObjServer.OwnerClientId != testObjClient.OwnerClientId ||
testCompServer.OwnerWritable_Position.Value != testCompClient.OwnerWritable_Position.Value ||
testCompServer.OwnerWritable_Position.ReadPerm != testCompClient.OwnerWritable_Position.ReadPerm ||
testCompServer.OwnerWritable_Position.WritePerm != testCompClient.OwnerWritable_Position.WritePerm)
{
return false;
}
}
return true;
}
private IEnumerator WaitForServerWritableAreEqualOnAll()
{
yield return WaitForConditionOrTimeOut(CheckServerWritableAreEqualOnAll);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut);
}
private bool CheckServerWritableAreEqualOnAll()
{
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testCompServer.ServerWritable_Position.Value != testCompClient.ServerWritable_Position.Value ||
testCompServer.ServerWritable_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm ||
testCompServer.ServerWritable_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm)
{
return false;
}
}
return true;
}
private bool CheckOwnerReadWriteAreEqualOnOwnerAndServer()
{
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testObjServer.OwnerClientId == testObjClient.OwnerClientId &&
testCompServer.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value &&
testCompServer.OwnerReadWrite_Position.ReadPerm == testCompClient.ServerWritable_Position.ReadPerm &&
testCompServer.OwnerReadWrite_Position.WritePerm == testCompClient.ServerWritable_Position.WritePerm)
{
return true;
}
}
return false;
}
private bool CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(NetVarPermTestComp ownerReadWriteObject)
{
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
if (testObjClient.OwnerClientId != ownerReadWriteObject.OwnerClientId ||
ownerReadWriteObject.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value ||
ownerReadWriteObject.OwnerReadWrite_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm ||
ownerReadWriteObject.OwnerReadWrite_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm)
{
return false;
}
}
return true;
}
[UnityTest]
public IEnumerator ServerChangesOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
var oldValue = testCompServer.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompServer.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ServerChangesServerWritableNetVar()
{
yield return WaitForServerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
var oldValue = testCompServer.ServerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompServer.ServerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, newValue);
yield return WaitForServerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ClientChangesOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
var oldValue = testCompClient.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompClient.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
/// <summary>
/// This tests the scenario where a client owner has both read and write
/// permissions set. The server should be the only instance that can read
/// the NetworkVariable. ServerCannotChangeOwnerWritableNetVar performs
/// the same check to make sure the server cannot write to a client owner
/// NetworkVariable with owner write permissions.
/// </summary>
[UnityTest]
public IEnumerator ClientOwnerWithReadWriteChangesNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
var oldValue = testCompClient.OwnerReadWrite_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
testCompClient.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
// Verify the client owner and server match
yield return CheckOwnerReadWriteAreEqualOnOwnerAndServer();
// Verify the non-owner clients do not have the same Value but do have the same permissions
yield return CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(testCompClient);
}
[UnityTest]
public IEnumerator ClientCannotChangeServerWritableNetVar()
{
yield return WaitForServerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForServerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
var oldValue = testCompClient.ServerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
Assert.That(() => testCompClient.ServerWritable_Position.Value = newValue, Throws.TypeOf<InvalidOperationException>());
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, oldValue);
yield return WaitForServerWritableAreEqualOnAll();
testCompServer.ServerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompServer.ServerWritable_Position, newValue);
yield return WaitForServerWritableAreEqualOnAll();
}
[UnityTest]
public IEnumerator ServerCannotChangeOwnerWritableNetVar()
{
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
testObjServer.ChangeOwnership(newOwnerClientId);
yield return WaitForTicks(m_ServerNetworkManager, 2);
yield return WaitForOwnerWritableAreEqualOnAll();
var oldValue = testCompServer.OwnerWritable_Position.Value;
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
Assert.That(() => testCompServer.OwnerWritable_Position.Value = newValue, Throws.TypeOf<InvalidOperationException>());
yield return WaitForPositionsAreEqual(testCompServer.OwnerWritable_Position, oldValue);
yield return WaitForOwnerWritableAreEqualOnAll();
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
testCompClient.OwnerWritable_Position.Value = newValue;
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
yield return WaitForOwnerWritableAreEqualOnAll();
}
}
internal struct TestStruct : INetworkSerializable, IEquatable<TestStruct> internal struct TestStruct : INetworkSerializable, IEquatable<TestStruct>
{ {
public uint SomeInt; public uint SomeInt;
@@ -438,6 +146,255 @@ namespace Unity.Netcode.RuntimeTests
} }
} }
/// <summary>
/// Handles the more generic conditional logic for NetworkList tests
/// which can be used with the <see cref="NetcodeIntegrationTest.WaitForConditionOrTimeOut"/>
/// that accepts anything derived from the <see cref="ConditionalPredicateBase"/> class
/// as a parameter.
/// </summary>
internal class NetworkListTestPredicate : ConditionalPredicateBase
{
private const int k_MaxRandomValue = 1000;
private Dictionary<NetworkListTestStates, Func<bool>> m_StateFunctions;
// Player1 component on the Server
private NetworkVariableTest m_Player1OnServer;
// Player1 component on client1
private NetworkVariableTest m_Player1OnClient1;
private string m_TestStageFailedMessage;
public enum NetworkListTestStates
{
Add,
ContainsLarge,
Contains,
VerifyData,
IndexOf,
}
private NetworkListTestStates m_NetworkListTestState;
public void SetNetworkListTestState(NetworkListTestStates networkListTestState)
{
m_NetworkListTestState = networkListTestState;
}
/// <summary>
/// Determines if the condition has been reached for the current NetworkListTestState
/// </summary>
protected override bool OnHasConditionBeenReached()
{
var isStateRegistered = m_StateFunctions.ContainsKey(m_NetworkListTestState);
Assert.IsTrue(isStateRegistered);
return m_StateFunctions[m_NetworkListTestState].Invoke();
}
/// <summary>
/// Provides all information about the players for both sides for simplicity and informative sake.
/// </summary>
/// <returns></returns>
private string ConditionFailedInfo()
{
return $"{m_NetworkListTestState} condition test failed:\n Server List Count: {m_Player1OnServer.TheList.Count} vs Client List Count: {m_Player1OnClient1.TheList.Count}\n" +
$"Server List Count: {m_Player1OnServer.TheLargeList.Count} vs Client List Count: {m_Player1OnClient1.TheLargeList.Count}\n" +
$"Server Delegate Triggered: {m_Player1OnServer.ListDelegateTriggered} | Client Delegate Triggered: {m_Player1OnClient1.ListDelegateTriggered}\n";
}
/// <summary>
/// When finished, check if a time out occurred and if so assert and provide meaningful information to troubleshoot why
/// </summary>
protected override void OnFinished()
{
Assert.IsFalse(TimedOut, $"{nameof(NetworkListTestPredicate)} timed out waiting for the {m_NetworkListTestState} condition to be reached! \n" + ConditionFailedInfo());
}
// Uses the ArrayOperator and validates that on both sides the count and values are the same
private bool OnVerifyData()
{
// Wait until both sides have the same number of elements
if (m_Player1OnServer.TheList.Count != m_Player1OnClient1.TheList.Count)
{
return false;
}
// Check the client values against the server values to make sure they match
for (int i = 0; i < m_Player1OnServer.TheList.Count; i++)
{
if (m_Player1OnServer.TheList[i] != m_Player1OnClient1.TheList[i])
{
return false;
}
}
return true;
}
/// <summary>
/// Verifies the data count, values, and that the ListDelegate on both sides was triggered
/// </summary>
private bool OnAdd()
{
bool wasTriggerred = m_Player1OnServer.ListDelegateTriggered && m_Player1OnClient1.ListDelegateTriggered;
return wasTriggerred && OnVerifyData();
}
/// <summary>
/// The current version of this test only verified the count of the large list, so that is what this does
/// </summary>
private bool OnContainsLarge()
{
return m_Player1OnServer.TheLargeList.Count == m_Player1OnClient1.TheLargeList.Count;
}
/// <summary>
/// Tests NetworkList.Contains which also verifies all values are the same on both sides
/// </summary>
private bool OnContains()
{
// Wait until both sides have the same number of elements
if (m_Player1OnServer.TheList.Count != m_Player1OnClient1.TheList.Count)
{
return false;
}
// Parse through all server values and use the NetworkList.Contains method to check if the value is in the list on the client side
foreach (var serverValue in m_Player1OnServer.TheList)
{
if (!m_Player1OnClient1.TheList.Contains(serverValue))
{
return false;
}
}
return true;
}
/// <summary>
/// Tests NetworkList.IndexOf and verifies that all values are aligned on both sides
/// </summary>
private bool OnIndexOf()
{
foreach (var serverSideValue in m_Player1OnServer.TheList)
{
var indexToTest = m_Player1OnServer.TheList.IndexOf(serverSideValue);
if (indexToTest != m_Player1OnServer.TheList.IndexOf(serverSideValue))
{
return false;
}
}
return true;
}
public NetworkListTestPredicate(NetworkVariableTest player1OnServer, NetworkVariableTest player1OnClient1, NetworkListTestStates networkListTestState, int elementCount)
{
m_NetworkListTestState = networkListTestState;
m_Player1OnServer = player1OnServer;
m_Player1OnClient1 = player1OnClient1;
m_StateFunctions = new Dictionary<NetworkListTestStates, Func<bool>>
{
{ NetworkListTestStates.Add, OnAdd },
{ NetworkListTestStates.ContainsLarge, OnContainsLarge },
{ NetworkListTestStates.Contains, OnContains },
{ NetworkListTestStates.VerifyData, OnVerifyData },
{ NetworkListTestStates.IndexOf, OnIndexOf }
};
if (networkListTestState == NetworkListTestStates.ContainsLarge)
{
for (var i = 0; i < elementCount; ++i)
{
m_Player1OnServer.TheLargeList.Add(new FixedString128Bytes());
}
}
else
{
for (int i = 0; i < elementCount; i++)
{
m_Player1OnServer.TheList.Add(Random.Range(0, k_MaxRandomValue));
}
}
}
}
internal class NetvarDespawnShutdown : NetworkBehaviour
{
private NetworkVariable<int> m_IntNetworkVariable = new NetworkVariable<int>();
private NetworkList<int> m_IntList;
private void Awake()
{
m_IntList = new NetworkList<int>();
}
public override void OnNetworkDespawn()
{
if (IsServer)
{
m_IntNetworkVariable.Value = 5;
for (int i = 0; i < 10; i++)
{
m_IntList.Add(i);
}
}
base.OnNetworkDespawn();
}
}
/// <summary>
/// Validates that setting values for NetworkVariable or NetworkList during the
/// OnNetworkDespawn method will not cause an exception to occur.
/// </summary>
internal class NetworkVariableModifyOnNetworkDespawn : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
private GameObject m_TestPrefab;
protected override void OnOneTimeSetup()
{
NetworkVariableBase.IgnoreInitializeWarning = true;
base.OnOneTimeSetup();
}
protected override void OnOneTimeTearDown()
{
NetworkVariableBase.IgnoreInitializeWarning = false;
base.OnOneTimeTearDown();
}
protected override void OnServerAndClientsCreated()
{
m_TestPrefab = CreateNetworkObjectPrefab("NetVarDespawn");
m_TestPrefab.AddComponent<NetvarDespawnShutdown>();
base.OnServerAndClientsCreated();
}
private bool OnClientSpawnedTestPrefab(ulong networkObjectId)
{
var clientId = m_ClientNetworkManagers[0].LocalClientId;
if (!s_GlobalNetworkObjects.ContainsKey(clientId))
{
return false;
}
if (!s_GlobalNetworkObjects[clientId].ContainsKey(networkObjectId))
{
return false;
}
return true;
}
[UnityTest]
public IEnumerator ModifyNetworkVariableOrListOnNetworkDespawn()
{
var instance = SpawnObject(m_TestPrefab, m_ServerNetworkManager);
yield return WaitForConditionOrTimeOut(() => OnClientSpawnedTestPrefab(instance.GetComponent<NetworkObject>().NetworkObjectId));
m_ServerNetworkManager.Shutdown();
// As long as no excetptions occur, the test passes.
}
}
#if !MULTIPLAYER_TOOLS #if !MULTIPLAYER_TOOLS
[TestFixture(true)] [TestFixture(true)]
@@ -612,7 +569,9 @@ namespace Unity.Netcode.RuntimeTests
InitializeServerAndClients(useHost); InitializeServerAndClients(useHost);
// client must not be allowed to write to a server auth variable // client must not be allowed to write to a server auth variable
Assert.Throws<InvalidOperationException>(() => m_Player1OnClient1.TheScalar.Value = k_TestVal1);
LogAssert.Expect(LogType.Error, m_Player1OnClient1.TheScalar.GetWritePermissionError());
m_Player1OnClient1.TheScalar.Value = k_TestVal1;
} }
/// <summary> /// <summary>
@@ -1053,7 +1012,8 @@ namespace Unity.Netcode.RuntimeTests
} }
[Test] [Test]
public void TestUnsupportedManagedTypesWithUserSerializationDoNotThrowExceptions() // Exceptions should be thrown in DA mode with UserNetworkVariableSerialization
public void TestUnsupportedManagedTypesWithUserSerializationDoNotThrowExceptionsInClientServerMode()
{ {
var variable = new NetworkVariable<string>(); var variable = new NetworkVariable<string>();
UserNetworkVariableSerialization<string>.ReadValue = (FastBufferReader reader, out string value) => UserNetworkVariableSerialization<string>.ReadValue = (FastBufferReader reader, out string value) =>
@@ -1079,6 +1039,10 @@ namespace Unity.Netcode.RuntimeTests
variable.ReadField(reader); variable.ReadField(reader);
Assert.AreEqual("012345", variable.Value); Assert.AreEqual("012345", variable.Value);
} }
catch (Exception)
{
Assert.True(NetworkVariableSerialization<UserNetworkVariableSerialization<string>>.IsDistributedAuthority);
}
finally finally
{ {
UserNetworkVariableSerialization<string>.ReadValue = null; UserNetworkVariableSerialization<string>.ReadValue = null;
@@ -5125,408 +5089,5 @@ namespace Unity.Netcode.RuntimeTests
yield return base.OnTearDown(); yield return base.OnTearDown();
} }
} }
/// <summary>
/// Handles the more generic conditional logic for NetworkList tests
/// which can be used with the <see cref="NetcodeIntegrationTest.WaitForConditionOrTimeOut"/>
/// that accepts anything derived from the <see cref="ConditionalPredicateBase"/> class
/// as a parameter.
/// </summary>
internal class NetworkListTestPredicate : ConditionalPredicateBase
{
private const int k_MaxRandomValue = 1000;
private Dictionary<NetworkListTestStates, Func<bool>> m_StateFunctions;
// Player1 component on the Server
private NetworkVariableTest m_Player1OnServer;
// Player1 component on client1
private NetworkVariableTest m_Player1OnClient1;
private string m_TestStageFailedMessage;
public enum NetworkListTestStates
{
Add,
ContainsLarge,
Contains,
VerifyData,
IndexOf,
}
private NetworkListTestStates m_NetworkListTestState;
public void SetNetworkListTestState(NetworkListTestStates networkListTestState)
{
m_NetworkListTestState = networkListTestState;
}
/// <summary>
/// Determines if the condition has been reached for the current NetworkListTestState
/// </summary>
protected override bool OnHasConditionBeenReached()
{
var isStateRegistered = m_StateFunctions.ContainsKey(m_NetworkListTestState);
Assert.IsTrue(isStateRegistered);
return m_StateFunctions[m_NetworkListTestState].Invoke();
}
/// <summary>
/// Provides all information about the players for both sides for simplicity and informative sake.
/// </summary>
/// <returns></returns>
private string ConditionFailedInfo()
{
return $"{m_NetworkListTestState} condition test failed:\n Server List Count: {m_Player1OnServer.TheList.Count} vs Client List Count: {m_Player1OnClient1.TheList.Count}\n" +
$"Server List Count: {m_Player1OnServer.TheLargeList.Count} vs Client List Count: {m_Player1OnClient1.TheLargeList.Count}\n" +
$"Server Delegate Triggered: {m_Player1OnServer.ListDelegateTriggered} | Client Delegate Triggered: {m_Player1OnClient1.ListDelegateTriggered}\n";
}
/// <summary>
/// When finished, check if a time out occurred and if so assert and provide meaningful information to troubleshoot why
/// </summary>
protected override void OnFinished()
{
Assert.IsFalse(TimedOut, $"{nameof(NetworkListTestPredicate)} timed out waiting for the {m_NetworkListTestState} condition to be reached! \n" + ConditionFailedInfo());
}
// Uses the ArrayOperator and validates that on both sides the count and values are the same
private bool OnVerifyData()
{
// Wait until both sides have the same number of elements
if (m_Player1OnServer.TheList.Count != m_Player1OnClient1.TheList.Count)
{
return false;
}
// Check the client values against the server values to make sure they match
for (int i = 0; i < m_Player1OnServer.TheList.Count; i++)
{
if (m_Player1OnServer.TheList[i] != m_Player1OnClient1.TheList[i])
{
return false;
}
}
return true;
}
/// <summary>
/// Verifies the data count, values, and that the ListDelegate on both sides was triggered
/// </summary>
private bool OnAdd()
{
bool wasTriggerred = m_Player1OnServer.ListDelegateTriggered && m_Player1OnClient1.ListDelegateTriggered;
return wasTriggerred && OnVerifyData();
}
/// <summary>
/// The current version of this test only verified the count of the large list, so that is what this does
/// </summary>
private bool OnContainsLarge()
{
return m_Player1OnServer.TheLargeList.Count == m_Player1OnClient1.TheLargeList.Count;
}
/// <summary>
/// Tests NetworkList.Contains which also verifies all values are the same on both sides
/// </summary>
private bool OnContains()
{
// Wait until both sides have the same number of elements
if (m_Player1OnServer.TheList.Count != m_Player1OnClient1.TheList.Count)
{
return false;
}
// Parse through all server values and use the NetworkList.Contains method to check if the value is in the list on the client side
foreach (var serverValue in m_Player1OnServer.TheList)
{
if (!m_Player1OnClient1.TheList.Contains(serverValue))
{
return false;
}
}
return true;
}
/// <summary>
/// Tests NetworkList.IndexOf and verifies that all values are aligned on both sides
/// </summary>
private bool OnIndexOf()
{
foreach (var serverSideValue in m_Player1OnServer.TheList)
{
var indexToTest = m_Player1OnServer.TheList.IndexOf(serverSideValue);
if (indexToTest != m_Player1OnServer.TheList.IndexOf(serverSideValue))
{
return false;
}
}
return true;
}
public NetworkListTestPredicate(NetworkVariableTest player1OnServer, NetworkVariableTest player1OnClient1, NetworkListTestStates networkListTestState, int elementCount)
{
m_NetworkListTestState = networkListTestState;
m_Player1OnServer = player1OnServer;
m_Player1OnClient1 = player1OnClient1;
m_StateFunctions = new Dictionary<NetworkListTestStates, Func<bool>>
{
{ NetworkListTestStates.Add, OnAdd },
{ NetworkListTestStates.ContainsLarge, OnContainsLarge },
{ NetworkListTestStates.Contains, OnContains },
{ NetworkListTestStates.VerifyData, OnVerifyData },
{ NetworkListTestStates.IndexOf, OnIndexOf }
};
if (networkListTestState == NetworkListTestStates.ContainsLarge)
{
for (var i = 0; i < elementCount; ++i)
{
m_Player1OnServer.TheLargeList.Add(new FixedString128Bytes());
}
}
else
{
for (int i = 0; i < elementCount; i++)
{
m_Player1OnServer.TheList.Add(Random.Range(0, k_MaxRandomValue));
}
}
}
}
[TestFixtureSource(nameof(TestDataSource))]
internal class NetworkVariableInheritanceTests : NetcodeIntegrationTest
{
public NetworkVariableInheritanceTests(HostOrServer hostOrServer)
: base(hostOrServer)
{
}
protected override int NumberOfClients => 2;
public static IEnumerable<TestFixtureData> TestDataSource() =>
Enum.GetValues(typeof(HostOrServer)).OfType<HostOrServer>().Select(x => new TestFixtureData(x));
internal class ComponentA : NetworkBehaviour
{
public NetworkVariable<int> PublicFieldA = new NetworkVariable<int>(1);
protected NetworkVariable<int> m_ProtectedFieldA = new NetworkVariable<int>(2);
private NetworkVariable<int> m_PrivateFieldA = new NetworkVariable<int>(3);
public void ChangeValuesA(int pub, int pro, int pri)
{
PublicFieldA.Value = pub;
m_ProtectedFieldA.Value = pro;
m_PrivateFieldA.Value = pri;
}
public bool CompareValuesA(ComponentA other)
{
return PublicFieldA.Value == other.PublicFieldA.Value &&
m_ProtectedFieldA.Value == other.m_ProtectedFieldA.Value &&
m_PrivateFieldA.Value == other.m_PrivateFieldA.Value;
}
}
internal class ComponentB : ComponentA
{
public NetworkVariable<int> PublicFieldB = new NetworkVariable<int>(11);
protected NetworkVariable<int> m_ProtectedFieldB = new NetworkVariable<int>(22);
private NetworkVariable<int> m_PrivateFieldB = new NetworkVariable<int>(33);
public void ChangeValuesB(int pub, int pro, int pri)
{
PublicFieldB.Value = pub;
m_ProtectedFieldB.Value = pro;
m_PrivateFieldB.Value = pri;
}
public bool CompareValuesB(ComponentB other)
{
return PublicFieldB.Value == other.PublicFieldB.Value &&
m_ProtectedFieldB.Value == other.m_ProtectedFieldB.Value &&
m_PrivateFieldB.Value == other.m_PrivateFieldB.Value;
}
}
internal class ComponentC : ComponentB
{
public NetworkVariable<int> PublicFieldC = new NetworkVariable<int>(111);
protected NetworkVariable<int> m_ProtectedFieldC = new NetworkVariable<int>(222);
private NetworkVariable<int> m_PrivateFieldC = new NetworkVariable<int>(333);
public void ChangeValuesC(int pub, int pro, int pri)
{
PublicFieldC.Value = pub;
m_ProtectedFieldA.Value = pro;
m_PrivateFieldC.Value = pri;
}
public bool CompareValuesC(ComponentC other)
{
return PublicFieldC.Value == other.PublicFieldC.Value &&
m_ProtectedFieldC.Value == other.m_ProtectedFieldC.Value &&
m_PrivateFieldC.Value == other.m_PrivateFieldC.Value;
}
}
private GameObject m_TestObjectPrefab;
private ulong m_TestObjectId = 0;
protected override void OnOneTimeSetup()
{
NetworkVariableBase.IgnoreInitializeWarning = true;
base.OnOneTimeSetup();
}
protected override void OnOneTimeTearDown()
{
NetworkVariableBase.IgnoreInitializeWarning = false;
base.OnOneTimeTearDown();
}
protected override void OnServerAndClientsCreated()
{
m_TestObjectPrefab = CreateNetworkObjectPrefab($"[{nameof(NetworkVariableInheritanceTests)}.{nameof(m_TestObjectPrefab)}]");
m_TestObjectPrefab.AddComponent<ComponentA>();
m_TestObjectPrefab.AddComponent<ComponentB>();
m_TestObjectPrefab.AddComponent<ComponentC>();
}
protected override IEnumerator OnServerAndClientsConnected()
{
var serverTestObject = SpawnObject(m_TestObjectPrefab, m_ServerNetworkManager).GetComponent<NetworkObject>();
m_TestObjectId = serverTestObject.NetworkObjectId;
var serverTestComponentA = serverTestObject.GetComponent<ComponentA>();
var serverTestComponentB = serverTestObject.GetComponent<ComponentB>();
var serverTestComponentC = serverTestObject.GetComponent<ComponentC>();
serverTestComponentA.ChangeValuesA(1000, 2000, 3000);
serverTestComponentB.ChangeValuesA(1000, 2000, 3000);
serverTestComponentB.ChangeValuesB(1100, 2200, 3300);
serverTestComponentC.ChangeValuesA(1000, 2000, 3000);
serverTestComponentC.ChangeValuesB(1100, 2200, 3300);
serverTestComponentC.ChangeValuesC(1110, 2220, 3330);
yield return WaitForTicks(m_ServerNetworkManager, 2);
}
private bool CheckTestObjectComponentValuesOnAll()
{
var serverTestObject = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjectId];
var serverTestComponentA = serverTestObject.GetComponent<ComponentA>();
var serverTestComponentB = serverTestObject.GetComponent<ComponentB>();
var serverTestComponentC = serverTestObject.GetComponent<ComponentC>();
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
var clientTestObject = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjectId];
var clientTestComponentA = clientTestObject.GetComponent<ComponentA>();
var clientTestComponentB = clientTestObject.GetComponent<ComponentB>();
var clientTestComponentC = clientTestObject.GetComponent<ComponentC>();
if (!serverTestComponentA.CompareValuesA(clientTestComponentA) ||
!serverTestComponentB.CompareValuesA(clientTestComponentB) ||
!serverTestComponentB.CompareValuesB(clientTestComponentB) ||
!serverTestComponentC.CompareValuesA(clientTestComponentC) ||
!serverTestComponentC.CompareValuesB(clientTestComponentC) ||
!serverTestComponentC.CompareValuesC(clientTestComponentC))
{
return false;
}
}
return true;
}
[UnityTest]
public IEnumerator TestInheritedFields()
{
yield return WaitForConditionOrTimeOut(CheckTestObjectComponentValuesOnAll);
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, nameof(CheckTestObjectComponentValuesOnAll));
}
}
internal class NetvarDespawnShutdown : NetworkBehaviour
{
private NetworkVariable<int> m_IntNetworkVariable = new NetworkVariable<int>();
private NetworkList<int> m_IntList;
private void Awake()
{
m_IntList = new NetworkList<int>();
}
public override void OnNetworkDespawn()
{
if (IsServer)
{
m_IntNetworkVariable.Value = 5;
for (int i = 0; i < 10; i++)
{
m_IntList.Add(i);
}
}
base.OnNetworkDespawn();
}
}
/// <summary>
/// Validates that setting values for NetworkVariable or NetworkList during the
/// OnNetworkDespawn method will not cause an exception to occur.
/// </summary>
internal class NetworkVariableModifyOnNetworkDespawn : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
private GameObject m_TestPrefab;
protected override void OnOneTimeSetup()
{
NetworkVariableBase.IgnoreInitializeWarning = true;
base.OnOneTimeSetup();
}
protected override void OnOneTimeTearDown()
{
NetworkVariableBase.IgnoreInitializeWarning = false;
base.OnOneTimeTearDown();
}
protected override void OnServerAndClientsCreated()
{
m_TestPrefab = CreateNetworkObjectPrefab("NetVarDespawn");
m_TestPrefab.AddComponent<NetvarDespawnShutdown>();
base.OnServerAndClientsCreated();
}
private bool OnClientSpawnedTestPrefab(ulong networkObjectId)
{
var clientId = m_ClientNetworkManagers[0].LocalClientId;
if (!s_GlobalNetworkObjects.ContainsKey(clientId))
{
return false;
}
if (!s_GlobalNetworkObjects[clientId].ContainsKey(networkObjectId))
{
return false;
}
return true;
}
[UnityTest]
public IEnumerator ModifyNetworkVariableOrListOnNetworkDespawn()
{
var instance = SpawnObject(m_TestPrefab, m_ServerNetworkManager);
yield return WaitForConditionOrTimeOut(() => OnClientSpawnedTestPrefab(instance.GetComponent<NetworkObject>().NetworkObjectId));
m_ServerNetworkManager.Shutdown();
// As long as no excetptions occur, the test passes.
}
}
} }
#endif #endif

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Unity.Netcode.TestHelpers.Runtime; using Unity.Netcode.TestHelpers.Runtime;
@@ -50,7 +49,6 @@ namespace Unity.Netcode.RuntimeTests
public override void OnNetworkSpawn() public override void OnNetworkSpawn()
{ {
Objects[CurrentlySpawning, NetworkManager.LocalClientId] = GetComponent<OwnerPermissionObject>(); Objects[CurrentlySpawning, NetworkManager.LocalClientId] = GetComponent<OwnerPermissionObject>();
//Debug.Log($"Object index ({CurrentlySpawning}) spawned on client {NetworkManager.LocalClientId}");
} }
private void Awake() private void Awake()
@@ -85,6 +83,8 @@ namespace Unity.Netcode.RuntimeTests
} }
} }
internal class OwnerPermissionHideTests : NetcodeIntegrationTest internal class OwnerPermissionHideTests : NetcodeIntegrationTest
{ {
protected override int NumberOfClients => 2; protected override int NumberOfClients => 2;
@@ -130,70 +130,44 @@ namespace Unity.Netcode.RuntimeTests
for (var clientWriting = 0; clientWriting < 3; clientWriting++) for (var clientWriting = 0; clientWriting < 3; clientWriting++)
{ {
// ==== Server-writable NetworkVariable ==== // ==== Server-writable NetworkVariable ====
var gotException = false;
VerboseDebug($"Writing to server-write variable on object {objectIndex} on client {clientWriting}"); VerboseDebug($"Writing to server-write variable on object {objectIndex} on client {clientWriting}");
try nextValueToWrite++;
if (clientWriting != serverIndex)
{ {
nextValueToWrite++; LogAssert.Expect(LogType.Error, OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkVariableServer.GetWritePermissionError());
OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkVariableServer.Value = nextValueToWrite;
} }
catch (Exception) OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkVariableServer.Value = nextValueToWrite;
{
gotException = true;
}
// Verify server-owned netvar can only be written by server
Debug.Assert(gotException == (clientWriting != serverIndex));
// ==== Owner-writable NetworkVariable ==== // ==== Owner-writable NetworkVariable ====
gotException = false;
VerboseDebug($"Writing to owner-write variable on object {objectIndex} on client {clientWriting}"); VerboseDebug($"Writing to owner-write variable on object {objectIndex} on client {clientWriting}");
try nextValueToWrite++;
if (clientWriting != objectIndex)
{ {
nextValueToWrite++; LogAssert.Expect(LogType.Error, OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkVariableOwner.GetWritePermissionError());
OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkVariableOwner.Value = nextValueToWrite;
} }
catch (Exception) OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkVariableOwner.Value = nextValueToWrite;
{
gotException = true;
}
// Verify client-owned netvar can only be written by owner
Debug.Assert(gotException == (clientWriting != objectIndex));
// ==== Server-writable NetworkList ==== // ==== Server-writable NetworkList ====
gotException = false; VerboseDebug($"Writing to [Add] server-write NetworkList on object {objectIndex} on client {clientWriting}");
try nextValueToWrite++;
if (clientWriting != serverIndex)
{ {
nextValueToWrite++; LogAssert.Expect(LogType.Error, OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkListServer.GetWritePermissionError());
OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkListServer.Add(nextValueToWrite);
} }
catch (Exception) OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkListServer.Add(nextValueToWrite);
{
gotException = true;
}
// Verify server-owned networkList can only be written by server
Debug.Assert(gotException == (clientWriting != serverIndex));
// ==== Owner-writable NetworkList ==== // ==== Owner-writable NetworkList ====
gotException = false; VerboseDebug($"Writing to [Add] owner-write NetworkList on object {objectIndex} on client {clientWriting}");
try nextValueToWrite++;
if (clientWriting != objectIndex)
{ {
nextValueToWrite++; LogAssert.Expect(LogType.Error, OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkListOwner.GetWritePermissionError());
OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkListOwner.Add(nextValueToWrite);
} }
catch (Exception) OwnerPermissionObject.Objects[objectIndex, clientWriting].MyNetworkListOwner.Add(nextValueToWrite);
{
gotException = true;
}
// Verify client-owned networkList can only be written by owner
Debug.Assert(gotException == (clientWriting != objectIndex));
yield return WaitForTicks(m_ServerNetworkManager, 5); yield return WaitForTicks(m_ServerNetworkManager, 5);
yield return WaitForTicks(m_ClientNetworkManagers[0], 5); yield return WaitForTicks(m_ClientNetworkManagers[0], 5);

View File

@@ -2,23 +2,23 @@
"name": "com.unity.netcode.gameobjects", "name": "com.unity.netcode.gameobjects",
"displayName": "Netcode for GameObjects", "displayName": "Netcode for GameObjects",
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.", "description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
"version": "2.0.0-pre.2", "version": "2.0.0-pre.4",
"unity": "6000.0", "unity": "6000.0",
"dependencies": { "dependencies": {
"com.unity.nuget.mono-cecil": "1.11.4", "com.unity.nuget.mono-cecil": "1.11.4",
"com.unity.transport": "2.2.1" "com.unity.transport": "2.3.0"
}, },
"_upm": { "_upm": {
"changelog": "### Added\n\n- Added `AnticipatedNetworkVariable<T>`, which adds support for client anticipation of `NetworkVariable` values, allowing for more responsive gameplay. (#2957)\n- Added `AnticipatedNetworkTransform`, which adds support for client anticipation of NetworkTransforms. (#2957)\n- Added `NetworkVariableBase.ExceedsDirtinessThreshold` to allow network variables to throttle updates by only sending updates when the difference between the current and previous values exceeds a threshold. (This is exposed in `NetworkVariable<T>` with the callback `NetworkVariable<T>.CheckExceedsDirtinessThreshold`). (#2957)\n- Added `NetworkVariableUpdateTraits`, which add additional throttling support: `MinSecondsBetweenUpdates` will prevent the `NetworkVariable` from sending updates more often than the specified time period (even if it exceeds the dirtiness threshold), while `MaxSecondsBetweenUpdates` will force a dirty `NetworkVariable` to send an update after the specified time period even if it has not yet exceeded the dirtiness threshold. (#2957)\n- Added virtual method `NetworkVariableBase.OnInitialize` which can be used by `NetworkVariable` subclasses to add initialization code. (#2957)\n- Added `NetworkTime.TickWithPartial`, which represents the current tick as a double that includes the fractional/partial tick value. (#2957)\n- Added `NetworkTickSystem.AnticipationTick`, which can be helpful with implementation of client anticipation. This value represents the tick the current local client was at at the beginning of the most recent network round trip, which enables it to correlate server update ticks with the client tick that may have triggered them. (#2957)\n- Added event `NetworkManager.OnSessionOwnerPromoted` that is invoked when a new session owner promotion occurs. (#2948)\n- Added `NetworkRigidBodyBase.GetLinearVelocity` and `NetworkRigidBodyBase.SetLinearVelocity` convenience/helper methods. (#2948)\n- Added `NetworkRigidBodyBase.GetAngularVelocity` and `NetworkRigidBodyBase.SetAngularVelocity` convenience/helper methods. (#2948)\n\n### Fixed\n\n- Fixed issue when `NetworkTransform` half float precision is enabled and ownership changes the current base position was not being synchronized. (#2948)\n- Fixed issue where `OnClientConnected` not being invoked on the session owner when connecting to a new distributed authority session. (#2948)\n- Fixed issue where Rigidbody micro-motion (i.e. relatively small velocities) would result in non-authority instances slightly stuttering as the body would come to a rest (i.e. no motion). Now, the threshold value can increase at higher velocities and can decrease slightly below the provided threshold to account for this. (#2948)\n\n### Changed\n\n- Changed `NetworkAnimator` no longer requires the `Animator` component to exist on the same `GameObject`. (#2957)\n- Changed `NetworkObjectReference` and `NetworkBehaviourReference` to allow null references when constructing and serializing. (#2957)\n- Changed the client's owned objects is now returned (`NetworkClient` and `NetworkSpawnManager`) as an array as opposed to a list for performance purposes. (#2948)\n- Changed `NetworkTransfrom.TryCommitTransformToServer` to be internal as it will be removed by the final 2.0.0 release. (#2948)\n- Changed `NetworkTransformEditor.OnEnable` to a virtual method to be able to customize a `NetworkTransform` derived class by creating a derived editor control from `NetworkTransformEditor`. (#2948)" "changelog": "### Added\n\n- Added `NetworkVariable.CheckDirtyState` that is to be used in tandem with collections in order to detect whether the collection or an item within the collection has changed. (#3004)\n\n### Fixed\n\n- Fixed issue where nested `NetworkTransform` components were not getting updated. (#3016)\n- Fixed issue by adding null checks in `NetworkVariableBase.CanClientRead` and `NetworkVariableBase.CanClientWrite` methods to ensure safe access to `NetworkBehaviour`. (#3012)\n- Fixed issue where `FixedStringSerializer<T>` was using `NetworkVariableSerialization<byte>.AreEqual` to determine if two bytes were equal causes an exception to be thrown due to no byte serializer having been defined. (#3009)\n- Fixed Issue where a state with dual triggers, inbound and outbound, could cause a false layer to layer state transition message to be sent to non-authority `NetworkAnimator` instances and cause a warning message to be logged. (#3008)\n- Fixed issue using collections within `NetworkVariable` where the collection would not detect changes to items or nested items. (#3004)\n- Fixed issue where `List`, `Dictionary`, and `HashSet` collections would not uniquely duplicate nested collections. (#3004)\n- Fixed issue where `NotAuthorityTarget` would include the service observer in the list of targets to send the RPC to as opposed to excluding the service observer as it should. (#3000)\n- Fixed issue where `ProxyRpcTargetGroup` could attempt to send a message if there were no targets to send to. (#3000)\n\n### Changed\n\n- Changed `NetworkAnimator` to automatically switch to owner authoritative mode when using a distributed authority network topology. (#3021)\n- Changed permissions exception thrown in `NetworkList` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3004)\n- Changed permissions exception thrown in `NetworkVariable.Value` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3004)"
}, },
"upmCi": { "upmCi": {
"footprint": "dffe7543c19c2670755cc7da27e1d4522c9414a9" "footprint": "48286e9f7b0e053fe7f7b524bafc69a99c2906fc"
}, },
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.0/manual/index.html", "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.0/manual/index.html",
"repository": { "repository": {
"url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git", "url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git",
"type": "git", "type": "git",
"revision": "2ecbd14351cac29ab3a18cc159c0a82513e9d241" "revision": "2802dfcd13c3be1ac356191cc87d1559203d2db3"
}, },
"samples": [ "samples": [
{ {