Skip to content

Commit 15263c7

Browse files
fix: NetworkTransform fails to sync multiple state updates (#3614)
## Purpose of this PR This PR resolves an issue where a client, under above average latency and packet loss conditions, could receive multiple `NetworkTransform` state updates in one frame and when processing the state updates only the last state update would be applied to the transform. fix: #3571 ### Jira ticket [MTTB-1530](https://jira.unity3d.com/browse/MTTB-1530) ### Changelog [//]: # (updated with all public facing changes - API changes, UI/UX changes, behaviour changes, bug fixes. Remove if not relevant.) - Fixed: Issue where a client, under above average latency and packet loss conditions, could receive multiple `NetworkTransform` state updates in one frame and when processing the state updates only the last state update would be applied to the transform if interpolation was disabled. <!-- Uncomment and mark items off with a * if this PR deprecates any API: ### Deprecated API - [ ] An `[Obsolete]` attribute was added along with a `(RemovedAfter yyyy-mm-dd)` entry. - [ ] An [api updater](https://confluence.unity3d.com/display/DEV/Obsolete+API+updaters) was added. - [ ] Deprecation of the API is explained in the CHANGELOG. - [ ] The users can understand why this API was removed and what they should use instead. --> ## Documentation [//]: # ( This section is REQUIRED and should mention what documentation changes were following the changes in this PR. We should always evaluate if the changes in this PR require any documentation changes. ) - No documentation changes or additions were necessary. ## Testing & QA [//]: # ( This section is REQUIRED and should describe how the changes were tested and how should they be tested when Playtesting for the release. It can range from "edge case covered by unit tests" to "manual testing required and new sample was added". Expectation is that PR creator does some manual testing and provides a summary of it here.) ### Functional Testing [//]: # (If checked, List manual tests that have been performed.) _Manual testing :_ - [X] `Manual testing done` - [PR-46](https://github.cds.internal.unity3d.com/unity/Asteroids-CMB-NGO-Sample/pull/46) manual test. _Automated tests:_ - [ ] `Covered by existing automated tests` - [X] `Covered by new automated tests` _Does the change require QA team to:_ - [ ] `Review automated tests`? - [ ] `Execute manual tests`? If any boxes above are checked, please add QA as a PR reviewer. ## Backport This is an NGO v2.x only issue.
1 parent 506d525 commit 15263c7

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
2222

2323
### Fixed
2424

25+
- Fixed issue where a client, under above average latency and packet loss conditions, could receive multiple NetworkTransform state updates in one frame and when processing the state updates only the last state update would be applied to the transform if interpolation was disabled. (#3614)
26+
2527

2628
### Security
2729

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3003,7 +3003,7 @@ private void ApplyTeleportingState(NetworkTransformState newState)
30033003
/// <remarks>
30043004
/// Only non-authoritative instances should invoke this
30053005
/// </remarks>
3006-
private void ApplyUpdatedState(NetworkTransformState newState)
3006+
internal void ApplyUpdatedState(NetworkTransformState newState)
30073007
{
30083008
// Set the transforms's synchronization modes
30093009
InLocalSpace = newState.InLocalSpace;
@@ -3052,6 +3052,7 @@ private void ApplyUpdatedState(NetworkTransformState newState)
30523052

30533053
if (!Interpolate)
30543054
{
3055+
ApplyAuthoritativeState();
30553056
return;
30563057
}
30573058

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,72 @@ protected override void OnOneTimeTearDown()
3737
base.OnOneTimeTearDown();
3838
}
3939

40+
/// <summary>
41+
/// This validates an issue where if multiple state updates are received in a single frame
42+
/// and interpolation is disabled, only the last state udpate processed gets applied.
43+
/// </summary>
44+
[Test]
45+
public void TestMultipleStateSynchronization([Values] bool isLocal, [Values] bool timeTravelBetweenStateUpdates)
46+
{
47+
// Assure no new state updates are pushed.
48+
TimeTravel(0.5f, 60);
49+
50+
// Disable interpolation and set world or local space
51+
m_NonAuthoritativeTransform.Interpolate = false;
52+
m_NonAuthoritativeTransform.InLocalSpace = isLocal;
53+
54+
// Get the non-authority's state
55+
var localState = m_NonAuthoritativeTransform.LocalAuthoritativeNetworkState;
56+
57+
// Assure this is not set to avoid a false positive result with teleporting
58+
localState.IsTeleportingNextFrame = false;
59+
60+
// Simulate a state update
61+
localState.UseInterpolation = false;
62+
localState.CurrentPosition = new Vector3(5.0f, 0.0f, 0.0f);
63+
localState.HasPositionX = true;
64+
localState.PositionX = 5.0f;
65+
localState.NetworkTick++;
66+
67+
var lastStateTick = localState.NetworkTick;
68+
// Apply the simualted state update to the non-authority instance
69+
m_NonAuthoritativeTransform.ApplyUpdatedState(localState);
70+
// Simulate both having time between state updates and having state updates delivered back to back on the same frame
71+
if (timeTravelBetweenStateUpdates)
72+
{
73+
TimeTravelAdvanceTick();
74+
}
75+
76+
// Validate the state update was applied
77+
var xValue = isLocal ? m_NonAuthoritativeTransform.transform.localPosition.x : m_NonAuthoritativeTransform.transform.position.x;
78+
Assert.IsTrue(xValue == 5.0f, $"[Test1][IsLocal: {isLocal}] X axis ({xValue}) does not equal 5.0f!");
79+
80+
81+
// Get the non-authority state
82+
localState = m_NonAuthoritativeTransform.LocalAuthoritativeNetworkState;
83+
84+
//Assure we have not received any state updates from the authority that could skew the test
85+
Assert.IsTrue(localState.NetworkTick == lastStateTick, $"Previous Non-authority state tick was {lastStateTick} but is now {localState.NetworkTick}. Authority pushed a state update.");
86+
87+
// Simualate a 2nd state update on a different position axis
88+
localState.HasPositionX = false;
89+
localState.HasPositionZ = true;
90+
localState.PositionZ = -5.0f;
91+
localState.NetworkTick++;
92+
m_NonAuthoritativeTransform.ApplyUpdatedState(localState);
93+
// Simulate both having time between state updates and having state updates delivered back to back on the same frame
94+
if (timeTravelBetweenStateUpdates)
95+
{
96+
TimeTravelAdvanceTick();
97+
}
98+
var zValue = isLocal ? m_NonAuthoritativeTransform.transform.localPosition.z : m_NonAuthoritativeTransform.transform.position.z;
99+
xValue = isLocal ? m_NonAuthoritativeTransform.transform.localPosition.x : m_NonAuthoritativeTransform.transform.position.x;
100+
101+
// Verify the previous state update's position and current state update's position
102+
Assert.IsTrue(xValue == 5.0f, $"[Test2][IsLocal: {isLocal}] X axis ({xValue}) does not equal 5.0f!");
103+
Assert.IsTrue(zValue == -5.0f, $"[Test2][IsLocal: {isLocal}] Z axis ({zValue}) does not equal -5.0f!");
104+
}
105+
40106
/// <summary>
41107
/// Test to verify nonAuthority cannot change the transform directly
42108
/// </summary>

0 commit comments

Comments
 (0)