diff --git a/src/server/game/Cache/WhoListCacheMgr.cpp b/src/server/game/Cache/WhoListCacheMgr.cpp new file mode 100644 index 000000000..bebd3e8c6 --- /dev/null +++ b/src/server/game/Cache/WhoListCacheMgr.cpp @@ -0,0 +1,63 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "WhoListCacheMgr.h" +#include "GuildMgr.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "World.h" +#include "WorldSession.h" + +WhoListCacheMgr* WhoListCacheMgr::instance() +{ + static WhoListCacheMgr instance; + return &instance; +} + +void WhoListCacheMgr::Update() +{ + // clear current list + _whoListStorage.clear(); + _whoListStorage.reserve(sWorld->GetPlayerCount() + 1); + + for (auto const& [guid, player] : ObjectAccessor::GetPlayers()) + { + if (!player->FindMap() || player->GetSession()->PlayerLoading()) + continue; + + std::string playerName = player->GetName(); + std::wstring widePlayerName; + + if (!Utf8toWStr(playerName, widePlayerName)) + continue; + + wstrToLower(widePlayerName); + + std::string guildName = sGuildMgr->GetGuildNameById(player->GetGuildId()); + std::wstring wideGuildName; + + if (!Utf8toWStr(guildName, wideGuildName)) + continue; + + wstrToLower(wideGuildName); + + _whoListStorage.emplace_back(player->GetGUID(), player->GetTeamId(), player->GetSession()->GetSecurity(), player->getLevel(), + player->getClass(), player->getRace(), + (player->IsSpectator() ? 4395 /*Dalaran*/ : player->GetZoneId()), player->getGender(), player->IsVisible(), + widePlayerName, wideGuildName, playerName, guildName); + } +} diff --git a/src/server/game/Cache/WhoListCacheMgr.h b/src/server/game/Cache/WhoListCacheMgr.h new file mode 100644 index 000000000..ebeab820e --- /dev/null +++ b/src/server/game/Cache/WhoListCacheMgr.h @@ -0,0 +1,98 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef _WHO_LISTCACHE_H_ +#define _WHO_LISTCACHE_H_ + +#include "Common.h" +#include "SharedDefines.h" +#include "ObjectGuid.h" + +class WhoListPlayerInfo +{ +public: + WhoListPlayerInfo(ObjectGuid guid, TeamId team, AccountTypes security, uint8 level, uint8 clss, uint8 race, uint32 zoneid, uint8 gender, bool visible, std::wstring const& widePlayerName, + std::wstring const& wideGuildName, std::string const& playerName, std::string const& guildName) : + _guid(guid), + _team(team), + _security(security), + _level(level), + _class(clss), + _race(race), + _zoneid(zoneid), + _gender(gender), + _visible(visible), + _widePlayerName(widePlayerName), + _wideGuildName(wideGuildName), + _playerName(playerName), + _guildName(guildName) { } + + ObjectGuid GetGuid() const { return _guid; } + TeamId GetTeamId() const { return _team; } + AccountTypes GetSecurity() const { return _security; } + uint8 GetLevel() const { return _level; } + uint8 GetClass() const { return _class; } + uint8 GetRace() const { return _race; } + uint32 GetZoneId() const { return _zoneid; } + uint8 GetGender() const { return _gender; } + bool IsVisible() const { return _visible; } + std::wstring const& GetWidePlayerName() const { return _widePlayerName; } + std::wstring const& GetWideGuildName() const { return _wideGuildName; } + std::string const& GetPlayerName() const { return _playerName; } + std::string const& GetGuildName() const { return _guildName; } + +private: + ObjectGuid _guid; + TeamId _team; + AccountTypes _security; + uint8 _level; + uint8 _class; + uint8 _race; + uint32 _zoneid; + uint8 _gender; + bool _visible; + std::wstring _widePlayerName; + std::wstring _wideGuildName; + std::string _playerName; + std::string _guildName; +}; + +using WhoListInfoVector = std::vector; + +class AC_GAME_API WhoListCacheMgr +{ + WhoListCacheMgr() = default; + ~WhoListCacheMgr() = default; + + WhoListCacheMgr(WhoListCacheMgr const&) = delete; + WhoListCacheMgr(WhoListCacheMgr&&) = delete; + + WhoListCacheMgr& operator= (WhoListCacheMgr const&) = delete; + WhoListCacheMgr& operator= (WhoListCacheMgr&&) = delete; +public: + static WhoListCacheMgr* instance(); + + void Update(); + WhoListInfoVector const& GetWhoList() const { return _whoListStorage; } + +protected: + WhoListInfoVector _whoListStorage; +}; + +#define sWhoListCacheMgr WhoListCacheMgr::instance() + +#endif diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 62c9eb705..b78632302 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -22,8 +22,8 @@ #include "Chat.h" #include "Common.h" #include "CreatureAI.h" -#include "DatabaseEnv.h" #include "DBCEnums.h" +#include "DatabaseEnv.h" #include "GameObjectAI.h" #include "GossipDef.h" #include "Group.h" @@ -45,10 +45,11 @@ #include "Spell.h" #include "UpdateData.h" #include "Vehicle.h" +#include "WhoListCacheMgr.h" #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "zlib.h" +#include #ifdef ELUNA #include "LuaEngine.h" @@ -211,70 +212,67 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) { LOG_DEBUG("network", "WORLD: Recvd CMSG_WHO Message"); - time_t now = time(nullptr); - if (now < timeWhoCommandAllowed) - return; - timeWhoCommandAllowed = now + 3; + uint32 matchCount = 0; - uint32 matchcount = 0; + uint32 levelMin, levelMax, racemask, classmask, zonesCount, strCount; + std::array zoneids = {}; // 10 is client limit + std::string packetPlayerName, packetGuildName; - uint32 level_min, level_max, racemask, classmask, zones_count, str_count; - uint32 zoneids[10]; // 10 is client limit - std::string player_name, guild_name; + recvData >> levelMin; // maximal player level, default 0 + recvData >> levelMax; // minimal player level, default 100 (MAX_LEVEL) + recvData >> packetPlayerName; // player name, case sensitive... - recvData >> level_min; // maximal player level, default 0 - recvData >> level_max; // minimal player level, default 100 (MAX_LEVEL) - recvData >> player_name; // player name, case sensitive... + recvData >> packetGuildName; // guild name, case sensitive... - recvData >> guild_name; // guild name, case sensitive... + recvData >> racemask; // race mask + recvData >> classmask; // class mask + recvData >> zonesCount; // zones count, client limit = 10 (2.0.10) - recvData >> racemask; // race mask - recvData >> classmask; // class mask - recvData >> zones_count; // zones count, client limit = 10 (2.0.10) - - if (zones_count > 10) + if (zonesCount > 10) return; // can't be received from real client or broken packet - for (uint32 i = 0; i < zones_count; ++i) + for (uint32 i = 0; i < zonesCount; ++i) { uint32 temp; - recvData >> temp; // zone id, 0 if zone is unknown... + recvData >> temp; // zone id, 0 if zone is unknown... zoneids[i] = temp; - LOG_DEBUG("network", "Zone %u: %u", i, zoneids[i]); + FMT_LOG_DEBUG("network.who", "Zone {}: {}", i, zoneids[i]); } - recvData >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10) + recvData >> strCount; // user entered strings count, client limit=4 (checked on 2.0.10) - if (str_count > 4) + if (strCount > 4) return; // can't be received from real client or broken packet - LOG_DEBUG("network", "Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", level_min, level_max, player_name.c_str(), guild_name.c_str(), racemask, classmask, zones_count, str_count); + FMT_LOG_DEBUG("network.who", "Minlvl {}, maxlvl {}, name {}, guild {}, racemask {}, classmask {}, zones {}, strings {}", + levelMin, levelMax, packetPlayerName, packetGuildName, racemask, classmask, zonesCount, strCount); std::wstring str[4]; // 4 is client limit - for (uint32 i = 0; i < str_count; ++i) + for (uint32 i = 0; i < strCount; ++i) { std::string temp; - recvData >> temp; // user entered string, it used as universal search pattern(guild+player name)? + recvData >> temp; // user entered string, it used as universal search pattern(guild+player name)? if (!Utf8toWStr(temp, str[i])) continue; wstrToLower(str[i]); - LOG_DEBUG("network", "String %u: %s", i, temp.c_str()); + FMT_LOG_DEBUG("network.who", "String {}: {}", i, temp); } - std::wstring wplayer_name; - std::wstring wguild_name; - if (!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name))) + std::wstring wpacketPlayerName; + std::wstring wpacketGuildName; + if (!(Utf8toWStr(packetPlayerName, wpacketPlayerName) && Utf8toWStr(packetGuildName, wpacketGuildName))) return; - wstrToLower(wplayer_name); - wstrToLower(wguild_name); - // client send in case not set max level value 100 but Trinity supports 255 max level, + wstrToLower(wpacketPlayerName); + wstrToLower(wpacketGuildName);; + + // client send in case not set max level value 100 but Acore supports 255 max level, // update it to show GMs with characters after 100 level - if (level_max >= MAX_LEVEL) - level_max = STRONG_MAX_LEVEL; + if (levelMax >= MAX_LEVEL) + levelMax = STRONG_MAX_LEVEL; uint32 team = _player->GetTeamId(); uint32 security = GetSecurity(); @@ -282,126 +280,138 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); uint32 displaycount = 0; - WorldPacket data(SMSG_WHO, 50); // guess size - data << uint32(matchcount); // placeholder, count of players matching criteria - data << uint32(displaycount); // placeholder, count of players displayed + WorldPacket data(SMSG_WHO, 50); // guess size + data << uint32(matchCount); // placeholder, count of players matching criteria + data << uint32(displaycount); // placeholder, count of players displayed - std::shared_lock lock(*HashMapHolder::GetLock()); - HashMapHolder::MapType const& m = ObjectAccessor::GetPlayers(); - for (HashMapHolder::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr) + for (auto const& target : sWhoListCacheMgr->GetWhoList()) { if (AccountMgr::IsPlayerAccount(security)) { // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST - if (itr->second->GetTeamId() != team && !allowTwoSideWhoList) + if (target.GetTeamId() != team && !allowTwoSideWhoList) + { continue; + } // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST - if ((itr->second->GetSession()->GetSecurity() > AccountTypes(gmLevelInWhoList))) + if (target.GetSecurity() > AccountTypes(gmLevelInWhoList)) + { continue; + } } - //do not process players which are not in world - if (!(itr->second->IsInWorld())) - continue; - // check if target is globally visible for player - if (!(itr->second->IsVisibleGloballyFor(_player))) + if ((_player->GetGUID() != target.GetGuid() && !target.IsVisible()) && + (AccountMgr::IsPlayerAccount(_player->GetSession()->GetSecurity()) || target.GetSecurity() > _player->GetSession()->GetSecurity())) + { continue; + } // check if target's level is in level range - uint8 lvl = itr->second->getLevel(); - if (lvl < level_min || lvl > level_max) + uint8 lvl = target.GetLevel(); + if (lvl < levelMin || lvl > levelMax) + { continue; + } // check if class matches classmask - uint32 class_ = itr->second->getClass(); + uint8 class_ = target.GetClass(); if (!(classmask & (1 << class_))) + { continue; + } // check if race matches racemask - uint32 race = itr->second->getRace(); + uint32 race = target.GetRace(); if (!(racemask & (1 << race))) - continue; - - uint32 pzoneid = itr->second->GetZoneId(); - uint8 gender = itr->second->getGender(); - - bool z_show = true; - for (uint32 i = 0; i < zones_count; ++i) { - if (zoneids[i] == pzoneid) + continue; + } + + uint32 playerZoneId = target.GetZoneId(); + uint8 gender = target.GetGender(); + + bool showZones = true; + for (uint32 i = 0; i < zonesCount; ++i) + { + if (zoneids[i] == playerZoneId) { - z_show = true; + showZones = true; break; } - z_show = false; + showZones = false; } - if (!z_show) - continue; - std::string pname = itr->second->GetName(); - std::wstring wpname; - if (!Utf8toWStr(pname, wpname)) + if (!showZones) + { continue; - wstrToLower(wpname); + } - if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos)) + std::wstring const& wideplayername = target.GetWidePlayerName(); + if (!(wpacketPlayerName.empty() || wideplayername.find(wpacketPlayerName) != std::wstring::npos)) + { continue; + } - std::string gname = sGuildMgr->GetGuildNameById(itr->second->GetGuildId()); - std::wstring wgname; - if (!Utf8toWStr(gname, wgname)) - continue; - wstrToLower(wgname); - - if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos)) + std::wstring const& wideguildname = target.GetWideGuildName(); + if (!(wpacketGuildName.empty() || wideguildname.find(wpacketGuildName) != std::wstring::npos)) + { continue; + } std::string aname; - if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(itr->second->GetZoneId())) + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(playerZoneId)) + { aname = areaEntry->area_name[GetSessionDbcLocale()]; + } bool s_show = true; - for (uint32 i = 0; i < str_count; ++i) + for (uint32 i = 0; i < strCount; ++i) { if (!str[i].empty()) { - if (wgname.find(str[i]) != std::wstring::npos || - wpname.find(str[i]) != std::wstring::npos || - Utf8FitTo(aname, str[i])) + if (wideguildname.find(str[i]) != std::wstring::npos || + wideplayername.find(str[i]) != std::wstring::npos || + Utf8FitTo(aname, str[i])) { s_show = true; break; } + s_show = false; } } + if (!s_show) + { continue; + } // 49 is maximum player count sent to client - can be overridden // through config, but is unstable - if ((matchcount++) >= sWorld->getIntConfig(CONFIG_MAX_WHO_LIST_RETURN)) + if ((matchCount++) >= sWorld->getIntConfig(CONFIG_MAX_WHO_LIST_RETURN)) + { continue; + } - data << pname; // player name - data << gname; // guild name + data << target.GetPlayerName(); // player name + data << target.GetGuildName(); // guild name data << uint32(lvl); // player level data << uint32(class_); // player class data << uint32(race); // player race data << uint8(gender); // player gender - data << uint32(pzoneid); // player zone id + data << uint32(playerZoneId); // player zone id ++displaycount; } data.put(0, displaycount); // insert right count, count displayed - data.put(4, matchcount); // insert right count, count of matches + data.put(4, matchCount); // insert right count, count of matches SendPacket(&data); - // LOG_DEBUG("network", "WORLD: Send SMSG_WHO Message"); + FMT_LOG_DEBUG("network", "WORLD: Send SMSG_WHO Message"); } void WorldSession::HandleLogoutRequestOpcode(WorldPacket& /*recv_data*/) diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index eec83688c..0b66552ea 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -166,7 +166,6 @@ void WorldSession::HandleMoveWorldportAck() _player->SetIsSpectator(false); GetPlayer()->SetPendingSpectatorForBG(0); - timeWhoCommandAllowed = time(nullptr) + sWorld->GetNextWhoListUpdateDelaySecs() + 1; // after exiting arena Subscribe will scan for a player and cached data says he is still in arena, so disallow until next update if (uint32 inviteInstanceId = _player->GetPendingSpectatorInviteInstanceId()) { diff --git a/src/server/game/Misc/WhoListCache.cpp b/src/server/game/Misc/WhoListCache.cpp deleted file mode 100644 index 9680da250..000000000 --- a/src/server/game/Misc/WhoListCache.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "GuildMgr.h" -#include "ObjectAccessor.h" -#include "Player.h" -#include "WhoListCache.h" -#include "World.h" - -std::vector WhoListCacheMgr::m_whoOpcodeList; - -void WhoListCacheMgr::Update() -{ - // clear current list - m_whoOpcodeList.clear(); - m_whoOpcodeList.reserve(sWorld->GetPlayerCount() + 1); - - std::shared_lock lock(*HashMapHolder::GetLock()); - HashMapHolder::MapType const& m = ObjectAccessor::GetPlayers(); - for (HashMapHolder::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr) - { - if (!itr->second->FindMap() || itr->second->GetSession()->PlayerLoading()) - continue; - - if (itr->second->GetSession()->GetSecurity() > SEC_PLAYER) - continue; - - std::string pname = itr->second->GetName(); - std::wstring wpname; - if (!Utf8toWStr(pname, wpname)) - continue; - wstrToLower(wpname); - - std::string gname = sGuildMgr->GetGuildNameById(itr->second->GetGuildId()); - std::wstring wgname; - if (!Utf8toWStr(gname, wgname)) - continue; - wstrToLower(wgname); - - std::string aname; - if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(itr->second->GetZoneId())) - aname = areaEntry->area_name[sWorld->GetDefaultDbcLocale()]; - - if (itr->second->IsSpectator()) - aname = "Dalaran"; - - m_whoOpcodeList.push_back( WhoListPlayerInfo(itr->second->GetTeamId(), itr->second->GetSession()->GetSecurity(), itr->second->getLevel(), itr->second->getClass(), itr->second->getRace(), (itr->second->IsSpectator() ? 4395 /*Dalaran*/ : itr->second->GetZoneId()), itr->second->getGender(), wpname, wgname, aname, pname, gname) ); - } -} diff --git a/src/server/game/Misc/WhoListCache.h b/src/server/game/Misc/WhoListCache.h deleted file mode 100644 index ddc54a8e7..000000000 --- a/src/server/game/Misc/WhoListCache.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __WHOLISTCACHE_H -#define __WHOLISTCACHE_H - -#include "Common.h" -#include "SharedDefines.h" - -struct WhoListPlayerInfo -{ - TeamId teamId; - AccountTypes security; - uint8 level; - uint8 clas; - uint8 race; - uint32 zoneid; - uint8 gender; - std::wstring wpname; - std::wstring wgname; - std::string aname; - std::string pname; - std::string gname; - - WhoListPlayerInfo(TeamId teamId, AccountTypes security, uint8 level, uint8 clas, uint8 race, uint32 zoneid, uint8 gender, std::wstring wpname, std::wstring wgname, std::string aname, std::string pname, std::string gname) : - teamId(teamId), security(security), level(level), clas(clas), race(race), zoneid(zoneid), gender(gender), wpname(wpname), wgname(wgname), aname(aname), pname(pname), gname(gname) {} -}; - -class WhoListCacheMgr -{ -public: - static void Update(); - static std::vector* GetWhoList() { return &m_whoOpcodeList; } - -protected: - static std::vector m_whoOpcodeList; -}; - -#endif diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index b048bbbd3..56b1dd208 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -132,7 +132,6 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr _recvQueue; uint32 m_currentVendorEntry; ObjectGuid m_currentBankerGUID; - time_t timeWhoCommandAllowed; uint32 _offlineTime; bool _kicked; bool _shouldSetOfflineInDB; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index a42bc627b..35f69e7ed 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -86,7 +86,7 @@ #include "WardenCheckMgr.h" #include "WaypointMovementGenerator.h" #include "WeatherMgr.h" -#include "WhoListCache.h" +#include "WhoListCacheMgr.h" #include "WorldPacket.h" #include "WorldSession.h" #include @@ -1990,6 +1990,8 @@ void World::SetInitialWorldSettings() // our speed up m_timers[WUPDATE_5_SECS].SetInterval(5 * IN_MILLISECONDS); + m_timers[WUPDATE_WHO_LIST].SetInterval(5 * IN_MILLISECONDS); // update who list cache every 5 seconds + mail_expire_check_timer = time(nullptr) + 6 * 3600; ///- Initilize static helper structures @@ -2237,14 +2239,19 @@ void World::Update(uint32 diff) // moved here from HandleCharEnumOpcode CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_BANS); CharacterDatabase.Execute(stmt); - - // copy players hashmapholder to avoid mutex - WhoListCacheMgr::Update(); } ///- Update the game time and check for shutdown time _UpdateGameTime(); + ///- Update Who List Cache + if (m_timers[WUPDATE_WHO_LIST].Passed()) + { + METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update who list")); + m_timers[WUPDATE_WHO_LIST].Reset(); + sWhoListCacheMgr->Update(); + } + { METRIC_TIMER("world_update_time", METRIC_TAG("type", "Check quest reset times")); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 4aa5470bb..f6786f34d 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -69,6 +69,7 @@ enum WorldTimers WUPDATE_MAILBOXQUEUE, WUPDATE_PINGDB, WUPDATE_5_SECS, + WUPDATE_WHO_LIST, WUPDATE_COUNT };