Repository navigation
fix: network clock adjustment overshoot and per-frame NetworkManager scene search - #4195
NoelStephensUnity wants to merge 16 commits into
Conversation
…ing past it Once a client's clock reached the offset it adjusts towards, each frame stepped the full AdjustmentRatio past it and back, so LocalTime and ServerTime alternated between running fast and slow every frame.
…r them every frame UnifiedUpdateConnections called Object.FindObjectsByType<NetworkManager>() on every update, whose cost grows with the number of MonoBehaviours in the scene: about 1.5 ms a call with 10,000 and 14 ms with 50,000. NetworkManager.Instances is filled in Awake and emptied in OnDestroy, and the system visits that.
|
Check the configuration's TOML syntax, permitted settings, and size limit. See the error below. Error message: Configuration content:# This is the configuration for the u-pr bot
# https://github.cds.internal.unity3d.com/unity/u-pr
# For configuration of this file:
# https://developer.portal.internal.unity.com/catalog/default/component/u-pr/docs/configuration/automatic_runs/
[github_app]
handle_pr_actions = ['opened', 'ready_for_review'] # PR events that auto-run pr_commands
pr_commands = ["/harness-review"] # what runs on those events
reviewer_commands = ["/harness-review"] # what runs when @u-pr is added as reviewer
|
UnifiedUpdateConnections now runs only in client and server worlds that hold the NetworkManager they were created for, and updates that NetworkManager alone. This replaces the NetworkManager.Instances list, which a derived NetworkManager declaring its own Awake never entered.
Codecov ReportAttention: Patch coverage is @@ Coverage Diff @@
## develop-3.x.x #4195 +/- ##
=================================================
- Coverage 76.11% 76.04% -0.07%
=================================================
Files 159 159
Lines 27333 27338 +5
=================================================
- Hits 20805 20790 -15
- Misses 6528 6548 +20
|
|
@u-pr please review again |
Review SummaryThe shift to binding the A few observations and suggestions: Observations & Suggestions1. Null / Destroyed
|
…uilds Netcode for Entities cannot create a single world host in a dedicated server build, which every hybrid server used. A build for only one side now logs an error when asked to start the side it does not support.
EmandM
left a comment
There was a problem hiding this comment.
A couple of nit-pick comments but this looks fantastic! I really, really like this approach!!
| /// </summary> | ||
| internal struct HybridNetworkManager : IComponentData | ||
| { | ||
| public UnityObjectRef<NetworkManager> Value; |
There was a problem hiding this comment.
Oh I LIKE this!!
Though a deep nit-pick, maybe Value isn't the best name here? It's very vague.
There was a problem hiding this comment.
Good call, Value didn't say much. Renamed it to NetworkManager, so it is now .NetworkManager.Value
| m_CurrentLocalTimeOffset += deltaTimeSec * (m_DesiredLocalTimeOffset > m_CurrentLocalTimeOffset ? AdjustmentRatio : -AdjustmentRatio); | ||
| m_CurrentServerTimeOffset += deltaTimeSec * (m_DesiredServerTimeOffset > m_CurrentServerTimeOffset ? AdjustmentRatio : -AdjustmentRatio); | ||
| // Stop on the desired offset rather than step past it, or a converged clock alternates between running | ||
| // fast and slow by the whole ratio every frame, and everything rendered from it judders. |
There was a problem hiding this comment.
nit: I don't understand this code comment. It wouldn't help me know what's going on here if I ever needed to interact with this code.
There was a problem hiding this comment.
That is a fair point. The old comment described the symptom rather than what the code does. It now reads:
Move each offset toward its target by at most AdjustmentRatio of this frame's delta, stopping once it gets there. Stepping the full amount every frame overshot the target, so a synced clock alternated between running fast and slow, and anything driven by it juddered.
…updates The hybrid world outlives its NetworkManager if ShutdownInternal throws before disposing it, so UnifiedUpdateConnections now disconnects every connection and announces none instead of throwing each frame. HybridNetworkManager.Value is renamed to NetworkManager, the clock adjustment comment says what the clamp does, and the unused Unity.Entities using that failed the Standards check is removed.
|
@u-pr please review again. |
Review SummaryThe recent updates nicely address the previous feedback:
A couple of minor observations and suggestions: Suggestions1. Repeated
|
…ssure theory Revert before merge.
A client drains its incoming queue while it disconnects, after the unified transport has removed the server connection, so a queued TimeSyncMessage made GetCurrentRtt throw KeyNotFoundException. It now returns 0 for a connection it no longer has, as UnityTransport does.
… whether 6000.7.0b6 raised memory use Replaces the b1.xlarge test. Revert before merge.
…ed test pass The IL post-processor and compiler processes the editor starts stay resident through the test pass and, on a b1.large agent, leave too little memory for the hybrid tests. The project is now imported and compiled in its own batchmode session that exits first, any build process that outlives it is stopped, and the job log lists the largest processes left before the tests start. Also drops the temporary 6000.7.0b4 editor pin.
The test runner compiles in Release. An editor whose code optimization preference is Debug builds the other configuration in the warm-up, and the test session then recompiles every assembly and restarts the IL post-processor. CI's agents already default to Release, so this is a guard rather than a fix there; it removed the recompile on a machine set to Debug.
…d a negative clock step While its NetworkManager is shutting down, UnifiedUpdateConnections now disconnects only the connections that still have ConnectionState, which it removes, so OnNetCodeDisconnect is not invoked again every frame for the same connections. It also stops collecting new connections while shutting down and drops an unused local. NetworkTimeSystem.Advance no longer throws when the delta time or AdjustmentRatio is negative.
|
🤖 Point 1 was already fixed: a destroyed or missing NetworkManager is treated as shutting down. Point 3 is applied: a negative step no longer reaches |
…created Every NetworkManager that starts a hybrid session creates a UnifiedBootstrap that is then discarded, and its finalizer cleared the static LastCreatedWorld and Instance from the GC finalizer thread. When one ran while another session was starting, Initialize found its new world null, logged "World is null!" and returned false: the NetworkManager had no world, its transport threw a NullReferenceException on start, and the created world was never disposed. The finalizer is removed and Initialize checks the world it created rather than the static.
|
@u-pr please review again. |
Review SummaryThe latest updates bring substantial stability and correctness improvements:
Suggestions1. Mutually Exclusive Disconnect Queries during Shutdown
🤖 Helpful? 👍/👎 |
| { | ||
| // Only in-game connections have ConnectionState, and removing it here means each one is only reported once. | ||
| foreach (var (networkId, entity) in SystemAPI.Query<NetworkId>().WithAll<ConnectionState>().WithEntityAccess()) | ||
| { |
There was a problem hiding this comment.
If a connection loses its NetworkStreamConnection during the same frame that isShuttingDown is true, it matches the disconnect query at line 58 (WithNone<NetworkStreamConnection>). Because commandBuffer.Playback has not occurred yet, the entity still has ConnectionState in the EntityManager, so it will match this query as well and invoke OnNetCodeDisconnect a second time.
Consider requiring NetworkStreamConnection here so the two disconnect queries remain mutually exclusive:
| { | |
| foreach (var (networkId, entity) in SystemAPI.Query<NetworkId>().WithAll<ConnectionState, NetworkStreamConnection>().WithEntityAccess()) |
🤖 Helpful? 👍/👎
Purpose of this PR
Four small, independent fixes, plus a change to the Unified CI job.
Network clock adjustment overshoot: without this fix, a client's clock keeps alternating between running slightly fast and slightly slow on every frame, even after it has synchronized with the server. Interpolated objects then judder (a periodic motion artifact caused by mismatched frame rates): they move in uneven steps instead of smoothly. It is barely noticeable at the default
NetworkTimeSystem.AdjustmentRatio, and clearly visible at higher values.Per-frame NetworkManager scene search: without this fix,
UnifiedUpdateConnectionssearches the whole scene forNetworkManagerinstances every frame, in every Entities world, even when there is no hybrid session, so the frame time grows with the size of the scene (about 1.5 ms per frame with 10,000 MonoBehaviours). It now only exists in client and server worlds, and only runs in a world created for a hybrid session, where it updates the connections of that world's ownNetworkManager.Hybrid mode in dedicated server builds: Netcode for Entities cannot create a single world host in a dedicated server build, which every hybrid server used. A dedicated server build now creates a server-only world, and a build for only one side logs an error when asked to start the other side.
RTT lookup after a hybrid connection is removed: a client that disconnects could still process a queued
TimeSyncMessageafter the unified transport has already removed the server connection. This edge case would throw aKeyNotFoundException.GetCurrentRttnow returns 0 for a connection it no longer has, asUnityTransportdoes.(The last three are excluded from the changelog since the unified feature set has not yet been released)
Unified CI job: on a
b1.largeagent, the IL post-processor and compiler processes would hold about 1.5 to 2 GiB due to the required script rebuild. They continued to hold that memory throughout the tests, which could take, cumulatively, up to 50 minutes, with some tests failing on clients not connecting.This job now runs a "warm-up" headless editor pass first so the project's Library can be built; that editor instance then exits (so the IL post-processor and compiler processes are terminated), and the test runner pass can start and run without that additional overhead. With the warm-up adjustment, the same job passes in about 14 minutes.
Jira ticket
None.
Changelog
NetworkTimeSystem.AdjustmentRatio, and clearly visible at higher values.Documentation
Testing & QA (How your changes can be verified during release Playtest)
Functional Testing
Automated tests:
Covered by new automated tests:ClientNetworkTimeSystemTests.ConvergedClockAdvancesAtRealRate,UnifiedNetworkTransformTest.EachWorldUpdatesConnectionsForItsOwnNetworkManager,UnifiedUpdateConnectionsTests.NoWorldUpdatesConnectionsWithoutGhostPrefabsUp-port
Not needed: this targets develop-3.x.x.
Backports
None.