Skip to content

Commit e0e0098

Browse files
fix: integration tests and NetworkTransform teleport issue (#2451)
* Test Updates and Fixes Adding UNITY_INCLUDE_TESTS define checks for integration tests Some modifications to reduce the over-all NetworkTransform test times. Reducing NetworkAnimator tests time. Making sure NetworkAnimatorTests.ShutdownWhileSpawnedAndStartBackUpTest fails if the client cannot reconnect after shutting down. Improving the nested network transform test accuracy and assuring that we are actually comparing against the authority's pushed position and not comparing values in-between ticks. Making sure all clients are disconnected before shutting down server in NetworkAnimator (trying to see if I can get it more stable on Mac 2020.3) This update provides a new TimeOutHelper derived class TimeoutFrameCountHelper that requires the expected number of frames to pass and the time out period to be reached before it considers the condition check timed out. Making 2020.3 Mac OS X runs use a higher threshold as it fails on full precision when interpolation is enabled. Making the test component for TransformInterpolationObject derive from NetworkTransform to assure that we are checking values after they have been updated. Updated TransformInterpolationTest to be based on frames and not time. Making adjustments for systems that run below (or well below) the expected frame rate. Adding some additional checks as well as providing additional information about time and frame count in NestedNetworkTransformTests. * fix Fixing some tests and NetworkTranaform teleporting issue where some deltas could be lost in-between ticks. Fixing an issue with the network config hash internally stored value not clearing when the server-host or the client shutsdown in the event that prefabs change depending upon the network session being connected to.
1 parent 5de83b3 commit e0e0098

File tree

12 files changed

+508
-145
lines changed

12 files changed

+508
-145
lines changed

com.unity.netcode.gameobjects/Components/NetworkTransform.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,7 @@ internal void UpdatePositionInterpolator(Vector3 position, double time, bool res
11781178
}
11791179
}
11801180

1181-
#if DEBUG_NETWORKTRANSFORM
1181+
#if DEBUG_NETWORKTRANSFORM || UNITY_INCLUDE_TESTS
11821182
/// <summary>
11831183
/// For debugging delta position and half vector3
11841184
/// </summary>
@@ -1366,6 +1366,8 @@ private void TryCommitTransform(ref Transform transformToCommit, bool synchroniz
13661366

13671367
// "push"/commit the state
13681368
ReplicatedNetworkState.Value = m_LocalAuthoritativeNetworkState;
1369+
1370+
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
13691371
}
13701372
}
13711373

@@ -1424,10 +1426,11 @@ internal bool ApplyTransformToNetworkState(ref NetworkTransformState networkStat
14241426
[MethodImpl(MethodImplOptions.AggressiveInlining)]
14251427
private bool ApplyTransformToNetworkStateWithInfo(ref NetworkTransformState networkState, ref Transform transformToUse, bool isSynchronization = false, ulong targetClientId = 0)
14261428
{
1429+
var isTeleportingAndNotSynchronizing = networkState.IsTeleportingNextFrame && !isSynchronization;
14271430
var isDirty = false;
1428-
var isPositionDirty = false;
1429-
var isRotationDirty = false;
1430-
var isScaleDirty = false;
1431+
var isPositionDirty = isTeleportingAndNotSynchronizing ? networkState.HasPositionChange : false;
1432+
var isRotationDirty = isTeleportingAndNotSynchronizing ? networkState.HasRotAngleChange : false;
1433+
var isScaleDirty = isTeleportingAndNotSynchronizing ? networkState.HasScaleChange : false;
14311434

14321435
var position = InLocalSpace ? transformToUse.localPosition : transformToUse.position;
14331436
var rotAngles = InLocalSpace ? transformToUse.localEulerAngles : transformToUse.eulerAngles;
@@ -2346,7 +2349,7 @@ private void AxisChangedDeltaPositionCheck()
23462349
internal void OnUpdateAuthoritativeState(ref Transform transformSource)
23472350
{
23482351
// If our replicated state is not dirty and our local authority state is dirty, clear it.
2349-
if (!ReplicatedNetworkState.IsDirty() && m_LocalAuthoritativeNetworkState.IsDirty)
2352+
if (!ReplicatedNetworkState.IsDirty() && m_LocalAuthoritativeNetworkState.IsDirty && !m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame)
23502353
{
23512354
// Now clear our bitset and prepare for next network tick state update
23522355
m_LocalAuthoritativeNetworkState.ClearBitSetForNextTick();
@@ -2397,10 +2400,6 @@ public override void OnNetworkSpawn()
23972400
var currentRotation = GetSpaceRelativeRotation();
23982401
// Teleport to current position
23992402
SetStateInternal(currentPosition, currentRotation, transform.localScale, true);
2400-
2401-
// Force the state update to be sent
2402-
var transformToCommit = transform;
2403-
TryCommitTransform(ref transformToCommit);
24042403
}
24052404
}
24062405

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ public void FromBase64(string base64)
208208

209209
private ulong? m_ConfigHash = null;
210210

211+
/// <summary>
212+
/// Clears out the configuration hash value generated for a specific network session
213+
/// </summary>
214+
internal void ClearConfigHash()
215+
{
216+
m_ConfigHash = null;
217+
}
218+
211219
/// <summary>
212220
/// Gets a SHA256 hash of parts of the NetworkConfig instance
213221
/// </summary>

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,10 @@ internal void ShutdownInternal()
13221322

13231323
// This cleans up the internal prefabs list
13241324
NetworkConfig?.Prefabs.Shutdown();
1325+
1326+
// Reset the configuration hash for next session in the event
1327+
// that the prefab list changes
1328+
NetworkConfig?.ClearConfigHash();
13251329
}
13261330

13271331
/// <inheritdoc />

com.unity.netcode.gameobjects/TestHelpers/Runtime/TimeoutHelper.cs

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,138 @@ namespace Unity.Netcode.TestHelpers.Runtime
88
/// </summary>
99
public class TimeoutHelper
1010
{
11-
private const float k_DefaultTimeOutWaitPeriod = 2.0f;
11+
protected const float k_DefaultTimeOutWaitPeriod = 2.0f;
12+
1213

1314
private float m_MaximumTimeBeforeTimeOut;
1415
private float m_TimeOutPeriod;
1516

16-
private bool m_IsStarted;
17+
protected bool m_IsStarted { get; private set; }
1718
public bool TimedOut { get; internal set; }
1819

20+
private float m_TimeStarted;
21+
private float m_TimeStopped;
22+
23+
public float GetTimeElapsed()
24+
{
25+
if (m_IsStarted)
26+
{
27+
return Time.realtimeSinceStartup - m_TimeStarted;
28+
}
29+
else
30+
{
31+
return m_TimeStopped - m_TimeStarted;
32+
}
33+
}
34+
35+
protected virtual void OnStart()
36+
{
37+
}
38+
1939
public void Start()
2040
{
41+
m_TimeStopped = 0.0f;
42+
m_TimeStarted = Time.realtimeSinceStartup;
2143
m_MaximumTimeBeforeTimeOut = Time.realtimeSinceStartup + m_TimeOutPeriod;
2244
m_IsStarted = true;
2345
TimedOut = false;
46+
OnStart();
47+
}
48+
49+
protected virtual void OnStop()
50+
{
2451
}
2552

2653
public void Stop()
2754
{
55+
if (m_TimeStopped == 0.0f)
56+
{
57+
m_TimeStopped = Time.realtimeSinceStartup;
58+
}
2859
TimedOut = HasTimedOut();
2960
m_IsStarted = false;
61+
OnStop();
3062
}
3163

32-
public bool HasTimedOut()
64+
protected virtual bool OnHasTimedOut()
3365
{
3466
return m_IsStarted ? m_MaximumTimeBeforeTimeOut < Time.realtimeSinceStartup : TimedOut;
3567
}
3668

69+
public bool HasTimedOut()
70+
{
71+
return OnHasTimedOut();
72+
}
73+
3774
public TimeoutHelper(float timeOutPeriod = k_DefaultTimeOutWaitPeriod)
3875
{
3976
m_TimeOutPeriod = timeOutPeriod;
4077
}
4178
}
79+
80+
/// <summary>
81+
/// This can be used in place of TimeoutHelper if you suspect a test is having
82+
/// issues on a system where the frame rate is running slow than expected and
83+
/// allowing a certain number of frame updates is required.
84+
/// </summary>
85+
public class TimeoutFrameCountHelper : TimeoutHelper
86+
{
87+
private const uint k_DefaultTickRate = 30;
88+
89+
private float m_TotalFramesToWait;
90+
private int m_StartFrameCount;
91+
private int m_EndFrameCount;
92+
private bool m_ReachedFrameCount;
93+
94+
public int GetFrameCount()
95+
{
96+
if (m_IsStarted)
97+
{
98+
return Time.frameCount - m_StartFrameCount;
99+
}
100+
else
101+
{
102+
return m_EndFrameCount - m_StartFrameCount;
103+
}
104+
}
105+
106+
protected override void OnStop()
107+
{
108+
if (m_EndFrameCount == 0)
109+
{
110+
m_EndFrameCount = Time.frameCount;
111+
}
112+
base.OnStop();
113+
}
114+
115+
protected override bool OnHasTimedOut()
116+
{
117+
var currentFrameCountDelta = Time.frameCount - m_StartFrameCount;
118+
if (m_IsStarted)
119+
{
120+
m_ReachedFrameCount = currentFrameCountDelta >= m_TotalFramesToWait;
121+
}
122+
// Only time out if we have both exceeded the time period and the expected number of frames has reached the expected number of frames
123+
// (this handles the scenario where some systems are running a much lower frame rate)
124+
return m_ReachedFrameCount && base.OnHasTimedOut();
125+
}
126+
127+
protected override void OnStart()
128+
{
129+
m_EndFrameCount = 0;
130+
m_StartFrameCount = Time.frameCount;
131+
base.OnStart();
132+
}
133+
134+
public TimeoutFrameCountHelper(float timeOutPeriod = k_DefaultTimeOutWaitPeriod, uint tickRate = k_DefaultTickRate) : base(timeOutPeriod)
135+
{
136+
// Calculate the expected number of frame updates that should occur during the tick count wait period
137+
var frameFrequency = 1.0f / (Application.targetFrameRate >= 60 && Application.targetFrameRate <= 100 ? Application.targetFrameRate : 60.0f);
138+
var tickFrequency = 1.0f / tickRate;
139+
var framesPerTick = tickFrequency / frameFrequency;
140+
var totalExpectedTicks = timeOutPeriod / tickFrequency;
141+
142+
m_TotalFramesToWait = framesPerTick * totalExpectedTicks;
143+
}
144+
}
42145
}

com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ public void WhenModifyingPrefabListUsingNetworkManagerAPI_ModificationIsLocal()
163163
networkManager2.NetworkConfig.NetworkTransport = networkManager.gameObject.AddComponent<UnityTransport>();
164164

165165
var object1 = new GameObject("Object 1").AddComponent<NetworkObject>();
166+
166167
var object2 = new GameObject("Object 2").AddComponent<NetworkObject>();
167168
var object3 = new GameObject("Object 3").AddComponent<NetworkObject>();
168169

com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
301301
// Step 3: disable a particular sync flag, expect state to be not dirty
302302
// We do this last because it changes which axis will be synchronized.
303303
{
304+
// Reset the NetworkTransformState since teleporting will preserve
305+
// any dirty values
304306
networkTransformState = new NetworkTransform.NetworkTransformState
305307
{
306308
InLocalSpace = inLocalSpace,
@@ -333,6 +335,13 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
333335
}
334336
}
335337

338+
// Reset the NetworkTransformState since teleporting will preserve
339+
// any dirty values
340+
networkTransformState = new NetworkTransform.NetworkTransformState
341+
{
342+
InLocalSpace = inLocalSpace,
343+
IsTeleportingNextFrame = isTeleporting,
344+
};
336345
// SyncPositionY
337346
if (syncPosY)
338347
{
@@ -358,6 +367,13 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
358367
}
359368
}
360369

370+
// Reset the NetworkTransformState since teleporting will preserve
371+
// any dirty values
372+
networkTransformState = new NetworkTransform.NetworkTransformState
373+
{
374+
InLocalSpace = inLocalSpace,
375+
IsTeleportingNextFrame = isTeleporting,
376+
};
361377
// SyncPositionZ
362378
if (syncPosZ)
363379
{
@@ -383,6 +399,13 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
383399
}
384400
}
385401

402+
// Reset the NetworkTransformState since teleporting will preserve
403+
// any dirty values
404+
networkTransformState = new NetworkTransform.NetworkTransformState
405+
{
406+
InLocalSpace = inLocalSpace,
407+
IsTeleportingNextFrame = isTeleporting,
408+
};
386409
// SyncRotAngleX - Now test that we don't synchronize this specific axis as long as we are not using quaternion synchronization
387410
if (syncRotX && m_Rotation == Rotation.Euler)
388411
{
@@ -407,6 +430,14 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
407430
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
408431
}
409432
}
433+
434+
// Reset the NetworkTransformState since teleporting will preserve
435+
// any dirty values
436+
networkTransformState = new NetworkTransform.NetworkTransformState
437+
{
438+
InLocalSpace = inLocalSpace,
439+
IsTeleportingNextFrame = isTeleporting,
440+
};
410441
// SyncRotAngleY - Now test that we don't synchronize this specific axis as long as we are not using quaternion synchronization
411442
if (syncRotY && m_Rotation == Rotation.Euler)
412443
{
@@ -431,6 +462,14 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
431462
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
432463
}
433464
}
465+
466+
// Reset the NetworkTransformState since teleporting will preserve
467+
// any dirty values
468+
networkTransformState = new NetworkTransform.NetworkTransformState
469+
{
470+
InLocalSpace = inLocalSpace,
471+
IsTeleportingNextFrame = isTeleporting,
472+
};
434473
// SyncRotAngleZ - Now test that we don't synchronize this specific axis as long as we are not using quaternion synchronization
435474
if (syncRotZ && m_Rotation == Rotation.Euler)
436475
{
@@ -456,6 +495,13 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
456495
}
457496
}
458497

498+
// Reset the NetworkTransformState since teleporting will preserve
499+
// any dirty values
500+
networkTransformState = new NetworkTransform.NetworkTransformState
501+
{
502+
InLocalSpace = inLocalSpace,
503+
IsTeleportingNextFrame = isTeleporting,
504+
};
459505
// SyncScaleX
460506
if (syncScaX)
461507
{
@@ -480,6 +526,14 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
480526
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
481527
}
482528
}
529+
530+
// Reset the NetworkTransformState since teleporting will preserve
531+
// any dirty values
532+
networkTransformState = new NetworkTransform.NetworkTransformState
533+
{
534+
InLocalSpace = inLocalSpace,
535+
IsTeleportingNextFrame = isTeleporting,
536+
};
483537
// SyncScaleY
484538
if (syncScaY)
485539
{
@@ -504,6 +558,14 @@ public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Valu
504558
Assert.IsFalse(networkTransform.ApplyTransformToNetworkState(ref networkTransformState, 0, networkTransform.transform));
505559
}
506560
}
561+
562+
// Reset the NetworkTransformState since teleporting will preserve
563+
// any dirty values
564+
networkTransformState = new NetworkTransform.NetworkTransformState
565+
{
566+
InLocalSpace = inLocalSpace,
567+
IsTeleportingNextFrame = isTeleporting,
568+
};
507569
// SyncScaleZ
508570
if (syncScaZ)
509571
{

0 commit comments

Comments
 (0)