first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# `entity-sync`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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
|
||||
|
||||
```ts
|
||||
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:
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user