Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

- 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)


### Security

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3003,7 +3003,7 @@ private void ApplyTeleportingState(NetworkTransformState newState)
/// <remarks>
/// Only non-authoritative instances should invoke this
/// </remarks>
private void ApplyUpdatedState(NetworkTransformState newState)
internal void ApplyUpdatedState(NetworkTransformState newState)
{
// Set the transforms's synchronization modes
InLocalSpace = newState.InLocalSpace;
Expand Down Expand Up @@ -3052,6 +3052,7 @@ private void ApplyUpdatedState(NetworkTransformState newState)

if (!Interpolate)
{
ApplyAuthoritativeState();
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,72 @@ protected override void OnOneTimeTearDown()
base.OnOneTimeTearDown();
}

/// <summary>
/// This validates an issue where if multiple state updates are received in a single frame
/// and interpolation is disabled, only the last state udpate processed gets applied.
/// </summary>
[Test]
public void TestMultipleStateSynchronization([Values] bool isLocal, [Values] bool timeTravelBetweenStateUpdates)
{
// Assure no new state updates are pushed.
TimeTravel(0.5f, 60);

// Disable interpolation and set world or local space
m_NonAuthoritativeTransform.Interpolate = false;
m_NonAuthoritativeTransform.InLocalSpace = isLocal;

// Get the non-authority's state
var localState = m_NonAuthoritativeTransform.LocalAuthoritativeNetworkState;

// Assure this is not set to avoid a false positive result with teleporting
localState.IsTeleportingNextFrame = false;

// Simulate a state update
localState.UseInterpolation = false;
localState.CurrentPosition = new Vector3(5.0f, 0.0f, 0.0f);
localState.HasPositionX = true;
localState.PositionX = 5.0f;
localState.NetworkTick++;

var lastStateTick = localState.NetworkTick;
// Apply the simualted state update to the non-authority instance
m_NonAuthoritativeTransform.ApplyUpdatedState(localState);
// Simulate both having time between state updates and having state updates delivered back to back on the same frame
if (timeTravelBetweenStateUpdates)
{
TimeTravelAdvanceTick();
}

// Validate the state update was applied
var xValue = isLocal ? m_NonAuthoritativeTransform.transform.localPosition.x : m_NonAuthoritativeTransform.transform.position.x;
Assert.IsTrue(xValue == 5.0f, $"[Test1][IsLocal: {isLocal}] X axis ({xValue}) does not equal 5.0f!");


// Get the non-authority state
localState = m_NonAuthoritativeTransform.LocalAuthoritativeNetworkState;

//Assure we have not received any state updates from the authority that could skew the test
Assert.IsTrue(localState.NetworkTick == lastStateTick, $"Previous Non-authority state tick was {lastStateTick} but is now {localState.NetworkTick}. Authority pushed a state update.");

// Simualate a 2nd state update on a different position axis
localState.HasPositionX = false;
localState.HasPositionZ = true;
localState.PositionZ = -5.0f;
localState.NetworkTick++;
m_NonAuthoritativeTransform.ApplyUpdatedState(localState);
// Simulate both having time between state updates and having state updates delivered back to back on the same frame
if (timeTravelBetweenStateUpdates)
{
TimeTravelAdvanceTick();
}
var zValue = isLocal ? m_NonAuthoritativeTransform.transform.localPosition.z : m_NonAuthoritativeTransform.transform.position.z;
xValue = isLocal ? m_NonAuthoritativeTransform.transform.localPosition.x : m_NonAuthoritativeTransform.transform.position.x;

// Verify the previous state update's position and current state update's position
Assert.IsTrue(xValue == 5.0f, $"[Test2][IsLocal: {isLocal}] X axis ({xValue}) does not equal 5.0f!");
Assert.IsTrue(zValue == -5.0f, $"[Test2][IsLocal: {isLocal}] Z axis ({zValue}) does not equal -5.0f!");
}

/// <summary>
/// Test to verify nonAuthority cannot change the transform directly
/// </summary>
Expand Down