Polygon startboxes via mapmetadata modoptions#7513
Conversation
Integration Test Results14 tests - 2 6 ✅ - 2 3s ⏱️ ±0s Results for commit fa97411. ± Comparison against base commit 7a41d24. This pull request removes 2 tests.♻️ This comment has been updated with latest results. |
|
On the assumption that this (related) PR #7516 is approved (I can't see a reason why it shouldn't; these are codepaths that have been dead for years), I'm going to modify this pull request to remove the modconfig support. |
Mapside configs (e.g. Onyx Cauldron) call ZK-style gametype.isFFA() etc. which don't exist as bare globals. Add a compatibility shim mapping known ZK method names to BAR equivalents, with unknown methods returning false. Mapside configs return 1-based Lua arrays while engine allyTeamIDs are 0-based. Normalize keys after loading so lookups by allyTeamID succeed. Also harden WrappedInclude to restore env on error and wrap ParseBoxes calls in pcall for both the synced gadget and the widget.
The StartBoxes directory has been empty since Feb 2021. Mapside configs and autohost modoptions remain as the active startbox sources.
The N/S or E/W fallback prevented the widget from reaching the engine's actual start rects (set by Chobby via the start script). Without polygon config data, return an empty table so consumers fall through to Spring.GetAllyTeamStartBox().
da14ec0 to
55ef4b4
Compare
|
Based on feedback from @PtaQQ this functionality was extended slightly to also provide support for splined polygons, for map makers who want curves in the polygon paths. This uses Catmull-Rom splines rather than the more data-complex Bezier curve that requires inbound and outbound vectors (and would also change the startbox data into a directional path).
Where the original plan for polygon startboxes were to have each startbox defined by an array of polygon points, this new support changes the typing to add on an optional |
|
I have fundamental design disagreements with the code having to support both BAR and ZK. These are separate, independent games, they do not need to be cross-compatible. |
|
Are you referring to the gametype shims specifically, or the If there is already an existing, opinionated format that we'd like to use for polygon startbox data, we can (even if it would mean maps with ZK-style startbox data wouldn't work with it); but since this functionality was already driven by a data pattern that didn't seem to exist (that I could find) I followed the established convention from ZK so it would be less friction for those particular maps. |
Yes.
Seems correct.
Maybe we can emit deprecation messages and move things along, for now. |
|
I don't have a strong opinion either way, honestly, so don't let my decision (in the absence of any information as to preferred pattern) serve as governance. That maps might need to be updated in order to function probably isn't a very high bar; perhaps low enough to justify doing without the shims. |
|
@AntlerForce looks like this might impact your workflow regarding putting ZK startbox configs in your maps. |
|
I have indeed expressed great desire to have polygonal startboxes available in BAR to remove the need for maps like that ptaq made me do this abomination. As long as I can include configs in maps or submit them gameside I really don't care whether they must be done in pascalcase or camelcase or purpleunicorncase it really doesn't matter to me. |
The request from our end is to remove the shim in the near-term. I am indifferent to how that's achieved, so whatever works between @AntlerForce and @burnhamrobertp. It could be a short change window -- we allow some log noise around maps with the deprecated casing for X weeks -- after which the shim is yanked. Or we can park this PR until maps are updated to the same casing. I don't think there is a bad timeline for either of these. |
|
It sounds like the cleanest approach would be to remove the shims now and expect that maps which want to opt into this functionality would need to be updated. I can make that change here (and elsewhere in my other PRs if I included anything similar to them....I genuinely don't remember). I'm not sure if there are hold-ups to any of the other repos' changes; but having at least the game's changes merged in might pave the way to facilitate the others being mergable. |
Engine supports rectangle startboxes natively, they are being put in correct property in startscript by chobby locally and spads in multiplayer. Polygon startboxes are not, must be passed via startscript via modoption.
I don't know what you are exactly asking for so can't anwer
polygon starboxes is game-side functionality, so game must provide correct interface, so yes it is game concern. |
I understand all this, I was asking about how chobby knew / could render the map-specific start boxes in the first place. I found my answer though, by revisiting the code; chobby has a This was what I was asking about before, and the cause of my confusion. Should the chobby PR also address this change? Instead of adding support for polygon daata in saveBoxes.dat, should it transition chobby from savedBoxes.dat -> mapsmetadata_startboxesSet ? If so, we'd have to wait on all of this to do the rowy/maps-metadata side first, I believe?
I was meaning it wouldn't require any special considerations, besides support in modoptions (but the game doesn't care where the modoptions are coming from originally, only that they're well-formed) |
It is the only per-map modoption. There is a bunch of other data that is synced, all other pieces are not modoptions, and often have dedicated handling for various reasons just like rectangle startboxes. This distinction that this is modoption and not other piece of data does matter.
Think how it will be handled in multiplayer games, in chobby, with spads: polygon configuration needs to come from server somehow, adding custom messages, extending spads to supports this doesn't make much sense. Next let's look at new new lobby, it does not use SPADS, resolutions and storage of maps metadata is handled teiserver side, recoil autohost doesn't care, and there is preference to treat it just as modoption that will have special handling lobby side beyond-all-reason/maps-metadata#605 (comment) So, for the multiplayer side, it looks to me all will be done by moving around modoption, server side in this setup doesn't care about polygons at all, lobbies decode it and handle it in a special way, they need code for polygons anyway. If lobbies have to decode modoption value anyway, why not then just also push in that format for single player. If this will be handled as modoption, let's also then make sure that game takes in a modoption format that is not hostile for infra and both lobby implementations. |
|
The only other remaining question I have pertains to the data structure that would be passed from rowy -> maps-metadata -> downwards And that is, at what "level" of how this data gets passed down do we take So do we have the lobby do it (for preview purposes only) and then pass the full And does the players ability to define their own start boxes throw a wrench into this approach? Because if they do define their own startboxes, well then we aren't really sending a |
FWIW the “set” info never makes it to the Tachyon server in a multiplayer match. Instead the start boxes are set per team as part of the “allyTeamConfig”. Even when using the preconfigured start boxes, it’s basically just passed as if it were custom every time. Only the client itself knows if it was or not. Polys as a modoption should be the same - the server can just take the settings and not care if it was predefined or custom (if/when that can be done in the new client). |
Not exactly. The Tachyon server is aware of With standard rectangular startboxes, the Tachyon server needs to be aware of them, just like SPADS is aware of them, to properly do the initial setup of the multiplayer room and automatically update and resolve the startboxes configuration when the number of players changes. The server must be aware of it because it configures the exact startboxes that must be used in the startscript.
This is the core question we need to answer, and it needs to be informed by:
Server-driven startareasCurrently, both in the Tachyon server (via pushed metadata) and in SPADS (via map battle presets), we can configure a modoption value to set for a selected map. Thus the "set of startareas" is already supported. We don't currently have the capability to pick a modoption per both map and number of teams, the way it's supported for rectangular startboxes in both SPADS and teiserver. Supporting this would require changing teiserver and creating a custom SPADS plugin that does modoption switching based on the combination of map and number of teams. Setting custom startareasBecause startareas will take precedence over the native engine startboxes configured in the startscript (if the configuration matches the number of teams), any setting of custom startareas, both engine-native ones and custom polygon ones, will require modifying some modoption.
We can decide that the lobby will always set the startarea modoption for a custom value and never manipulate engine-native ones. Switching map/number of teams in lobbyIn my opinion, a change of map or number of teams shouldn't require two separate votes in lobby. This is why a construct where the lobby does "set map and startboxes" is problematic, because we don't currently have a mechanism to trigger a single vote for two changes. We have two scenarios to consider, and we need to see what the behavior will be when using a modoption encoding a startareas set versus a single startarea. 1. No custom startareas are setHere the behavior is controlled by the server.
2. Custom startareas are setThis one is a bit tricky.
For both cases, resetting to the previous value for the current map and player config is a bit annoying, and it can differ between Tachyon and SPADS (in SPADS you could do "Why not both?"As you can see, neither the startareas set nor the single startarea option is perfect; each has issues in both legacy and new infra. There is another possibility: have both a startareas set and a single startarea override. In that situation, for both lobby and game, the logic looks something like this: With this:
The disadvantage is slightly more complex startbox resolution, but I believe the rest of the maintenance of modoption values for other journeys becomes easier. I hope I didn't miss any cases, but please think it over too. What do you think about the 3 presented options? Having worked through this, I gravitate towards the last one: two modoptions, one for the set and one for the custom override. |
|
Got a few things I'm not clear on just due to my ignorance and uncertainty about everything involved here:
The set lookup in your pseudocode keys only on num_teams, but maps-metadata has maxPlayersPerStartbox on each startboxesInfo entry too. Is that just informational, or does it factor in somewhere? Not sure what happens if two entries had the same num_teams with different maxPlayersPerStartbox. I think this is an existing behavior/uncertainty but I'm curious whether its worth settling now, as part of this?
Where does that num_teams metadata live - top-level next to the arrangement data, or embedded somewhere else?
What about Spring.GetModOptions().startboxes (the lua string one SPADS sends today)? Does that stay in the chain between set and engine.startboxes, or is the new set modoption supposed to replace it?
Is using |
At the moment it's only used to attach correct tags and map list automatically.
It's not allowed, it will fail.
It was just simplification for purpose of pseudocode. All existing logic in maps-metadata just takes
It's dead code, nothing sets this modoption, SPADS sets only engine native one, it's not even declared in
I used it just for the purpose of that reply so it better stand out in text when I'm taking about "rectangular startboxes" and "polygon startareas", we can keep using the startboxes. Sorry for confustion. |
|
One more question on the resolution chain - if the set is populated but doesn't have an entry for the current team count, your pseudocode falls straight through to engine.startboxes:
Chobby's existing handling for rectangular startboxes ( |
|
I think the fallback behavior like this into the larger direction is good idea. Into the smaller I don't have opinion, there is no good way to handle that case, you can only hope that engine native has something reasonable. If no engine native startbox is set AFAIK engine defaults to a box that covers the whole map. |
Headless verificationDid some local verification before pushing. Ran 9 scripted scenarios through spring-headless with temporary Spring.Echo markers in ParseBoxes (since stripped) to confirm the resolution chain behaves how the design says. Payloads were Python-encoded base64url(zlib(json)) to match what the lobby will produce. Test map was Supreme Isthmus v2.1, each scenario quit at frame 3 via debugcommands. The markers logged: numTeams, whether each modoption decoded, the final configSource, and a per-entry summary. A: baseline (no modoption) 2-team game; nothing set. Hardcoded fallback fires; W/E for the landscape map. B: set with exact match 2-team game; set carries a Exact lookup hit. 2-point poly arrays got expanded to 4-vertex rectangles during parse. C: set with larger fallback 2-team game; set only has a Next-larger fallback uses the 4-team arrangement. All 4 boxes get entries in startBoxConfig; consumers iterate D: set with smaller-only 4-team game; set only has a No exact match, no larger key, so the resolution picks the largest smaller key. Teams 0 and 1 get polygons from the 2-team config; teams 2 and 3 have no entry in startBoxConfig and consumers fall through to engine startrect for them. E: override match 2-team game; override carries a 2-box arrangement of 3-anchor triangles. Override accepted ( F: override mismatch + set match 2-team game; override has 4 boxes (won't match), set has Override rejected on count mismatch, falls through to set which matches. G: 2-point rect expansion Same as B but specifically watching the 2-point rect -> 4-vertex rectangle conversion.
H: polygon with spline strength 2-team game; set entry has 3-anchor polygons with strength=1.0 on every anchor. 3 anchors at full strength tessellate to 36 vertices (3 anchors * 12 default segments). The strength field survives encoding and drives Catmull-Rom via common/lib_spline.lua at parse. I: malformed payload 2-team game; set is the literal string pcall guards swallow the decode errors, falls through to hardcoded default. No crash, no warning spam. Provided I've understood everything you were asking for @p2004a , this should be good for another (hopefully final) review. I'm starting on the corresponding changes to chobby and the new lobby. One thing I'm not sure of is whether there needs to be any (even small) changes in any of the infrastructure code to facilitate this move? I think you've spoken on this and I thought the answer was that there isn't...but it seemed worth asking just in case. |
|
The resolution of startboxes needs to be centralized, it almost is, and there must be a very clear description of the resolution order, data format, fallbacks etc. Right now this documentation doesn't exist in the source, so it can't be verified against implementation, and then in the lobby PRs. This is important because we need to have something to point out from multiple repos/places that we need to change, resolution logic must be identical in chobby and new lobby. |
|
I'm not sure if that's what you had in mind ^ but along with changes to the actual lua, I wrote a small documentation trying to think about it from the perspective of someone external to the game code (or this script in particular) and with references to the important bits of information most likely to be overlooked / unknown. Edit: also, as seen below, I added the smaller rect fallback as well, this was really just to minimize changes in chobby (which already expects the same behavior) |
…tartboxes # Conflicts: # luarules/gadgets/game_initial_spawn.lua
p2004a
left a comment
There was a problem hiding this comment.
LGTM for:
- Setup of modoptions
- Documentation of the code
I've looked mainly at luarules/gadgets/include/startbox_utilities.lua, I've not looked at any other bits in this PR, that's for other devs as I'm not working with Lua much.
efrec
left a comment
There was a problem hiding this comment.
LGTM these are only some nitpicks.
Picks up where #574 left off, which rendered polygon startboxes in the preview but still handed the game a plain bounding-box rectangle at launch, so an offline game never got the real shape. Now the offline start script sends the start areas as modoptions, the way the game PR settled on: a selected preset goes out as the set (`mapmetadata_startboxes_set`, the server-owned default), and custom drag-edited boxes go out as the override (`mapmetadata_startbox_override`, lobby-owned) which the game prefers over the set. So an offline game enforces the real shape, custom boxes aren't silently replaced by the map's default, and online games get the set from the server. It also brings the preview's spline in line with the game and Chobby so the curves match exactly. The old math overshot at sharp corners and could draw a small curl the game never showed. Related PRs: - Core game: beyond-all-reason/Beyond-All-Reason#7513 - Chobby: beyond-all-reason/BYAR-Chobby#1184 - Maps-metadata: beyond-all-reason/maps-metadata#615 - Rowy fork: p2004a/rowy#1 - Original bar-lobby PR (merged): #574 Assisted by Claude Code (Opus 4.8); all code reviewed and verified locally.


Work done
Adds polygon startbox containment to the game and routes startbox data through two new hidden modoptions in the
mapmetadata_*family. Replaces the previous map-archive read path and the dead legacystartboxesmodoption per design discussion with @p2004a (see comments below).Two new modoptions in
modoptions.lua, encoded asbase64url(zlib(json))matching the existingmapmetadata_startposconvention:mapmetadata_startboxes_set- per-num-teams arrangements (server-side / lobby default).mapmetadata_startbox_override- single custom arrangement (lobby user customization).Resolution chain in
ParseBoxes(rewritten):arrangement.startboxes.length == num_teams.set[tostring(num_teams)].When no explicit (modoption) source applies,
GG.IsInsideStartboxreturnsniland consumers fall back to engine startrect, preserving current behavior for any launcher that hasn't been amended to inject the new modoptions.Wire format per arrangement is maps-metadata-native
startboxesInfo:{startboxes: [{poly: [{x, y, strength?}, ...]}], maxPlayersPerStartbox}(maxPlayersPerStartboxis informational only per @p2004a).polyis a rectangle (opposite corners) and gets expanded to a 4-vertex ring at parse.polywith optional per-anchorstrengthruns throughcommon/lib_spline.luafor Catmull-Rom tessellation, matching the TS port already inbar-lobby/src/renderer/utils/spline-tessellation.ts.Removed:
mapconfig/map_startboxes.luamap-archive read path.Spring.GetModOptions().startboxesloadstringparser. Per @p2004a, this is dead code - nothing sets the modoption today.WrappedIncludehelper (only existed for the removed paths).Preserved:
common/lib_polygon.lua,common/lib_spline.lua, the hardcoded N/S or E/W default, and engine-startrect fallback. Backwards compatible for any launcher / map combination that doesn't carry the new modoptions.Cross-repo coordination
These game-side changes do not produce polygon startboxes end-to-end on their own. Pending:
mapmetadata_startboxes_setand_override(follow-up to merged Stdev52 #574 which only rendered preview).polyarrays.Test steps
busted spec/- 21 tests pass (lib_polygon and lib_spline specs unchanged).spring-headlessagainst 9 scripted scenarios: baseline (no modoption), set exact match, set larger fallback, set smaller direction (no fallback, hardcoded default fires), override match, override mismatch (falls through to set), 2-point rect expansion produces 4-vertex rings, polygon with spline strength (3 anchors -> 36 tessellated verts), malformed payload graceful fallback. All passed.Related
AI / LLM usage statement
Developed with assistance from Claude Code (Opus 4.7). Used for implementation, design exploration through the PR thread, and headless verification scripting. All code was reviewed and locally verified.