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)
This commit is contained in:
Unity Technologies
2024-08-21 00:00:00 +00:00
parent a813ba0dd6
commit eab996f3ac
41 changed files with 4052 additions and 838 deletions

View File

@@ -6,6 +6,30 @@ 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).
## [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

View File

@@ -498,9 +498,13 @@ namespace Unity.Netcode.Components
/// <summary>
/// Override this method and return false to switch to owner authoritative mode
/// </summary>
/// <remarks>
/// When using a distributed authority network topology, this will default to
/// owner authoritative.
/// </remarks>
protected virtual bool OnIsServerAuthoritative()
{
return true;
return NetworkManager ? !NetworkManager.DistributedAuthorityMode : true;
}
// Animators only support up to 32 parameters
@@ -851,7 +855,12 @@ namespace Unity.Netcode.Components
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})");
}
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
m_TransitionHash[layer] = tt.fullPathHash;
@@ -860,6 +869,10 @@ namespace Unity.Netcode.Components
animState.CrossFade = false;
animState.Transition = true;
animState.NormalizedTime = tt.normalizedTime;
if (m_DestinationStateToTransitioninfo.ContainsKey(layer) && m_DestinationStateToTransitioninfo[layer].ContainsKey(nt.fullPathHash))
{
animState.DestinationStateHash = nt.fullPathHash;
}
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})");
}

View File

@@ -2960,7 +2960,10 @@ namespace Unity.Netcode.Components
#else
var forUpdate = true;
#endif
NetworkManager?.NetworkTransformRegistration(this, forUpdate, false);
if (m_CachedNetworkObject != null)
{
NetworkManager?.NetworkTransformRegistration(m_CachedNetworkObject, forUpdate, false);
}
DeregisterForTickUpdate(this);
CanCommitToTransform = false;
}
@@ -3069,7 +3072,7 @@ namespace Unity.Netcode.Components
if (CanCommitToTransform)
{
// 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)
{
m_HalfPositionState = new NetworkDeltaPosition(currentPosition, m_CachedNetworkManager.ServerTime.Tick, math.bool3(SyncPositionX, SyncPositionY, SyncPositionZ));
@@ -3090,7 +3093,7 @@ namespace Unity.Netcode.Components
else
{
// 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
DeregisterForTickUpdate(this);
ResetInterpolatedStateToCurrentAuthoritativeState();

View File

@@ -806,7 +806,8 @@ namespace Unity.Netcode
}
/// <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>
public virtual void OnGainedOwnership() { }
@@ -834,7 +835,9 @@ namespace Unity.Netcode
}
/// <summary>
/// Gets called when ownership of this object is lost.
/// 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>
public virtual void OnLostOwnership() { }

View File

@@ -8,7 +8,6 @@ using UnityEditor;
#endif
using UnityEngine.SceneManagement;
using Debug = UnityEngine.Debug;
using Unity.Netcode.Components;
namespace Unity.Netcode
{
@@ -215,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
internal Dictionary<ulong, NetworkTransform> NetworkTransformFixedUpdate = new Dictionary<ulong, NetworkTransform>();
internal Dictionary<ulong, NetworkObject> NetworkTransformFixedUpdate = new Dictionary<ulong, NetworkObject>();
#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 (!NetworkTransformUpdate.ContainsKey(networkTransform.NetworkObjectId))
if (!NetworkTransformUpdate.ContainsKey(networkObject.NetworkObjectId))
{
NetworkTransformUpdate.Add(networkTransform.NetworkObjectId, networkTransform);
NetworkTransformUpdate.Add(networkObject.NetworkObjectId, networkObject);
}
}
else
{
NetworkTransformUpdate.Remove(networkTransform.NetworkObjectId);
NetworkTransformUpdate.Remove(networkObject.NetworkObjectId);
}
}
#if COM_UNITY_MODULES_PHYSICS
@@ -241,14 +240,14 @@ namespace Unity.Netcode
{
if (register)
{
if (!NetworkTransformFixedUpdate.ContainsKey(networkTransform.NetworkObjectId))
if (!NetworkTransformFixedUpdate.ContainsKey(networkObject.NetworkObjectId))
{
NetworkTransformFixedUpdate.Add(networkTransform.NetworkObjectId, networkTransform);
NetworkTransformFixedUpdate.Add(networkObject.NetworkObjectId, networkObject);
}
}
else
{
NetworkTransformFixedUpdate.Remove(networkTransform.NetworkObjectId);
NetworkTransformFixedUpdate.Remove(networkObject.NetworkObjectId);
}
}
#endif
@@ -289,11 +288,21 @@ namespace Unity.Netcode
#if COM_UNITY_MODULES_PHYSICS
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();
}
}
}
}
@@ -308,11 +317,21 @@ namespace Unity.Netcode
case NetworkUpdateStage.PreLateUpdate:
{
// 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();
}
}
}
}

View File

@@ -2359,7 +2359,7 @@ namespace Unity.Netcode
{
m_ChildNetworkBehaviours.Add(networkBehaviours[i]);
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)
{

View File

@@ -25,8 +25,6 @@ namespace Unity.Netcode
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)
{
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
@@ -126,10 +124,6 @@ namespace Unity.Netcode
}
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)
{
var size_marker = writer.Position;
@@ -167,8 +161,6 @@ namespace Unity.Netcode
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)
{
var networkManager = (NetworkManager)context.SystemOwner;

View File

@@ -29,6 +29,12 @@ namespace Unity.Netcode
{
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);
}
}
@@ -41,6 +47,12 @@ namespace Unity.Netcode
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)
{
m_LocalSendRpcTarget.Send(behaviour, ref message, delivery, rpcParams);

View File

@@ -17,6 +17,11 @@ namespace Unity.Netcode
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 };
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
var size =

View File

@@ -51,7 +51,6 @@ namespace Unity.Netcode
#pragma warning restore IDE0001
[Serializable]
[GenerateSerializationForGenericParameter(0)]
[GenerateSerializationForType(typeof(byte))]
public class AnticipatedNetworkVariable<T> : NetworkVariableBase
{
[SerializeField]

View File

@@ -393,7 +393,8 @@ namespace Unity.Netcode
// check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return;
}
m_List.Add(item);
@@ -414,7 +415,8 @@ namespace Unity.Netcode
// check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return;
}
m_List.Clear();
@@ -440,7 +442,8 @@ namespace Unity.Netcode
// check write permissions
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);
@@ -475,7 +478,8 @@ namespace Unity.Netcode
// check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return;
}
if (index < m_List.Length)
@@ -520,6 +524,8 @@ namespace Unity.Netcode
HandleAddListEvent(listEvent);
}
/// <inheritdoc />
public T this[int index]
{
@@ -529,7 +535,8 @@ namespace Unity.Netcode
// check write permissions
if (!CanClientWrite(m_NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return;
}
var previousValue = m_List[index];

View File

@@ -9,7 +9,6 @@ namespace Unity.Netcode
/// <typeparam name="T">the unmanaged type for <see cref="NetworkVariable{T}"/> </typeparam>
[Serializable]
[GenerateSerializationForGenericParameter(0)]
[GenerateSerializationForType(typeof(byte))]
public class NetworkVariable<T> : NetworkVariableBase
{
/// <summary>
@@ -95,27 +94,59 @@ namespace Unity.Netcode
/// <summary>
/// The value of the NetworkVariable container
/// </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
{
get => m_InternalValue;
set
{
// Compare bitwise
if (NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref value))
if (m_NetworkManager && !CanClientWrite(m_NetworkManager.LocalClientId))
{
LogWritePermissionError();
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()
{
return ref m_InternalValue;
@@ -194,19 +225,6 @@ namespace Unity.Netcode
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>
/// Writes the variable to the writer
/// </summary>
@@ -223,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>
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:
// 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
// would be stored in different fields
T previousValue = m_InternalValue;
NetworkVariableSerialization<T>.ReadDelta(reader, ref m_InternalValue);
if (keepDirtyDelta)
{
SetDirty(true);
}
OnValueChanged?.Invoke(previousValue, m_InternalValue);
OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue);
}
/// <inheritdoc />

View File

@@ -37,6 +37,16 @@ namespace Unity.Netcode
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
{
get
@@ -254,6 +264,11 @@ namespace Unity.Netcode
/// <returns>Whether or not the client has permission to read</returns>
public bool CanClientRead(ulong clientId)
{
if (!m_NetworkBehaviour)
{
return false;
}
// When in distributed authority mode, everyone can read (but only the owner can write)
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>
public bool CanClientWrite(ulong clientId)
{
if (!m_NetworkBehaviour)
{
return false;
}
switch (WritePerm)
{
default:

View File

@@ -458,7 +458,10 @@ namespace Unity.Netcode
duplicatedValue.Clear();
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);
}
}
}
@@ -548,6 +551,9 @@ namespace Unity.Netcode
duplicatedValue.Clear();
foreach (var item in value)
{
// Handles nested HashSets
T subValue = default;
NetworkVariableSerialization<T>.Duplicate(item, ref subValue);
duplicatedValue.Add(item);
}
}
@@ -641,7 +647,12 @@ namespace Unity.Netcode
duplicatedValue.Clear();
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);
}
}
}
@@ -924,7 +935,7 @@ namespace Unity.Netcode
{
var val = value[i];
var prevVal = previousValue[i];
if (!NetworkVariableSerialization<byte>.AreEqual(ref val, ref prevVal))
if (val != prevVal)
{
++numChanges;
changes.Set(i);
@@ -949,19 +960,11 @@ namespace Unity.Netcode
BytePacker.WriteValuePacked(writer, value.Length);
writer.WriteValueSafe(changes);
var ptr = value.GetUnsafePtr();
var prevPtr = previousValue.GetUnsafePtr();
for (var i = 0; i < value.Length; ++i)
{
if (changes.IsSet(i))
{
if (i < previousValue.Length)
{
NetworkVariableSerialization<byte>.WriteDelta(writer, ref ptr[i], ref prevPtr[i]);
}
else
{
NetworkVariableSerialization<byte>.Write(writer, ref ptr[i]);
}
writer.WriteByteSafe(ptr[i]);
}
}
}

View File

@@ -40,11 +40,6 @@ namespace Unity.Netcode.RuntimeTests
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)]
public void TestAuthorityRpc(byte[] _)
{
@@ -263,15 +258,6 @@ namespace Unity.Netcode.RuntimeTests
yield return m_ClientCodecHook.WaitForMessageReceived<NetworkVariableDeltaMessage>();
}
[UnityTest]
public IEnumerator NotAuthorityRpc()
{
Client.LocalClient.PlayerObject.GetComponent<TestNetworkComponent>().TestNotAuthorityRpc(new byte[] { 1, 2, 3, 4 });
// Universal Rpcs are sent as a ProxyMessage (which contains an RpcMessage)
yield return m_ClientCodecHook.WaitForMessageReceived<ProxyMessage>();
}
[UnityTest]
public IEnumerator ParentSync()
{

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
{
[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>
{
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
[TestFixture(true)]
@@ -612,7 +569,9 @@ namespace Unity.Netcode.RuntimeTests
InitializeServerAndClients(useHost);
// 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>
@@ -5130,408 +5089,5 @@ namespace Unity.Netcode.RuntimeTests
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

View File

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

View File

@@ -2,23 +2,23 @@
"name": "com.unity.netcode.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.",
"version": "2.0.0-pre.3",
"version": "2.0.0-pre.4",
"unity": "6000.0",
"dependencies": {
"com.unity.nuget.mono-cecil": "1.11.4",
"com.unity.transport": "2.3.0"
},
"_upm": {
"changelog": "### Added\n- Added: `UnityTransport.GetNetworkDriver` and `UnityTransport.GetLocalEndpoint` methods to expose the driver and local endpoint being used. (#2978)\n\n### Fixed\n\n- Fixed issue where deferred despawn was causing GC allocations when converting an `IEnumerable` to a list. (#2983)\n- 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)\n- Fixed issue where `NetworkManager.ScenesLoaded` was not being updated if `PostSynchronizationSceneUnloading` was set and any loaded scenes not used during synchronization were unloaded. (#2971)\n- Fixed issue where `Rigidbody2d` under Unity 6000.0.11f1 has breaking changes where `velocity` is now `linearVelocity` and `isKinematic` is replaced by `bodyType`. (#2971)\n- Fixed issue where `NetworkSpawnManager.InstantiateAndSpawn` and `NetworkObject.InstantiateAndSpawn` were not honoring the ownerClientId parameter when using a client-server network topology. (#2968)\n- 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)\n- Fixed issue when scene management was disabled and the session owner would still try to synchronize a late joining client. (#2962)\n- 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)\n- Fixed issue where attempting to spawn during `NetworkBehaviour.OnInSceneObjectsSpawned` and `NetworkBehaviour.OnNetworkSessionSynchronized` notifications would throw a collection modified exception. (#2962)\n\n### Changed\n\n- Changed logic where clients can now set the `NetworkSceneManager` client synchronization mode when using a distributed authority network topology. (#2985)"
"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": {
"footprint": "fbae2629229fb08020f4b9cef5656e6fdf517c3d"
"footprint": "48286e9f7b0e053fe7f7b524bafc69a99c2906fc"
},
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.0/manual/index.html",
"repository": {
"url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git",
"type": "git",
"revision": "8575c902227d221f987d9cb869d501749f8631b4"
"revision": "2802dfcd13c3be1ac356191cc87d1559203d2db3"
},
"samples": [
{