Skip to content

Detach player corpses from a map when their grid unloads - #489

Open
ornfelt wants to merge 2 commits into
mangoszero:masterfrom
ornfelt:fix/corpse-dangling-map
Open

ornfelt wants to merge 2 commits into
mangoszero:masterfrom
ornfelt:fix/corpse-dangling-map

Conversation

@ornfelt

@ornfelt ornfelt commented Nov 11, 2026 •

Copy link
Copy Markdown

Summary

Resurrectable player corpses are world objects owned by the CorpseManager and outlive their grid. ObjectGridUnloader only visits the GridTypeMapContainer and WorldObject::ResetMap() is empty, so a corpse kept its m_currMap after the grid unloaded. When the map itself was deleted - a battleground that ended while its owner was offline as a ghost - the next visibility check of the dead owner read the freed map and crashed the server:

Creature::IsVisibleInGridForPlayer -> InReach(*corpse, ...) -> InCommonFrame -> corpse->GetMap()->AsTransport()

  • take the player corpses of a grid out of the world and clear their map pointer when the whole grid unloads (ObjectGridUnloader::UnloadN(), new ObjectWorldUnloader, WorldObject::ForgetMap()); a single-cell unload (Map::UnloadCell()) leaves them alone, since AddCorpsesToGrid() links every corpse of the grid into the loading cell. LoadHelper(CellCorpseSet) puts them back with SetMap / AddToWorld when the grid loads again, as before
  • match the map in CorpseManager::AddCorpsesToGrid(): it compared only the grid coordinates, so another map's corpse could be linked into the grid
  • use FindMap() instead of GetMap() in InCommonFrame
  • skip a corpse that is not in the world in Creature::IsVisibleInGridForPlayer, Player::IsVisibleInGridForPlayer and HandleReclaimCorpseOpcode, as the note on Map::GetCorpse already asks

Verification

  • built mangosd in Release and RelWithDebInfo
  • resolved the original crash report against a linker map and the disassembly to the InCommonFrame read above
  • reproduced the sequence with the fix on a 1.12.1 client: died in Warsong Gulch, logged out as a ghost, removed from the battleground after MAX_OFFLINE_TIME, the battleground map unloaded (corpse detached), then logged in dead with the corpse out of the world - no crash, then revived normally
  • repeated after the review changes (whole-grid detach, map match): died in Warsong Gulch, logged out as a ghost, the battleground map unloaded, logged in at the battleground entry point - no crash
  • tested on a fork build that also carries local battleground changes

Corpses loaded from the database already stay out of the world until their grid loads; a corpse in an unloaded grid now behaves the same, so CorpseManager::Find no longer returns it until then (FindForPlayer is unchanged).

No database, configuration, or submodule changes are included.


This change is Reviewable

Resurrectable corpses are world objects owned by the CorpseManager and
outlive their grid. When the map itself goes away - an ended
battleground, an unloaded instance - the corpse kept pointing at the
deleted map, and the next visibility check of its dead owner
(Creature::IsVisibleInGridForPlayer -> InReach -> InCommonFrame) read
the freed map and crashed the server. Seen with a player who died in
Warsong Gulch, logged out as a ghost and logged in after the
battleground was gone.

The grid unloader now takes the corpses of the grid out of the world
and clears their map; the grid loader puts them back as before.
InCommonFrame uses FindMap(), and the corpse users (both visibility
checks, corpse reclaim) skip a corpse that is not in the world.
Copilot AI balanced review requested due to automatic review settings November 11, 2026 05:45
@codacy-production

codacy-production Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 6 complexity · 2 duplication

Metric Results
Complexity 6
Duplication 2

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Corpse unloading can detach corpses from still-loaded cells or unrelated maps, leaving players unable to see or reclaim them.

2 open issues
What changed in this PR

This PR addresses a dead-player login crash by detaching persistent player corpses from unloaded maps and guarding operations that require an in-world corpse.

Changes:

  • Add a world-object unload visitor that removes corpses from the world and clears their map pointers.
  • Use nullable map lookup in position comparisons.
  • Skip out-of-world corpses during visibility checks and corpse reclaim.
File Description
src/​game/​WorldHandlers/​ObjectGridLoader.h Declare the corpse-detachment visitor.
src/​game/​WorldHandlers/​ObjectGridLoader.cpp Visit world-object corpses during unloading and detach them.
src/​game/​WorldHandlers/​MiscHandler.cpp Reject reclaim requests for out-of-world corpses.
src/​game/​Object/​WorldObjectPosition.cpp Use FindMap() for nullable map access.
src/​game/​Object/​PlayerVisibility.cpp Ignore out-of-world corpses in player visibility checks.
src/​game/​Object/​Object.h Add ForgetMap() to clear retained map pointers.
src/​game/​Object/​Creature.cpp Ignore out-of-world corpses in creature visibility checks.

Review effort: Balanced

Comment on lines +464 to +466
corpse->RemoveFromWorld();
}
corpse->ForgetMap();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agreed, and that matching was already wrong before this change (a corpse could be linked into another map's grid with the same coordinates). Fixed in 30fedf4: CorpseManager::AddCorpsesToGrid() now skips corpses whose map ID is not the loading map's, before the instance check.

@AppVeyorBot

Copy link
Copy Markdown

AddCorpsesToGrid() links every corpse of a grid into the cell that
loaded it, so detaching corpses in ObjectGridUnloader::Unload() also
ran for Map::UnloadCell() and could take corpses of cells that stay
loaded out of the world. Detach them in UnloadN() only.

AddCorpsesToGrid() also matched the grid coordinates without the map,
so loading a continent grid could link another map's corpse into it.
Skip corpses of other maps.
Copilot AI balanced review requested due to automatic review settings November 11, 2026 09:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues were identified in the changes addressing stale corpse map pointers during whole-grid teardown.

1 open issue
1 resolved since last review

Review effort: Balanced

@AppVeyorBot

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants