refactor(Lua): Spring.X -> SpringBucket.X#2799
Conversation
AFAIK this is not correct,
AFAIK LuaLS should be able to infer that you've extended the table simply by doing: Spring.foo = 5Could you tell me more about the problem you ran into? Is this for a test suite? |
|
Hey @rhys-vdw . My goal is a "spring interface", which for my purposes behaves like an EmmyLua class. I want something I can inherrit from to guarantee completeness on my SpringMock when I inherit from ISpring or whatever. |
Sorry I'm not trying to push back too hard, but I did consider this exact thing and decided it was not the right approach at the time. I want to be careful about the change, but I'm open to being convinced. One immediate problem with this is it's going to change the docs to add a Does emmylua actually provide the completeness checks when inheriting? It was pretty jank when I was messing around with it. Have you actually tried adding the annotation? If you have got your whole thing running with the annotation, have tested the docs. It's annoying that they don't want to implement Anyway, not completely opposed so long as you've actually tried it and it's working. I would at least want a non-doc comment there explaining why there is a class. Also, is your mock part of Spring or an external project? |
|
nah I need to generate recoil-lua-library and do a post-Recoil-publication version, what I have gotten to work with some decent/surprising levels of edit-time type comprehension, if I jump through the correct hoops, is my own EmmyLua class as ISpring in But if we can get away with just telling EmmyLua to express that table as a type with one line, that'd be ideal until we figure out the more complicated and bifurcated pure-interface solution? I'll try to actually generate this and link you the commit diffing the class diff over in BAR and hopefully some red underlines soon. |
|
I tried to keep it as simple as possible so that it's easy to maintain. The only reason that pattern was used for the various callback classes was because it was necessarily to mix and match them. Since there is no way to type Adding extra classes is just going to make the docs not make sense, confuse people who are trying to document it, and discourage the many contributors who hate the idea of static analysis. That said, the Spring class might be fine. I need to read the rest of what you said. Unfortunately you've caught me in the middle of an insanely busy week. |
|
Actually I've changed my mind, you seem to have your head around it. Just try to keep things simple and consistent, add comments where there might be confusion. Maybe speak to @badosu too. I'm still pretty sure there's no way to change the type of |
|
--- DELETED because it's now just noise. Basically I used the CI to spit out the buckets I wanted from the source files by just coding the buckets into the CI, as described above. rhys-vdw preferred a more self-expressive decorator pattern similar to what we were already doing, so we came up with the context idea. -- |
|
Sorry haven't had a chance to have a full look at this, but if we're taking this route then it would be better to defined ---@class Spring : SpringSynced, SpringUnsynced
Spring = {} |
|
Yeah agreed. I pushed a commit doing exactly that -- Spring now inherits from SpringSynced and SpringUnsynced directly, with a SpringShared base for the 425 functions that exist in both contexts. The per-file output still uses Spring.* for now. Going fully "all the way through" means the per-file output would also use the constituent names, which breaks the 1:1 mapping from source files to docs pages. Two options: do it now as a follow-up, or ship this and let consumers migrate to the explicit types first, then graduate them to the primary output in a breaking version. Everyone accesses through Spring anyway so the migration path is just inheritance. I can see value in keeping the source file categories on the Recoil side, too. Maybe the ideal is forming the input categories (source files) into output categories (Shared/Synced/Unsynced) at the docs level? |
ab81622 to
5c4c9d5
Compare
5c4c9d5 to
7d01853
Compare
|
I'm not sure why the library build is failing, it looks as though there is a bug in the parser unfortuanately. |
Nah I think I just need to swap out the version of lua-doc-extractor as a separate docker image. The CI pulls from master currently and I didn't really sweat it. Works fine locally in my little test script so ostensibly it would work once https://github.com/keithharvey/lua-doc-extractor/tree/contexts hits main, just need to add a build_arg to the CI/docker over here [probably as a separate tiny PR] |
Oh that's bad. I have been releasing it on npm. I have some vague memory that there was a problem installing it from there, but now I'm not sure. Edit: |
|
Still haven't fixed the CI, but I did finish the |
|
Rebased on #2868 -- can always revert if that doesn't go but i need it for a unified workflow right now. |
8bc2619 to
7d9b121
Compare
98893c0 to
a884e58
Compare
|
The AST transform (keithharvey/bar#28) correctly maps every function to exactly one type |
66a3f87 to
b36d7b6
Compare
fdbd3dc to
39af2f0
Compare
|
The build is failing because the submodule commit does not exist on origin, it works if you wire up the upstream. Quick update but I went ahead and just changed the docs to be SpringBucket instead of Spring for consistency after talking with @rhys-vdw . This greatly simplified the lua-doc-extractor side of things. |
|
Nobody asked for this. Get buy-in from gamedevs first. Adding missing function annotations looks good, could put that in #2888. |
rhys-vdw
left a comment
There was a problem hiding this comment.
Just noticed a couple of things
I guess who do I need to convince? It's backward compatible, it immediately supports explicit type comprehension and mocking in BAR and other projects -- I guess I'm missing the reasons not to merge this? |
|
The code itself is backwards compatible but the, let's say "direction" is not. If this is to become a standard then everybody would have to follow it. If you're fine with this being done just for BAR you can do it locally (via |
|
Totally fair. Did you see my RFC for requiring BAR-Devtools? I think that one is already plenty big but I could do something similar for this idea. I get that it's out of left field, but I do think it's a technical improvement after staring at it for a bit here. Hmm maybe it belongs in that document as "Decision 6".. @sprunk Would you support it as a BAR-only decision, for now, since the conversion work there is already done? To demonstrate the usefulness over in ZK/Recoil later? I'm honestly not sure I would. It loses a lot of the engine-support we get by having it publish SpringSynced for our mocks to have comprehension on to ensure we don't drift.. that's the real win here. Real mocks and edit-time comprehension. |
|
3aada55 to
b22d9e0
Compare
Series of minor EmmyLua annotation corrections and small documentation fixes that accumulated during the spring-split exploration. None of these depend on or relate to the spring-split refactor; pulled out as a standalone PR to keep the spring-split review focused.
…in the shallow checkout
Splits the engine's `Spring` Lua API into three context-specific tables: - `SpringSynced` — gadget-only (mutates game state, deterministic) - `SpringUnsynced` — widget-only (visual/UI, free to be non-deterministic) - `SpringShared` — both contexts (read-only utilities + pure functions) Each Lua-exposed engine function is registered into exactly one of them. Per-context Lua environments expose only the relevant tables: gadgets see `SpringSynced` + `SpringShared`; widgets see `SpringUnsynced` + `SpringShared`. The legacy `Spring` table is retained as a deprecation alias so existing code keeps working during the transition window. Background and full RFC (audience: Recoil engine maintainers + Recoil-based games like Zero-K, BYAR-Chobby): see the design doc shared alongside this PR. Migration: - The `Spring.X` deprecation alias keeps existing code working; new code should use the namespaced form. - Downstream games can opt into the namespaced form on their own schedule; BAR has done so via its `mig-spring-split` codemod, reusable from beyond-all-reason/BAR-Devtools.
Two follow-ups to the SpringSynced/Unsynced/Shared split that the language server caught (`mise run lua_check` from doc/site). 1. Declare the three split namespaces as ---@Class in Spring.lua. The split inheritance line `Spring : SpringShared, SpringSynced, SpringUnsynced` referenced types that weren't declared anywhere, so every generated `SpringSynced.X = nil` / `SpringUnsynced.X = nil` assignment fired "undefined global" warnings. 2. Re-add `@function SpringUnsynced.GiveOrderTo*` annotations on the GiveOrderToUnit family in LuaUnsyncedCtrl.cpp. The split commit had dropped the original `@function Spring.X` lines without replacing them, leaving the surrounding `@param` annotations orphaned (each fired "undefined param").
b22d9e0 to
5b19ff1
Compare
rhys-vdw
left a comment
There was a problem hiding this comment.
Some notes. For some reason I didn't get the "suggest" button or else I'd have used that feature so you could just accept them all.
| Object rendering API — controls LOD, materials, and custom draw for units/features. | ||
| Registered as `Spring.UnitRendering` and `Spring.FeatureRendering`. | ||
| @see rts/Lua/LuaObjectRendering.cpp | ||
| */ |
There was a problem hiding this comment.
FYI freefloating comments like this are ignored by lua-doc-extractor because they're not attached to anything. Probably appropriate to include with @class ObjectRenderingTable.
| * @function LuaFont:SetOutlineColor | ||
| * @param color table Four-component RGBA array (`{r, g, b, a}`), or pass `r`, `g`, `b`, and optional `a` as separate numbers (requires at least three numeric components after the font). | ||
| */ | ||
| int LuaFonts::SetOutlineColor(lua_State* L) { return (SetTextColorShared(L, true)); } |
There was a problem hiding this comment.
Pretty sure this should be expressed as an overload (same as method above). Doesn't require a lengthy description.
/*** Sets the RGBA color used for text outline when outline rendering is enabled.
*
* @function LuaFont:SetOutlineColor
* @param color rgba Outline color
*/
/*** Sets the RGBA color used for text outline when outline rendering is enabled.
*
* @function LuaFont:SetOutlineColor
* @param r number Red
* @param g number Green
* @param b number Blue
* @param a number? Alpha
*/| /******************************************************************************/ | ||
|
|
||
| /*** | ||
| * @function Spring.RequestPath |
There was a problem hiding this comment.
Should these not live in one of the SpringX buckets?
| * @function Spring.SetUnitHarvestStorage | ||
| * @param unitID integer | ||
| * @param metal number | ||
| * @param metal number? |
There was a problem hiding this comment.
What would it mean to pass null here? Would be good to add a (Default: <whatever>). Hard to infer what this function is for without a value being passed.
| * @param dgun boolean? (Default: `false`) | ||
| * @param userTarget boolean? (Default: `false`) | ||
| * @param weaponNum number? (Default: `-1`) | ||
| * @param dontForceTarget boolean? |
There was a problem hiding this comment.
I don't see it, where is this coming from?
| * @param dgun boolean? (Default: `false`) | ||
| * @param userTarget boolean? (Default: `false`) | ||
| * @param weaponNum number? (Default: `-1`) | ||
| * @param dontForceTarget boolean? |
There was a problem hiding this comment.
I don't see it, where is this coming from?
| * @param unitID integer | ||
| * @param allyTeamID integer? | ||
| * @param raw false? Return a table. | ||
| * @param raw false? (Default: `false`) Return a table. |
There was a problem hiding this comment.
This is kind of confusing. I think the description should be Return a bitmask, since false implies that it would not return a table. If true is passed here then it will automatically use the overload which does return a bitmask.
| /*** @function Spring.SendLuaUIMsg | ||
| * @param message string | ||
| * @param mode string "s"/"specs" | "a"/"allies" | ||
| * @param mode string? "s"/"specs" | "a"/"allies" |
There was a problem hiding this comment.
@param mode ("s"|"specs"|"a"|"allies")?

Part of BAR type-error cleanup.
What's it do
Split the monolithic Spring table into SpringSynced, SpringShared and SpringUnsynced. This supports clearer documentation and more easily understood code. The Spring table still exists for back compatibility.
CI IS PASSING!
Also adds a LUA_DOC_EXTRACTOR_BRANCH build arg to the ci in order to allow for lua-doc-extractor forks to be used, so we can test the CI for a new version of Recoil with an experimental version of lua-doc-extractor.
Here is the passing workflow.
Related Work
zkbranch (vendored asAI/Skirmish/CircuitAI)barbarianbranch (vendored asAI/Skirmish/BARb)just lua::libraryto kick out new versions of recoil-lua-library utilizing a local checkout of lua-doc-extractor. Usejust docs::serverto build and then see the Hugo docs server locally.Motivation
The Recoil engine exposes a single
Springtable in Lua, but different contexts (synced vs unsynced) have access to different subsets of functions. These options allow generatingSpringSyncedandSpringUnsyncedtype libraries from the same C++ sources without duplicating shared helper types that already exist in the per-file output.