Skip to content

feat(network): Control arrival cushion and starting runahead, and start the match in sync - #664

Open
tintinhamans wants to merge 12 commits into
GeneralsOnlineDevelopmentTeam:mainfrom
tintinhamans:arctic/go/stack2/09-runahead-controller
Open

tintinhamans wants to merge 12 commits into
GeneralsOnlineDevelopmentTeam:mainfrom
tintinhamans:arctic/go/stack2/09-runahead-controller

Conversation

@tintinhamans

@tintinhamans tintinhamans commented Oct 25, 2026 •

Copy link
Copy Markdown

Merge after #663.

A latency formula sizes the run ahead from a round trip, but what stalls the game is whether the commands of the slowest sender arrive before the frame that needs them. The formula also has to guess a start value, and after a loss burst it kept the run ahead high for a long time.

With input_delay.controller (true) the run ahead follows the arrival cushion instead. Every player reports its lowest cushion per sender with its late commands and stall time, and the packet router sums the two directions of each pair and keeps input_delay.margin_frames (1) spare per direction. A baseline from a forgetting quantile of the normal windows is raised only for spikes that recur. With input_delay.fast_recovery (true) the run ahead falls a third of the excess per step after an isolated spike. input_delay.max_frames (64, retail 128) caps it. A slow producer is left out, an outage holds the run ahead, and without a complete pair the formula of the previous PR takes over. Stalls from a screenshot capture do not count: the hold runs up to 15 s from the capture of a gameplay screenshot and again from the start of its delayed upload, 2 s past the end of the upload, and 3 s for the other screenshot types. At match start the router decides the run ahead from every player's lobby round trip, and the load waits for it.

With the controller on, nothing in the game said why the input delay moved, so a log was needed to tell a lag spike from a stall from a start value. The network overlay header now shows whether the input delay is rising, lowering or steady for 10 s after a change, and why (start, lag spike, stall, clean). In the developer view (Shift+F10) the router also sees the controller's last decision: its reason (seed, hold, baseline, cover, uncover, congestion, outage or formula), the lowest pair sum against the 2 x margin_frames it needs and which pair that is, the baseline and cover, the spike rate per minute and median spike size, and the time since the last change. Other players see the last change and "host decides", and with the controller off it says "controller off". The cushion column also turns amber when the 5th percentile cushion is within the margin.

The match start is synchronized on the new NETWORK_CHANNEL_START. Players send READY at two sync points: on the load screen once loading is done (waiting up to 3 s for the direct path), and after local setup with the load screen still up. The router answers with GO and a per-peer delay from the seed latency (at most 250 ms), each stage times out after 10 s, players that were lost are left out, and it is online only. A GO is only accepted from the router, also when it arrives before the router is known. The stock fade-in from black advances only on logic frames, so a start stall showed a black screen.

Wire change: the run ahead metrics message carries the cushion, late count, stall ms and screenshot flag, and the start channel is new. Trade-off: the controller returns to a low run ahead quickly when loss ends, which costs more stall time if loss returns. In the returning-loss rig run the old code stayed at R 56 to 64 and this went back to about 7, but stalled about 30 s of 110 s against 18.7 s. The synchronized start costs about 60 to 80 ms per stage, and relay-only players add up to 3 s. controller and fast_recovery are on by default as in the tester build, and the service can turn them off.

Tested: builds x86 Release z_generals. Rig runs of the full stack, 3 players, 0 CRC mismatches, n=1. On a slow CPU-bound host R stayed at 4 to 5 instead of hitting the old 64 cap. Start sync rig: frame-1 skew went from 1.5 to 2 s down to 6 to 15 ms, no start stalls, the fade takes 0.6 s on both.

@tintinhamans
tintinhamans force-pushed the arctic/go/stack2/09-runahead-controller branch 2 times, most recently from a3681d2 to e3feda5 Compare October 25, 2026 11:15
@tintinhamans tintinhamans changed the title feat(network): Control arrival cushion and starting runahead feat(network): Control arrival cushion and starting runahead, and start the match in sync Oct 25, 2026
@tintinhamans
tintinhamans force-pushed the arctic/go/stack2/09-runahead-controller branch 2 times, most recently from 4c5132e to eb5d0a6 Compare October 26, 2026 15:39
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