Skip to content

packages/net-client ​

Client-side netcode without a DOM: Mirror (a physics twin of every snapshot entity in the local world, a snapshot older than the last applied ignored, and which one the player controls: the character on foot, the car while driving; the doors as the snapshot has them, twins of the items on the ground, the containers and the terminals, a character's role and its body (a dog's twin is a dog's low capsule), the server's inventory on the player's twin (applyInventory, the stored cars too), every car's owner, lock and paint and its key's tag (applyKeys), how far the player has picked a lock (picking)), Predictor (prediction and reconciliation: the server's word held against what the prediction had for the tick it acknowledges, the prediction left standing when they agree and nothing else near is simulated (standing: the car, and the light pieces near it within a step of their quantized pose, QUANTIZED_AGREEMENT), the inputs in flight replayed otherwise, a confirmed prediction kept over a replay that met no other car or person; the crashes and the knocks it feels told as they happen (takeFelt); of the controlled entity; its inputs numbered by the client's clock; the ticks a stall drops, and those a snapshot acknowledges before the page ran them, run on the server's guess, guess and catchUp), InputHistory (the last inputs, sent again in every frame of inputs with the snapshot tick to acknowledge: INPUTS_PER_FRAME, one over a WebSocket and four over a transport that loses frames; the acknowledgement alone every IDLE_ACK_MS while none goes; a gap in the numbers forgets the inputs before it), RemoteIntake (what a decoded frame does to the remotes: the entities it removed dropped first, every one it changed told, a sample pushed only where it measured a pose, at the tick measured, whatever the snapshot no longer lists dropped; the renderer and the netcode probe run the same), RemoteBuffer (interpolation between the server's measurements of one remote entity, drawn behind the render tick by its tier's delay, TIER_DELAY_TICKS, so a far entity lies between two measurements; a stop or a start bounding the motion between measurements to one snapshot of it, motionWindow; an entity at rest held where it was measured, one in motion carried on by its velocity for a while when its next measurement is late), PredictedRemotes (the remote cars and people on foot that may touch the controlled entity drawn at the local time, where their twins stand, blended in and out of interpolation at no more than a third again their pace (blendSeconds: a quarter of a second at least, longer by the gap between the two timelines' poses, measured every frame), their snapshot jumps smoothed; a person the predicted car knocked down goes limp at the hit, Mirror.predictsDown; Mirror.predicts puts an entity on the predicted timeline from as far as its blend needs before a possible touch and keeps it there a blend's length after it is out of reach, the simulated set unchanged), ClientClock (the server's time estimated in real ticks: an estimate held in a band a tick under the largest offset of the last two seconds of snapshots, brought down at 5 % per tick when the path got slower, raised at half a per cent when it got faster, snapped only when a discontinuity persists, so a late snapshot, a stall or its burst never moves the render tick and the jitter never moves it either; the render tick the interpolation delay behind it, never clamped to the newest snapshot; the tick-rate nudge; frameTicks, the ticks a frame runs, three at most) and SmoothingOffset (the decaying visual offset that hides corrections, a correction past what the decay can hide at the body's pace taken at once: snapsAt) and PredictedPieces (the level's light pieces near the player drawn where the local world has them from the tick they come loose, blended with their snapshots' drawing at the cars' pace bound). See the Netcode guide.

The local world simulates only what it predicts. On every snapshot, before the replay, the mirror makes a proxy (@foi/sim's setProxy: a kinematic body resting where its snapshot put it, solid and found by rays, never integrated) of every twin but the controlled entity, the player's character and whatever may touch the controlled entity before the next snapshot (mayTouch: within NEAR_MARGIN plus what both cover at their speeds over the replay and the next snapshot's wait). A step, and a reconciliation's replay, then costs what the player can touch, not the whole crowd. A simulated twin of a car coasts with its wheel held as its snapshot shows (holdWheel); one of a person on foot walks on as its snapshot measures it (walkOn), so a person crossing the predicted car's lane is hit in the local world where the server hits it. Which remotes are drawn at the local time (predicts) is a wider set, decided on the same snapshot from the lead that snapshot's reconcile leaves (Predictor.pendingAfter): those that may touch the controlled entity within their blend's length (blendHorizon) and that horizon, held for a blend's length after; a proxy among them is drawn at its measurement carried on by its velocity and spin for the lead (presentPose).

mirror.test.ts and predictor.test.ts run against a second in-process "server" world, which is also how the integration test in apps/server builds its bots; mirror.test.ts pins which twins are proxies (and that one never moves on its own and follows its snapshots), clock.test.ts the clock's contract (a 300 ms stall and its burst without a leap or a halt, jittered arrivals with the earliest rare within half a per cent of real time after the first window, a persistent discontinuity snapped to once within a window and a one-off early snapshot never, the path's delay growing by four ticks absorbed at 5 % and shrinking by four shed at half a per cent, a local clock drifting 0.05 %, a paused tab, a join with the level's build) and the cap on the ticks a frame runs and the ticks it drops, counted; remote-buffer.test.ts the tier's delay (a far entity measured every twelve ticks interpolated at every frame, never guessed on), its slew of five per cent a tick, the hold at rest and the motion windows; intake.test.ts the one rule of a frame's news; crash-correction.test.ts drives the player's car into a wall at 80 km/h over a simulated network at five round trips and holds every correction through the crash under 5 cm (46 cm at fifteen inputs in flight and 112 cm at twenty-one, when every snapshot replayed the crash on the manifold of its own future); predicted-pieces.test.ts knocks a hydrant loose at 80 km/h on test-breakables, the prediction on the server's tick, felt once, drawn at the local time from it and standing through it, and rewinding when the server's word on the piece is a centimetre off; predicted.test.ts runs a server world and a client over a simulated network and measures a ramming chaser drawn interpolated (metres short of the car it pushes) and predicted (touching it), holds the blend between the timelines to a third again the car's pace in and out (a 1.4 m gap at 12 m/s in 0.35 s, the car never drawn faster than 16 m/s) and a car jittering about the zone's edge to one flip in and one out; mirror.test.ts pins the horizon the predicted timeline is entered from (the closing speed over the blend's length, the replay and a snapshot's wait) and the hold after leaving it.