Skip to content

plot-ws/plot-coop-starter

Plot co-op starter

License: MIT Docs Discord CI PRs welcome

Plot co-op starter

A minimal, fully working starter for building co-op (players-vs-the-game) experiences on Plot with an authoritative server.

The server is the star: it owns the enemies, their movement, all damage resolution, and the shared base. Clients do almost nothing — they send WASD movement intents and render the world the server snapshots each tick. This is the foundation for co-op survival / PvE games (wave shooters, tower defense, horde survival) where you never want the world's truth to live on a client.

It is intentionally lean. Everything in it works; nothing is stubbed.

What's in the box

src/logic.ts        Pure game math — no @plot imports, unit-testable
src/handler.ts      The authoritative room (onJoin/onMessage/onTick/onLeave)
src/main.ts         Canvas client: predict self, interpolate others + enemies
src/logic.test.ts   Vitest tests for the pure logic
vendor/             Vendored @plot/client and @plot/handler

Run it

npm install
npm run dev

Then open two browser tabs at the dev URL — both join room COOP1, so you will see each other's player and the same server-driven enemies. Move with WASD. Stand near an enemy to auto-damage it; let one reach the base and the base HP drops.

You need either:

  • a VITE_PLOT_APP_KEY from plot.ws (hosted), or
  • a local Plot server via VITE_PLOT_API_URL (e.g. http://localhost:8787).

Set them in a .env.local file:

VITE_PLOT_APP_KEY=your_app_key
# or, for local development:
VITE_PLOT_API_URL=http://localhost:8787

The pattern

  • Authoritative onTick simulation. handler.ts advances the world 20 times a second via the pure stepWorld in logic.ts: it spawns enemies on a tick counter (never wall-clock time, so it stays deterministic), walks them toward the nearest player or the base, applies player auto-fire, and removes enemies that die or reach the base.
  • Client prediction. The client predicts only its own players.<id>.pos, so local movement feels instant and is reconciled against the server.
  • Interpolation. Other players (players.*.pos) and all enemies (enemies.*.pos) are interpolated for smooth motion between server snapshots.
  • Snapshot rendering. baseHp and score are read straight from room.currentState — the authoritative truth.

Extend this

  • Waves & a director: spawn in batches, gate on a clear, escalate difficulty.
  • Weapons & abilities: add input kinds beyond move and resolve them in onMessage / onTick.
  • Win / lose conditions: end the round when baseHp <= 0 or a wave goal is met, and broadcast a game-over event.
  • More enemy types: give enemies a kind, with different speed/HP/damage.
  • Leaderboards & persistence: the handler context exposes leaderboard, save, and profile.

Vendored @plot/*

@plot/client and @plot/handler are vendored under vendor/ and referenced as file: dependencies so the starter runs out of the box. Once the packages are published, replace the file: entries in package.json with normal npm versions:

npm i @plot/client @plot/handler

Deploy

Build the client with npm run build (output in dist/) and host it as static files anywhere (any static host / CDN). The authoritative room handler in src/handler.ts runs on Plot's infrastructure — deploy it through your Plot app. Point the client at your app via VITE_PLOT_APP_KEY.

License

MIT — see LICENSE.

About

Plot co-op starter — authoritative shared-world template for co-op / PvE games. Vanilla TS + Vite.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors