Clamp ally-team widget index in the map editor's teams editor - #175
Merged
Conversation
The teams editor converted the header's 1-based ally-team number to a 0-based widget index by bare subtraction. A header carrying 0, or a value above the map's team count, produced an index outside the rows the widget was populated with; MultiTextButton::setIndex stores the index unsigned and looks it up with vector::at, so the dialog threw std::out_of_range and crashed on open. The custom-game options screen had already fixed the same defect with a file-local clamp helper. Promote that helper to a shared header-only AllyTeamWidgetIndex.h and use it at both sites. Well-formed values still map to value - 1, so nothing written back changes; only headers that previously threw now show ally team 1. Also name the teams editor's widget return-code bases (player-active, color, AI selector, ally team) instead of hand-typing 100/200/300/400 at nine sites, and document generateGameHeader's inverse mapping. Verified: standalone driver tests/ally_team_widget_index_test.cpp passes; cpp-refactor.replay byte-identical after the change.
Giszmo
approved these changes
Sep 7, 2026
Giszmo
left a comment
Contributor
There was a problem hiding this comment.
Reviewed at head 4214b84, tested merged onto master bfc8fd7. Approve, no blockers.
Verified
- The crash is real:
MultiTextButton::setIndexusestexts.at()and the ally widget only hasnumberOfTeamsentries, so an ally number of 0 or above the team count threw on dialog open. The clamp is the same helper already used inCustomGameOtherOptions(4f13a60); well-formed values and theindex + 1write-back ingenerateGameHeaderare unchanged. - The named return-code constants keep exactly the old 100/200/300/400 ranges;
onActionlogic is untouched. scons -j16on master + PR builds clean with no new warnings in the touched files.tests/ally_team_widget_index_test.cppcompiles and passes with the command in its header. The branch is a few commits behind master but merges clean.
Non-blocking notes
- Reachability: the editor's own add/remove team already rebuilds the header from scratch (
MapEdit::regenerateGameHeader,src/map/edit/MapEditClicks.cpp:391), so this only fires for a map or game file whose stored header is already out of range. Fine as hardening; the PR text does not overclaim. - Fallback choice: every out-of-range value collapses to widget 0 = ally team 1, so after OK those teams end up allied with team 0. Falling back to the team's own index (the
reset()no-alliance layout, always< teamCountfor a valid team) would be a more neutral default. Keeping 0 is defensible for consistency with theCustomGameOtherOptionssite. - Test placement: the repo convention is
test/(singular) with CppUnit fixtures built bytest/SConstructintoTestsRunner, which CI runs. This file lives intests/and is hand-run only, so CI never executes it. Suggest moving it intotest/as a fixture (FilenameStripTest.cppis a minimal template). - Pre-existing, out of scope: the same malformed file also feeds
color[i]->setSelectedColor(teamNumber), whichColorButtonstores unchecked, so the dialog now opens but OK writes that team number back.
The test lived in tests/ as a hand-compiled main() that nothing built, so CI never ran it. Rewrite it as a CppUnit fixture in test/ and add it to TestsRunner, which the Linux CI runs. Same cases: well-formed values map to value - 1; 0, above-range, and 255 clamp to the first row.
Contributor
Author
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.
Summary
MultiTextButton::setIndexusesvector::at, so the dialog threw and crashed on open.AllyTeamWidgetIndex.hand uses it at both sites. Well-formed values still map tovalue - 1; only headers that previously threw now display ally team 1. Nothing written back changes.PLAYER_ACTIVE_BASE,COLOR_BASE,AI_SELECTOR_BASE,ALLY_TEAM_BASE) instead of hand-typed 100/200/300/400, and documentsgenerateGameHeader's inverse mapping.Verification
scons -j16clean.tests/ally_team_widget_index_test.cpp(standalone, compile line in its header) passes all cases: 0, above-range, and 255 clamp to 0; 1..teamCount map to 0..teamCount-1.cpp-refactor.replaybyte-identical (editor UI only, no sim path touched).Manual check
Editor → open teams editor. Before: a map whose game header carries an ally-team number outside
[1, numberOfTeams]crashes on dialog open. After: that row shows ally team 1. Well-formed maps look identical.