diff options
35 files changed, 344 insertions, 444 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 940bee4e3a2..3f6543716f9 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -345,6 +345,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CORPSE_PHASES, "SELECT cp.OwnerGuid, cp.PhaseId FROM corpse_phases cp LEFT JOIN corpse c ON cp.OwnerGuid = c.guid WHERE c.mapId = ? AND c.instanceId = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_INS_CORPSE_PHASES, "INSERT INTO corpse_phases (OwnerGuid, PhaseId) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CORPSE_PHASES, "DELETE FROM corpse_phases WHERE OwnerGuid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CORPSE_LOCATION, "SELECT mapId, posX, posY, posZ, orientation FROM corpse WHERE guid = ?", CONNECTION_ASYNC); // Creature respawn PrepareStatement(CHAR_SEL_CREATURE_RESPAWNS, "SELECT guid, respawnTime FROM creature_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 2054bfa7ca5..f5f907c2bf1 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -285,6 +285,7 @@ enum CharacterDatabaseStatements CHAR_SEL_CORPSE_PHASES, CHAR_INS_CORPSE_PHASES, CHAR_DEL_CORPSE_PHASES, + CHAR_SEL_CORPSE_LOCATION, CHAR_SEL_CREATURE_RESPAWNS, CHAR_REP_CREATURE_RESPAWN, diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index c6a16e1b2fd..d909713a204 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 fbd49acaf0b..dc1ecf7c1c3 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -711,7 +711,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 28205d493de..07c88486c50 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -396,7 +396,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(); } @@ -954,8 +954,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 4745a4d4e1b..4122c3a61c2 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -149,13 +149,18 @@ void Corpse::DeleteBonesFromWorld() void Corpse::DeleteFromDB(SQLTransaction& trans) { + DeleteFromDB(GetOwnerGUID(), trans); +} + +void Corpse::DeleteFromDB(ObjectGuid const& ownerGuid, SQLTransaction& trans) +{ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE); - stmt->setUInt64(0, GetOwnerGUID().GetCounter()); - trans->Append(stmt); + stmt->setUInt64(0, ownerGuid.GetCounter()); + CharacterDatabase.ExecuteOrAppend(trans, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE_PHASES); - stmt->setUInt64(0, GetOwnerGUID().GetCounter()); - trans->Append(stmt); + stmt->setUInt64(0, ownerGuid.GetCounter()); + CharacterDatabase.ExecuteOrAppend(trans, stmt); } bool Corpse::LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields) @@ -202,6 +207,10 @@ bool Corpse::LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields) bool Corpse::IsExpired(time_t t) const { + // Deleted character + if (!sWorld->GetCharacterInfo(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 29ae2a79d84..be2cb435ac9 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/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp index ec1e7050732..60747974a13 100644 --- a/src/server/game/Entities/Creature/GossipDef.cpp +++ b/src/server/game/Entities/Creature/GossipDef.cpp @@ -635,7 +635,7 @@ void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUI offer.QuestGiverGUID = npcGUID; // Is there a better way? what about game objects? - if (Creature const* creature = sObjectAccessor->GetCreature(*_session->GetPlayer(), npcGUID)) + if (Creature const* creature = ObjectAccessor::GetCreature(*_session->GetPlayer(), npcGUID)) offer.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry; offer.QuestID = quest->GetQuestId(); @@ -693,7 +693,7 @@ void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGU packet.QuestGiverGUID = npcGUID; // Is there a better way? what about game objects? - if (Creature const* creature = sObjectAccessor->GetCreature(*_session->GetPlayer(), npcGUID)) + if (Creature const* creature = ObjectAccessor::GetCreature(*_session->GetPlayer(), npcGUID)) packet.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry; packet.QuestID = quest->GetQuestId(); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 15b40c36c4a..9a01eacbf7f 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2166,7 +2166,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 b52172afcc8..85c2869e72a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -4129,10 +4129,6 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe charDeleteMethod = 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 (ObjectGuid::LowType guildId = GetGuildIdFromDB(playerguid)) if (Guild* guild = sGuildMgr->GetGuildById(guildId)) guild->DeleteMember(playerguid, false, false, true); @@ -4452,13 +4448,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt64(0, guid); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE); - stmt->setUInt64(0, guid); - trans->Append(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE_PHASES); - stmt->setUInt64(0, guid); - trans->Append(stmt); + Corpse::DeleteFromDB(playerguid, trans); Garrison::DeleteFromDB(guid, trans); @@ -4544,18 +4534,17 @@ void Player::BuildPlayerRepop() // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_SET_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 (%s) already has a corpse", GetName().c_str(), GetGUID().ToString().c_str()); 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 [%s]", GetName().c_str(), GetGUID().ToString().c_str()); @@ -4697,7 +4686,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(); @@ -4710,9 +4708,11 @@ void Player::CreateCorpse() if (!corpse->Create(GetMap()->GenerateLowGuid<HighGuid::Corpse>(), this)) { delete corpse; - return; + return nullptr; } + _corpseLocation.WorldRelocate(*this); + uint8 skin = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID); uint8 face = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID); uint8 hairstyle = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID); @@ -4756,19 +4756,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) @@ -8315,9 +8317,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; @@ -17438,18 +17442,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_LOCAL_FLAGS, PLAYER_LOCAL_FLAG_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_LOCAL_FLAGS, PLAYER_LOCAL_FLAG_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 05aa3d5bbf4..f139e8e3031 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -642,15 +642,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; @@ -986,6 +987,7 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_VOID_STORAGE, PLAYER_LOGIN_QUERY_LOAD_CURRENCY, PLAYER_LOGIN_QUERY_LOAD_CUF_PROFILES, + PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, PLAYER_LOGIN_QUERY_LOAD_GARRISON, PLAYER_LOGIN_QUERY_LOAD_GARRISON_BLUEPRINTS, PLAYER_LOGIN_QUERY_LOAD_GARRISON_BUILDINGS, @@ -1546,7 +1548,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); @@ -2088,15 +2090,18 @@ 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 const* data, bool self) override {SendMessageToSetInRange(data, GetVisibilityRange(), self); }// overwrite Object::SendMessageToSet - void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) override; // overwrite Object::SendMessageToSetInRange + void SendMessageToSet(WorldPacket const* data, bool self) override { SendMessageToSetInRange(data, GetVisibilityRange(), self); } + void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) override; void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool own_team_only); void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) override; 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() const; void ResurrectPlayer(float restore_percent, bool applySickness = false); void BuildPlayerRepop(); @@ -2913,6 +2918,8 @@ class Player : public Unit, public GridObject<Player> std::unique_ptr<Garrison> _garrison; bool _advancedCombatLoggingEnabled; + + 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 733e61876ad..d08fe61b397 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()) @@ -217,6 +213,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()); @@ -226,185 +227,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()); - - 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(), 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->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); - - bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); - bones->SetGuidValue(CORPSE_FIELD_OWNER, ObjectGuid::Empty); - bones->SetGuidValue(OBJECT_FIELD_DATA, corpse->GetGuidValue(OBJECT_FIELD_DATA)); - - for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - if (corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i)) - bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0); - } - - bones->CopyPhaseFrom(corpse); - - // 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 e4c1ac3aa41..a91a094954c 100644 --- a/src/server/game/Globals/ObjectAccessor.h +++ b/src/server/game/Globals/ObjectAccessor.h @@ -85,86 +85,48 @@ 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 AreaTrigger* GetAreaTrigger(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); + AreaTrigger* GetAreaTrigger(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 b048afe16a6..61e84c6aabe 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -7270,20 +7270,6 @@ void ObjectMgr::DeleteGOData(ObjectGuid::LowType guid) _gameObjectDataStore.erase(guid); } -void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, ObjectGuid 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, ObjectGuid 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(); @@ -8537,7 +8523,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 c5f3fb908e7..62791734b72 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -401,12 +401,10 @@ struct AreaTriggerStruct }; typedef std::set<ObjectGuid::LowType> CellGuidSet; -typedef std::map<ObjectGuid/*player guid*/, uint32/*instance*/> CellCorpseSet; 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; @@ -1214,9 +1212,6 @@ class ObjectMgr LocaleConstant GetDBCLocaleIndex() const { return DBCLocaleIndex; } void SetDBCLocaleIndex(LocaleConstant locale) { DBCLocaleIndex = locale; } - void AddCorpseCellData(uint32 mapid, uint32 cellid, ObjectGuid player_guid, uint32 instance); - void DeleteCorpseCellData(uint32 mapid, uint32 cellid, ObjectGuid player_guid); - // grid objects void AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const* data); void RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData const* data); diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index 365cfb63e08..27af37068c2 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -63,7 +63,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); @@ -73,8 +73,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*/) { } @@ -130,36 +131,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; - - Corpse* obj = sObjectAccessor->GetCorpseForPlayerGUID(itr->first); - 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(); @@ -177,19 +148,25 @@ void ObjectGridLoader::Visit(CreatureMapType &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; @@ -204,7 +181,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/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 3b0a8fc8913..4bf764dcdce 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -445,7 +445,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPackets::AuctionHouse::AuctionPlac SendAuctionCommandResult(auction, AUCTION_PLACE_BID, ERR_AUCTION_OK); // Not sure if we must send this now. - Player* owner = sObjectAccessor->FindConnectedPlayer(ObjectGuid::Create<HighGuid::Player>(auction->owner)); + Player* owner = ObjectAccessor::FindConnectedPlayer(ObjectGuid::Create<HighGuid::Player>(auction->owner)); Item* item = sAuctionMgr->GetAItem(auction->itemGUIDLow); if (owner && item) owner->GetSession()->SendAuctionOwnerBidNotification(auction, item); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index e2c1fe0a858..847574bf763 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -236,6 +236,10 @@ bool LoginQueryHolder::Initialize() stmt->setUInt64(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CURRENCY, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_LOCATION); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON); stmt->setUInt64(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GARRISON, stmt); @@ -1016,7 +1020,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()); if (pCurrChar->GetGuildId()) @@ -1055,7 +1059,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) sSocialMgr->SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUID(), 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) @@ -1844,23 +1848,22 @@ void WorldSession::HandleCharRaceOrFactionChangeCallback(PreparedQueryResult res return; } - /// All checks are fine, deal with race change now - + // All checks are fine, deal with race change now ObjectGuid::LowType lowGuid = factionChangeInfo->Guid.GetCounter(); - // resurrect the character in case he's dead - sObjectAccessor->ConvertCorpseForPlayer(factionChangeInfo->Guid); - PreparedStatement* stmt = nullptr; SQLTransaction trans = CharacterDatabase.BeginTransaction(); - /// Name Change and update atLogin flags + // resurrect the character in case he's dead + Player::OfflineResurrect(factionChangeInfo->Guid, trans); + + // Name Change and update atLogin flags { CharacterDatabase.EscapeString(factionChangeInfo->Name); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN); stmt->setString(0, factionChangeInfo->Name); - stmt->setUInt16(1, uint16(atLoginFlags & ~usedLoginFlag)); + stmt->setUInt16(1, uint16((atLoginFlags | AT_LOGIN_RESURRECT) & ~usedLoginFlag)); stmt->setUInt64(2, lowGuid); trans->Append(stmt); @@ -1871,7 +1874,7 @@ void WorldSession::HandleCharRaceOrFactionChangeCallback(PreparedQueryResult res trans->Append(stmt); } - /// Customize + // Customize { if (factionChangeInfo->SkinID) { @@ -1922,7 +1925,7 @@ void WorldSession::HandleCharRaceOrFactionChangeCallback(PreparedQueryResult res trans->Append(stmt); } - /// Race Change + // Race Change { stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_RACE); stmt->setUInt8(0, factionChangeInfo->RaceID); @@ -1996,7 +1999,7 @@ void WorldSession::HandleCharRaceOrFactionChangeCallback(PreparedQueryResult res trans->Append(stmt); } - /// Team Conversation + // Team Conversion if (factionChangeInfo->FactionChange) { // Delete all Flypaths @@ -2024,7 +2027,7 @@ void WorldSession::HandleCharRaceOrFactionChangeCallback(PreparedQueryResult res trans->Append(stmt); } - /// @todo: make this part asynch + /// @todo: make this part async if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) { // Reset guild diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index 49922edafd7..c200c0851b2 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -454,7 +454,7 @@ void WorldSession::HandleChatAddonMessage(ChatMsg type, std::string prefix, std: if (!normalizePlayerName(target)) break; - Player* receiver = sObjectAccessor->FindPlayerByName(target); + Player* receiver = ObjectAccessor::FindPlayerByName(target); if (!receiver) break; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 1d98b85a441..553f51d0461 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -418,7 +418,6 @@ void WorldSession::HandleReclaimCorpse(WorldPackets::Misc::ReclaimCorpse& /*pack 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 279345845a1..dad3d2f8a54 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -157,8 +157,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 2418bb84cac..a6e192521f4 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -409,10 +409,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 a9ab4ca841d..93611114b4e 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -169,9 +169,7 @@ void WorldSession::HandleGameObjectQueryOpcode(WorldPackets::Query::QueryGameObj void WorldSession::HandleQueryCorpseLocation(WorldPackets::Query::QueryCorpseLocationFromClient& /*packet*/) { - Corpse* corpse = GetPlayer()->GetCorpse(); - - if (!corpse) + if (!_player->HasCorpse()) { WorldPackets::Query::CorpseLocation packet; packet.Valid = false; // corpse not found @@ -179,11 +177,12 @@ void WorldSession::HandleQueryCorpseLocation(WorldPackets::Query::QueryCorpseLoc 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()) @@ -210,7 +209,7 @@ void WorldSession::HandleQueryCorpseLocation(WorldPackets::Query::QueryCorpseLoc packet.MapID = corpseMapID; packet.ActualMapID = mapID; packet.Position = G3D::Vector3(x, y, z); - packet.Transport = corpse->GetTransGUID(); + packet.Transport = ObjectGuid::Empty; SendPacket(packet.Write()); } @@ -280,12 +279,12 @@ void WorldSession::HandleQueryPageText(WorldPackets::Query::QueryPageText& packe } } -void WorldSession::HandleQueryCorpseTransport(WorldPackets::Query::QueryCorpseTransport& packet) +void WorldSession::HandleQueryCorpseTransport(WorldPackets::Query::QueryCorpseTransport& queryCorpseTransport) { Corpse* corpse = _player->GetCorpse(); WorldPackets::Query::CorpseTransportQuery response; - if (!corpse || corpse->GetTransGUID().IsEmpty() || corpse->GetTransGUID() != packet.Transport) + if (!corpse || corpse->GetTransGUID().IsEmpty() || corpse->GetTransGUID() != queryCorpseTransport.Transport) { response.Position = G3D::Vector3(0.0f, 0.0f, 0.0f); response.Facing = 0.0f; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index ccfd3ce33f7..1e35722202c 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -57,9 +57,9 @@ ZoneDynamicInfo::ZoneDynamicInfo() : MusicId(0), WeatherId(WEATHER_STATE_FINE), Map::~Map() { - sScriptMgr->OnDestroyMap(this); + // UnloadAll must be called before deleting the map - UnloadAll(); + sScriptMgr->OnDestroyMap(this); while (!i_worldObjects.empty()) { @@ -399,7 +399,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; } @@ -466,8 +466,6 @@ bool Map::EnsureGridLoaded(const Cell &cell) LoadGridObjects(grid, cell); - // 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; } @@ -516,6 +514,9 @@ bool Map::AddPlayerToMap(Player* player, bool initPlayer /*= true*/) player->UpdateObjectVisibility(false); player->SendUpdatePhasing(); + if (player->IsAlive()) + ConvertCorpseToBones(player->GetGUID()); + sScriptMgr->OnPlayerEnterMap(this, player); return true; } @@ -3580,7 +3581,7 @@ time_t Map::GetLinkedRespawnTime(ObjectGuid guid) const void Map::LoadCorpseData() { - std::unordered_map<uint64, std::unordered_set<uint32>> phases; + std::unordered_map<ObjectGuid::LowType, std::unordered_set<uint32>> phases; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_PHASES); stmt->setUInt32(0, GetId()); @@ -3594,7 +3595,7 @@ void Map::LoadCorpseData() do { Field* fields = phaseResult->Fetch(); - uint64 guid = fields[0].GetUInt64(); + ObjectGuid::LowType guid = fields[0].GetUInt64(); uint32 phaseId = fields[1].GetUInt32(); phases[guid].insert(phaseId); @@ -3612,7 +3613,6 @@ void Map::LoadCorpseData() if (!result) return; - uint32 count = 0; do { Field* fields = result->Fetch(); @@ -3634,8 +3634,8 @@ void Map::LoadCorpseData() for (auto phaseId : phases[guid]) corpse->SetInPhase(phaseId, false, true); - sObjectAccessor->AddCorpse(corpse); - ++count; + AddCorpse(corpse); + } while (result->NextRow()); } @@ -3648,6 +3648,99 @@ 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_GUID + 4; i < CORPSE_END; ++i) // don't overwrite guid + bones->SetUInt32Value(i, corpse->GetUInt32Value(i)); + + bones->SetGridCoord(corpse->GetGridCoord()); + bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); + + bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); + bones->SetGuidValue(CORPSE_FIELD_OWNER, ObjectGuid::Empty); + bones->SetGuidValue(OBJECT_FIELD_DATA, corpse->GetGuidValue(OBJECT_FIELD_DATA)); + + for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) + if (corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i)) + bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0); + + bones->CopyPhaseFrom(corpse); + + // 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 9331815bb39..596a753ad34 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -464,6 +464,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; } @@ -511,8 +529,13 @@ class Map : public GridRefManager<NGridType> void RemoveGORespawnTime(ObjectGuid::LowType dbGuid); void LoadRespawnTimes(); void DeleteRespawnTimes(); + 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); @@ -708,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 5cb3a2c26c5..bfaa9c588b9 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -163,10 +163,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) @@ -183,9 +183,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/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp index afeb3e2a6b6..f491b3a6be5 100644 --- a/src/server/game/Server/Packets/CharacterPackets.cpp +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -52,6 +52,9 @@ WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Fiel uint32 playerFlags = fields[14].GetUInt32(); uint32 atLoginFlags = fields[15].GetUInt16(); + if (atLoginFlags & AT_LOGIN_RESURRECT) + playerFlags &= ~PLAYER_FLAGS_GHOST; + if (playerFlags & PLAYER_FLAGS_HIDE_HELM) Flags |= CHARACTER_FLAG_HIDE_HELM; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 3acc8fb47f9..14c4d031cbd 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2269,7 +2269,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 @@ -3431,11 +3434,6 @@ void World::UpdateCharacterInfoDeleted(ObjectGuid const& guid, bool deleted, std itr->second.Name = *name; } -void World::UpdatePhaseDefinitions() -{ - -} - void World::ReloadRBAC() { // Passive reload, we mark the data as invalidated and next time a permission is checked it will be reloaded @@ -3445,6 +3443,11 @@ void World::ReloadRBAC() session->InvalidateRBACData(); } +void World::RemoveOldCorpses() +{ + m_timers[WUPDATE_CORPSES].SetCurrent(m_timers[WUPDATE_CORPSES].GetInterval()); +} + uint32 GetVirtualRealmAddress() { return uint32(realmHandle.Region) << 24 | uint32(realmHandle.Battlegroup) << 16 | realmHandle.Index; diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index b72591e04fa..1ac4cb2af75 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -788,9 +788,10 @@ class World void SetCleaningFlags(uint32 flags) { m_CleaningFlags = flags; } void ResetEventSeasonalQuests(uint16 event_id); - void UpdatePhaseDefinitions(); void ReloadRBAC(); + void RemoveOldCorpses(); + protected: void _UpdateGameTime(); // callback for UpdateRealmCharacters diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp index 6e82c4fbd1a..836d4caa22b 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -217,7 +217,7 @@ public: { playerName = param3; if (normalizePlayerName(playerName)) - player = sObjectAccessor->FindPlayerByName(playerName); + player = ObjectAccessor::FindPlayerByName(playerName); } if (!player) @@ -283,7 +283,7 @@ public: { playerName = param2; if (normalizePlayerName(playerName)) - player = sObjectAccessor->FindPlayerByName(playerName); + player = ObjectAccessor::FindPlayerByName(playerName); } if (!player) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 400ef88ff67..73fab358f38 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -620,8 +620,10 @@ public: target->SaveToDB(); } else - // will resurrected at login without corpse - sObjectAccessor->ConvertCorpseForPlayer(targetGuid); + { + SQLTransaction trans(nullptr); + Player::OfflineResurrect(targetGuid, trans); + } return true; } @@ -842,7 +844,7 @@ public: // Save all players in the world static bool HandleSaveAllCommand(ChatHandler* handler, char const* /*args*/) { - sObjectAccessor->SaveAllPlayers(); + ObjectAccessor::SaveAllPlayers(); handler->SendSysMessage(LANG_PLAYERS_SAVED); return true; } diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index c3ba942ef1f..be025ea7180 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -1117,7 +1117,6 @@ public: { TC_LOG_INFO("misc", "Reloading terrain_phase_info table..."); sObjectMgr->LoadTerrainPhaseInfo(); - sWorld->UpdatePhaseDefinitions(); handler->SendGlobalGMSysMessage("Terrain phase infos reloaded."); return true; } diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index e65ac5f2105..15078e4cc78 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -100,7 +100,7 @@ public: // Triggering corpses expire check in world static bool HandleServerCorpsesCommand(ChatHandler* /*handler*/, char const* /*args*/) { - sObjectAccessor->RemoveOldCorpses(); + sWorld->RemoveOldCorpses(); return true; } diff --git a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp index 74686f23721..b831f83dc41 100644 --- a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp @@ -527,7 +527,7 @@ public: case PHASE_PLANT_FIRST_DETONATE: // first explosives detonate finish for (GuidList::iterator itr = _explosivesGuids.begin(); itr != _explosivesGuids.end(); ++itr) { - if (GameObject* explosive = sObjectAccessor->GetGameObject(*me, *itr)) + if (GameObject* explosive = ObjectAccessor::GetGameObject(*me, *itr)) me->RemoveGameObject(explosive, true); } _explosivesGuids.clear(); @@ -624,7 +624,7 @@ public: case PHASE_PLANT_SECOND_DETONATE: // second explosives detonate finish for (GuidList::iterator itr = _explosivesGuids.begin(); itr != _explosivesGuids.end(); ++itr) { - if (GameObject* explosive = sObjectAccessor->GetGameObject(*me, *itr)) + if (GameObject* explosive = ObjectAccessor::GetGameObject(*me, *itr)) me->RemoveGameObject(explosive, true); } _explosivesGuids.clear(); diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 586697d3953..9c3c984b9b3 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -273,7 +273,6 @@ extern int main(int argc, char** argv) sInstanceSaveMgr->Unload(); sOutdoorPvPMgr->Die(); // unload it before MapManager sMapMgr->UnloadAll(); // unload all grids (including locked in memory) - sObjectAccessor->UnloadAll(); // unload 'i_player2corpse' storage and remove from world sScriptMgr->Unload(); // set server offline |