Skip to content

Seat fixes: per-tile state lookup and saving near the map edge - #24

Merged
tomluchowski merged 2 commits into
tomluchowski:shaders-improvementfrom
Upabjojr:split/seat-fixes
Aug 2, 2026
Merged

Seat fixes: per-tile state lookup and saving near the map edge#24
tomluchowski merged 2 commits into
tomluchowski:shaders-improvementfrom
Upabjojr:split/seat-fixes

Conversation

@Upabjojr

@Upabjojr Upabjojr commented Aug 1, 2026

Copy link
Copy Markdown

Two fixes to Seat, one commit each:

  • Per-tile state lookup: fixes how a seat looks up the state it keeps per tile.
  • Saving near the map edge: stops saving a game from walking off the side of the map.

Split out of #16 so each topic can be reviewed on its own. Merging all of the split PRs reproduces the tree of #16 exactly.

🤖 Generated with Claude Code

Upabjojr and others added 2 commits August 1, 2026 10:22
A seat remembers, for every tile of the game map and of the editor's copy paste
container, what it last told its player about it. The two containers keep their
states in two separate maps, filled once when each container is sized, and most
of the code looks a tile up in the first map and falls back to the second. Four
kinds of mistake had crept into that pattern, all in Seat.cpp:

hasVisionOnTile() and notifyTileClaimedByEnemy() find the tile in the fallback
map through one iterator and then read or write through the other, which is the
end of the first map. That is a read past the end of a std::map whenever the
server asks about a copy paste container tile.

updateTileStateForSeat() looked the tile up with operator[] before doing the
find with fallback. The lookup creates a default state for the tile in the game
map's map even when the tile belongs to the container, so the find always
succeeded, the fallback was dead code, and the update went to the stray entry
rather than to the container's own. The stray entries also outlive the
container whose tiles they are keyed by.

setVisibleBuildingOnTile(), notifyBuildingRemovedFromGameMap(),
tileMarkedDiggingNotifiedToPlayer() and exportTileToPacket() had no fallback at
all, only the inserting operator[]. They now share one lookup helper that
searches both maps and never inserts; the export keeps sending a default state
for a tile it does not know, since that is what the insertion amounted to.

Also two smaller ones found on the way, both in the render manager:
initGameRenderer() hides the creature overlays around its first render target
update with the same lockstep double loop, without null checks, that used to
crash ODFrameListener::frameStarted(); it gets the same fix. And a range for
in Building.cpp iterated the building objects through a reference of the wrong
pointer type, converting every element to a temporary pair on the way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 3804e4a)
Every seat writes the tiles it has ever seen, and the loop over the columns of
the map was bounded by the height of the map instead of its width. On a map
taller than it is wide it read past the end of the array: DuelToDeath is 120 by
151, and saving a game there left a level file cut off in the middle of that
list and took the game down with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit ab2858d)
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.

2 participants