Skip to content

Polygon startboxes via mapmetadata modoptions#7513

Merged
WatchTheFort merged 20 commits into
beyond-all-reason:masterfrom
burnhamrobertp:feature/polygon-startboxes
Jun 24, 2026
Merged

Polygon startboxes via mapmetadata modoptions#7513
WatchTheFort merged 20 commits into
beyond-all-reason:masterfrom
burnhamrobertp:feature/polygon-startboxes

Conversation

@burnhamrobertp

@burnhamrobertp burnhamrobertp commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

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 legacy startboxes modoption per design discussion with @p2004a (see comments below).

Two new modoptions in modoptions.lua, encoded as base64url(zlib(json)) matching the existing mapmetadata_startpos convention:

  • 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):

  1. Override, if arrangement.startboxes.length == num_teams.
  2. Set, exact match on set[tostring(num_teams)].
  3. Set, next larger entry (smallest key > num_teams).
  4. Hardcoded N/S or E/W fallback by aspect ratio.

When no explicit (modoption) source applies, GG.IsInsideStartbox returns nil and 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} (maxPlayersPerStartbox is informational only per @p2004a).
  • Coords are integers on the 0-200 grid; game converts to elmo at parse time.
  • 2-point poly is a rectangle (opposite corners) and gets expanded to a 4-vertex ring at parse.
  • 3+ point poly with optional per-anchor strength runs through common/lib_spline.lua for Catmull-Rom tessellation, matching the TS port already in bar-lobby/src/renderer/utils/spline-tessellation.ts.

Removed:

  • mapconfig/map_startboxes.lua map-archive read path.
  • Legacy Spring.GetModOptions().startboxes loadstring parser. Per @p2004a, this is dead code - nothing sets the modoption today.
  • WrappedInclude helper (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:

  • bar-lobby amendment to inject mapmetadata_startboxes_set and _override (follow-up to merged Stdev52 #574 which only rendered preview).
  • maps-metadata#615 schema extension to allow N-point poly arrays.
  • Chobby PR Fix Vanguard low/high trajectory script #1184 needs revision; as written it relies on the now-removed map-archive read path.
  • Teiserver / Tachyon stay rect-only at the protocol layer; polygons ride as opaque modoption blob per maps-metadata#605.

Test steps

  • busted spec/ - 21 tests pass (lib_polygon and lib_spline specs unchanged).
  • Load any rectangular-only map without the new modoptions set. Behavior identical to master (engine startrect used, hardcoded N/S or E/W default never visible).
  • Locally verified via spring-headless against 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.

@burnhamrobertp

burnhamrobertp commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author
d5

@github-actions

github-actions Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Integration Test Results

14 tests   - 2   6 ✅  - 2   3s ⏱️ ±0s
 1 suites ±0   8 💤 ±0 
 1 files   ±0   0 ❌ ±0 

Results for commit fa97411. ± Comparison against base commit 7a41d24.

This pull request removes 2 tests.
ai_stai_factory_rect.lua
gui_pip/test_gui_pip_autostart_maximize.lua

♻️ This comment has been updated with latest results.

@burnhamrobertp burnhamrobertp changed the title Add polygon startbox support with backwards-compatible validation Polygon startbox containment and multi-polygon rendering Apr 23, 2026
@burnhamrobertp

burnhamrobertp commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

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().
@burnhamrobertp burnhamrobertp force-pushed the feature/polygon-startboxes branch from da14ec0 to 55ef4b4 Compare April 29, 2026 13:06
@burnhamrobertp

burnhamrobertp commented May 4, 2026

Copy link
Copy Markdown
Contributor Author

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).

image

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 strength float (0.0-1.0) that determines the curvyness of the connections extending from a node to its neighbors base on the current node's strength and the neighboring node's strength (since both are necessary to determine the angle at which the two line should smoothly meet).

@WatchTheFort

Copy link
Copy Markdown
Member

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.

@burnhamrobertp

Copy link
Copy Markdown
Contributor Author

Are you referring to the gametype shims specifically, or the map_startboxes.lua format in general? From my understanding, maps like Cirolata ship with that file as part of the map itself, but it's not ZK game code, it's a map asset that BAR already loads. The camelCase shims are there because some of those map files were authored that way and would otherwise error out.

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.

@efrec

efrec commented May 7, 2026

Copy link
Copy Markdown
Collaborator

Are you referring to the gametype shims specifically

Yes.

From my understanding, maps like Cirolata ship with that file as part of the map itself, but it's not ZK game code, it's a map asset that BAR already loads.

Seems correct.

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.

Maybe we can emit deprecation messages and move things along, for now.

@burnhamrobertp

Copy link
Copy Markdown
Contributor Author

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.

@sprunk

sprunk commented May 7, 2026

Copy link
Copy Markdown
Collaborator

@AntlerForce looks like this might impact your workflow regarding putting ZK startbox configs in your maps.

@AntlerForce

Copy link
Copy Markdown
Contributor

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.
@WatchTheFort and @efrec Can you clarify what needs to change in order for this to get in the game?

@efrec

efrec commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Can you clarify what needs to change in order for this to get in the game?

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.

@burnhamrobertp

Copy link
Copy Markdown
Contributor Author

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.

@p2004a

p2004a commented May 22, 2026

Copy link
Copy Markdown
Collaborator

how are they coming through in the preview in chobby?

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.

are there any particular considerations

I don't know what you are exactly asking for so can't anwer

wouldn't affect this PR or bar-game

polygon starboxes is game-side functionality, so game must provide correct interface, so yes it is game concern.

@burnhamrobertp

burnhamrobertp commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

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 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 savedBoxes.dat file which is synced automatically from the maps-metadata repo.

This was what I was asking about before, and the cause of my confusion. mapsmetadata_startPos is not the only data sourced from rowy and synced to this repo for use as a modoption for a specific map. startboxesSet is also used in a very similar fashion; it populates the savedBoxes.dat file.

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?

polygon starboxes is game-side functionality, so game must provide correct interface, so yes it is game concern.

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)

@p2004a

p2004a commented May 22, 2026

Copy link
Copy Markdown
Collaborator

mapsmetadata_startPos is not the only data sourced from rowy and synced to this repo for use as a modoption

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.

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 ?

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.

@burnhamrobertp

burnhamrobertp commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

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 mapsmetadata_startboxesSet and choose the specific set of startboxes out to use? mapsmetadata_startpos sends the entire set, but the lobby already needs to do this as well in order to render the preview of the startboxes.

So do we have the lobby do it (for preview purposes only) and then pass the full mapsmetadata_startboxesSet via modoptions for bar-game to effectively do the same thing that bar-lobby already did?

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 startboxesSet unless chobby sets the data structure to include the correct number of teams and players, needlessly, just to conform to this pattern?

@Hectate

Hectate commented May 22, 2026

Copy link
Copy Markdown

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 startboxesSet unless chobby sets the data structure to include the correct number of teams and players, needlessly, just to conform to this pattern?

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).

@p2004a

p2004a commented May 23, 2026

Copy link
Copy Markdown
Collaborator

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.

Not exactly. The Tachyon server is aware of startboxesSet because it has to be in order to resolve and pick the correct one when creating a match. We push the set to teiserver.

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.

at what "level" of how this data gets passed down do we take mapsmetadata_startboxesSet and choose the specific set of startareas out to use

This is the core question we need to answer, and it needs to be informed by:

  1. Server side drives the choice of default startareas.
  2. Being able to set custom startareas at all (rectangular or poly).
  3. What the behavior is when switching the number of teams and map in lobby.

Server-driven startareas

Currently, 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 startareas

Because 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.

  1. To set engine-native startboxes, any startarea modoption needs to be cleared so they don't take precedence.
  2. To set polygon startareas, the lobby must generate and set the correct modoption value. There is very little difference between startareas set vs single startarea.

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 lobby

In 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 set

Here the behavior is controlled by the server.

  • Startareas set modoption: the server switches it on map change and doesn't touch that value at all when the number of teams changes. The server still continues to ensure that a proper number of engine-native startboxes are configured at all times. The lobby, and then the game, switches between the engine-native startboxes and startareas depending on whether startareas are available for the configuration or not.
  • Single startarea modoption: the server sets the modoption if it finds a configuration for the map and number of teams; the rest of the logic is pretty much like in the previous point.

2. Custom startareas are set

This one is a bit tricky.

  • Startareas set modoption

    Setting the modoption overrides a whole set. It seems like it works, but when the number of teams changes, the server in its current state will not revert back to the default setting, so we will not pick the default configuration.

    To avoid that, instead of overriding the whole set, we can override just the configuration for the selected number of teams. It has a chance of working, but it might be slightly more annoying: switching to a different number of teams and then back doesn't restore the default.

  • Single startarea modoption

    Here there is a clear advantage, because the server will automatically pick the correct configuration when switching.

    When no per map per number of teams configuration exists, it will probably keep the old value. That's not a problem because fallbacks should kick in in that case.

    Clearing the value to default should happen automatically when switching between team numbers.

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 !list bSettings to get all possible values and fetch the default from there).

"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:

if modoption.startarea_override is set and modoption.startarea_override.num_teams == num_teams:
  return modoption.startarea_override
elif modoption.startarea_set is set and modoption.startarea_set[num_teams] is set:
  return modoption.startarea_set[num_teams]
else:
  return engine.startboxes

With this:

  • the server side fully controls the value of the startareas set, and the lobby fully controls the value of startarea_override to support custom overrides. They don't fight with each other.
  • the startareas set, a single per-map modoption, is supported in the infra already, so no changes are needed
  • easy to restore the default — just clear the override

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.

@burnhamrobertp

Copy link
Copy Markdown
Contributor Author

Got a few things I'm not clear on just due to my ignorance and uncertainty about everything involved here:

elif modoption.startarea_set is set and modoption.startarea_set[num_teams] is set:

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?

if modoption.startarea_override is set and modoption.startarea_override.num_teams == num_teams:

Where does that num_teams metadata live - top-level next to the arrangement data, or embedded somewhere else?

else:
return engine.startboxes

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?

the server side fully controls the value of the startareas set, and the lobby fully controls the value of startarea_override to support custom overrides

Is using startareas instead of startboxes intentional? Should the new modoptions follow that naming?

@p2004a

p2004a commented May 30, 2026

Copy link
Copy Markdown
Collaborator

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?

At the moment it's only used to attach correct tags and map list automatically.

Not sure what happens if two entries had the same num_teams

It's not allowed, it will fail.

Where does that num_teams metadata live - top-level next to the arrangement data, or embedded somewhere else?

It was just simplification for purpose of pseudocode. All existing logic in maps-metadata just takes .length to get number of teams for configuration

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?

It's dead code, nothing sets this modoption, SPADS sets only engine native one, it's not even declared in modoptions.lua, and we definitely do not want to do any loadstring like that.

Is using startareas instead of startboxes intentional? Should the new modoptions follow that naming?

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.

@burnhamrobertp

Copy link
Copy Markdown
Contributor Author

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:

elif modoption.startarea_set is set and modoption.startarea_set[num_teams] is set:
return modoption.startarea_set[num_teams]
else:
return engine.startboxes

Chobby's existing handling for rectangular startboxes (selectStartBoxesForAllyTeamCount on the savedBoxes data) has fallback rules - exact match for the team count, then next larger set, then next smaller. Should the new modoption resolution mirror that fallback behavior, or stick with strict-match like the pseudocode?

@p2004a

p2004a commented May 31, 2026

Copy link
Copy Markdown
Collaborator

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.

@burnhamrobertp burnhamrobertp changed the title Polygon startbox containment and multi-polygon rendering Polygon startboxes via mapmetadata modoptions Jun 5, 2026
@burnhamrobertp

burnhamrobertp commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Headless verification

Did 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.

[TEST] ParseBoxes numTeams=2 overrideDecoded=false setDecoded=false configSource=fallback
[TEST] entry allyTeamID=0 nameShort=W numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=1 nameShort=E numBoxes=1 firstPolyVerts=4

Hardcoded fallback fires; W/E for the landscape map.


B: set with exact match

2-team game; set carries a "2" entry with two rectangular polys.

[TEST] ParseBoxes numTeams=2 overrideDecoded=false setDecoded=true configSource=modoption_set
[TEST] entry allyTeamID=0 nameShort=W numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=1 nameShort=E numBoxes=1 firstPolyVerts=4

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 "4" entry.

[TEST] ParseBoxes numTeams=2 overrideDecoded=false setDecoded=true configSource=modoption_set
[TEST] entry allyTeamID=0 nameShort=NW numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=1 nameShort=NE numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=2 nameShort=SW numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=3 nameShort=SE numBoxes=1 firstPolyVerts=4

Next-larger fallback uses the 4-team arrangement. All 4 boxes get entries in startBoxConfig; consumers iterate Spring.GetAllyTeamList so the extras get ignored.


D: set with smaller-only

4-team game; set only has a "2" entry. Smaller-direction fallback uses the 2-team arrangement (matching Chobby's existing rectangle behavior).

[TEST] ParseBoxes numTeams=4 overrideDecoded=false setDecoded=true configSource=modoption_set
[TEST] entry allyTeamID=0 nameShort=W numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=1 nameShort=E numBoxes=1 firstPolyVerts=4

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.

[TEST] ParseBoxes numTeams=2 overrideDecoded=true setDecoded=false configSource=modoption_override
[TEST] entry allyTeamID=0 nameShort=W numBoxes=1 firstPolyVerts=3
[TEST] entry allyTeamID=1 nameShort=E numBoxes=1 firstPolyVerts=3

Override accepted (startboxes.length == num_teams). 3-vertex polygons stay 3-vertex since no spline strength was set.


F: override mismatch + set match

2-team game; override has 4 boxes (won't match), set has "2".

[TEST] ParseBoxes numTeams=2 overrideDecoded=true setDecoded=true configSource=modoption_set
[TEST] entry allyTeamID=0 nameShort=W numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=1 nameShort=E numBoxes=1 firstPolyVerts=4

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.

[TEST] ParseBoxes numTeams=2 overrideDecoded=false setDecoded=true configSource=modoption_set
[TEST] entry allyTeamID=0 nameShort=W numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=1 nameShort=E numBoxes=1 firstPolyVerts=4

poly.length == 2 (the schema's rectangle case) got expanded so PolygonLib.PointInPolygon can handle it without a special case.


H: polygon with spline strength

2-team game; set entry has 3-anchor polygons with strength=1.0 on every anchor.

[TEST] ParseBoxes numTeams=2 overrideDecoded=false setDecoded=true configSource=modoption_set
[TEST] entry allyTeamID=0 nameShort=W numBoxes=1 anchors=3 tessellatedVerts=36
[TEST] entry allyTeamID=1 nameShort=E numBoxes=1 anchors=3 tessellatedVerts=36

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 "this is not valid base64url(zlib(json))".

[TEST] ParseBoxes numTeams=2 overrideDecoded=false setDecoded=false configSource=fallback
[TEST] entry allyTeamID=0 nameShort=W numBoxes=1 firstPolyVerts=4
[TEST] entry allyTeamID=1 nameShort=E numBoxes=1 firstPolyVerts=4

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.

@p2004a

p2004a commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

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.

@burnhamrobertp

burnhamrobertp commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

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)

@burnhamrobertp burnhamrobertp requested a review from p2004a June 12, 2026 17:03

@p2004a p2004a left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 efrec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM these are only some nitpicks.

Comment thread common/lib_polygon.lua Outdated
Comment thread common/lib_spline.lua Outdated
Comment thread luarules/gadgets/include/startbox_utilities.lua Outdated
@WatchTheFort WatchTheFort merged commit 8fd54ac into beyond-all-reason:master Jun 24, 2026
3 checks passed
p2004a pushed a commit to beyond-all-reason/bar-lobby that referenced this pull request Jun 25, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants