Skip to content

feat: NetworkTransform with blittable states and handled in jobs - #4123

Open
NoelStephensUnity wants to merge 61 commits into
develop-3.x.xfrom
feat/networktransform-handled-in-jobs
Open

feat: NetworkTransform with blittable states and handled in jobs#4123
NoelStephensUnity wants to merge 61 commits into
develop-3.x.xfrom
feat/networktransform-handled-in-jobs

Conversation

@NoelStephensUnity

@NoelStephensUnity NoelStephensUnity commented Aug 16, 2026

Copy link
Copy Markdown
Member

Purpose of this PR

This PR does two things:

1. NetworkTransform's state is now blittable. All of the types it uses to detect and apply
transform changes can be used from a job. This includes the interpolators, quaternion compression, and
the lerp and smooth dampening math, which previously relied on engine calls that Burst cannot compile.

2. It adds an opt-in batched synchronization mode. Instead of every NetworkTransform checking
itself and sending its own message, one job checks them all in parallel and the server sends a single
message per tick. This uses noticeably less bandwidth.
2.a It defaults to the per-instance mode. Users upgrading to v3.0.0 NetworkTransform will continue to work as expected.

The new mode is off by default and there are no breaking changes.

Architectural overview

image

Turning it on

NetworkConfig.TransformSyncMode is set per NetworkManager, either under "Transform
Synchronization" in the inspector or from script before starting a session. It is a session-wide
setting: everything is either batched or per-instance, not a mix.

Every peer in a session has to agree on the mode. This is part of the connection configuration hash, so
a mismatched client is rejected at connection rather than failing quietly. The value the session starts
with applies for the whole session, so writing the field mid-session has no effect and is safe.

What stays on the per-instance path

Some instances cannot be batched, so they are routed to the per-instance path automatically even when
batched mode is on. No configuration is needed for this to happen, and they behave exactly as they do in v2.x.x.x:

  • Distributed authority sessions. The batched message is not yet handled by the CMB service so all messages are sent per-instance.
    • Instances owned by a client, using an owner authoritative motion model, and using a client-server network topology will send per-instance. Currently, clients do not break up batched messages based on observers.
    • Both of these will be resolved when adding support for distributed authority.
  • Instances with a Rigidbody, NetworkRigidbody, and the NetworkRigidbody has the "Use Rigidbody for Motion" setting checked, default to per-instance mode because a job cannot read a rigidbody's position and rotation.
    • There is a clear path to handling this when using a client-server network topology and you do not need client-side collisions:
      • Using a prefab handler, one can use a prefab override approach where the server-side prefab only has a Rigidbody and the client side prefab does not. Both do not have a NetworkRigidbody.
  • Nested NetworkTransform instances, which are already ticked by their parent.

Note: These instances will still get the job-based delta detection. Only how the message is sent is the primary difference.

NetworkTransform.UseUnreliableDeltas does not apply in batched mode. Delivery is decided per state
update rather than per component.

Tradeoffs worth knowing about

  • Batched mode is session-wide rather than per instance.
    • With some of the caveats listed above about how state updates are sent.
  • Batched instances read their position and rotation from the transform rather than from a Rigidbody.
    • Rigidbody cannot be accessed in a job.

Jira ticket

N/A

Changelog

  • Added: NetworkConfig.TransformSyncMode, which determines whether NetworkTransform instances detect
    and synchronize their state individually or as a single batched message per tick.
  • Fixed: Re-enabling a NetworkTransform position axis that drifted out of half float delta range while
    it was disabled did not teleport.

Documentation

  • Will require documentation updates.
  • Needs a new section covering NetworkConfig.TransformSyncMode: how to set it, that every peer has to
    match, and which instances stay on the per-instance path.
  • Includes documentation for the new public API entry points (TransformSyncModes and
    NetworkConfig.TransformSyncMode).

The only public API added is the TransformSyncModes enum and the NetworkConfig.TransformSyncMode
field. Everything else in this PR is internal.

Testing & QA (How your changes can be verified during release Playtest)

Both modes are exercised by the same tests, so anything that regresses one and not the other shows up
as a parity failure.

New test fixtures:

  • NetworkTransformSyncModeParityTests runs the same scenarios through both modes and both authority
    models to assure non-authority instances directly align with the authority instances.
    • Covers: motion, client-owned instances, teleports, observer filtering with NetworkHide/NetworkShow, ownership
      changes, and despawn/respawn.
  • NetworkTransformSyncModeConfigurationTests assures the synchronization mode selected is used during runtime.
  • NativeInterpolatorTests validates that the job-friendly interpolator and the managed one provide the same results when using identical input.
  • NetworkTransformMathTests measures the job-friendly math against the managed math to assure both yield the same expected results.
  • NetworkTransformStateBaselineTests records the exact serialized bytes of a NetworkTransformState
    across every serialization branch. This is the proof that the per-instance wire format did not
    change
    — it passes unmodified.
    • !!Note!!: This uses an embedded table that is generated each time it runs. If something changes the wire format, then this test will fail and requires using the table generated when it fails. This prevents "silent" changes to this format from happening. When the wire format is changed, we should bump the protocol version for NGO in order to assure old clients do not attempt to connect to sessions using the changed format. This differs from our normal message versioning system intentionally.
  • TransformHandleAllocatorTests validates transform handle allocation and reuse.

Playtest suggestion: run an existing scene with many moving NetworkTransform objects once in each
mode and confirm motion looks identical. Batched mode is set on the NetworkManager before starting a session (i.e. it can be set in the editor or at runtime).
Worth including a case with a NetworkRigidbody and a case with parented objects, since those take
the per-instance path.

Bandwidth

Batched mode can yield up to a ~54% in bandwidth savings.
PR with additional optimizations pending as this PR is already too large.

Functional Testing

Manual testing :

  • Manual testing done

Automated tests:

  • Covered by existing automated tests
  • Covered by new automated tests

Does the change require QA team to:

  • Review automated tests?
  • Execute manual tests?
  • Provide feedback about the PR?

If any boxes above are checked the QA team will be automatically added as a PR reviewer.

Up-port

No up-port is required.

Backports

No backport is required.

NoelStephensUnity and others added 28 commits August 14, 2026 16:37
First phase of the NetworkTransform optimization pass.
First part of the second pass which begins to get the job integration side of things.
This provides burst compatible math operations for the non-burst compatible ones that NetworkTransform users.
Connecting the jobs to the previous work as well as the batched message type.
Detecting changes in state and interpolation are handled in jobs as opposed to the main thread.
Some additional changes but for the most part this is the core framework to abstracting NetworkTransform away from the managed layer. This includes some core-feature parity tests when using per-instance vs batched synchronization modes.
Ownership transfer when using an owner authoritative motion model was not replicating the state properly upon the transfer of ownership causing an out of sync server transform state.
removing link.xml that was accidentally included.
NetworkDeltaPosition carries the half float rounding loss of each update
into the next one, which keeps the average transmitted position accurate
while a value is moving. The loss alone is enough to change the encoded
delta, so once the value stops moving that mechanism keeps changing what is
sent even though the position has not moved. The encoded value alternates
between neighbouring representable values and a stationary object is
transmitted as one that oscillates. The rounding loss is now only carried
forward while the value moves by at least one representable step.

MaxDeltaBeforeAdjustment also determined the transmitted resolution, since
a half float's step size grows with its magnitude. At 64 the coarsest step
was 31.25mm, so objects away from their base position were reproduced in
~3cm increments. At 2 it is 0.977mm. Folding the delta into the base more
often costs no bandwidth with reliable deltas because both sides apply the
same rule to the same value, and the reconstructed position is unchanged by
the fold. UseUnreliableDeltas forces a full precision base synchronization
per fold, so those projects will send those more often.

Measured on 10 settling physics objects with half float enabled: 28-42mm of
oscillation before, none after, matching the same scene with half float
disabled. Objects in motion improve as well, peak error dropping from
12.5mm to 0.587mm.

Sender and receiver must agree on MaxDeltaBeforeAdjustment, so this is not
compatible across builds. NetworkConstants.PROTOCOL_VERSION already
participates in the connection config hash, so mismatched versions cannot
connect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two NetcodeIntegrationTest cases, one for an object moving in steps too
small for the encoding to represent and one for an object at rest. Both
move the authority forwards only and require non-authority instances to
follow without ever moving backwards. Interpolation cannot overshoot, so
movement opposite to the authority's has to have come from the encoding.
That also avoids a tolerance that would need revisiting whenever the
resolution changes.

Two setup details are needed for these to detect anything. The object has to
travel away from the base position established when it spawned, since
resolution is fine near the base. It then has to step by an amount the
encoding cannot represent before coming to rest, because a position a half
float represents exactly leaves no rounding loss and so cannot exhibit the
problem: resting on 30.0 produces no backwards movement at all while resting
on 30.0007 produces 15.6mm.

Verified in both directions. Without the fix all four cases fail on the
intended assertion, reporting 7.9mm to 10.1mm of backwards movement. With
the fix all four pass.

These do not use the time travel harness because the behavior only appears
over multiple real state update and interpolation cycles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Updated the changelog to include issue #4129 regarding the precision of NetworkTransform synchronization.
Fixed jitter issue with NetworkTransform.UseHalfFloatPrecision on non-authority instances.
…polation time

The lerp smoothing pass used by the Lerp and SmoothDampening interpolation
types applied a fixed factor of 1.0 minus the maximum interpolation time once
per frame, with no delta time. The wall clock smoothing rate therefore scaled
with the frame rate, so the same setting smoothed by different amounts on
different hardware. The factor is now raised to the number of 60fps reference
frames elapsed, which makes the rate a function of elapsed time. Results at
60fps are unchanged for every legal setting.

Separately, a maximum interpolation time of 1.0 (the upper bound of the
inspector range) produced a factor of exactly 0, so the interpolated value
never advanced and the transform stopped moving entirely on all three axes.
The retained portion is now clamped just below 1.0. This is an independent
defect, as raising 1.0 to any power is still 1.0.

The LegacyLerp path was already frame rate correct and is unchanged. The
documentation on the lerp smoothing fields described the LegacyLerp formula
for all interpolation types and has been corrected.
Adding PR number to change log entries.
Adds an integration test that measures how far behind the server clock the
state a non-authority NetworkTransform is interpolating towards was sent.

Only states sent at or before the render time are eligible to be interpolated
towards, and the render time is the server clock minus the tick latency, so
that measurement can never be less than the tick latency. It currently is,
and goes negative, meaning the interpolator is chasing a state that the server
clock says has not happened yet.

An in-process integration test has effectively no round trip time, so the test
first widens the client's local time buffer to separate LocalTime and
ServerTime by a known amount and waits for that separation to take hold.
Without it the two clocks sit close enough together that the test would pass
regardless of which one the render time is derived from.

This commit contains the test only, so it can be run against an unfixed tree.
A NetworkTransform state's SentTime comes from its NetworkTick, which is a
server tick, but the render time the interpolators were given was derived from
LocalTime. That mixes two clocks. LocalTime leads ServerTime, so subtracting
the tick latency from it lands the render time back at approximately ServerTime
rather than a whole tick latency behind it, and a state's SentTime is floored to
a tick boundary on top of that. The render time therefore sat at or ahead of the
newest state that could exist and the interpolator had nothing to interpolate
towards.

Measuring from ServerTime makes the offset the whole tick latency instead of
whatever is left of it, and is self correcting: as the round trip time grows the
tick latency grows and the render time moves further back with it. This also
matches the rest of the component, which already resets the interpolators using
ServerTime.

This is a no-op on a host or server, where the two clocks are the same, so it
only affects clients.

GetTickLatencyInSeconds returns an absolute time rather than a duration and had
the same defect, so it now derives from ServerTime as well. GetTickLatency is
left alone because it returns a tick count rather than a point in time.
Comment and changelog wording only, no behavioral or test logic changes.

Trims the explanation in UpdateInterpolation from twenty one lines to six and
drops the measurement anecdote and the unfilled Jira placeholder, keeping the
reason the server clock is the correct one to measure from. Shortens the test's
remarks and constant comments to match the density of the surrounding tests.

The removed detail, the measurements behind the fix, and the metrics that were
tried and rejected while building the test are recorded outside the repository.
Adding PR number to changelog entries.
Adding PR number to changelog entries.
Adding better coverage and adjusting some of the test to better leverage from NetcodeIntegrationTest helper methods.
Moving the NetworkDeltaPositionTests into its own file.
Bumping the protocol version to assure legacy clients cannot connect to a session with the fixes. While the fixes aren't technically a "breaking change", any projects using the legacy lerp will end up with an offset from the expected final position.  Since motion continually feeds the full position (half float or full precision) as deltas this would prevent from "long term drift".
Either case, updating the protocol version only assures that clients of a previous version cannot connect to a session with the newer version.
GetTickLatencyInSeconds returned TimeTicksAgo(...).Time, which is an absolute
network timestamp rather than a duration, so the value grew for as long as the
session ran. It is documented as returning the tick latency in seconds, and
NetworkTimeSystem.TickLatency points at it as a way to inspect that latency, so
the contract was misleading regardless of which clock it was measured from. It
now returns the tick count multiplied by the tick interval.

This also takes the clock question out of this method entirely, since a duration
does not reference LocalTime or ServerTime. The change to derive interpolation
render time from ServerTime now applies only to UpdateInterpolation.

Adds integration tests covering the documented contract: the value tracks the
tick latency rather than elapsed time, and lengthens by exactly the tick
interval for each tick of additional buffering. Both fail against the previous
implementation, the second regardless of how long the session has run, since
buffering more ticks used to make the reported latency smaller.
up-porting style fix
NetworkTimeSystem.TickLatency is recomputed from the averaged round trip time
and can legitimately change mid-run. Both tests assumed it would not, and one
failed on macOS when it moved from two ticks to three, reporting the value as
having gone from 0.0666s to 0.1s.

The duration is now only held to being unchanged across samples where the tick
latency itself did not change, and the buffer offset test accounts for any tick
latency movement between its two samples so that only the buffering is held to
an exact figure.

Both still fail against the previous absolute timestamp implementation.
- Drop the redundant HostOrServer fixture argument and the UseCMBService
  override; Host is the default and a client-server fixture never runs
  under the CMB service.
- Use WaitForSpawnedOnAllOrTimeOut, GetNonAuthorityNetworkManager and
  WaitForTicks instead of hand rolled equivalents.
- There is only ever one connected client, so drop the collections and
  refer to the single non-authority instance directly.
- Fold the two tick latency tests into one and drop the assertion that
  recomputed the implementation's own formula. What is left is what can
  actually regress: the value does not drift with session time, and it
  grows by exactly the ticks added to the interpolation buffer.
netcode-ci-service and others added 10 commits August 28, 2026 01:56
…ther-up-port' into feat/networktransform-handled-in-jobs

# Conflicts:
#	com.unity.netcode.gameobjects/CHANGELOG.md
…framerate-up-port' into feat/networktransform-handled-in-jobs

# Conflicts:
#	com.unity.netcode.gameobjects/CHANGELOG.md
…on-render-time-up-port-2' into feat/networktransform-handled-in-jobs

# Conflicts:
#	com.unity.netcode.gameobjects/CHANGELOG.md
#	com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs
…leport check

Three separate problems, all found by auditing the batched path against the per instance one.

A Teleport or a SetState raises the teleport and explicit set flags on the main thread, between
ticks, on m_LocalAuthoritativeNetworkState. The delta check job reads TransformDeltaEntry.State,
which ApplyBatchedDeltaEntry then assigns straight back over the top, so those flags never reached
the job and a batched teleport converged as an ordinary delta. PrepareBatchedDeltaEntry now seeds the
entry from the authoritative state, which is a no-op on any tick where the main thread changed
nothing.

AxisChangedDeltaPositionCheck ran only from OnUpdateAuthoritativeState, which a registered instance
never reaches, so a batched instance skipped the teleport that re-enabling a drifted axis requires.
It now runs from PrepareBatchedDeltaEntry as well.

That same check assigned its per axis result instead of accumulating it, so re-enabling X and Z
together discarded X's out of range result when Z was in range. It also assigned the outcome over
NetworkTransformState.IsTeleportingNextFrame, which could clear a teleport already pending for the
tick. Both are now or-ed. This one is not specific to the batched path: it is in develop-2.0.0 and
develop-3.x.x as written.

TransformDeltaEntry.ForceState is removed. It was read in three places and never set, because every
caller that forces a full state update is ticking a nested instance and a nested instance is never
registered for batching.

NetworkTransformSyncModeParityTests gains a state recorder so a teleport is asserted by the flag
that arrives rather than by where the object ends up, which interpolation reaches either way. Both
tests fail on all four fixtures without these changes.
ClearForNextTick had one production call site, inside OnUpdateAuthoritativeState, which a registered
instance never reaches because the tick loop skips anything the batched delta check already handled.
So in batched mode the change flags were never reset: any axial group that changed once kept its
Has*Change bit for the life of the instance and was serialized on every state update from then on.

Measured at 200 instances over 60 ticks, half float position and a compressed quaternion, with scale
registered and never touched: 100% of entries carried HasScaleChange and paid 6 bytes for a scale that
had not moved since spawn. The per instance path, which does clear, costs the same with scale
registered or not.

This is the third defect of the same shape, so rather than a second copy of the block the clear is now
ClearStateForNextTick, called from OnUpdateAuthoritativeState and from PrepareBatchedDeltaEntry. The
per instance side is a pure extraction: same condition, same body, same order relative to the axis
check and the delta check.
…onfig

The project wide setting reached NetworkConfig through an IProcessSceneWithReport, which cannot reach
a scene placed NetworkManager in play mode. NetworkManager.OnEnable moves itself into the
DontDestroyOnLoad scene, and it does so before the callback runs, so the scene the callback is handed
no longer contains the object it is looking for. It applied to zero NetworkManagers every time and
nothing reported that, because the setting it read was correct and only the write was missing. A
build was unaffected, since there the callback runs at build time against the authored scene. Its
sibling SetInScenePlaced survives the same callback only because NetworkObject sets InScenePlaced at
runtime as a fallback, which is why this is the first place the callback's limit was visible.

TransformSyncMode is now public on NetworkConfig and authored per NetworkManager under "Transform
Synchronization" in the inspector. It is serialized by the same mechanism as every other NetworkConfig
value, so scenes, prefabs and builds all carry it with no build pipeline involvement. That also
removes the prefab processor, which ran on import and therefore baked whatever the setting happened to
be the last time a prefab was imported.

Being public also lets a project offer the mode as a pre session choice instead of an editor round
trip. NetworkManager captures the value into ActiveTransformSyncMode when it starts and everything on
the send and receive paths reads that, so a write during a session cannot leave already spawned
instances registered under one mode while new ones use the other, and cannot move the connection
configuration hash out from under a client that is still joining.

NetworkTransform's inspector no longer hides UseUnreliableDeltas under the batched mode. The mode
belongs to the NetworkManager that will run the instance, which a prefab cannot know, and the runtime
already ignores the value while batching.

NetworkTransformSyncModeConfigurationTests covers the delivery that nothing covered before: every
other fixture reaches the mode through the same assignment the integration test harness makes, so a
delivery path that never ran left them all green.
Six comments asserted something that was not true. None of them fail a test, and none are visible from
the code they sit next to, which is what they have in common: every one is a claim about lifetime,
ordering, or the other implementation rather than about the lines below it.

- NativeInterpolator.ResetTo claimed the managed implementation seeds a baseline measurement and this
  one does not. Neither seeds one. The divergence it spent thirty lines explaining does not exist, and
  the explanation that does belong lives once, on BufferedLinearInterpolator.ResetTo.
- NetworkTransformStateManager said Register and Deregister are the only places the collections change.
  There are two independent sets, and the interpolation set changes in its own pair of methods.
- The WriteBatch guard said only the server registers instances for batching, which contradicts
  RegisterForBatchedStateTracking. A client authority does register. What is client-server only is the
  queuing, and a distributed authority session queues nothing at all.
- The handle allocator field said a handle is assigned to every synchronized instance. Only in batched
  mode.
- DetectTransformDeltaJob said every caller forcing a full state update ticks a nested instance. There
  is one such caller and it is gated on SwitchTransformSpaceWhenParented, which does not exclude an
  instance from batching. It runs from CommitDetectedState after the job, which is the real reason the
  job never needs to be told to force.
- Two test remarks described designs that had moved: the interpolator reset test described the bug in
  the present tense, and the state baseline test attributed the ReliableSequenced derivation to
  NetworkSerialize rather than to UpdateReliability.

The rest is verbosity. Remarks that ran to a paragraph per method are now one statement per line,
big-O notation is replaced by what actually happens, and the justifications for why the code is
correct are gone where the rule alone is enough.

serverTime is dropped from the internal interpolator reset path. BufferedLinearInterpolator.ResetTo
forwards it into an AddMeasurement call it does not make, so nothing reads it in either implementation.
The managed signature is public API and keeps it, with a note to obsolete it later.
@NoelStephensUnity
NoelStephensUnity marked this pull request as ready for review August 30, 2026 19:49
@NoelStephensUnity
NoelStephensUnity requested a review from a team as a code owner August 30, 2026 19:49

@u-pr u-pr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Harness Review

Needs changes

The new session-level batching paths introduce concrete edge-case failures in startup validation and state dispatch when runtime callbacks mutate registrations.

Reviewed commit 2c7322f

🤖 Helpful? 👍/👎

Comment thread com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
NetworkTransform.cs and NativeInterpolator.cs picked up CRLF when the
interpolation render time up-port was merged. A merge writes the result
straight into the index, so the .gitattributes eol=lf normalization that
runs on add never got a chance at them.

- PVP-121-2: both files back to LF.
- PVP-124-2: the three lines with trailing whitespace.
- PVP-150-1: TransformSyncModes used <see></see> rather than <see/>.
…register

Both raised by the automated review on PR #4123.

GetConfig caches, and ClearConfigHash only ran on shutdown, so a GetConfig
call made before the session started cached a hash built from the default
ActiveTransformSyncMode. Initialize then captured the authored mode and the
server compared connecting clients against the stale hash, rejecting them
for a configuration mismatch. Clear the cache where the mode is captured.

RunDeltaCheck iterates by index so an instance that despawns from within its
own state update callback cannot invalidate the iteration, but Deregister
swaps the last registered instance into the vacated slot and the increment
then stepped straight over it. Its entry was already complete, so it was a
tick of delay for no reason.
@NoelStephensUnity

Copy link
Copy Markdown
Member Author

@u-pr Re-check the PR for the 2 issues you found. Those should be fixed with the recent commits.

@u-pr

u-pr Bot commented Aug 30, 2026

Copy link
Copy Markdown

Both issues have been re-checked and verified as fixed:

  1. Cached Config Hash Invalidation (NetworkManager.Initialize):

    • NetworkConfig.ClearConfigHash() is now called immediately after NetworkConfig.ActiveTransformSyncMode = NetworkConfig.TransformSyncMode; in NetworkManager.Initialize(). This ensures any configuration hash cached before the session started is invalidated and recalculated with the captured active synchronization mode.
  2. Swap-back Index Handling on Self-Deregistration (NetworkTransformStateManager.RunDeltaCheck):

    • RunDeltaCheck() now inspects instance.StateManagerIndex == i after calling ApplyBatchedDeltaEntry(). If an instance deregisters (e.g. via a despawn callback), i-- decrements the loop counter so the entry swapped into index i is processed on the current tick rather than skipped by the increment.

🤖 Helpful? 👍/👎

What the CI standards job's own --fix pass produced.

.editorconfig has dotnet_style_null_propagation and dotnet_style_coalesce_expression
at error, so the two null ternaries had to go. Both are safe against Unity's ==
overload: InvokeBehaviourOnNetworkObjectParentChanged passes either a literal null
or a spawned object whose transform it has already dereferenced, and SpawnMover is
only ever called with no prefab or a real one.

NativeInterpolatorTests no longer uses anything from System.
@codecov-github-com

codecov-github-com Bot commented Aug 30, 2026

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 90.04894% with 122 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...time/Components/Interpolator/NativeInterpolator.cs 83.12% 55 Missing ⚠️
...Runtime/Components/NetworkTransformStateManager.cs 86.59% 39 Missing ⚠️
...cts/Runtime/Components/TransformHandleAllocator.cs 78.72% 10 Missing ⚠️
...Messaging/Messages/NetworkTransformBatchMessage.cs 78.78% 7 Missing ⚠️
...objects/Runtime/Components/NetworkTransformMath.cs 96.74% 4 Missing ⚠️
...s/Runtime/Components/NetworkTransformDeltaCheck.cs 98.93% 3 Missing ⚠️
...ects/Runtime/Components/DetectTransformDeltaJob.cs 88.23% 2 Missing ⚠️
...Components/Interpolator/InterpolateTransformJob.cs 95.45% 1 Missing ⚠️
...e.gameobjects/Runtime/Messaging/MessageDelivery.cs 0.00% 1 Missing ⚠️
@@                Coverage Diff                @@
##           develop-3.x.x    #4123      +/-   ##
=================================================
+ Coverage          78.01%   79.40%   +1.39%     
=================================================
  Files                153      161       +8     
  Lines              26260    28487    +2227     
=================================================
+ Hits               20486    22621    +2135     
- Misses              5774     5866      +92     
Flag Coverage Δ
NGOv2_project_testproject_ubuntu_pinnedTrunk 78.43% <90.04%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...code.gameobjects/Runtime/Components/HalfVector3.cs 86.56% <100.00%> (+3.00%) ⬆️
...ponents/Interpolator/BufferedLinearInterpolator.cs 86.61% <100.00%> (+0.29%) ⬆️
...objects/Runtime/Components/NetworkDeltaPosition.cs 100.00% <100.00%> (+28.88%) ⬆️
...gameobjects/Runtime/Components/NetworkTransform.cs 91.80% <ø> (+3.37%) ⬆️
...objects/Runtime/Components/QuaternionCompressor.cs 100.00% <100.00%> (ø)
...gameobjects/Runtime/Configuration/NetworkConfig.cs 63.63% <100.00%> (+0.51%) ⬆️
...netcode.gameobjects/Runtime/Core/NetworkManager.cs 80.13% <100.00%> (+0.35%) ⬆️
...meobjects/Runtime/Messaging/ILPPMessageProvider.cs 58.13% <ø> (ø)
...Components/Interpolator/InterpolateTransformJob.cs 95.45% <95.45%> (ø)
...e.gameobjects/Runtime/Messaging/MessageDelivery.cs 7.01% <0.00%> (-0.13%) ⬇️
... and 7 more

... and 4 files with indirect coverage changes

Components Coverage Δ
com.unity.netcode.gameobjects 79.40% <90.20%> (+1.39%) ⬆️

ℹ️ Need help interpreting these results?

NetworkTransformStateSerializationBaseline failed on both Ubuntu jobs, on Mono
and on IL2CPP, with the same two mismatches and the same actual hashes:
QuaternionSync.Full and QuaternionSync.Teleport. Byte length was unchanged in
both, so it was the payload and not the format.

Those two are the only cases that put an uncompressed quaternion on the wire,
and the fixture built it with Quaternion.Euler. That is native trig, and Linux
lands a ULP away from Windows where the baseline was recorded. Compressed and
half float quaternion quantize the difference away, and the euler cases write
the RotAngle literals, which is why the other 23 agreed.

Not the serializer: FullPrecision.AllAxes writes nine raw floats and passed, so
float serialization is byte identical on both platforms.

Rotation is now literal components, so every input to the matrix is a literal or
a managed function of one. Re-recorded the four QuaternionSync signatures; the
lengths are unchanged and the other 21 are untouched.
The why sat in a sixteen line block inside the internal overload's body, while
NativeInterpolator.ResetTo pointed at the public overload's remarks for it. Moved
it to where that reference already aims and left a one line marker on the call.

Dropped the proof that the resulting state is safe and the ownership transition
scenario. Both are readable from Clear and InternalReset, or belong in the PR.
Simplified the TransformHandleAllocator class comments.
Verbosity and clarity pass for the tests added.
Clean up and organization.
Minor PR number ordering within Fixed.
Brings in #4132 and #4135 now that both have landed on develop-3.x.x.

Two conflicts, both resolved in favour of this branch:

NetworkTransform.cs. #4135 moved UpdateInterpolation from LocalTime to
ServerTime, but this branch hoists that work into the static
RefreshInterpolationFrameData, where the same change was already mirrored by
hand when the up-port was first merged. Develop's per-instance rigidbody delta
time selection is also already handled here, per instance in UpdateInterpolation
out of InterpolationFrameData, which is where it has to stay since
m_UseRigidbodyForMotion is not a per session value. Develop's other changes to
the file (the five lerp smoothing doc updates and the GetTickLatencyInSeconds
duration fix) merged cleanly and are present.

CHANGELOG.md. Kept this branch's entries; the result stays in descending PR
order.
EditorGUILayout.PropertyField(m_TransformSyncModeProperty, new GUIContent("Mode"));
if (m_NetworkManager.NetworkConfig.TransformSyncMode == TransformSyncModes.Batched)
{
EditorGUILayout.HelpBox(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we deprecate UseUnreliableDeltas, then we can remove this, but if we do not then we have to make sure users know this (even though I doubt there are many users actually using that feature).

EditorGUILayout.Space();
EditorGUILayout.LabelField("Delivery", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_TickSyncChildren);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto on the notes, if deprecated this would change and no need to call it out.

/// Checked before sending so a client that observes none of this tick's updates gets no message at all
/// rather than one containing a count of zero.
/// </remarks>
private bool HasAnythingFor(ulong clientId)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not happy with this name.
Perhaps HasObservedChangesPending or HasObservedDirtyStates...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants