feat: port the four asobi_lua commits that landed after the merge snapshot - #353
Merged
Conversation
Ported from widgrensit/asobi_lua#131 (af391ff), which landed after the asobi_lua snapshot merged into this repo. Entities created by game.zone.spawn kept the binary key shape Luerl decodes, because asobi_zone_spawner merges a template's base_state (and any override table) straight into the new entity while only x/y/type are stamped as atoms. Every atom-keyed consumer in asobi_zone then read the wrong shape - snapshot_entities' `persistent` check, the crossing clauses, asobi_spatial - until some later zone_tick happened to round-trip the entity through atomize_entities/1 and fix it up. Convert base_state and override keys at the same boundary #270 uses for tick results, so a spawned entity has the same key shape whether or not a tick has elapsed since it was created.
Ported from widgrensit/asobi_lua#134 (1bc28be), which landed after the asobi_lua snapshot merged into this repo. #303 already rejects an out-of-shape broadcast event name at the socket boundary, so a script that trips one of those rules lost the event with only a server-side log line to explain why. game.broadcast now asks the same guard before the fan-out and returns `{ error = "..." }` at the call site. Reworked from the asobi_lua original: in a single repo the two halves can share an owner, so asobi_ws_handler exports event_name_binary/1 and asobi_lua_api calls it instead of restating the length, charset and reserved-name rules. That removes the "both lists must move together" hazard the split-repo version had to carry.
Ported from widgrensit/asobi_lua#133 (3354e5e), which landed after the asobi_lua snapshot merged into this repo. asobi_lua_api_tests mocks asobi_repo for every game.storage.* case, so the suite could only ever confirm the assumption baked into the mock. #296 (update_all/2 rejecting a raw jsonb map, second set silently dropped) shipped past exactly that. asobi_lua_storage_SUITE drives game.storage.set/get/player_set/player_get through Luerl against the Docker Postgres, asserting both the Lua-visible result and the row in the database: set -> set -> get, version bump, and both directions of the global vs player-scoped namespace isolation. Verified the suite fails against both defect shapes: dropping the `player_id IS NULL` predicate from storage_find/3 fails global_write_does_not_clobber_player_row, and restoring the unscoped update_all/2 write fails set_twice_updates_the_row plus player_write_does_not_clobber_global_row. Setup uses asobi_test_helpers:start/1 rather than the standalone pgo/kura_migrator boot the asobi_lua original needed, and ci.yml already passes extra-services-compose, so no CI change was required.
Ported from widgrensit/asobi_lua#132 (4b4bf8e), which landed after the asobi_lua snapshot merged into this repo. Registration posture was only reachable through the release sys.config, which no engine-hosted game can edit, so every hosted game silently ran the `open` default. A game can now declare `registration = "open" | "oauth_only" | "closed"` in the same config script that carries `guest_auth`. Reworked for ADR 0006. The asobi_lua original called application:set_env(asobi, registration, Mode) from the loader, which in this repo would be both an unowned writer and the trust inversion ADR 0006 exists to prevent - a game bundle overwriting operator config. So registration becomes a second layer exactly like the mode registry: asobi_game_config writes the declared value to `script_registration`, never to the operator's `registration` key, and asobi_registration composes the two with the operator layer on top. A bundle can pick a posture for a deployment that states none, and can never widen one that does. Omitting the global leaves both keys alone, so a self-hoster's sys.config still wins; an unrecognised value is logged and dropped by the loader rather than downgrading the posture to `open`.
🟡 Code Coverage — 73%5629 of 7707 lines covered. |
Taure
added a commit
to widgrensit/asobi_lua
that referenced
this pull request
Aug 4, 2026
v0.51.4 predated widgrensit/asobi#353, which ported the four commits that landed in this repo after the merge snapshot was taken. Publishing the alias against v0.51.4 would have shipped an image missing asobi_lua#131, #132, #133 and #134 - exactly the regression this retirement was held back to avoid. Verified in the assembled prod release: asobi-0.53.0 is the only asobi lib, each shared module ships exactly once, and the three code fixes are present in the fetched dependency source.
Taure
added a commit
to widgrensit/asobi_lua
that referenced
this pull request
Aug 4, 2026
* chore: retire asobi_lua to a Docker alias for asobi The Lua runtime moved into asobi (widgrensit/asobi#339). asobi main now defines 16 of this repo's 17 modules, so any asobi pin at or past that merge makes the release unbuildable: ===> Error generating release: Duplicated modules: asobi_lua_game_error specified in asobi and asobi_lua asobi_lua_dev_errors specified in asobi and asobi_lua Urgent part: .github/workflows/bump-asobi-pin.yml ran daily and moved the pin to asobi main unattended. The next run would have hit exactly that error and left ghcr.io/widgrensit/asobi_lua unpublishable. It is deleted, not paused. The published image keeps building. It is now an alias: same name, same tags, same bin/asobi_lua entrypoint, same port, same environment, but the release is assembled from asobi alone, whose own supervision tree starts the Lua runtime. Nothing changes for anyone running it. - Delete src/, include/ and test/ - all duplicated by asobi. Only asobi_lua_app.erl was unique, and asobi_sup already does its job. - Pin asobi to the v0.51.4 tag rather than a branch, since nothing bumps it automatically any more. - Replace the erlang-ci delegation with a release build plus an assertion that no Lua module ships twice. Delete the nightly property soak; its suites moved. - Close the tracker to new issues via .github/ISSUE_TEMPLATE/config.yml redirecting to widgrensit/asobi. Existing issues stay open and readable. - Deprecation notice at the top of README, plus AGENTS.md and SECURITY.md. * chore(deps): pin asobi v0.53.0, the release carrying the ported fixes v0.51.4 predated widgrensit/asobi#353, which ported the four commits that landed in this repo after the merge snapshot was taken. Publishing the alias against v0.51.4 would have shipped an image missing asobi_lua#131, #132, #133 and #134 - exactly the regression this retirement was held back to avoid. Verified in the assembled prod release: asobi-0.53.0 is the only asobi lib, each shared module ships exactly once, and the three code fixes are present in the fetched dependency source.
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.
asobi_lua was merged into asobi at 733cec4, snapshotting asobi_lua at b01c5dc. Four asobi_lua commits landed after that point and never made it across. asobi_lua#135 retires that repo and deletes its
src/, so merging it without these first would regress the shipped runtime.This ports all four, one commit each, in dependency order. Unblocks widgrensit/asobi_lua#135 and #346.
What landed
af391ffatomize spawn-templatebase_state/zone.spawnoverridesc8504e91bc28bevalidategame.broadcastevent names97ebac63354e5ereal-Postgres CT forgame.storage.*ed7a63a4b4bf8egame-declared registration mode1736cd5The two reworks worth reviewing
#134 - one owner instead of two copies. In separate repos, asobi_lua had to restate the length, charset and reserved-name rules that
asobi_ws_handleralready enforces at the socket boundary (#303); its own comment said "both lists must move together". In one repo they do not have to.asobi_ws_handlernow exportsevent_name_binary/1next toreserved_event_names/0, andasobi_lua_api:do_broadcast/4calls it. The core guard is unchanged and still the last line of defence; this is purely the script-facing half, so an author gets{ error = "..." }at thegame.broadcastcall site instead of a dropped event and a server-side log line.game_broadcast_rejects_all_reservediteratesasobi_ws_handler:reserved_event_names(), so a name added to core is covered without touching the Lua tests.#132 - registration is a second layer, not an overwrite. The original called
application:set_env(asobi, registration, Mode)straight from the loader. In this repo that is both an unowned writer (ADR 0006:asobi_game_configis the only one) and the exact trust inversion ADR 0006 exists to stop - a game bundle overwriting operator config. It is also a security posture, so a bundle widening acloseddeployment back toopenis not a trade worth making for a smaller diff.So registration gets the same shape as the mode registry:
asobi_game_config:apply_config/1writes the declared value toscript_registration, never to the operator'sregistrationkey;asobi_registration:classify/0composes the two, operator layer on top.A bundle can pick a posture for a deployment that states none - which is the actual bug, since an engine-hosted game has no
sys.configto edit and every hosted game silently ranopen- and can never widen one that does. Omitting the global leaves both keys alone; an unrecognised value is logged and dropped by the loader rather than downgrading toopen.What proves each fix landed
Every ported fix was verified by reverting the source change and confirming the test fails.
asobi_lua_zone_spawn_tests:lua_spawn_reaches_zone/0readsmaps:get(health, Goblin)(atom key) before any tick has returned entities;asobi_lua_world_tests:spawn_templates_decodes_test/0asserts#{hp => 10}forbase_state. Revertingasobi_lua_api.erl+asobi_lua_world.erl: 2 failures.assert_broadcast_rejected/1asserts a binary error came back and?assertNot(meck:called(asobi_match_server, broadcast_event, '_')). Revertingdo_broadcast/4: 5 failures.player_id IS NULLpredicate fromstorage_find/3failsglobal_write_does_not_clobber_player_row;update_all/2write failsset_twice_updates_the_rowandplayer_write_does_not_clobber_global_row.registration_global_sets_mode/0assertsasobi_registration:mode() =:= closedandcheck(password) =:= {deny, ~"registration_closed"}(the effective posture, not the raw key). Reverting all three source files: 3 failures.operator_registration_beats_script/0is the guard on the rework specifically - makingapply_config/1writeregistrationinstead ofscript_registrationfails it and nothing else.Notes
asobi_lua_storage_SUITEusesasobi_test_helpers:start/1rather than the standalonepgo/kura_migratorboot the asobi_lua original needed, andci.ymlalready passesextra-services-compose, so no CI change was required. No name clash with the existingasobi_storage_SUITE, which covers the core REST storage path rather than the Lua write path.AGENTS.mdandci.ymlhunks from fix(world): snapshot and restore per-zone state across idle reap #133 (repo-specific; the equivalent asobiAGENTS.mdline is included).Checks
fmt --check,xref,dialyzer,ex_docclean.eunit1137/1137.ct297 passed, 1 failed:asobi_iap_SUITE:apple_valid_jws_correct_bundle, which fails identically on a cleanorigin/mainworktree at a5e27d3 and is untouched by this branch.