diff options
Diffstat (limited to 'src/server/game')
23 files changed, 322 insertions, 415 deletions
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 1a51bb2d897..7c507ad59b6 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -208,7 +208,7 @@ void SmartAI::EndPath(bool fail) if (targets->size() == 1 && GetScript()->IsPlayer((*targets->begin()))) { Player* player = (*targets->begin())->ToPlayer(); - if (!fail && player->IsAtGroupRewardDistance(me) && !player->GetCorpse()) + if (!fail && player->IsAtGroupRewardDistance(me) && !player->HasCorpse()) player->GroupEventHappens(mEscortQuestID, me); if (fail && player->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) @@ -220,7 +220,7 @@ void SmartAI::EndPath(bool fail) { Player* groupGuy = groupRef->GetSource(); - if (!fail && groupGuy->IsAtGroupRewardDistance(me) && !groupGuy->GetCorpse()) + if (!fail && groupGuy->IsAtGroupRewardDistance(me) && !groupGuy->HasCorpse()) groupGuy->AreaExploredOrEventHappens(mEscortQuestID); if (fail && groupGuy->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) groupGuy->FailQuest(mEscortQuestID); @@ -233,7 +233,7 @@ void SmartAI::EndPath(bool fail) if (GetScript()->IsPlayer((*iter))) { Player* player = (*iter)->ToPlayer(); - if (!fail && player->IsAtGroupRewardDistance(me) && !player->GetCorpse()) + if (!fail && player->IsAtGroupRewardDistance(me) && !player->HasCorpse()) player->AreaExploredOrEventHappens(mEscortQuestID); if (fail && player->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) player->FailQuest(mEscortQuestID); diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 46181011719..d22ac0ea5d5 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -695,7 +695,7 @@ void BfGraveyard::Resurrect() player->CastSpell(player, 6962, true); player->CastSpell(player, SPELL_SPIRIT_HEAL_MANA, true); - sObjectAccessor->ConvertCorpseForPlayer(player->GetGUID()); + player->SpawnCorpseBones(false); } m_ResurrectQueue.clear(); diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 2b4d5a4df5e..ad8a7b9e729 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -367,7 +367,7 @@ inline void Battleground::_ProcessResurrect(uint32 diff) player->ResurrectPlayer(1.0f); player->CastSpell(player, 6962, true); player->CastSpell(player, SPELL_SPIRIT_HEAL_MANA, true); - sObjectAccessor->ConvertCorpseForPlayer(*itr); + player->SpawnCorpseBones(false); } m_ResurrectQueue.clear(); } @@ -889,8 +889,11 @@ void Battleground::RemovePlayerAtLeave(ObjectGuid guid, bool Transport, bool Sen player->SpawnCorpseBones(); } } - else // try to resurrect the offline player. If he is alive nothing will happen - sObjectAccessor->ConvertCorpseForPlayer(guid); + else + { + SQLTransaction trans(nullptr); + Player::OfflineResurrect(guid, trans); + } RemovePlayer(player, guid, team); // BG subclass specific code diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index 11c0885beef..360cfdf1ae3 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -22,6 +22,7 @@ #include "UpdateMask.h" #include "ObjectAccessor.h" #include "DatabaseEnv.h" +#include "World.h" Corpse::Corpse(CorpseType type) : WorldObject(type != CORPSE_BONES), m_type(type) { @@ -138,10 +139,14 @@ void Corpse::DeleteBonesFromWorld() void Corpse::DeleteFromDB(SQLTransaction& trans) { - PreparedStatement* stmt = NULL; - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE); - stmt->setUInt32(0, GetOwnerGUID().GetCounter()); - trans->Append(stmt); + DeleteFromDB(GetOwnerGUID(), trans); +} + +void Corpse::DeleteFromDB(ObjectGuid const& ownerGuid, SQLTransaction& trans) +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE); + stmt->setUInt32(0, ownerGuid.GetCounter()); + CharacterDatabase.ExecuteOrAppend(trans, stmt); } bool Corpse::LoadCorpseFromDB(uint32 guid, Field* fields) @@ -193,6 +198,10 @@ bool Corpse::LoadCorpseFromDB(uint32 guid, Field* fields) bool Corpse::IsExpired(time_t t) const { + // Deleted character + if (!sWorld->GetCharacterNameData(GetOwnerGUID())) + return true; + if (m_type == CORPSE_BONES) return m_time < t - 60 * MINUTE; else diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index c3a693d30d3..eeb22a064ba 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -63,6 +63,7 @@ class Corpse : public WorldObject, public GridObject<Corpse> void DeleteBonesFromWorld(); void DeleteFromDB(SQLTransaction& trans); + static void DeleteFromDB(ObjectGuid const& ownerGuid, SQLTransaction& trans); ObjectGuid GetOwnerGUID() const { return GetGuidValue(CORPSE_FIELD_OWNER); } diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index f5af3556332..21a65440a55 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1743,7 +1743,7 @@ void WorldObject::SendObjectDeSpawnAnim(ObjectGuid guid) void WorldObject::SetMap(Map* map) { ASSERT(map); - ASSERT(!IsInWorld() || GetTypeId() == TYPEID_CORPSE); + ASSERT(!IsInWorld()); if (m_currMap == map) // command add npc: first create, than loadfromdb return; if (m_currMap) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b5d3ebaa562..e4452b84da4 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2022,6 +2022,8 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) uint32 charFlags = 0; uint32 playerFlags = fields[14].GetUInt32(); + if (atLoginFlags & AT_LOGIN_RESURRECT) + playerFlags &= ~PLAYER_FLAGS_GHOST; if (playerFlags & PLAYER_FLAGS_HIDE_HELM) charFlags |= CHARACTER_FLAG_HIDE_HELM; if (playerFlags & PLAYER_FLAGS_HIDE_CLOAK) @@ -4537,10 +4539,6 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe charDelete_method = CHAR_DELETE_REMOVE; } - // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry) - // bones will be deleted by corpse/bones deleting thread shortly - sObjectAccessor->ConvertCorpseForPlayer(playerguid); - if (uint32 guildId = GetGuildIdFromDB(playerguid)) if (Guild* guild = sGuildMgr->GetGuildById(guildId)) guild->DeleteMember(playerguid, false, false, true); @@ -4841,6 +4839,8 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt32(0, guid); trans->Append(stmt); + Corpse::DeleteFromDB(playerguid, trans); + CharacterDatabase.CommitTransaction(trans); break; } @@ -4940,18 +4940,17 @@ void Player::BuildPlayerRepop() // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK // there must be SMSG.STOP_MIRROR_TIMER - // there we must send 888 opcode - // the player cannot have a corpse already, only bones which are not returned by GetCorpse - if (GetCorpse()) + // the player cannot have a corpse already on current map, only bones which are not returned by GetCorpse + WorldLocation corpseLocation = GetCorpseLocation(); + if (corpseLocation.GetMapId() == GetMapId()) { TC_LOG_ERROR("entities.player", "BuildPlayerRepop: player %s(%d) already has a corpse", GetName().c_str(), GetGUID().GetCounter()); return; } // create a corpse and place it at the player's location - CreateCorpse(); - Corpse* corpse = GetCorpse(); + Corpse* corpse = CreateCorpse(); if (!corpse) { TC_LOG_ERROR("entities.player", "Error creating corpse for Player %s [%u]", GetName().c_str(), GetGUID().GetCounter()); @@ -5093,7 +5092,16 @@ void Player::KillPlayer() UpdateObjectVisibility(); } -void Player::CreateCorpse() +void Player::OfflineResurrect(ObjectGuid const& guid, SQLTransaction& trans) +{ + Corpse::DeleteFromDB(guid, trans); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + stmt->setUInt16(0, uint16(AT_LOGIN_RESURRECT)); + stmt->setUInt64(1, guid.GetCounter()); + CharacterDatabase.ExecuteOrAppend(trans, stmt); +} + +Corpse* Player::CreateCorpse() { // prevent existence 2 corpse for player SpawnCorpseBones(); @@ -5106,9 +5114,11 @@ void Player::CreateCorpse() if (!corpse->Create(GetMap()->GenerateLowGuid<HighGuid::Corpse>(), this)) { delete corpse; - return; + return nullptr; } + _corpseLocation.WorldRelocate(*this); + _uf = GetUInt32Value(UNIT_FIELD_BYTES_0); _pb = GetUInt32Value(PLAYER_BYTES); _pb2 = GetUInt32Value(PLAYER_BYTES_2); @@ -5159,19 +5169,21 @@ void Player::CreateCorpse() corpse->SaveToDB(); // register for player, but not show - sObjectAccessor->AddCorpse(corpse); + GetMap()->AddCorpse(corpse); + return corpse; } -void Player::SpawnCorpseBones() +void Player::SpawnCorpseBones(bool triggerSave /*= true*/) { - if (sObjectAccessor->ConvertCorpseForPlayer(GetGUID())) - if (!GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player - SaveToDB(); // prevent loading as ghost without corpse + _corpseLocation.WorldRelocate(); + if (GetMap()->ConvertCorpseToBones(GetGUID())) + if (triggerSave && !GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player + SaveToDB(); // prevent loading as ghost without corpse } Corpse* Player::GetCorpse() const { - return sObjectAccessor->GetCorpseForPlayerGUID(GetGUID()); + return GetMap()->GetCorpseByPlayer(GetGUID()); } void Player::DurabilityLossAll(double percent, bool inventory) @@ -8580,9 +8592,11 @@ void Player::RemovedInsignia(Player* looterPlr) RepopAtGraveyard(); } + _corpseLocation.WorldRelocate(); + // We have to convert player corpse to bones, not to be able to resurrect there // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG - Corpse* bones = sObjectAccessor->ConvertCorpseForPlayer(GetGUID(), true); + Corpse* bones = GetMap()->ConvertCorpseToBones(GetGUID(), true); if (!bones) return; @@ -17910,18 +17924,24 @@ void Player::_LoadGlyphAuras() } } -void Player::LoadCorpse() +void Player::LoadCorpse(PreparedQueryResult result) { - if (IsAlive()) - sObjectAccessor->ConvertCorpseForPlayer(GetGUID()); - else + if (IsAlive() || HasAtLoginFlag(AT_LOGIN_RESURRECT)) + SpawnCorpseBones(false); + + if (!IsAlive()) { - if (Corpse* corpse = GetCorpse()) - ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable()); + if (result && !HasAtLoginFlag(AT_LOGIN_RESURRECT)) + { + Field* fields = result->Fetch(); + _corpseLocation.WorldRelocate(fields[0].GetUInt16(), fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat()); + ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(_corpseLocation.GetMapId())->Instanceable()); + } else - //Prevent Dead Player login without corpse ResurrectPlayer(0.5f); } + + RemoveAtLoginFlag(AT_LOGIN_RESURRECT); } void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 94d0fa5697c..e798f5d98d7 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -503,15 +503,16 @@ enum PlayerExtraFlags // 2^n values enum AtLoginFlags { - AT_LOGIN_NONE = 0x00, - AT_LOGIN_RENAME = 0x01, - AT_LOGIN_RESET_SPELLS = 0x02, - AT_LOGIN_RESET_TALENTS = 0x04, - AT_LOGIN_CUSTOMIZE = 0x08, - AT_LOGIN_RESET_PET_TALENTS = 0x10, - AT_LOGIN_FIRST = 0x20, - AT_LOGIN_CHANGE_FACTION = 0x40, - AT_LOGIN_CHANGE_RACE = 0x80 + AT_LOGIN_NONE = 0x000, + AT_LOGIN_RENAME = 0x001, + AT_LOGIN_RESET_SPELLS = 0x002, + AT_LOGIN_RESET_TALENTS = 0x004, + AT_LOGIN_CUSTOMIZE = 0x008, + AT_LOGIN_RESET_PET_TALENTS = 0x010, + AT_LOGIN_FIRST = 0x020, + AT_LOGIN_CHANGE_FACTION = 0x040, + AT_LOGIN_CHANGE_RACE = 0x080, + AT_LOGIN_RESURRECT = 0x100, }; typedef std::map<uint32, QuestStatusData> QuestStatusMap; @@ -819,6 +820,7 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES = 30, PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS = 31, PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS = 32, + PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION = 33, MAX_PLAYER_LOGIN_QUERY }; @@ -1343,7 +1345,7 @@ class Player : public Unit, public GridObject<Player> void AddItemDurations(Item* item); void RemoveItemDurations(Item* item); void SendItemDurations(); - void LoadCorpse(); + void LoadCorpse(PreparedQueryResult result); void LoadPet(); bool AddItem(uint32 itemId, uint32 count); @@ -1835,17 +1837,20 @@ class Player : public Unit, public GridObject<Player> bool UpdatePosition(const Position &pos, bool teleport = false) { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); } void UpdateUnderwaterState(Map* m, float x, float y, float z) override; - void SendMessageToSet(WorldPacket* data, bool self) override {SendMessageToSetInRange(data, GetVisibilityRange(), self); }// overwrite Object::SendMessageToSet - void SendMessageToSetInRange(WorldPacket* data, float fist, bool self) override;// overwrite Object::SendMessageToSetInRange + void SendMessageToSet(WorldPacket* data, bool self) override { SendMessageToSetInRange(data, GetVisibilityRange(), self); } + void SendMessageToSetInRange(WorldPacket* data, float dist, bool self) override; void SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool own_team_only); void SendMessageToSet(WorldPacket* data, Player const* skipped_rcvr) override; void SendTeleportAckPacket(); Corpse* GetCorpse() const; - void SpawnCorpseBones(); - void CreateCorpse(); + void SpawnCorpseBones(bool triggerSave = true); + Corpse* CreateCorpse(); void KillPlayer(); + static void OfflineResurrect(ObjectGuid const& guid, SQLTransaction& trans); + bool HasCorpse() const { return _corpseLocation.GetMapId() != MAPID_INVALID; } + WorldLocation GetCorpseLocation() const { return _corpseLocation; } uint32 GetResurrectionSpellId(); void ResurrectPlayer(float restore_percent, bool applySickness = false); void BuildPlayerRepop(); @@ -2624,6 +2629,8 @@ class Player : public Unit, public GridObject<Player> uint32 _pendingBindTimer; uint32 _activeCheats; + + WorldLocation _corpseLocation; }; void AddItemsSetItem(Player* player, Item* item); diff --git a/src/server/game/Globals/ObjectAccessor.cpp b/src/server/game/Globals/ObjectAccessor.cpp index f5e9b6bb5b7..ca974ba9a37 100644 --- a/src/server/game/Globals/ObjectAccessor.cpp +++ b/src/server/game/Globals/ObjectAccessor.cpp @@ -34,10 +34,6 @@ #include <boost/thread/shared_mutex.hpp> #include <boost/thread/locks.hpp> -ObjectAccessor::ObjectAccessor() { } - -ObjectAccessor::~ObjectAccessor() { } - WorldObject* ObjectAccessor::GetWorldObject(WorldObject const& p, ObjectGuid const& guid) { switch (guid.GetHigh()) @@ -210,6 +206,11 @@ Player* ObjectAccessor::FindConnectedPlayerByName(std::string const& name) return NULL; } +HashMapHolder<Player>::MapType const& ObjectAccessor::GetPlayers() +{ + return HashMapHolder<Player>::GetContainer(); +} + void ObjectAccessor::SaveAllPlayers() { boost::shared_lock<boost::shared_mutex> lock(*HashMapHolder<Player>::GetLock()); @@ -219,185 +220,6 @@ void ObjectAccessor::SaveAllPlayers() itr->second->SaveToDB(); } -Corpse* ObjectAccessor::GetCorpseForPlayerGUID(ObjectGuid const& guid) -{ - boost::shared_lock<boost::shared_mutex> lock(_corpseLock); - - Player2CorpsesMapType::iterator iter = i_player2corpse.find(guid); - if (iter == i_player2corpse.end()) - return NULL; - - ASSERT(iter->second->GetType() != CORPSE_BONES); - - return iter->second; -} - -void ObjectAccessor::RemoveCorpse(Corpse* corpse) -{ - ASSERT(corpse && corpse->GetType() != CORPSE_BONES); - - boost::upgrade_lock<boost::shared_mutex> lock(_corpseLock); - - /// @todo more works need to be done for corpse and other world object - if (Map* map = corpse->FindMap()) - { - corpse->DestroyForNearbyPlayers(); - if (corpse->IsInGrid()) - map->RemoveFromMap(corpse, false); - else - { - corpse->RemoveFromWorld(); - corpse->ResetMap(); - } - } - else - corpse->RemoveFromWorld(); - - // Critical section - { - boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock); - - Player2CorpsesMapType::iterator iter = i_player2corpse.find(corpse->GetOwnerGUID()); - if (iter == i_player2corpse.end()) /// @todo Fix this - return; - - // build mapid*cellid -> guid_set map - CellCoord cellCoord = Trinity::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); - sObjectMgr->DeleteCorpseCellData(corpse->GetMapId(), cellCoord.GetId(), corpse->GetOwnerGUID().GetCounter()); - - i_player2corpse.erase(iter); - } -} - -void ObjectAccessor::AddCorpse(Corpse* corpse) -{ - ASSERT(corpse && corpse->GetType() != CORPSE_BONES); - - // Critical section - { - boost::unique_lock<boost::shared_mutex> lock(_corpseLock); - - ASSERT(i_player2corpse.find(corpse->GetOwnerGUID()) == i_player2corpse.end()); - i_player2corpse[corpse->GetOwnerGUID()] = corpse; - - // build mapid*cellid -> guid_set map - CellCoord cellCoord = Trinity::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); - sObjectMgr->AddCorpseCellData(corpse->GetMapId(), cellCoord.GetId(), corpse->GetOwnerGUID().GetCounter(), corpse->GetInstanceId()); - } -} - -void ObjectAccessor::AddCorpsesToGrid(GridCoord const& gridpair, GridType& grid, Map* map) -{ - boost::shared_lock<boost::shared_mutex> lock(_corpseLock); - - for (Player2CorpsesMapType::iterator iter = i_player2corpse.begin(); iter != i_player2corpse.end(); ++iter) - { - // We need this check otherwise a corpose may be added to a grid twice - if (iter->second->IsInGrid()) - continue; - - if (iter->second->GetGridCoord() == gridpair) - { - // verify, if the corpse in our instance (add only corpses which are) - if (map->Instanceable()) - { - if (iter->second->GetInstanceId() == map->GetInstanceId()) - grid.AddWorldObject(iter->second); - } - else - grid.AddWorldObject(iter->second); - } - } -} - -Corpse* ObjectAccessor::ConvertCorpseForPlayer(ObjectGuid const& player_guid, bool insignia /*=false*/) -{ - Corpse* corpse = GetCorpseForPlayerGUID(player_guid); - if (!corpse) - { - //in fact this function is called from several places - //even when player doesn't have a corpse, not an error - return NULL; - } - - TC_LOG_DEBUG("misc", "Deleting Corpse and spawned bones."); - - // Map can be NULL - Map* map = corpse->FindMap(); - - // remove corpse from player_guid -> corpse map and from current map - RemoveCorpse(corpse); - - // remove corpse from DB - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - corpse->DeleteFromDB(trans); - CharacterDatabase.CommitTransaction(trans); - - Corpse* bones = NULL; - // create the bones only if the map and the grid is loaded at the corpse's location - // ignore bones creating option in case insignia - - if (map && (insignia || - (map->IsBattlegroundOrArena() ? sWorld->getBoolConfig(CONFIG_DEATH_BONES_BG_OR_ARENA) : sWorld->getBoolConfig(CONFIG_DEATH_BONES_WORLD))) && - !map->IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY())) - { - // Create bones, don't change Corpse - bones = new Corpse; - bones->Create(corpse->GetGUID().GetCounter(), map); - - for (uint8 i = OBJECT_FIELD_TYPE + 1; i < CORPSE_END; ++i) // don't overwrite guid and object type - bones->SetUInt32Value(i, corpse->GetUInt32Value(i)); - - bones->SetGridCoord(corpse->GetGridCoord()); - // bones->m_time = m_time; // don't overwrite time - // bones->m_type = m_type; // don't overwrite type - bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); - bones->SetPhaseMask(corpse->GetPhaseMask(), false); - - bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); - bones->SetGuidValue(CORPSE_FIELD_OWNER, ObjectGuid::Empty); - - for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - if (corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i)) - bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0); - } - - // add bones in grid store if grid loaded where corpse placed - map->AddToMap(bones); - } - - // all references to the corpse should be removed at this point - delete corpse; - - return bones; -} - -void ObjectAccessor::RemoveOldCorpses() -{ - time_t now = time(NULL); - Player2CorpsesMapType::iterator next; - for (Player2CorpsesMapType::iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); itr = next) - { - next = itr; - ++next; - - if (!itr->second->IsExpired(now)) - continue; - - ConvertCorpseForPlayer(itr->first); - } -} - -void ObjectAccessor::UnloadAll() -{ - for (Player2CorpsesMapType::const_iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); ++itr) - { - itr->second->RemoveFromWorld(); - delete itr->second; - } -} - /// Define the static members of HashMapHolder template <class T> typename HashMapHolder<T>::MapType HashMapHolder<T>::_objectMap; diff --git a/src/server/game/Globals/ObjectAccessor.h b/src/server/game/Globals/ObjectAccessor.h index 54e2a7298ef..d5fb8564b96 100644 --- a/src/server/game/Globals/ObjectAccessor.h +++ b/src/server/game/Globals/ObjectAccessor.h @@ -85,85 +85,47 @@ class HashMapHolder static MapType _objectMap; }; -class ObjectAccessor +namespace ObjectAccessor { - private: - ObjectAccessor(); - ~ObjectAccessor(); - ObjectAccessor(const ObjectAccessor&); - ObjectAccessor& operator=(const ObjectAccessor&); - - public: - /// @todo: Override these template functions for each holder type and add assertions - - static ObjectAccessor* instance() - { - static ObjectAccessor instance; - return &instance; - } - - // these functions return objects only if in map of specified object - static WorldObject* GetWorldObject(WorldObject const&, ObjectGuid const&); - static Object* GetObjectByTypeMask(WorldObject const&, ObjectGuid const&, uint32 typemask); - static Corpse* GetCorpse(WorldObject const& u, ObjectGuid const& guid); - static GameObject* GetGameObject(WorldObject const& u, ObjectGuid const& guid); - static Transport* GetTransport(WorldObject const& u, ObjectGuid const& guid); - static DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid); - static Unit* GetUnit(WorldObject const&, ObjectGuid const& guid); - static Creature* GetCreature(WorldObject const& u, ObjectGuid const& guid); - static Pet* GetPet(WorldObject const&, ObjectGuid const& guid); - static Player* GetPlayer(Map const*, ObjectGuid const& guid); - static Player* GetPlayer(WorldObject const&, ObjectGuid const& guid); - static Creature* GetCreatureOrPetOrVehicle(WorldObject const&, ObjectGuid const&); - - // these functions return objects if found in whole world - // ACCESS LIKE THAT IS NOT THREAD SAFE - static Player* FindPlayer(ObjectGuid const&); - static Player* FindPlayerByName(std::string const& name); - - // this returns Player even if he is not in world, for example teleporting - static Player* FindConnectedPlayer(ObjectGuid const&); - static Player* FindConnectedPlayerByName(std::string const& name); - - // when using this, you must use the hashmapholder's lock - static HashMapHolder<Player>::MapType const& GetPlayers() - { - return HashMapHolder<Player>::GetContainer(); - } - - template<class T> static void AddObject(T* object) - { - HashMapHolder<T>::Insert(object); - } - - template<class T> static void RemoveObject(T* object) - { - HashMapHolder<T>::Remove(object); - } - - static void SaveAllPlayers(); - - //non-static functions - - //Thread safe - Corpse* GetCorpseForPlayerGUID(ObjectGuid const& guid); - void RemoveCorpse(Corpse* corpse); - void AddCorpse(Corpse* corpse); - void AddCorpsesToGrid(GridCoord const& gridpair, GridType& grid, Map* map); - Corpse* ConvertCorpseForPlayer(ObjectGuid const& player_guid, bool insignia = false); - - //Thread unsafe - void RemoveOldCorpses(); - void UnloadAll(); - - private: - typedef std::unordered_map<ObjectGuid, Corpse*> Player2CorpsesMapType; - typedef std::unordered_map<Player*, UpdateData>::value_type UpdateDataValueType; - - Player2CorpsesMapType i_player2corpse; - - boost::shared_mutex _corpseLock; + // these functions return objects only if in map of specified object + WorldObject* GetWorldObject(WorldObject const&, ObjectGuid const&); + Object* GetObjectByTypeMask(WorldObject const&, ObjectGuid const&, uint32 typemask); + Corpse* GetCorpse(WorldObject const& u, ObjectGuid const& guid); + GameObject* GetGameObject(WorldObject const& u, ObjectGuid const& guid); + Transport* GetTransport(WorldObject const& u, ObjectGuid const& guid); + DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid); + Unit* GetUnit(WorldObject const&, ObjectGuid const& guid); + Creature* GetCreature(WorldObject const& u, ObjectGuid const& guid); + Pet* GetPet(WorldObject const&, ObjectGuid const& guid); + Player* GetPlayer(Map const*, ObjectGuid const& guid); + Player* GetPlayer(WorldObject const&, ObjectGuid const& guid); + Creature* GetCreatureOrPetOrVehicle(WorldObject const&, ObjectGuid const&); + + // these functions return objects if found in whole world + // ACCESS LIKE THAT IS NOT THREAD SAFE + Player* FindPlayer(ObjectGuid const&); + Player* FindPlayerByName(std::string const& name); + + // this returns Player even if he is not in world, for example teleporting + Player* FindConnectedPlayer(ObjectGuid const&); + Player* FindConnectedPlayerByName(std::string const& name); + + // when using this, you must use the hashmapholder's lock + HashMapHolder<Player>::MapType const& GetPlayers(); + + template<class T> + void AddObject(T* object) + { + HashMapHolder<T>::Insert(object); + } + + template<class T> + void RemoveObject(T* object) + { + HashMapHolder<T>::Remove(object); + } + + void SaveAllPlayers(); }; -#define sObjectAccessor ObjectAccessor::instance() #endif diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 4bf86facbef..b5d8d08a6a4 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -7358,20 +7358,6 @@ void ObjectMgr::DeleteGOData(uint32 guid) _gameObjectDataStore.erase(guid); } -void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance) -{ - // corpses are always added to spawn mode 0 and they are spawned by their instance id - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(mapid, 0)][cellid]; - cell_guids.corpses[player_guid] = instance; -} - -void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid) -{ - // corpses are always added to spawn mode 0 and they are spawned by their instance id - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(mapid, 0)][cellid]; - cell_guids.corpses.erase(player_guid); -} - void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table, bool starter, bool go) { uint32 oldMSTime = getMSTime(); @@ -8590,7 +8576,7 @@ void ObjectMgr::LoadScriptNames() do { - _scriptNamesStore.emplace_back((*result)[0].GetCString()); + _scriptNamesStore.push_back((*result)[0].GetString()); } while (result->NextRow()); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index b9d755636cd..462370001dd 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -445,13 +445,11 @@ struct BroadcastText typedef std::unordered_map<uint32, BroadcastText> BroadcastTextContainer; -typedef std::set<uint32> CellGuidSet; -typedef std::map<uint32/*player guid*/, uint32/*instance*/> CellCorpseSet; +typedef std::set<ObjectGuid::LowType> CellGuidSet; struct CellObjectGuids { CellGuidSet creatures; CellGuidSet gameobjects; - CellCorpseSet corpses; }; typedef std::unordered_map<uint32/*cell_id*/, CellObjectGuids> CellObjectGuidsMap; typedef std::unordered_map<uint32/*(mapid, spawnMode) pair*/, CellObjectGuidsMap> MapObjectGuids; @@ -1219,9 +1217,6 @@ class ObjectMgr LocaleConstant GetDBCLocaleIndex() const { return DBCLocaleIndex; } void SetDBCLocaleIndex(LocaleConstant locale) { DBCLocaleIndex = locale; } - void AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance); - void DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid); - // grid objects void AddCreatureToGrid(uint32 guid, CreatureData const* data); void RemoveCreatureFromGrid(uint32 guid, CreatureData const* data); diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index d0ae90d002f..de0ab490e9c 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -62,7 +62,7 @@ class ObjectWorldLoader { public: explicit ObjectWorldLoader(ObjectGridLoader& gloader) - : i_cell(gloader.i_cell), i_map(gloader.i_map), i_corpses (0) + : i_cell(gloader.i_cell), i_map(gloader.i_map), i_grid(gloader.i_grid), i_corpses(gloader.i_corpses) { } void Visit(CorpseMapType &m); @@ -72,8 +72,9 @@ class ObjectWorldLoader private: Cell i_cell; Map* i_map; + NGridType& i_grid; public: - uint32 i_corpses; + uint32& i_corpses; }; template<class T> void ObjectGridLoader::SetObjectCell(T* /*obj*/, CellCoord const& /*cellCoord*/) { } @@ -129,38 +130,6 @@ void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager<T> } } -void LoadHelper(CellCorpseSet const& cell_corpses, CellCoord &cell, CorpseMapType &m, uint32 &count, Map* map) -{ - if (cell_corpses.empty()) - return; - - for (CellCorpseSet::const_iterator itr = cell_corpses.begin(); itr != cell_corpses.end(); ++itr) - { - if (itr->second != map->GetInstanceId()) - continue; - - ObjectGuid player_guid(HighGuid::Player, itr->first); - - Corpse* obj = sObjectAccessor->GetCorpseForPlayerGUID(player_guid); - if (!obj) - continue; - - /// @todo this is a hack - // corpse's map should be reset when the map is unloaded - // but it may still exist when the grid is unloaded but map is not - // in that case map == currMap - obj->SetMap(map); - - if (obj->IsInGrid()) - { - obj->AddToWorld(); - continue; - } - - AddObjectHelper(cell, m, count, map, obj); - } -} - void ObjectGridLoader::Visit(GameObjectMapType &m) { CellCoord cellCoord = i_cell.GetCellCoord(); @@ -175,22 +144,28 @@ void ObjectGridLoader::Visit(CreatureMapType &m) LoadHelper(cell_guids.creatures, cellCoord, m, i_creatures, i_map); } -void ObjectWorldLoader::Visit(CorpseMapType &m) +void ObjectWorldLoader::Visit(CorpseMapType& /*m*/) { CellCoord cellCoord = i_cell.GetCellCoord(); - // corpses are always added to spawn mode 0 and they are spawned by their instance id - CellObjectGuids const& cell_guids = sObjectMgr->GetCellObjectGuids(i_map->GetId(), 0, cellCoord.GetId()); - LoadHelper(cell_guids.corpses, cellCoord, m, i_corpses, i_map); + if (std::unordered_set<Corpse*> const* corpses = i_map->GetCorpsesInCell(cellCoord.GetId())) + { + for (Corpse* corpse : *corpses) + { + corpse->AddToWorld(); + i_grid.GetGridType(i_cell.CellX(), i_cell.CellY()).AddWorldObject(corpse); + ++i_corpses; + } + } } void ObjectGridLoader::LoadN(void) { i_gameObjects = 0; i_creatures = 0; i_corpses = 0; i_cell.data.Part.cell_y = 0; - for (unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x) + for (uint32 x = 0; x < MAX_NUMBER_OF_CELLS; ++x) { i_cell.data.Part.cell_x = x; - for (unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y) + for (uint32 y = 0; y < MAX_NUMBER_OF_CELLS; ++y) { i_cell.data.Part.cell_y = y; @@ -205,7 +180,6 @@ void ObjectGridLoader::LoadN(void) ObjectWorldLoader worker(*this); TypeContainerVisitor<ObjectWorldLoader, WorldTypeMapContainer> visitor(worker); i_grid.VisitGrid(x, y, visitor); - i_corpses += worker.i_corpses; } } } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 61b2837dc10..ba22ab6aa07 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -203,6 +203,10 @@ bool LoginQueryHolder::Initialize() stmt->setUInt32(0, m_accountId); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_LOCATION); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt); + return res; } @@ -892,7 +896,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation()); } - sObjectAccessor->AddObject(pCurrChar); + ObjectAccessor::AddObject(pCurrChar); //TC_LOG_DEBUG("Player %s added to Map.", pCurrChar->GetName().c_str()); pCurrChar->SendInitialPacketsAfterAddToMap(); @@ -923,7 +927,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) sSocialMgr->SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUID().GetCounter(), true); // Place character in world (and load zone) before some object loading - pCurrChar->LoadCorpse(); + pCurrChar->LoadCorpse(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION)); // setting Ghost+speed if dead if (pCurrChar->m_deathState != ALIVE) @@ -1672,11 +1676,11 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) } } - // resurrect the character in case he's dead - sObjectAccessor->ConvertCorpseForPlayer(factionChangeInfo.Guid); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + // resurrect the character in case he's dead + Player::OfflineResurrect(factionChangeInfo.Guid, trans); + CharacterDatabase.EscapeString(factionChangeInfo.Name); Player::Customize(&factionChangeInfo, trans); @@ -1820,6 +1824,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) trans->Append(stmt); } + /// @todo: make this part async if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) { // Reset guild diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 51f7e3f840a..2eebbfc1004 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -741,7 +741,6 @@ void WorldSession::HandleReclaimCorpseOpcode(WorldPacket& recvData) return; Corpse* corpse = _player->GetCorpse(); - if (!corpse) return; diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 518baa42e97..84816bf3f33 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -142,8 +142,8 @@ void WorldSession::HandleMoveWorldportAckOpcode() } // resurrect character at enter into instance where his corpse exist after add to map - Corpse* corpse = GetPlayer()->GetCorpse(); - if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId()) + Corpse* corpse = GetPlayer()->GetMap()->GetCorpseByPlayer(GetPlayer()->GetGUID()); + if (corpse && corpse->GetType() != CORPSE_BONES) { if (mEntry->IsDungeon()) { diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 14436a864f9..f8d7f73e077 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -403,10 +403,12 @@ void WorldSession::SendSpiritResurrect() // get corpse nearest graveyard WorldSafeLocsEntry const* corpseGrave = NULL; - Corpse* corpse = _player->GetCorpse(); - if (corpse) - corpseGrave = sObjectMgr->GetClosestGraveYard( - corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam()); + WorldLocation corpseLocation = _player->GetCorpseLocation(); + if (_player->HasCorpse()) + { + corpseGrave = sObjectMgr->GetClosestGraveYard(corpseLocation.GetPositionX(), corpseLocation.GetPositionY(), + corpseLocation.GetPositionZ(), corpseLocation.GetMapId(), _player->GetTeam()); + } // now can spawn bones _player->SpawnCorpseBones(); diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 906c3017100..6d896bee26c 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -218,11 +218,7 @@ void WorldSession::HandleGameObjectQueryOpcode(WorldPacket& recvData) void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recvData*/) { - TC_LOG_DEBUG("network", "WORLD: Received MSG_CORPSE_QUERY"); - - Corpse* corpse = GetPlayer()->GetCorpse(); - - if (!corpse) + if (!_player->HasCorpse()) { WorldPacket data(MSG_CORPSE_QUERY, 1); data << uint8(0); // corpse not found @@ -230,24 +226,25 @@ void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recvData*/) return; } - uint32 mapid = corpse->GetMapId(); - float x = corpse->GetPositionX(); - float y = corpse->GetPositionY(); - float z = corpse->GetPositionZ(); - uint32 corpsemapid = mapid; + WorldLocation corpseLocation = _player->GetCorpseLocation(); + uint32 corpseMapID = corpseLocation.GetMapId(); + uint32 mapID = corpseLocation.GetMapId(); + float x = corpseLocation.GetPositionX(); + float y = corpseLocation.GetPositionY(); + float z = corpseLocation.GetPositionZ(); // if corpse at different map - if (mapid != _player->GetMapId()) + if (mapID != _player->GetMapId()) { // search entrance map for proper show entrance - if (MapEntry const* corpseMapEntry = sMapStore.LookupEntry(mapid)) + if (MapEntry const* corpseMapEntry = sMapStore.LookupEntry(mapID)) { if (corpseMapEntry->IsDungeon() && corpseMapEntry->entrance_map >= 0) { // if corpse map have entrance if (Map const* entranceMap = sMapMgr->CreateBaseMap(corpseMapEntry->entrance_map)) { - mapid = corpseMapEntry->entrance_map; + mapID = corpseMapEntry->entrance_map; x = corpseMapEntry->entrance_x; y = corpseMapEntry->entrance_y; z = entranceMap->GetHeight(GetPlayer()->GetPhaseMask(), x, y, MAX_HEIGHT); @@ -258,11 +255,11 @@ void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recvData*/) WorldPacket data(MSG_CORPSE_QUERY, 1+(6*4)); data << uint8(1); // corpse found - data << int32(mapid); + data << int32(mapID); data << float(x); data << float(y); data << float(z); - data << int32(corpsemapid); + data << int32(corpseMapID); data << uint32(0); // unknown SendPacket(&data); } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index b50099b9787..7acafb3c341 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -50,9 +50,9 @@ GridState* si_GridStates[MAX_GRID_STATE]; Map::~Map() { - sScriptMgr->OnDestroyMap(this); + // UnloadAll must be called before deleting the map - UnloadAll(); + sScriptMgr->OnDestroyMap(this); while (!i_worldObjects.empty()) { @@ -400,7 +400,7 @@ void Map::DeleteFromWorld(T* obj) template<> void Map::DeleteFromWorld(Player* player) { - sObjectAccessor->RemoveObject(player); + ObjectAccessor::RemoveObject(player); RemoveUpdateObject(player); /// @todo I do not know why we need this, it should be removed in ~Object anyway delete player; } @@ -468,8 +468,6 @@ bool Map::EnsureGridLoaded(const Cell &cell) ObjectGridLoader loader(*grid, this, cell); loader.LoadN(); - // Add resurrectable corpses to world object list in grid - sObjectAccessor->AddCorpsesToGrid(GridCoord(cell.GridX(), cell.GridY()), grid->GetGridType(cell.CellX(), cell.CellY()), this); Balance(); return true; } @@ -507,6 +505,9 @@ bool Map::AddPlayerToMap(Player* player) player->m_clientGUIDs.clear(); player->UpdateObjectVisibility(false); + if (player->IsAlive()) + ConvertCorpseToBones(player->GetGUID()); + sScriptMgr->OnPlayerEnterMap(this, player); return true; } @@ -2610,7 +2611,7 @@ void Map::SendObjectUpdates() _updateObjects.erase(_updateObjects.begin()); obj->BuildUpdate(update_players); } - + WorldPacket packet; // here we allocate a std::vector with a size of 0x10000 for (UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter) { @@ -3530,7 +3531,6 @@ void Map::LoadCorpseData() if (!result) return; - uint32 count = 0; do { Field* fields = result->Fetch(); @@ -3550,8 +3550,8 @@ void Map::LoadCorpseData() continue; } - sObjectAccessor->AddCorpse(corpse); - ++count; + AddCorpse(corpse); + } while (result->NextRow()); } @@ -3564,6 +3564,97 @@ void Map::DeleteCorpseData() CharacterDatabase.Execute(stmt); } +void Map::AddCorpse(Corpse* corpse) +{ + corpse->SetMap(this); + + CellCoord cellCoord = Trinity::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); + _corpsesByCell[cellCoord.GetId()].insert(corpse); + _corpsesByPlayer[corpse->GetOwnerGUID()] = corpse; +} + +void Map::RemoveCorpse(Corpse* corpse) +{ + ASSERT(corpse && corpse->GetType() != CORPSE_BONES); + + corpse->DestroyForNearbyPlayers(); + if (corpse->IsInGrid()) + RemoveFromMap(corpse, false); + else + { + corpse->RemoveFromWorld(); + corpse->ResetMap(); + } + + CellCoord cellCoord = Trinity::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); + _corpsesByCell[cellCoord.GetId()].erase(corpse); + _corpsesByPlayer.erase(corpse->GetOwnerGUID()); +} + +Corpse* Map::ConvertCorpseToBones(ObjectGuid const& ownerGuid, bool insignia /*= false*/) +{ + Corpse* corpse = GetCorpseByPlayer(ownerGuid); + if (!corpse) + return nullptr; + + RemoveCorpse(corpse); + + // remove corpse from DB + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + corpse->DeleteFromDB(trans); + CharacterDatabase.CommitTransaction(trans); + + Corpse* bones = NULL; + + // create the bones only if the map and the grid is loaded at the corpse's location + // ignore bones creating option in case insignia + if ((insignia || + (IsBattlegroundOrArena() ? sWorld->getBoolConfig(CONFIG_DEATH_BONES_BG_OR_ARENA) : sWorld->getBoolConfig(CONFIG_DEATH_BONES_WORLD))) && + !IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY())) + { + // Create bones, don't change Corpse + bones = new Corpse(); + bones->Create(corpse->GetGUID().GetCounter(), this); + + for (uint8 i = OBJECT_FIELD_TYPE + 1; i < CORPSE_END; ++i) // don't overwrite guid and object type + bones->SetUInt32Value(i, corpse->GetUInt32Value(i)); + + bones->SetGridCoord(corpse->GetGridCoord()); + bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); + bones->SetPhaseMask(corpse->GetPhaseMask(), false); + + bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); + bones->SetGuidValue(CORPSE_FIELD_OWNER, ObjectGuid::Empty); + + for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) + if (corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i)) + bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0); + + // add bones in grid store if grid loaded where corpse placed + AddToMap(bones); + } + + // all references to the corpse should be removed at this point + delete corpse; + + return bones; +} + +void Map::RemoveOldCorpses() +{ + time_t now = time(nullptr); + + std::vector<ObjectGuid> corpses; + corpses.reserve(_corpsesByPlayer.size()); + + for (auto const& p : _corpsesByPlayer) + if (p.second->IsExpired(now)) + corpses.push_back(p.first); + + for (ObjectGuid const& ownerGuid : corpses) + ConvertCorpseToBones(ownerGuid); +} + void Map::SendZoneDynamicInfo(Player* player) { uint32 zoneId = GetZoneId(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 9b5e67cdfaf..2ba5e10c82c 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -459,6 +459,24 @@ class Map : public GridRefManager<NGridType> typedef std::unordered_multimap<ObjectGuid::LowType, GameObject*> GameObjectBySpawnIdContainer; GameObjectBySpawnIdContainer& GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; } + std::unordered_set<Corpse*> const* GetCorpsesInCell(uint32 cellId) const + { + auto itr = _corpsesByCell.find(cellId); + if (itr != _corpsesByCell.end()) + return &itr->second; + + return nullptr; + } + + Corpse* GetCorpseByPlayer(ObjectGuid const& ownerGuid) const + { + auto itr = _corpsesByPlayer.find(ownerGuid); + if (itr != _corpsesByPlayer.end()) + return itr->second; + + return nullptr; + } + MapInstanced* ToMapInstanced() { if (Instanceable()) return reinterpret_cast<MapInstanced*>(this); return NULL; } MapInstanced const* ToMapInstanced() const { if (Instanceable()) return reinterpret_cast<MapInstanced const*>(this); return NULL; } @@ -508,6 +526,10 @@ class Map : public GridRefManager<NGridType> void LoadCorpseData(); void DeleteCorpseData(); + void AddCorpse(Corpse* corpse); + void RemoveCorpse(Corpse* corpse); + Corpse* ConvertCorpseToBones(ObjectGuid const& ownerGuid, bool insignia = false); + void RemoveOldCorpses(); static void DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId); @@ -709,6 +731,8 @@ class Map : public GridRefManager<NGridType> MapStoredObjectTypesContainer _objectsStore; CreatureBySpawnIdContainer _creatureBySpawnIdStore; GameObjectBySpawnIdContainer _gameobjectBySpawnIdStore; + std::unordered_map<uint32/*cellId*/, std::unordered_set<Corpse*>> _corpsesByCell; + std::unordered_map<ObjectGuid, Corpse*> _corpsesByPlayer; std::unordered_set<Object*> _updateObjects; }; diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp index 82be6336dc6..0e7f9dbb611 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -164,10 +164,10 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) if (!player->IsAlive()) { - if (Corpse* corpse = player->GetCorpse()) + if (player->HasCorpse()) { // let enter in ghost mode in instance that connected to inner instance with corpse - uint32 corpseMap = corpse->GetMapId(); + uint32 corpseMap = player->GetCorpseLocation().GetMapId(); do { if (corpseMap == mapid) @@ -184,9 +184,8 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) TC_LOG_DEBUG("maps", "MAP: Player '%s' does not have a corpse in instance '%s' and cannot enter.", player->GetName().c_str(), mapName); return false; } + TC_LOG_DEBUG("maps", "MAP: Player '%s' has corpse in instance '%s' and can enter.", player->GetName().c_str(), mapName); - player->ResurrectPlayer(0.5f, false); - player->SpawnCorpseBones(); } else TC_LOG_DEBUG("maps", "Map::CanPlayerEnter - player '%s' is dead but does not have a corpse!", player->GetName().c_str()); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index ce1837fbc86..2e61aecfb5f 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2150,7 +2150,10 @@ void World::Update(uint32 diff) if (m_timers[WUPDATE_CORPSES].Passed()) { m_timers[WUPDATE_CORPSES].Reset(); - sObjectAccessor->RemoveOldCorpses(); + sMapMgr->DoForAllMaps([](Map* map) + { + map->RemoveOldCorpses(); + }); } ///- Process Game events when necessary @@ -3264,3 +3267,9 @@ void World::ReloadRBAC() if (WorldSession* session = itr->second) session->InvalidateRBACData(); } + +void World::RemoveOldCorpses() +{ + m_timers[WUPDATE_CORPSES].SetCurrent(m_timers[WUPDATE_CORPSES].GetInterval()); +} + diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index af89adcb04e..256b856cf73 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -761,6 +761,8 @@ class World void ReloadRBAC(); + void RemoveOldCorpses(); + protected: void _UpdateGameTime(); // callback for UpdateRealmCharacters |
