Files
skills/docs/threejs/r3f/r3f-networking/src/sync/entity-sync/entity-sync.md
2026-05-29 08:20:56 +01:00

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 StateBuffer for remote sources, automatic EntityInterpolator binding.
  • 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 the out_of_order outcome surfaced from StateBuffer.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): boolean
  • collectLocalUpdates(timestamp): EntityUpdateMessage[]
  • getSnapshot(): SyncSnapshot
  • getEntityCount(): 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.