[Performance] Pre-Compute CLE Server Lists (#4838)

* [Performance] Pre-Compute CLE Server Lists

* Remove debug
This commit is contained in:
Chris Miles 2025-04-09 21:11:52 -05:00 committed by GitHub
parent c2989e019a
commit ac1469bac2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 104 additions and 54 deletions

View File

@ -347,6 +347,7 @@ RULE_STRING(World, SupportedClients, "RoF2", "Comma-delimited list of clients to
RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1") RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1")
RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files") RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files")
RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified") RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified")
RULE_BOOL(World, RealTimeCalculateGuilds, false, "(Temp feature flag) If true, guilds will be calculated in real time instead of at zone boot. This is a performance hit but allows for more dynamic guilds.")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Zone) RULE_CATEGORY(Zone)

View File

@ -42,11 +42,16 @@ extern ZSList zoneserver_list;
uint32 numplayers = 0; //this really wants to be a member variable of ClientList... uint32 numplayers = 0; //this really wants to be a member variable of ClientList...
ClientList::ClientList() ClientList::ClientList()
: CLStale_timer(10000) : CLStale_timer(10000),
m_poll_cache_timer(6000)
{ {
NextCLEID = 1; NextCLEID = 1;
m_tick = std::make_unique<EQ::Timer>(5000, true, std::bind(&ClientList::OnTick, this, std::placeholders::_1)); m_tick = std::make_unique<EQ::Timer>(5000, true, std::bind(&ClientList::OnTick, this, std::placeholders::_1));
// pre-allocate / pin memory for the zone server caches
m_gm_zone_server_ids.reserve(512);
m_guild_zone_server_ids.reserve(1024);
} }
ClientList::~ClientList() { ClientList::~ClientList() {
@ -57,6 +62,10 @@ void ClientList::Process() {
if (CLStale_timer.Check()) if (CLStale_timer.Check())
CLCheckStale(); CLCheckStale();
if (m_poll_cache_timer.Check()) {
RebuildZoneServerCaches();
}
LinkedListIterator<Client*> iterator(list); LinkedListIterator<Client*> iterator(list);
iterator.Reset(); iterator.Reset();
@ -384,6 +393,7 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
} }
else { else {
cle->Update(zoneserver, scl); cle->Update(zoneserver, scl);
AddToZoneServerCaches(cle);
} }
return; return;
} }
@ -458,6 +468,7 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
); );
clientlist.Insert(cle); clientlist.Insert(cle);
AddToZoneServerCaches(cle);
zoneserver->ChangeWID(scl->charid, cle->GetID()); zoneserver->ChangeWID(scl->charid, cle->GetID());
} }
@ -1857,10 +1868,43 @@ std::map<uint32, ClientListEntry *> ClientList::GetGuildClientsWithTributeOptIn(
return guild_members; return guild_members;
} }
#include <unordered_set> void ClientList::RebuildZoneServerCaches()
{
// Clear without freeing memory (buckets stay allocated)
m_gm_zone_server_ids.clear();
m_guild_zone_server_ids.clear();
LinkedListIterator<ClientListEntry*> iterator(clientlist);
iterator.Reset();
while (iterator.MoreElements()) {
ClientListEntry* cle = iterator.GetData();
if (cle->Online() != CLE_Status::InZone || !cle->Server()) {
iterator.Advance();
continue;
}
uint32_t server_id = cle->Server()->GetID();
// Track GM zone server
if (cle->GetGM()) {
m_gm_zone_server_ids.insert(server_id);
}
// Track guild zone servers
if (cle->GuildID() > 0) {
auto& guild_set = m_guild_zone_server_ids[cle->GuildID()];
guild_set.insert(server_id);
}
iterator.Advance();
}
}
std::vector<uint32_t> ClientList::GetGuildZoneServers(uint32 guild_id) std::vector<uint32_t> ClientList::GetGuildZoneServers(uint32 guild_id)
{ {
if (RuleB(World, RealTimeCalculateGuilds)) {
std::vector<uint32_t> zone_server_ids; std::vector<uint32_t> zone_server_ids;
std::unordered_set<uint32_t> seen_ids; std::unordered_set<uint32_t> seen_ids;
@ -1893,35 +1937,28 @@ std::vector<uint32_t> ClientList::GetGuildZoneServers(uint32 guild_id)
return zone_server_ids; return zone_server_ids;
} }
std::vector<uint32_t> ClientList::GetZoneServersWithGMs() auto it = m_guild_zone_server_ids.find(guild_id);
if (it == m_guild_zone_server_ids.end()) {
return {};
}
return {it->second.begin(), it->second.end()};
}
void ClientList::AddToZoneServerCaches(ClientListEntry* cle)
{ {
std::vector<uint32_t> zone_server_ids; if (!cle || cle->Online() != CLE_Status::InZone || !cle->Server()) {
std::unordered_set<uint32_t> seen_ids; return;
LinkedListIterator<ClientListEntry *> iterator(clientlist);
iterator.Reset();
while (iterator.MoreElements()) {
ClientListEntry *cle = iterator.GetData();
if (cle->Online() != CLE_Status::InZone) {
iterator.Advance();
continue;
} }
if (!cle->Server()) { uint32_t server_id = cle->Server()->GetID();
iterator.Advance();
continue; // Add GM zone server if applicable
if (cle->GetGM()) {
m_gm_zone_server_ids.insert(server_id);
} }
if (cle->Admin() > 0) { // Add guild zone server if applicable
uint32_t id = cle->Server()->GetID(); if (cle->GuildID() > 0) {
if (seen_ids.insert(id).second) { m_guild_zone_server_ids[cle->GuildID()].insert(server_id);
zone_server_ids.emplace_back(id);
} }
} }
iterator.Advance();
}
return zone_server_ids;
}

View File

@ -60,8 +60,6 @@ public:
void CLCheckStale(); void CLCheckStale();
void CLEKeepAlive(uint32 numupdates, uint32* wid); void CLEKeepAlive(uint32 numupdates, uint32* wid);
void CLEAdd(uint32 login_server_id, const char* login_server_name, const char* login_name, const char* login_key, int16 world_admin = AccountStatus::Player, uint32 ip_address = 0, uint8 is_local=0); void CLEAdd(uint32 login_server_id, const char* login_server_name, const char* login_name, const char* login_key, int16 world_admin = AccountStatus::Player, uint32 ip_address = 0, uint8 is_local=0);
std::vector<uint32_t> GetGuildZoneServers(uint32 guild_id);
std::vector<uint32_t> GetZoneServersWithGMs();
void UpdateClientGuild(uint32 char_id, uint32 guild_id); void UpdateClientGuild(uint32 char_id, uint32 guild_id);
bool IsAccountInGame(uint32 iLSID); bool IsAccountInGame(uint32 iLSID);
@ -78,6 +76,15 @@ public:
void SendCharacterMessageID(const std::string& character_name, int chat_type, int eqstr_id, std::initializer_list<std::string> args = {}); void SendCharacterMessageID(const std::string& character_name, int chat_type, int eqstr_id, std::initializer_list<std::string> args = {});
void SendCharacterMessageID(ClientListEntry* character, int chat_type, int eqstr_id, std::initializer_list<std::string> args = {}); void SendCharacterMessageID(ClientListEntry* character, int chat_type, int eqstr_id, std::initializer_list<std::string> args = {});
void AddToZoneServerCaches(ClientListEntry* cle);
void RebuildZoneServerCaches();
std::vector<uint32_t> GetGuildZoneServers(uint32 guild_id);
inline std::vector<uint32_t> GetZoneServersWithGMs()
{
return {m_gm_zone_server_ids.begin(), m_gm_zone_server_ids.end()};
}
private: private:
void OnTick(EQ::Timer *t); void OnTick(EQ::Timer *t);
inline uint32 GetNextCLEID() { return NextCLEID++; } inline uint32 GetNextCLEID() { return NextCLEID++; }
@ -92,6 +99,11 @@ private:
std::unique_ptr<EQ::Timer> m_tick; std::unique_ptr<EQ::Timer> m_tick;
// Zone server routing caches
Timer m_poll_cache_timer;
std::unordered_set<uint32_t> m_gm_zone_server_ids;
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> m_guild_zone_server_ids;
}; };
#endif /*CLIENTLIST_H_*/ #endif /*CLIENTLIST_H_*/