Repository navigation
feat(scripting): complete Script Runtime Contract v1 surface — entity_find + input (#237) - #756
Conversation
…_find + input (#237) The merged v1 (#749) shipped a subset of the RFC §1 surface. This adds the four functions the RFC lists but that landing omitted, so a version-1 host binary actually conforms to the documented v1 surface: labelle_entity_find(name) -> u64 labelle_input_key_down(key) -> i32 (bool) labelle_input_key_pressed(key) -> i32 (bool) labelle_input_mouse(x_out,y_out) -> void - entity_find resolves through a registered `Name`/`Tag` component carrying a string field (name/value/tag), comptime-detected over the game's own ComponentRegistry — a zero-cost always-0 no-op for games that register no such component ("by the Name/tag component if one exists"). First-match snapshot over the ECS view. - input_* poll the game's static InputInterface (the same path game.isKeyDown/getMouse take, minus the KeyboardKey enum conversion the C surface skips). Main-thread, during the plugin tick. Safe pre-bind no-ops (keys not-down, mouse at origin). - All four wired through the existing type-erased vtable/Holder(GP) bridge; contract version stays 1 (additive symbols complete v1, they don't break it). Header (contract/labelle_script.h) updated. Tests: +4 (entity_find happy/no-match/destroyed + comptime-gated-out; input key down/pressed; input mouse incl. NULL out-pointers) and the pre-bind no-op test extended. Full engine suite green (945 tests). Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Warning Review limit reached
Next review available in: 48 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e9647eb888
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for (Components.names()) |n| { | ||
| if (!std.mem.eql(u8, n, "Name") and !std.mem.eql(u8, n, "Tag")) continue; |
There was a problem hiding this comment.
Prefer Name before Tag fallback
When a registry contains both Tag and Name but declares Tag first, this single pass binds entity_find to Tag and never considers Name, so an entity with Name.name = "player" will not be found unless its tag also matches. Prefer Name over the fallback Tag (or search both) so a tag component cannot shadow valid entity names.
Useful? React with 👍 / 👎.
What & why
The Script Runtime Contract v1 (RFC-LANGUAGE-PLUGINS, #237 Phase 1) already landed engine-side in #749 (
src/script_contract.zig) — versioned, comptime-gated, JSON components over the serde-reflection registry, subscribe/poll events, full tests. But that landing shipped a subset of the RFC §1 surface: it omittedentity_findand the three input pollers the RFC (and the POC'scontract.h) list. A language plugin that checkslabelle_contract_version() == 1and then looks up those symbols would fail at bind/link.This PR closes that gap so a version-1 host binary actually conforms to the documented v1 surface.
Surface added
entity_findresolves through a registeredName(orTag) component carrying a[]const u8field (name/value/tag), comptime-detected over the game's ownComponentRegistry. Games that register no such component get a zero-cost always-0 no-op — the RFC's "by the Name/tag component if one exists." First-match snapshot over the ECS view at call time (same semantics asquery). The single-well-known-component convention is deliberately narrow so the future language sub-modules bind one name; widening it is a contract change, not a per-game one.input_*poll the game's staticInputInterface— the same pathgame.isKeyDown/game.getMousetake, minus theKeyboardKeyenum conversion the C surface skips (the raw integer keycode goes straight to the backend). Main-thread, during the plugin tick.How it fits the existing machinery
Holder(GP)pattern verbatim —bind(&g)stores the concrete*Gamebehind a type-erasedVTable; the newexport fns dispatch through three added vtable slots and carry no comptime type info. This is the POC (spike/language-plugins/main.zig, POC: language-plugins spike — one contract, three languages, identical worlds (#237) #734) shape.root.script_contractconst, so script-less builds emit none of these symbols.1. These are additive symbols that complete v1 (they don't break existing v1 consumers); the header note frames it that way.entity_find→ 0, key reads → not-down (0),input_mouse→ writes the origin. The pre-bind no-op test is extended to cover them.contract/labelle_script.hupdated with the four decls + anInputsection.Tests
test/script_contract_test.zig(+4 tests, pre-bind test extended):entity_find: resolves byName; no-match/empty → 0; a destroyed named entity stops resolving.entity_find: comptime-gated out for a game whose registry has noName/Tag(always 0, no crash).input_key_down/input_key_pressed: reflect a controllable input backend; unknown codes read up.input_mouse: writes reported coords; NULL out-pointers skip that axis.Confirmed the new tests actually execute (canary-verified: the
script_contractbinary is now 39 tests, was 35). Full engine suite green — 945 tests,zig build testexit 0.Deferred to the next slice (unchanged by this PR)
The
labelle-scriptingplugin repo + Lua sub-module that consumes this contract: VM embedding, script discovery/embed,init/update/deinitper script, and the idiomatic bindings (game:query,entity:get/set,input:isKeyDown) that wrap these raw symbols. A batchedeach(names, fn)and a tagged-binary component encoding remain v2.Refs #237, #749, RFC-LANGUAGE-PLUGINS §1.
https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw