Files
transcience/docs/ROADMAP.md
T
claude 005679f1b5 Fix bullet/ship desync; rework death, escape, arrival and hub awareness
(1) Bullets appeared to trail the ship. Two independent causes, measured with
the new tools/diag_prediction.gd rather than guessed at:
  - ServerRuntime ticked before ClientRuntime, so input sampled on frame N was
    not consumed until frame N+1, leaving the drawn ship a constant one tick
    (4.00px at 240 u/s) ahead of the authoritative one that bullets spawn from.
    ClientRuntime now sets process_physics_priority = -10. Gap on a listen
    server: 4.00px -> 0.10px mean, 0.30px worst.
  - PLAYER_MUZZLE_OFFSET was PLAYER_RADIUS + 6 = 12px against a 13px drawn
    ship, so bullets were born inside the sprite. Regression from the previous
    commit's hitbox shrink; it now derives from PLAYER_VISUAL_RADIUS.

(2) No more timed respawn. A downed player stays down until they ask for the
hub (E), which is an ordinary input -- the server has no "revive me" message.

(3) Escape channel 3s -> 1s, and damage no longer cancels it. An interruptible
channel makes killing the process strictly better than using the button, so a
dropped connection now runs the same channel: the player stays in the world as
linkdead, still killable, and is only released once it completes. Instances
refuse to close while a linkdead body is resolving, or a solo drop would delete
it on the next tick and hand the exploit straight back.

(4) Escape opens an in-game menu: return to hub (routed through the same held-
escape channel, not a new message), disconnect, quit.

(5) Server pushes a roster so the hub shows who is online and which dungeon
they are in. Entering a dungeon grants 2s arrival protection -- invulnerable
AND weapons-cold, since invulnerability alone would make the spawn a free
firing position -- flagged in the snapshot and drawn on every protected ship.

(6) Cleared dungeons hold the party 30s (was 5s) with a visible countdown.

(7) The hub's grey circle was a 100k-HP target dummy that read as scenery. Now
drawn as a bullseye so its purpose is legible.

Protocol version 1 -> 2. 91 tests (was 78); smoke.sh gains a bot that is
SIGKILLed mid-dungeon to prove the disconnect path end to end. check.sh,
test.sh and smoke.sh all pass.
2026-09-03 18:43:19 +02:00

3.9 KiB

Roadmap

Built

MVP requirement State
Top-down 2D twin-stick bullet hell Movement, aim, fire, i-frames, death and respawn; MultiMesh bullet rendering.
Multiplayer with server-side hit validation Authoritative dedicated server; clients send input only; all hits resolved server-side for both players and enemies. No PvP.
Simple, predictable enemies Drifter, Turret, Stalker, plus a lobby dummy. Five movement behaviours, all closed-form.
Stationary boss, adaptable format The Warden of the Fold: four phases, data-defined. A new boss is one function and zero simulation changes.
Lobby hub with dungeon entry Persistent lobby instance, portal, party forming window, dungeons opened on demand.
Emergency escape One-second server-owned channel, cancelled by release but not by damage; a disconnect runs the same channel so quitting is never a cheaper exit.
Leaving and arriving Downed players return to the hub on request (no timed respawn); entering a dungeon grants 2s of invulnerable, weapons-cold arrival protection.
Hub awareness Server-pushed roster showing who is online and which dungeon they are in.
In-game menu Escape opens return-to-hub / disconnect / quit.

91 tests plus an end-to-end smoke test over a real socket, including a bot that is SIGKILLed mid-dungeon to prove the disconnect path.

Next, in rough order of value

1. Make the dungeon a place. Right now it is one arena and three stages. Rooms, doors, and a SimWorld with static geometry — which means adding a collision representation for walls (segment-vs-circle for players, segment crossing for bullets) since there is no physics engine to lean on.

1b. Progression, and what it turns on. Several things are currently sized for "no persistence yet" and should be revisited together with it: death sends you to the hub rather than costing anything, a linkdead body is simply deleted once it channels out (there is no hub state to put it in), and peer_names is client-supplied and trusted for display. All three are fine now and none of them are once a character has anything worth losing.

2. Art and feel. Everything is drawn with draw_circle and a generated dot texture. Sprites, hit flashes, screen shake, muzzle flashes, death effects, and sound. None of it touches the simulation — this is entirely src/view/.

3. A second boss. The format claims to be reusable; the way to find out is to use it. A mobile boss will exercise BossDef.stationary, which the runtime already reads but no content uses yet.

4. Progression. Loot, character stats, persistence. Needs a decision on storage: for a dedicated server, player state belongs server-side in a database, not in a client save file.

5. Netcode hardening. In the order they will actually matter:

  • DTLS on ENetMultiplayerPeer before any public server.
  • Authentication; peer_names is currently client-supplied and trusted for display only, which is fine now and will not be once there is progression.
  • Snapshot delta compression, once enemy counts grow.
  • Interest management inside an instance, once arenas are larger than a screen.

6. Scale. The simulation costs ~1.4% of a core per instance, so the ceiling is bandwidth and process supervision, not CPU. A shard manager that runs several server processes behind a lobby-of-lobbies is the shape, but it is premature until there is a game to fill it.

Deliberately not done

  • Lag compensation. Discussed in NETCODE.md: rewinding for a shooter's view would mean a player who dodged still gets hit, which is the wrong trade for this genre.
  • Pattern-level bullet replication. A large bandwidth win that couples the client to emitter behaviour. Not worth it until bandwidth binds.
  • MultiplayerSynchronizer / MultiplayerSpawner. Right tools, wrong shape for a bullet hell. See ARCHITECTURE.md.