2.7 KiB
entity-sync
(no submodules)
Purpose
Top-level manager for networked entity state. Tracks every entity by id,
buffers remote-peer history for interpolation, samples local entities at the
configured send rate, and exposes the diagnostic snapshot needed by HUD
overlays. Hot-path methods stay allocation-free except for the result array
returned by collectLocalUpdates.
Features
- Per-entity
StateBufferfor remote sources, automaticEntityInterpolatorbinding. - Per-frame
updateRemoteEntities(serverTime)walks every remote and writes the interpolated transform back to the entity record. shouldSend(currentTime)gates outbound messages against the configured send rate.- Records
r3f_net.snapshots_total{outcome}for every receive, including theout_of_orderoutcome surfaced fromStateBuffer.push. - Zod-validated options; bad input throws
R3F_NET.INVALID_OPTIONS.
Usage: Basic Example
import { EntitySyncManager } from "@suaveplan/r3f-networking";
const sync = new EntitySyncManager({ sendRate: 20 });
sync.registerEntity("e1", "me", true);
sync.registerEntity("e2", "peer", false);
sync.receiveUpdate({
entityId: "e2",
transform: { position: [1, 0, 0], rotation: [0,0,0,1], scale: [1,1,1] },
timestamp: 1000,
seq: 1,
});
sync.updateRemoteEntities(1100);
Usage: Advanced Example
Drive sync from a transport receive callback and a per-frame R3F hook:
transport.onMessage((peer, env) => {
const msg = decodeUpdate(env.payload);
sync.receiveUpdate(msg);
});
useFrame((state) => {
const t = state.clock.elapsedTime * 1000;
sync.updateRemoteEntities(t);
if (sync.shouldSend(t)) {
for (const m of sync.collectLocalUpdates(t)) transport.broadcast(m);
}
});
API Reference
new EntitySyncManager(config?: EntitySyncConfig)registerEntity(id, ownerId, isLocal, initial?)/unregisterEntity(id)receiveUpdate(message: EntityUpdateMessage)updateRemoteEntities(serverTime: number)updateLocalEntity(id, transform)shouldSend(currentTime): booleancollectLocalUpdates(timestamp): EntityUpdateMessage[]getSnapshot(): SyncSnapshotgetEntityCount(): number/clear()
Implementation Notes
The send-rate gate uses wall-clock time on each frame rather than a setInterval
so missed frames cannot accumulate. Remote out_of_order errors are swallowed
and reported through the OTel counter — surfacing them as exceptions would
cascade-kill the consuming r3f frame loop on transient jitter. The
enablePrediction flag is consumed by hooks that wire ClientPredictor
externally; the manager itself does not predict.