com.unity.netcode.gameobjects@1.6.0
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.6.0] - 2023-08-09 ### Added - Added a protected virtual method `NetworkTransform.OnInitialize(ref NetworkTransformState replicatedState)` that just returns the replicated state reference. ### Fixed - Fixed issue where invoking `NetworkManager.Shutdown` within `NetworkManager.OnClientStopped` or `NetworkManager.OnServerStopped` would force `NetworkManager.ShutdownInProgress` to remain true after completing the shutdown process. (#2661) - Fixed issue with client synchronization of position when using half precision and the delta position reaches the maximum value and is collapsed on the host prior to being forwarded to the non-owner clients. (#2636) - Fixed issue with scale not synchronizing properly depending upon the spawn order of NetworkObjects. (#2636) - Fixed issue position was not properly transitioning between ownership changes with an owner authoritative NetworkTransform. (#2636) - Fixed issue where a late joining non-owner client could update an owner authoritative NetworkTransform if ownership changed without any updates to position prior to the non-owner client joining. (#2636) ### Changed
This commit is contained in:
@@ -41,7 +41,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
}
|
||||
|
||||
Assert.True(observer.Found);
|
||||
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead, observer.Value);
|
||||
Assert.AreEqual(((FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead) + 7) & ~7, observer.Value);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
@@ -61,8 +61,6 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
writer.Dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
var nbFrames = 0;
|
||||
while (!observer.Found || nbFrames < 10)
|
||||
{
|
||||
@@ -71,7 +69,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
}
|
||||
|
||||
Assert.True(observer.Found);
|
||||
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead, observer.Value);
|
||||
Assert.AreEqual(((FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead) + 7) & ~7, observer.Value);
|
||||
}
|
||||
|
||||
private class TotalBytesObserver : IMetricObserver
|
||||
@@ -89,12 +87,22 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
|
||||
public long Value { get; private set; }
|
||||
|
||||
private int m_BytesFoundCounter;
|
||||
private long m_TotalBytes;
|
||||
|
||||
public void Observe(MetricCollection collection)
|
||||
{
|
||||
if (collection.TryGetCounter(m_MetricInfo.Id, out var counter) && counter.Value > 0)
|
||||
{
|
||||
Found = true;
|
||||
Value = counter.Value;
|
||||
// Don't assign another observed value once one is already observed
|
||||
if (!Found)
|
||||
{
|
||||
Found = true;
|
||||
Value = counter.Value;
|
||||
m_TotalBytes += ((counter.Value + 7) & ~7);
|
||||
m_BytesFoundCounter++;
|
||||
UnityEngine.Debug.Log($"[{m_BytesFoundCounter}] Bytes Observed {counter.Value} | Total Bytes Observed: {m_TotalBytes}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,93 @@ namespace Unity.Netcode.RuntimeTests
|
||||
base.OnServerAndClientsCreated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clients created during a test need to have their prefabs list updated to
|
||||
/// match the server's prefab list.
|
||||
/// </summary>
|
||||
protected override void OnNewClientCreated(NetworkManager networkManager)
|
||||
{
|
||||
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
|
||||
{
|
||||
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
|
||||
}
|
||||
|
||||
base.OnNewClientCreated(networkManager);
|
||||
}
|
||||
|
||||
private bool ClientIsOwner()
|
||||
{
|
||||
var clientId = m_ClientNetworkManagers[0].LocalClientId;
|
||||
if (!VerifyObjectIsSpawnedOnClient.GetClientsThatSpawnedThisPrefab().Contains(clientId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (VerifyObjectIsSpawnedOnClient.GetClientInstance(clientId).OwnerClientId != clientId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test verifies a late joining client cannot change the transform when:
|
||||
/// - A NetworkObject is spawned with a host and one or more connected clients
|
||||
/// - The NetworkTransform is owner authoritative and spawned with the host as the owner
|
||||
/// - The host does not change the transform values
|
||||
/// - One of the already connected clients gains ownership of the spawned NetworkObject
|
||||
/// - The new client owner does not change the transform values
|
||||
/// - A new late joining client connects and is synchronized
|
||||
/// - The newly connected late joining client tries to change the transform of the NetworkObject
|
||||
/// it does not own
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator LateJoinedNonOwnerClientCannotChangeTransform()
|
||||
{
|
||||
// Spawn the m_ClientNetworkTransformPrefab with the host starting as the owner
|
||||
var hostInstance = SpawnObject(m_ClientNetworkTransformPrefab, m_ServerNetworkManager);
|
||||
|
||||
// Wait for the client to spawn it
|
||||
yield return WaitForConditionOrTimeOut(() => VerifyObjectIsSpawnedOnClient.GetClientsThatSpawnedThisPrefab().Contains(m_ClientNetworkManagers[0].LocalClientId));
|
||||
|
||||
// Change the ownership to the connectd client
|
||||
hostInstance.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
||||
|
||||
// Wait until the client gains ownership
|
||||
yield return WaitForConditionOrTimeOut(ClientIsOwner);
|
||||
|
||||
// Spawn a new client
|
||||
yield return CreateAndStartNewClient();
|
||||
|
||||
// Get the instance of the object relative to the newly joined client
|
||||
var newClientObjectInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(m_ClientNetworkManagers[1].LocalClientId);
|
||||
|
||||
// Attempt to change the transform values
|
||||
var currentPosition = newClientObjectInstance.transform.position;
|
||||
newClientObjectInstance.transform.position = GetRandomVector3(0.5f, 10.0f);
|
||||
var rotation = newClientObjectInstance.transform.rotation;
|
||||
var currentRotation = rotation.eulerAngles;
|
||||
rotation.eulerAngles = GetRandomVector3(1.0f, 180.0f);
|
||||
var currentScale = newClientObjectInstance.transform.localScale;
|
||||
newClientObjectInstance.transform.localScale = GetRandomVector3(0.25f, 4.0f);
|
||||
|
||||
// Wait one frame so the NetworkTransform can apply the owner's last state received on the late joining client side
|
||||
// (i.e. prevent the non-owner from changing the transform)
|
||||
yield return null;
|
||||
|
||||
// Get the owner instance
|
||||
var ownerInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(m_ClientNetworkManagers[0].LocalClientId);
|
||||
|
||||
// Verify that the non-owner instance transform values are the same before they were changed last frame
|
||||
Assert.True(Approximately(currentPosition, newClientObjectInstance.transform.position), $"Non-owner instance was able to change the position!");
|
||||
Assert.True(Approximately(currentRotation, newClientObjectInstance.transform.rotation.eulerAngles), $"Non-owner instance was able to change the rotation!");
|
||||
Assert.True(Approximately(currentScale, newClientObjectInstance.transform.localScale), $"Non-owner instance was able to change the scale!");
|
||||
|
||||
// Verify that the non-owner instance transform is still the same as the owner instance transform
|
||||
Assert.True(Approximately(ownerInstance.transform.position, newClientObjectInstance.transform.position), "Non-owner and owner instance position values are not the same!");
|
||||
Assert.True(Approximately(ownerInstance.transform.rotation.eulerAngles, newClientObjectInstance.transform.rotation.eulerAngles), "Non-owner and owner instance rotation values are not the same!");
|
||||
Assert.True(Approximately(ownerInstance.transform.localScale, newClientObjectInstance.transform.localScale), "Non-owner and owner instance scale values are not the same!");
|
||||
}
|
||||
|
||||
public enum StartingOwnership
|
||||
{
|
||||
HostStartsAsOwner,
|
||||
|
||||
@@ -2,6 +2,7 @@ using NUnit.Framework;
|
||||
using Unity.Netcode.Components;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
|
||||
@@ -89,6 +90,125 @@ namespace Unity.Netcode.RuntimeTests
|
||||
networkTransform.SyncPositionX || networkTransform.SyncPositionY || networkTransform.SyncPositionZ;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NetworkTransformStateFlags()
|
||||
{
|
||||
var indexValues = new System.Collections.Generic.List<uint>();
|
||||
var currentFlag = (uint)0x00000001;
|
||||
for (int j = 0; j < 18; j++)
|
||||
{
|
||||
indexValues.Add(currentFlag);
|
||||
currentFlag = currentFlag << 1;
|
||||
}
|
||||
|
||||
// TrackByStateId is unique
|
||||
indexValues.Add(0x10000000);
|
||||
|
||||
var boolSet = new System.Collections.Generic.List<bool>();
|
||||
var transformState = new NetworkTransform.NetworkTransformState();
|
||||
// Test setting one at a time.
|
||||
for (int j = 0; j < 19; j++)
|
||||
{
|
||||
boolSet = new System.Collections.Generic.List<bool>();
|
||||
for (int i = 0; i < 19; i++)
|
||||
{
|
||||
if (i == j)
|
||||
{
|
||||
boolSet.Add(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
boolSet.Add(false);
|
||||
}
|
||||
}
|
||||
transformState = new NetworkTransform.NetworkTransformState()
|
||||
{
|
||||
InLocalSpace = boolSet[0],
|
||||
HasPositionX = boolSet[1],
|
||||
HasPositionY = boolSet[2],
|
||||
HasPositionZ = boolSet[3],
|
||||
HasRotAngleX = boolSet[4],
|
||||
HasRotAngleY = boolSet[5],
|
||||
HasRotAngleZ = boolSet[6],
|
||||
HasScaleX = boolSet[7],
|
||||
HasScaleY = boolSet[8],
|
||||
HasScaleZ = boolSet[9],
|
||||
IsTeleportingNextFrame = boolSet[10],
|
||||
UseInterpolation = boolSet[11],
|
||||
QuaternionSync = boolSet[12],
|
||||
QuaternionCompression = boolSet[13],
|
||||
UseHalfFloatPrecision = boolSet[14],
|
||||
IsSynchronizing = boolSet[15],
|
||||
UsePositionSlerp = boolSet[16],
|
||||
IsParented = boolSet[17],
|
||||
TrackByStateId = boolSet[18],
|
||||
};
|
||||
Assert.True((transformState.BitSet & indexValues[j]) == indexValues[j], $"[FlagTest][Individual] Set flag value {indexValues[j]} at index {j}, but BitSet value did not match!");
|
||||
}
|
||||
|
||||
// Test setting all flag values
|
||||
boolSet = new System.Collections.Generic.List<bool>();
|
||||
for (int i = 0; i < 19; i++)
|
||||
{
|
||||
boolSet.Add(true);
|
||||
}
|
||||
|
||||
transformState = new NetworkTransform.NetworkTransformState()
|
||||
{
|
||||
InLocalSpace = boolSet[0],
|
||||
HasPositionX = boolSet[1],
|
||||
HasPositionY = boolSet[2],
|
||||
HasPositionZ = boolSet[3],
|
||||
HasRotAngleX = boolSet[4],
|
||||
HasRotAngleY = boolSet[5],
|
||||
HasRotAngleZ = boolSet[6],
|
||||
HasScaleX = boolSet[7],
|
||||
HasScaleY = boolSet[8],
|
||||
HasScaleZ = boolSet[9],
|
||||
IsTeleportingNextFrame = boolSet[10],
|
||||
UseInterpolation = boolSet[11],
|
||||
QuaternionSync = boolSet[12],
|
||||
QuaternionCompression = boolSet[13],
|
||||
UseHalfFloatPrecision = boolSet[14],
|
||||
IsSynchronizing = boolSet[15],
|
||||
UsePositionSlerp = boolSet[16],
|
||||
IsParented = boolSet[17],
|
||||
TrackByStateId = boolSet[18],
|
||||
};
|
||||
|
||||
for (int j = 0; j < 19; j++)
|
||||
{
|
||||
Assert.True((transformState.BitSet & indexValues[j]) == indexValues[j], $"[FlagTest][All] All flag values are set but failed to detect flag value {indexValues[j]}!");
|
||||
}
|
||||
|
||||
// Test getting all flag values
|
||||
transformState = new NetworkTransform.NetworkTransformState();
|
||||
for (int i = 0; i < 19; i++)
|
||||
{
|
||||
transformState.BitSet |= indexValues[i];
|
||||
}
|
||||
|
||||
Assert.True(transformState.InLocalSpace, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.InLocalSpace)}!");
|
||||
Assert.True(transformState.HasPositionX, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasPositionX)}!");
|
||||
Assert.True(transformState.HasPositionY, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasPositionY)}!");
|
||||
Assert.True(transformState.HasPositionZ, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasPositionZ)}!");
|
||||
Assert.True(transformState.HasRotAngleX, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasRotAngleX)}!");
|
||||
Assert.True(transformState.HasRotAngleY, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasRotAngleY)}!");
|
||||
Assert.True(transformState.HasRotAngleZ, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasRotAngleZ)}!");
|
||||
Assert.True(transformState.HasScaleX, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasScaleX)}!");
|
||||
Assert.True(transformState.HasScaleY, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasScaleY)}!");
|
||||
Assert.True(transformState.HasScaleZ, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasScaleZ)}!");
|
||||
Assert.True(transformState.IsTeleportingNextFrame, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.IsTeleportingNextFrame)}!");
|
||||
Assert.True(transformState.UseInterpolation, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.UseInterpolation)}!");
|
||||
Assert.True(transformState.QuaternionSync, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.QuaternionSync)}!");
|
||||
Assert.True(transformState.QuaternionCompression, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.QuaternionCompression)}!");
|
||||
Assert.True(transformState.UseHalfFloatPrecision, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.UseHalfFloatPrecision)}!");
|
||||
Assert.True(transformState.IsSynchronizing, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.IsSynchronizing)}!");
|
||||
Assert.True(transformState.UsePositionSlerp, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.UsePositionSlerp)}!");
|
||||
Assert.True(transformState.IsParented, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.IsParented)}!");
|
||||
Assert.True(transformState.TrackByStateId, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.TrackByStateId)}!");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Values] SyncAxis syncAxis)
|
||||
|
||||
|
||||
@@ -64,24 +64,50 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper component for NetworkTransform parenting tests when
|
||||
/// a child is a parent of another child (i.e. "sub child")
|
||||
/// </summary>
|
||||
public class SubChildObjectComponent : ChildObjectComponent
|
||||
{
|
||||
protected override bool IsSubChild()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper component for NetworkTransform parenting tests
|
||||
/// </summary>
|
||||
public class ChildObjectComponent : NetworkTransform
|
||||
{
|
||||
public static readonly List<ChildObjectComponent> Instances = new List<ChildObjectComponent>();
|
||||
public static readonly List<ChildObjectComponent> SubInstances = new List<ChildObjectComponent>();
|
||||
public static ChildObjectComponent AuthorityInstance { get; internal set; }
|
||||
public static ChildObjectComponent AuthoritySubInstance { get; internal set; }
|
||||
public static readonly Dictionary<ulong, NetworkObject> ClientInstances = new Dictionary<ulong, NetworkObject>();
|
||||
public static readonly Dictionary<ulong, NetworkObject> ClientSubChildInstances = new Dictionary<ulong, NetworkObject>();
|
||||
|
||||
public static bool HasSubChild;
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
AuthorityInstance = null;
|
||||
AuthoritySubInstance = null;
|
||||
HasSubChild = false;
|
||||
ClientInstances.Clear();
|
||||
ClientSubChildInstances.Clear();
|
||||
Instances.Clear();
|
||||
SubInstances.Clear();
|
||||
}
|
||||
|
||||
public bool ServerAuthority;
|
||||
|
||||
protected virtual bool IsSubChild()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool OnIsServerAuthoritative()
|
||||
{
|
||||
return ServerAuthority;
|
||||
@@ -92,13 +118,34 @@ namespace Unity.Netcode.RuntimeTests
|
||||
base.OnNetworkSpawn();
|
||||
if (CanCommitToTransform)
|
||||
{
|
||||
AuthorityInstance = this;
|
||||
if (!IsSubChild())
|
||||
{
|
||||
AuthorityInstance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
AuthoritySubInstance = this;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Instances.Add(this);
|
||||
if (!IsSubChild())
|
||||
{
|
||||
Instances.Add(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
SubInstances.Add(this);
|
||||
}
|
||||
}
|
||||
if (HasSubChild && IsSubChild())
|
||||
{
|
||||
ClientSubChildInstances.Add(NetworkManager.LocalClientId, NetworkObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientInstances.Add(NetworkManager.LocalClientId, NetworkObject);
|
||||
}
|
||||
ClientInstances.Add(NetworkManager.LocalClientId, NetworkObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +164,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
private NetworkObject m_AuthoritativePlayer;
|
||||
private NetworkObject m_NonAuthoritativePlayer;
|
||||
private NetworkObject m_ChildObject;
|
||||
private NetworkObject m_SubChildObject;
|
||||
private NetworkObject m_ParentObject;
|
||||
|
||||
private NetworkTransformTestComponent m_AuthoritativeTransform;
|
||||
@@ -232,6 +280,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
protected override void OnServerAndClientsCreated()
|
||||
{
|
||||
var subChildObject = CreateNetworkObjectPrefab("SubChildObject");
|
||||
var subChildNetworkTransform = subChildObject.AddComponent<SubChildObjectComponent>();
|
||||
subChildNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
|
||||
m_SubChildObject = subChildObject.GetComponent<NetworkObject>();
|
||||
|
||||
var childObject = CreateNetworkObjectPrefab("ChildObject");
|
||||
var childNetworkTransform = childObject.AddComponent<ChildObjectComponent>();
|
||||
childNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
|
||||
@@ -242,13 +295,19 @@ namespace Unity.Netcode.RuntimeTests
|
||||
parentNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
|
||||
m_ParentObject = parentObject.GetComponent<NetworkObject>();
|
||||
|
||||
|
||||
// Now apply local transform values
|
||||
m_ChildObject.transform.position = m_ChildObjectLocalPosition;
|
||||
var childRotation = m_ChildObject.transform.rotation;
|
||||
childRotation.eulerAngles = m_ChildObjectLocalRotation;
|
||||
m_ChildObject.transform.rotation = childRotation;
|
||||
m_ChildObject.transform.localScale = m_ChildObjectLocalScale;
|
||||
|
||||
m_SubChildObject.transform.position = m_SubChildObjectLocalPosition;
|
||||
var subChildRotation = m_SubChildObject.transform.rotation;
|
||||
subChildRotation.eulerAngles = m_SubChildObjectLocalRotation;
|
||||
m_SubChildObject.transform.rotation = childRotation;
|
||||
m_SubChildObject.transform.localScale = m_SubChildObjectLocalScale;
|
||||
|
||||
if (m_EnableVerboseDebug)
|
||||
{
|
||||
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
|
||||
@@ -302,6 +361,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ChildObjectComponent.HasSubChild && ChildObjectComponent.AuthoritySubInstance == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||
{
|
||||
if (!ChildObjectComponent.ClientInstances.ContainsKey(clientNetworkManager.LocalClientId))
|
||||
@@ -321,6 +385,16 @@ namespace Unity.Netcode.RuntimeTests
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (ChildObjectComponent.HasSubChild)
|
||||
{
|
||||
foreach (var instance in ChildObjectComponent.ClientSubChildInstances.Values)
|
||||
{
|
||||
if (instance.transform.parent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -328,6 +402,10 @@ namespace Unity.Netcode.RuntimeTests
|
||||
private Vector3 m_ChildObjectLocalPosition = new Vector3(5.0f, 0.0f, -5.0f);
|
||||
private Vector3 m_ChildObjectLocalRotation = new Vector3(-35.0f, 90.0f, 270.0f);
|
||||
private Vector3 m_ChildObjectLocalScale = new Vector3(0.1f, 0.5f, 0.4f);
|
||||
private Vector3 m_SubChildObjectLocalPosition = new Vector3(2.0f, 1.0f, -1.0f);
|
||||
private Vector3 m_SubChildObjectLocalRotation = new Vector3(5.0f, 15.0f, 124.0f);
|
||||
private Vector3 m_SubChildObjectLocalScale = new Vector3(1.0f, 0.15f, 0.75f);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A wait condition specific method that assures the local space coordinates
|
||||
@@ -375,17 +453,17 @@ namespace Unity.Netcode.RuntimeTests
|
||||
/// If not, it generates a message containing the axial values that did not match
|
||||
/// the target/start local space values.
|
||||
/// </summary>
|
||||
private void AllChildrenLocalTransformValuesMatch()
|
||||
private void AllChildrenLocalTransformValuesMatch(bool useSubChild)
|
||||
{
|
||||
var success = WaitForConditionOrTimeOutWithTimeTravel(AllInstancesKeptLocalTransformValues);
|
||||
//TimeTravelToNextTick();
|
||||
var infoMessage = new StringBuilder($"Timed out waiting for all children to have the correct local space values:\n");
|
||||
var authorityObjectLocalPosition = m_AuthorityChildObject.transform.localPosition;
|
||||
var authorityObjectLocalRotation = m_AuthorityChildObject.transform.localRotation.eulerAngles;
|
||||
var authorityObjectLocalScale = m_AuthorityChildObject.transform.localScale;
|
||||
var authorityObjectLocalPosition = useSubChild ? m_AuthoritySubChildObject.transform.localPosition : m_AuthorityChildObject.transform.localPosition;
|
||||
var authorityObjectLocalRotation = useSubChild ? m_AuthoritySubChildObject.transform.localRotation.eulerAngles : m_AuthorityChildObject.transform.localRotation.eulerAngles;
|
||||
var authorityObjectLocalScale = useSubChild ? m_AuthoritySubChildObject.transform.localScale : m_AuthorityChildObject.transform.localScale;
|
||||
|
||||
if (s_GlobalTimeoutHelper.TimedOut || !success)
|
||||
{
|
||||
var instances = useSubChild ? ChildObjectComponent.SubInstances : ChildObjectComponent.Instances;
|
||||
foreach (var childInstance in ChildObjectComponent.Instances)
|
||||
{
|
||||
var childLocalPosition = childInstance.transform.localPosition;
|
||||
@@ -428,8 +506,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
private NetworkObject m_AuthorityParentObject;
|
||||
private NetworkTransformTestComponent m_AuthorityParentNetworkTransform;
|
||||
private NetworkObject m_AuthorityChildObject;
|
||||
private NetworkObject m_AuthoritySubChildObject;
|
||||
private ChildObjectComponent m_AuthorityChildNetworkTransform;
|
||||
|
||||
private ChildObjectComponent m_AuthoritySubChildNetworkTransform;
|
||||
|
||||
/// <summary>
|
||||
/// Validates that transform values remain the same when a NetworkTransform is
|
||||
/// parented under another NetworkTransform under all of the possible axial conditions
|
||||
@@ -451,9 +532,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
authorityNetworkManager = m_ClientNetworkManagers[0];
|
||||
}
|
||||
|
||||
// Spawn a parent and child object
|
||||
// Spawn a parent and children
|
||||
ChildObjectComponent.HasSubChild = true;
|
||||
var serverSideParent = SpawnObject(m_ParentObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
|
||||
var serverSideChild = SpawnObject(m_ChildObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
|
||||
var serverSideSubChild = SpawnObject(m_SubChildObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
|
||||
|
||||
// Assure all of the child object instances are spawned before proceeding to parenting
|
||||
var success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesAreSpawned);
|
||||
@@ -462,6 +545,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// Get the authority parent and child instances
|
||||
m_AuthorityParentObject = NetworkTransformTestComponent.AuthorityInstance.NetworkObject;
|
||||
m_AuthorityChildObject = ChildObjectComponent.AuthorityInstance.NetworkObject;
|
||||
m_AuthoritySubChildObject = ChildObjectComponent.AuthoritySubInstance.NetworkObject;
|
||||
|
||||
// The child NetworkTransform will use world space when world position stays and
|
||||
// local space when world position does not stay when parenting.
|
||||
@@ -470,15 +554,26 @@ namespace Unity.Netcode.RuntimeTests
|
||||
ChildObjectComponent.AuthorityInstance.UseQuaternionSynchronization = rotation == Rotation.Quaternion;
|
||||
ChildObjectComponent.AuthorityInstance.UseQuaternionCompression = rotationCompression == RotationCompression.QuaternionCompress;
|
||||
|
||||
ChildObjectComponent.AuthoritySubInstance.InLocalSpace = !worldPositionStays;
|
||||
ChildObjectComponent.AuthoritySubInstance.UseHalfFloatPrecision = precision == Precision.Half;
|
||||
ChildObjectComponent.AuthoritySubInstance.UseQuaternionSynchronization = rotation == Rotation.Quaternion;
|
||||
ChildObjectComponent.AuthoritySubInstance.UseQuaternionCompression = rotationCompression == RotationCompression.QuaternionCompress;
|
||||
|
||||
// Set whether we are interpolating or not
|
||||
m_AuthorityParentNetworkTransform = m_AuthorityParentObject.GetComponent<NetworkTransformTestComponent>();
|
||||
m_AuthorityParentNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_AuthorityChildNetworkTransform = m_AuthorityChildObject.GetComponent<ChildObjectComponent>();
|
||||
m_AuthorityChildNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_AuthoritySubChildNetworkTransform = m_AuthoritySubChildObject.GetComponent<ChildObjectComponent>();
|
||||
m_AuthoritySubChildNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
|
||||
|
||||
// Apply a scale to the parent object to make sure the scale on the child is properly updated on
|
||||
// non-authority instances.
|
||||
m_AuthorityParentObject.transform.localScale = new Vector3(scale, scale, scale);
|
||||
var halfScale = scale * 0.5f;
|
||||
m_AuthorityParentObject.transform.localScale = GetRandomVector3(scale - halfScale, scale + halfScale);
|
||||
m_AuthorityChildObject.transform.localScale = GetRandomVector3(scale - halfScale, scale + halfScale);
|
||||
m_AuthoritySubChildObject.transform.localScale = GetRandomVector3(scale - halfScale, scale + halfScale);
|
||||
|
||||
// Allow one tick for authority to update these changes
|
||||
TimeTravelToNextTick();
|
||||
@@ -486,12 +581,18 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// Parent the child under the parent with the current world position stays setting
|
||||
Assert.True(serverSideChild.TrySetParent(serverSideParent.transform, worldPositionStays), "[Server-Side Child] Failed to set child's parent!");
|
||||
|
||||
// Parent the sub-child under the child with the current world position stays setting
|
||||
Assert.True(serverSideSubChild.TrySetParent(serverSideChild.transform, worldPositionStays), "[Server-Side SubChild] Failed to set sub-child's parent!");
|
||||
|
||||
// This waits for all child instances to be parented
|
||||
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesHaveChild);
|
||||
Assert.True(success, "Timed out waiting for all instances to have parented a child!");
|
||||
|
||||
// This validates each child instance has preserved their local space values
|
||||
AllChildrenLocalTransformValuesMatch();
|
||||
AllChildrenLocalTransformValuesMatch(false);
|
||||
|
||||
// This validates each sub-child instance has preserved their local space values
|
||||
AllChildrenLocalTransformValuesMatch(true);
|
||||
|
||||
// Verify that a late joining client will synchronize to the parented NetworkObjects properly
|
||||
CreateAndStartNewClientWithTimeTravel();
|
||||
@@ -500,8 +601,15 @@ namespace Unity.Netcode.RuntimeTests
|
||||
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesAreSpawned);
|
||||
Assert.True(success, "Timed out waiting for all child instances to be spawned!");
|
||||
|
||||
// Assure the newly connected client's child object's transform values are correct
|
||||
AllChildrenLocalTransformValuesMatch();
|
||||
// This waits for all child instances to be parented
|
||||
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesHaveChild);
|
||||
Assert.True(success, "Timed out waiting for all instances to have parented a child!");
|
||||
|
||||
// This validates each child instance has preserved their local space values
|
||||
AllChildrenLocalTransformValuesMatch(false);
|
||||
|
||||
// This validates each sub-child instance has preserved their local space values
|
||||
AllChildrenLocalTransformValuesMatch(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1054,7 +1162,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
m_AuthoritativeTransform.transform.rotation = Quaternion.Euler(1, 2, 3);
|
||||
var serverLastSentState = m_AuthoritativeTransform.AuthorityLastSentState;
|
||||
var clientReplicatedState = m_NonAuthoritativeTransform.ReplicatedNetworkState.Value;
|
||||
var clientReplicatedState = m_NonAuthoritativeTransform.LocalAuthoritativeNetworkState;
|
||||
var success = WaitForConditionOrTimeOutWithTimeTravel(() => ValidateBitSetValues(serverLastSentState, clientReplicatedState));
|
||||
Assert.True(success, $"Timed out waiting for Authoritative Bitset state to equal NonAuthoritative replicated Bitset state!");
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
return TestComplete;
|
||||
}
|
||||
|
||||
protected override void OnInitialize(ref NetworkVariable<NetworkTransformState> replicatedState)
|
||||
protected override void OnInitialize(ref NetworkTransformState replicatedState)
|
||||
{
|
||||
m_LocalSpaceToggles = 0;
|
||||
m_FrameRateFractional = 1.0f / Application.targetFrameRate;
|
||||
|
||||
Reference in New Issue
Block a user