add Box3D-powered Bad Movers game
All checks were successful
build / image (push) Successful in 1m39s

This commit is contained in:
Syncer Deploy
2026-08-31 12:32:39 -04:00
parent 147baf6b13
commit 38ac419204
18 changed files with 3320 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
# Syncer
Three playable browser games built on one generic TypeScript multiplayer higher-order configuration. The engine runs authoritative servers, predicted clients, clocks, reconciliation, validation, privacy-aware replication, and bandwidth-budgeted interest management around game-supplied rules.
Four playable browser games built on one generic TypeScript multiplayer higher-order configuration. The engine runs authoritative servers, predicted clients, clocks, reconciliation, validation, privacy-aware replication, pluggable physics, and bandwidth-budgeted interest management around game-supplied rules.
## Structure
@@ -10,7 +10,7 @@ apps/
web/ React + Vite predicted client
packages/
engine/ Generic clocks, authority, prediction, validation, and protocol
shared/ Game definitions: Sync Arena, Flux Relay, and Syncer Royale
shared/ Game definitions: Arena, Flux Relay, Royale, and Bad Movers
```
## Play
@@ -20,13 +20,14 @@ npm install
npm run dev
```
Open `http://localhost:5173`. Use the selector to switch between three games:
Open `http://localhost:5173`. Use the selector to switch between four games:
- **Sync Arena** — click **Deploy**, then use `WASD`, `Shift`, mouse look, left click, `R`, and weapon keys `1``3`.
- **Flux Relay** — hold `Space` or the on-screen thruster to pull the shared core toward your team's gate while managing private energy.
- **Syncer Royale** — click **Drop In**, then use `WASD`, `Shift`, mouse look, left click, and `R`. Survive 31 server bots, loot automatically, and stay inside the shrinking circle on a streamed 2 km island. Open it directly at `http://localhost:5173/#royale`.
- **Bad Movers** — use `WASD`, `Shift`, hold `Space` to grab, `E` to throw, and `F` to close your truck. Real Box3D rigid bodies run in WebAssembly on both the authority and predicted client. Open it directly at `http://localhost:5173/#movers`.
The arena includes pickups, armor, three weapons, headshots, reloads, respawns, a scoreboard, filtered spatial sound, and killcams; its server bots are disabled for player-only office matches. Flux Relay is intentionally unrelated to a shooter: it has teams, a shared core, balanced server bots, exact private energy, per-viewer events, and no weapons, visibility cones, or 3D physics. Royale proves the same engine can drive a much larger world: the map is generated client-side from a public seed, while players, private loot, combat, and the storm remain server authoritative.
The arena includes pickups, armor, three weapons, headshots, reloads, respawns, a scoreboard, filtered spatial sound, and killcams; its server bots are disabled for player-only office matches. Flux Relay proves the API is not shooter-specific. Royale proves it can drive a much larger generated world. Bad Movers proves a developer can attach a third-party deterministic physics backend while keeping plain serializable state, authority, prediction, reconciliation, and transport generic.
## Define a game
@@ -89,7 +90,23 @@ const protocol = game.protocol;
`createJsonCodec()` is included for prototypes and low-volume messages; games can replace it with custom packed binary codecs without changing any rules. `defineNetworkedGame()` remains available as the lower-level surface used internally and by advanced integrations. Every existing HOF—lag compensation, time travel, and replay transport—accepts the result of either definition API.
Flux Relay in `packages/shared/src/flux-game.ts` is the compact reference implementation. Both games are mounted by the same `hostNetworkedGame()` transport adapter, demonstrating that the server loop has no shooter knowledge.
Flux Relay in `packages/shared/src/flux-game.ts` is the compact reference implementation. Every game is mounted by the same `hostNetworkedGame()` transport adapter, demonstrating that the server loop has no game-specific knowledge.
## Attach a physics backend
`definePhysicsBackend<State>()` defines the lifecycle for a native, WASM, or JavaScript physics plug-in. The backend owns runtime handles while the game state stays serializable for snapshots and checkpoints:
```ts
const physics = definePhysicsBackend<GameState>()({
metadata: { name: "My Physics", runtime: "WebAssembly" },
initialize: createWorldFromState,
step: stepWorldAndWriteBackState,
reconcile: reconcileWorldToSnapshot,
reset: resetWorldFromState,
});
```
Bad Movers uses this API in `packages/shared/src/movers-box3d.ts` with Erin Catto's Box3D C17 engine compiled to WebAssembly SIMD. Both authority and prediction use a fixed 30 Hz step with four solver substeps; only the server decides damage, scoring, and winning.
## Stream stateful input safely