Skip to content

Commit 2b38d55

Browse files
fix: unreliable deltas half float synch jitter (#2922)
* fix This fixes an issue with using unreliable state updates, half-floats, and NetworkDeltaPosition. * test Adding test to validate the fix for using half float precision with unreliable deltas.
1 parent 66d4134 commit 2b38d55

File tree

4 files changed

+84
-2
lines changed

4 files changed

+84
-2
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2525

2626
### Fixed
2727

28+
- Fixed issue where `NetworkDeltaPosition` would "jitter" periodically if both unreliable delta state updates and half-floats were used together. (#2922)
2829
- Fixed issue where `NetworkRigidbody2D` would not properly change body type based on the instance's authority when spawned. (#2916)
2930
- Fixed issue where a `NetworkObject` component's associated `NetworkBehaviour` components would not be detected if scene loading is disabled in the editor and the currently loaded scene has in-scene placed `NetworkObject`s. (#2906)
3031

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@ public struct NetworkDeltaPosition : INetworkSerializable
3232
/// </summary>
3333
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
3434
{
35-
HalfVector3.NetworkSerialize(serializer);
36-
if (SynchronizeBase)
35+
if (!SynchronizeBase)
3736
{
37+
HalfVector3.NetworkSerialize(serializer);
38+
}
39+
else
40+
{
41+
serializer.SerializeValue(ref DeltaPosition);
3842
serializer.SerializeValue(ref CurrentBasePosition);
3943
}
4044
}

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

+18
Original file line numberDiff line numberDiff line change
@@ -751,11 +751,29 @@ protected override void OnAuthorityPushTransformState(ref NetworkTransformState
751751
base.OnAuthorityPushTransformState(ref networkTransformState);
752752
}
753753

754+
public bool AuthorityMove;
755+
public Vector3 DirectionToMove;
756+
public float MoveSpeed;
757+
758+
protected override void Update()
759+
{
760+
if (CanCommitToTransform && AuthorityMove)
761+
{
762+
transform.position += DirectionToMove * MoveSpeed * Time.deltaTime;
763+
}
764+
base.Update();
765+
}
766+
767+
768+
public delegate void NonAuthorityReceivedTransformStateDelegateHandler(ref NetworkTransformState networkTransformState);
769+
770+
public event NonAuthorityReceivedTransformStateDelegateHandler NonAuthorityReceivedTransformState;
754771

755772
public bool StateUpdated { get; internal set; }
756773
protected override void OnNetworkTransformStateUpdated(ref NetworkTransformState oldState, ref NetworkTransformState newState)
757774
{
758775
StateUpdated = true;
776+
NonAuthorityReceivedTransformState?.Invoke(ref newState);
759777
base.OnNetworkTransformStateUpdated(ref oldState, ref newState);
760778
}
761779

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

+59
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,64 @@ public void NonAuthorityOwnerSettingStateTest([Values] Interpolation interpolati
310310
Assert.True(Approximately(newScale, m_AuthoritativeTransform.transform.localScale), "Authoritative scale does not match!");
311311
Assert.True(Approximately(newScale, m_NonAuthoritativeTransform.transform.localScale), "Non-Authoritative scale does not match!");
312312
}
313+
314+
/// <summary>
315+
/// Validates that the unreliable frame synchronization is correct on the
316+
/// non-authority side when using half float precision.
317+
/// </summary>
318+
[Test]
319+
public void UnreliableHalfPrecisionTest([Values] Interpolation interpolation)
320+
{
321+
var interpolate = interpolation != Interpolation.EnableInterpolate;
322+
m_AuthoritativeTransform.Interpolate = interpolate;
323+
m_NonAuthoritativeTransform.Interpolate = interpolate;
324+
m_AuthoritativeTransform.UseHalfFloatPrecision = true;
325+
m_NonAuthoritativeTransform.UseHalfFloatPrecision = true;
326+
m_AuthoritativeTransform.UseUnreliableDeltas = true;
327+
m_NonAuthoritativeTransform.UseUnreliableDeltas = true;
328+
m_AuthoritativeTransform.AuthorityPushedTransformState += AuthorityPushedTransformState;
329+
m_NonAuthoritativeTransform.NonAuthorityReceivedTransformState += NonAuthorityReceivedTransformState;
330+
m_AuthoritativeTransform.MoveSpeed = 6.325f;
331+
m_AuthoritativeTransform.AuthorityMove = true;
332+
m_AuthoritativeTransform.DirectionToMove = GetRandomVector3(-1.0f, 1.0f);
333+
334+
// Iterate several times so the authority moves around enough where we get 10 frame synchs to compare against.
335+
for (int i = 0; i < 10; i++)
336+
{
337+
m_AuthorityFrameSync = false;
338+
m_NonAuthorityFrameSync = false;
339+
VerboseDebug($"Starting with authority ({m_AuthoritativeTransform.transform.position}) and nonauthority({m_NonAuthoritativeTransform.transform.position})");
340+
var success = WaitForConditionOrTimeOutWithTimeTravel(() => m_AuthorityFrameSync && m_NonAuthorityFrameSync, 320);
341+
Assert.True(success, $"Timed out waiting for authority or nonauthority frame state synchronization!");
342+
VerboseDebug($"Comparing authority ({m_AuthorityPosition}) with nonauthority({m_NonAuthorityPosition})");
343+
Assert.True(Approximately(m_AuthorityPosition, m_NonAuthorityPosition), $"Non-Authoritative position {m_AuthorityPosition} does not match authortative position {m_NonAuthorityPosition}!");
344+
}
345+
346+
m_AuthoritativeTransform.AuthorityMove = false;
347+
m_AuthoritativeTransform.AuthorityPushedTransformState -= AuthorityPushedTransformState;
348+
m_NonAuthoritativeTransform.NonAuthorityReceivedTransformState -= NonAuthorityReceivedTransformState;
349+
}
350+
351+
private bool m_AuthorityFrameSync;
352+
private Vector3 m_AuthorityPosition;
353+
private bool m_NonAuthorityFrameSync;
354+
private Vector3 m_NonAuthorityPosition;
355+
private void AuthorityPushedTransformState(ref NetworkTransform.NetworkTransformState networkTransformState)
356+
{
357+
if (networkTransformState.UnreliableFrameSync)
358+
{
359+
m_AuthorityPosition = m_AuthoritativeTransform.GetSpaceRelativePosition();
360+
m_AuthorityFrameSync = true;
361+
}
362+
}
363+
364+
private void NonAuthorityReceivedTransformState(ref NetworkTransform.NetworkTransformState networkTransformState)
365+
{
366+
if (networkTransformState.UnreliableFrameSync)
367+
{
368+
m_NonAuthorityPosition = networkTransformState.NetworkDeltaPosition.GetFullPosition();
369+
m_NonAuthorityFrameSync = true;
370+
}
371+
}
313372
}
314373
}

0 commit comments

Comments
 (0)