Public: anyone can read this; the crew can change it.

forked from trevato/cs · proposes · 4 behind

The frame's plumbing becomes engine: one pooled scene, filled by a game, allocating nothing #98

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The frame's plumbing is now one module behind the door: `src/engine/scene.ts` owns the container — `createScene` at a `SceneCapacity` builds both buffers whole, `resetScene` puts the counts back, `nextInstance` hands out the next pooled instance and `pushLine` writes one line of two vertices, taking a second colour for the far end so both games' tracers still fade along their length. `Scene`, `Cam…

shipped production is serving it deploy ae07c2d

opened this

The frame's plumbing is now one module behind the door: src/engine/scene.ts owns the container — createScene at a SceneCapacity builds both buffers whole, resetScene puts the counts back, nextInstance hands out the next pooled instance and pushLine writes one line of two vertices, taking a second colour for the far end so both games' tracers still fade along their length. Scene, Camera and Instance moved here from renderer.ts, which now reads them: what a frame is does not need a GL context in the room, and the two games build hundreds of frames without one. The policy at a capacity is stated in the module header, in GUIDE.md's new scene section and in both games' constants, and it is the same in both directions: a full frame draws less rather than growing — nextInstance answers null, pushLine answers false, and a line is never written as half a line.

Both scene modules lost their pool arithmetic and kept every decision: who is drawn and in what order, how far a duck squashes a player, how a tracer fades, which gun is in your hands, which plate is dim. The shooter's capacities are 32 instances (the level, nine others in the 5v5 main.ts starts, and the bomb) and the 4,096 line vertices it always had; the range's are 16 and 2. The colour of a line is written into a scratch tint on its way to the pool rather than allocated, which is the one shape change worth reading — src/client/scene.ts's effect loop is where to look.

src/engine/scene.test.ts is the container's own test, and src/frames.test.ts at the root is the proof the games use it: two hundred frames of each, busy ones and quiet ones, then the instance array and the line buffer asserted to be the same objects and the same lengths as before the first frame, and each game asked for more than its pool holds. src/client/scene.test.ts, src/range/scene.test.ts, src/client/replay.test.ts and src/range/pin.test.ts are untouched and green; boundary.test.ts's written-out surface gained the four names and the type, and purity.test.ts and guide.test.ts are green. 519 tests pass. Neither ceiling moved: app 70,590 → 71,186 raw and 32,870 → 33,125 gzipped, range 26,658 → 27,234 raw and 11,456 → 11,655 gzipped — the container ships whole to a game that uses any of it.

What I chose not to do: no sorting, no culling and no batching went in with the plumbing, and nextInstance does not clear the instance it hands back — the pooled object arrives with last frame's numbers and the game writes every field it reads, which is what both games already did. In the preview, play a round at / (tracers, sparks, blood and the planted bomb's beam are the line buffer) and a run at /range.

🤖 Generated with Claude Code

Closes #78

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs