Publish the 51 prefabs gmx used to bundle - #4
Draft
opera-jimmym wants to merge 6 commits into
Draft
Conversation
These shipped inside the gmx binary. A catalog prefab is the same code reached by version instead of by release, so a project pins what it composed against rather than tracking whatever gmx last built. Each installs under `prefabs/<ns>/` with its symbols renamed, and is called as `::<ns>::thing()` — namespacing is what lets two libraries own a name like `state` without colliding. Each carries the manifest, a README, a LICENSE, a `project.toml` naming its demo rooms, and an `obj_demo`/`rm_demo` pair excluded from what a consumer receives. `exclude` is a top-level key. Written under a `[tuning]` or `[content]` header it parses as a knob instead, and leaks into the generated GML as a tuning value that excludes nothing.
`install = "copy"` rather than namespaced: these land in the project root and the project owns them from the moment they arrive. A file the project already has is never overwritten — the incoming copy goes to `.gmx/incoming/` — and rooms are appended to `[room_order]`. Terrain is a tile layer, not a text file beside one. A second copy of a level's shape is a thing that can disagree with the tiles, so the layer is the only copy: `platformer` and `maze_chase` carry a 47-mask auto-tile tileset, and the shape is edited through `gmx autotile shape`. Bricks and dots stay instances, because something counted or destroyed needs an identity a tilemap cell does not have. `[roles]` names what each object and room is for, which is the only thing that says which object is the game and which room it plays in. `traps` records what fails silently — writing `x` on a physics body, which the solver discards so the object simply never moves.
A deterministic run — fixed seed, scripted inputs — that boots the prefab and asserts the loop it exists to demonstrate. This is what has teeth: it checks behaviour rather than that the bytes did not change, and a golden can only ever tell you output moved, never that it was right. Each boots through the prefab's own title room and presses to start, rather than jumping to the play room. `::ns::` does not resolve inside a chunk — chunks are injected after namespace rewriting — so a chunk that calls into a library dies with a bare "runtime error" naming neither the line nor the identifier. Watch chunks record a running extreme, not the last sample: a check that reads the final frame passes for a player who fell through the floor and came back.
All 108 prefabs already here had one and none of the 51 did, which is the half a browser most wants a picture of. Taking one needs the prefab to run on its own, and that is what found the real gap: five components had no demo at all. Writing one for each turned up a prefab that could not work — `main_menu` declared `[[content.items]]` with no `id`, while `obj_menu` reads `items[i].id` in its Create, so placing it in any room killed it. Nothing had ever instantiated it. Two things a demo has to supply itself: the kernel is not booted, so anything reaching `::kernel::` dies naming a kernel script rather than the caller; and `<prefab>_tuning()` is generated at install, so a demo run from the prefab directory cannot call it. `gmx run` writes capture bookkeeping into the prefab it ran, and `gmx test` bakes `foo.gametest.json` to a `foo.json` of base64 bytecode. Neither belongs in the tree, and the baked one is not in `NOT_PAYLOAD`, so it installs into every consumer project.
The repo README was one line, which is how the imported set diverged without anyone noticing. It now records what a prefab carries, what each manifest field is for, and — the part that was wrong everywhere — how publishing actually works. Two different tags are involved and shipping one of them publishes nothing. Consumers load `prefab-index.json` at the newest tag with no `<prefab>@` prefix, so without a new `vX.Y.Z` a consumer sees the previous release's index however many per-prefab tags were pushed. `git push --tags` uploads tags and not the commit they point at, and a tag on a branch that never merged keeps resolving, quietly, to the wrong tree. Also drops `online_leaderboard`'s copy of the gmx scaffold — 878 lines pinned to gmx 0.3.27, teaching eight commands that no longer exist.
opera-jimmym
force-pushed
the
jimmym/bundled-prefabs
branch
from
September 1, 2026 09:34
640301a to
198e649
Compare
A four-lane rhythm game: 120 BPM, one note per beat from beat four after a count-in, arrow keys one per lane, perfect/good/miss judging with score and combo. The chart is an array in `obj_rhythm`'s Create and nothing else has to move with it. Its value beyond a new genre is that it is the only thing in the catalog that reaches GameMaker's headless audio path. A run with no audio device had `audio_play_sound` hand back a negative handle, and `audio_sound_get_track_position` then read 0 every frame — silently, so a rhythm game looked like it was working and simply never spawned a note. The runner's own self-test drives the transport struct directly and never reaches the entry points where that happened, which is why it shipped broken and reported OK. This reaches them through a real game. The eight asserts bite in both directions, and both were checked: all eight pass on a runtime with the fix, and all eight fail without it, `actual 0.0` on every one. Confirmed outside the gametest too — frames 60/150/240/330 of an install on an unfixed runtime are byte-identical at 14386 bytes with the HUD frozen at `0.00 / 8.00s`, so the failure is upstream of the chart entirely. `steps[]` says which runtime it needs and what its own failure looks like, so eight red asserts read as the bug being reported rather than as a recipe to repair. It composes on kernel like every other playable prefab, rather than carrying its own scoreboard. `install = "copy"` puts it in the project root beside whatever else is installed, so a private score and combo would have meant two counters in one project with the HUD showing one of them. Score goes through `kernel_score_add`, which scales by the combo multiplier — the combo is bumped first, so the hit that extends a run is the one that benefits. Kernel's pause had to be turned off explicitly, and that is recorded as a trap rather than left as a comment: it stops the game while the track keeps playing, so the song runs on behind a frozen screen and the chart is never in step again. Nothing errors. Anything driven by audio rather than by frames has the same problem. The song is a real 120 BPM track — kick on the beat, snare off it, a bass line — so the chart is checkable by ear rather than against a test tone. Art and audio are generated placeholders, CC0; the LICENSE is MIT like the other code prefabs, with that note kept underneath.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves the 51 prefabs
gmxused to bundle inside its binary into this catalog, then fixes what that move exposed.The move
gmxshipped 51 prefabs welded into its binary as a second format (recipe.toml+skeleton/) with its own loader — 3.5 MB and 862 files nobody could update without cutting a release. They land here as 21 games and components (install = "copy") and 30 libraries (install = "namespace"), each with a README.What changed after that
The goldens are gone. Each prefab carried an
expected.txthashing its install output. A game project never sees the file, consumers pin a version tag anyway, and the drift it guarded is self-inflicted: the 108 prefabs already here compose through a pinned[parent-link] source = "transition_base@v0.6.4"and have no drift problem, while the imported 51 compose throughrequires = ["kernel"]— a bare name resolved at catalog HEAD. The gate also recorded a bug rather than catching it, reporting 51/51 passing while shipping it.gmx prefab checkno longer needs a golden to run, so the checks worth having — dead[art]slots, stale README claims, the gametests — survive.One real bug.
excludesat under a[tuning]header in 20 manifests, so TOML parsed it as a data knob and it leaked into generated GML (exclude: ["expected.txt"]insidebullet_hell_tuning()).Conformance. A
LICENSEfor all 51 (every other prefab had one; six READMEs cited one that did not exist), acategoryon all 51,[roles]for the nine components missing them, and a regeneratedprefab-index.json— which was stale independently, still carryinginstall: nullon two prefabs. The repo README now documents what a prefab must carry.The level is the tile layer. Three prefabs authored their level in
levels/*.txtand generated room data from it. That is a second copy of the shape; gmx's own model has no room for it.maze_chase's 222 wall instances become an autotiled tile layer —obj_wallhad no GML at all — andpathgridgainspathgrid_block_tilemap. Bricks and dots stay instances, because the game counts them.Nine of the fourteen gametests never passed. They jumped straight to the play room, skipping the title room where
kernel_boot()runs, and crashed on boot whilestepsclaimed they passed. They now enter through the title room. Their frame budgets were guesses too: the runner is real-time and breakout ran 610 frames for a result final at 80. A full sweep is 540s → 206s.Verification
All 14 gametests pass, run the way CI runs them.
gmx prefab checkis clean across all 51, every prefab installs andgmx validates, andgmx prefab reindexis idempotent.Not done
<name>@1.0.0tags are local-only, so these prefabs are unreleased.requires— the actual fix for the drift the goldens proxied.🤖 Generated with Claude Code
https://claude.ai/code/session_01YMkJC93GC8J4kUzTRfh7N1