diff --git a/src/game/Object/CorpseManager.cpp b/src/game/Object/CorpseManager.cpp index 026a05c63..aa3b95637 100644 --- a/src/game/Object/CorpseManager.cpp +++ b/src/game/Object/CorpseManager.cpp @@ -152,6 +152,12 @@ void CorpseManager::AddCorpsesToGrid(GridPair const& gridpair, GridType& grid, M return; } + // the same grid coordinates exist on every map + if (corpse->GetMapId() != map->GetId()) + { + return; + } + // On an instanceable map take only the corpses belonging to this instance. if (map->Instanceable()) { diff --git a/src/game/Object/Creature.cpp b/src/game/Object/Creature.cpp index 50a954e95..0badba801 100644 --- a/src/game/Object/Creature.cpp +++ b/src/game/Object/Creature.cpp @@ -2429,7 +2429,8 @@ bool Creature::IsVisibleInGridForPlayer(Player* pl) const if (IsAlive()) { Corpse* corpse = pl->GetCorpse(); - if (corpse) + // a corpse can be out of the world (its grid unloaded, its battleground ended): no map to compare + if (corpse && corpse->IsInWorld()) { // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level if (InReach(*corpse, *this, (20 + 25)*sWorld.getConfig(CONFIG_FLOAT_RATE_CREATURE_AGGRO))) diff --git a/src/game/Object/Object.h b/src/game/Object/Object.h index 8c9eadd73..b1d86d32f 100644 --- a/src/game/Object/Object.h +++ b/src/game/Object/Object.h @@ -863,6 +863,11 @@ class WorldObject : public Object // used to check all object's GetMap() calls when object is not in world! void ResetMap(); + /// Drops the map pointer of an object that outlives its map: a player corpse stays in the + /// CorpseManager after its grid unloaded, and its map can be destroyed (an ended battleground, + /// an unloaded instance). FindMap() is NULL afterwards; the grid loader sets the map again. + void ForgetMap() { m_currMap = NULL; } + TerrainInfo const* GetTerrain() const; void AddToClientUpdateList() override; diff --git a/src/game/Object/PlayerVisibility.cpp b/src/game/Object/PlayerVisibility.cpp index 57260b243..66b9d6e13 100644 --- a/src/game/Object/PlayerVisibility.cpp +++ b/src/game/Object/PlayerVisibility.cpp @@ -116,7 +116,9 @@ bool Player::IsVisibleInGridForPlayer(Player* pl) const // Dead player see live players near own corpse if (IsAlive()) { - if (Corpse* corpse = pl->GetCorpse()) + Corpse* corpse = pl->GetCorpse(); + // a corpse can be out of the world (its grid unloaded, its battleground ended): no map to compare + if (corpse && corpse->IsInWorld()) { // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level if (InReach(*corpse, *this, (20 + 25) * sWorld.getConfig(CONFIG_FLOAT_RATE_CREATURE_AGGRO))) diff --git a/src/game/Object/WorldObjectPosition.cpp b/src/game/Object/WorldObjectPosition.cpp index 548095105..5a4a982a3 100644 --- a/src/game/Object/WorldObjectPosition.cpp +++ b/src/game/Object/WorldObjectPosition.cpp @@ -250,8 +250,9 @@ static bool InCommonFrame(WorldObject const& a, WorldObject const& b, return true; } - TransportMap* va = a.GetMap() ? a.GetMap()->AsTransport() : NULL; - TransportMap* vb = b.GetMap() ? b.GetMap()->AsTransport() : NULL; + // FindMap, not GetMap: a player corpse whose grid unloaded has no map any more (GetMap asserts) + TransportMap* va = a.FindMap() ? a.FindMap()->AsTransport() : NULL; + TransportMap* vb = b.FindMap() ? b.FindMap()->AsTransport() : NULL; if (!va && !vb) { diff --git a/src/game/WorldHandlers/MiscHandler.cpp b/src/game/WorldHandlers/MiscHandler.cpp index 1bd220149..1530e17cf 100644 --- a/src/game/WorldHandlers/MiscHandler.cpp +++ b/src/game/WorldHandlers/MiscHandler.cpp @@ -668,7 +668,8 @@ void WorldSession::HandleReclaimCorpseOpcode(WorldPacket& recv_data) Corpse* corpse = GetPlayer()->GetCorpse(); - if (!corpse) + // out of the world, the corpse has no map to reach it in (its grid unloaded, its battleground ended) + if (!corpse || !corpse->IsInWorld()) { return; } diff --git a/src/game/WorldHandlers/ObjectGridLoader.cpp b/src/game/WorldHandlers/ObjectGridLoader.cpp index ac8543fe5..c7eeecabc 100644 --- a/src/game/WorldHandlers/ObjectGridLoader.cpp +++ b/src/game/WorldHandlers/ObjectGridLoader.cpp @@ -443,6 +443,41 @@ ObjectGridUnloader::Unload(GridType& grid) grid.Visit(unloader); } +void ObjectGridUnloader::DetachCorpsesN() +{ + // the resurrectable player corpses are world objects and not deleted with the grid; only a whole + // grid detaches them - AddCorpsesToGrid puts every corpse of the grid in the cell that loaded it, + // so a single cell unload would also detach corpses of cells that stay loaded + ObjectWorldUnloader worldUnloader; + TypeContainerVisitor corpses(worldUnloader); + for (unsigned int x = 0; x < MAX_NUMBER_OF_CELLS; ++x) + { + for (unsigned int y = 0; y < MAX_NUMBER_OF_CELLS; ++y) + { + i_grid(x, y).Visit(corpses); + } + } +} + +void ObjectWorldUnloader::Visit(CorpseMapType& m) +{ + // collect first: RemoveFromWorld must not run while the references are walked + std::vector corpses; + for (CorpseMapType::iterator iter = m.begin(); iter != m.end(); ++iter) + { + corpses.push_back(iter->getSource()); + } + + for (Corpse* corpse : corpses) + { + if (corpse->IsInWorld()) + { + corpse->RemoveFromWorld(); + } + corpse->ForgetMap(); + } +} + template void ObjectGridUnloader::Visit(GridRefManager& m) { diff --git a/src/game/WorldHandlers/ObjectGridLoader.h b/src/game/WorldHandlers/ObjectGridLoader.h index 7c616b157..2c8a47116 100644 --- a/src/game/WorldHandlers/ObjectGridLoader.h +++ b/src/game/WorldHandlers/ObjectGridLoader.h @@ -77,14 +77,28 @@ class ObjectGridUnloader Unload(i_grid(x, y)); } } + DetachCorpsesN(); } void Unload(GridType& grid); + void DetachCorpsesN(); template void Visit(GridRefManager& m); private: NGridType& i_grid; }; +/** + * Takes the player corpses of a grid that unloads out of the world. The CorpseManager owns them and + * they outlive the grid - and the map, which an ended battleground or an unloaded instance deletes - + * so they must not keep pointing at it; LoadHelper puts them back when the grid loads again. + */ +class ObjectWorldUnloader +{ + public: + void Visit(CorpseMapType& m); + template void Visit(GridRefManager&) {} +}; + class ObjectGridStoper { public: