[DynamicZones] Bulk request dz member statuses on zone boot (#4769)

When dynamic zones are cached on zone boot each dz requests member
statuses from world separately. This causes a lot of network traffic
between world and booted zones when there are a lot of active dzs.

This changes it to make a single request to world on zone boot and a
single bulk reply back.
This commit is contained in:
hg 2025-03-11 02:13:29 -04:00 committed by GitHub
parent 84708edccf
commit 051ce3736f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 95 additions and 2 deletions

View File

@ -196,6 +196,7 @@
#define ServerOP_DzSaveInvite 0x0466
#define ServerOP_DzRequestInvite 0x0467
#define ServerOP_DzMakeLeader 0x0468
#define ServerOP_DzGetBulkMemberStatuses 0x0469
#define ServerOP_LSInfo 0x1000
#define ServerOP_LSStatus 0x1001
@ -1555,6 +1556,13 @@ struct ServerDzMemberStatuses_Struct {
ServerDzMemberStatusEntry_Struct entries[0];
};
struct ServerDzCerealData_Struct {
uint16_t zone_id;
uint16_t inst_id;
uint32_t cereal_size;
char cereal_data[1];
};
struct ServerDzMovePC_Struct {
uint32 dz_id;
uint16 sender_zone_id;

View File

@ -7,6 +7,7 @@
#include "zoneserver.h"
#include "../common/rulesys.h"
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
#include <cereal/types/utility.hpp>
extern ClientList client_list;
extern ZSList zoneserver_list;
@ -169,6 +170,33 @@ void DynamicZoneManager::LoadTemplates()
}
}
void DynamicZoneManager::SendBulkMemberStatuses(uint32_t zone_id, uint16_t inst_id)
{
std::vector<std::pair<uint32_t, std::vector<DynamicZoneMember>>> dzs;
dzs.reserve(dynamic_zone_cache.size());
for (const auto& [dz_id, dz] : dynamic_zone_cache)
{
dzs.emplace_back(dz_id, dz->GetMembers());
}
std::ostringstream ss;
{
cereal::BinaryOutputArchive archive(ss);
archive(dzs);
}
std::string_view sv = ss.view();
size_t size = sizeof(ServerDzCerealData_Struct) + sv.size();
ServerPacket pack(ServerOP_DzGetBulkMemberStatuses, static_cast<uint32_t>(size));
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack.pBuffer);
buf->cereal_size = static_cast<uint32_t>(sv.size());
memcpy(buf->cereal_data, sv.data(), sv.size());
zoneserver_list.SendPacket(zone_id, inst_id, &pack);
}
void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
{
switch (pack->opcode)
@ -338,6 +366,15 @@ void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
}
break;
}
case ServerOP_DzGetBulkMemberStatuses:
{
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack->pBuffer);
if (buf->zone_id != 0 && !dynamic_zone_cache.empty())
{
SendBulkMemberStatuses(buf->zone_id, buf->inst_id);
}
break;
}
case ServerOP_DzUpdateMemberStatus:
{
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);

View File

@ -30,6 +30,8 @@ public:
std::unordered_map<uint32_t, std::unique_ptr<DynamicZone>> dynamic_zone_cache;
private:
void SendBulkMemberStatuses(uint32_t zone_id, uint16_t inst_id);
Timer m_process_throttle_timer{};
std::unordered_map<uint32_t, DynamicZoneTemplatesRepository::DynamicZoneTemplates> m_dz_templates;
};

View File

@ -1480,6 +1480,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_DzSwapMembers:
case ServerOP_DzRemoveAllMembers:
case ServerOP_DzGetMemberStatuses:
case ServerOP_DzGetBulkMemberStatuses:
case ServerOP_DzSetSecondsRemaining:
case ServerOP_DzSetCompass:
case ServerOP_DzSetSafeReturn:

View File

@ -25,6 +25,7 @@
#include "worldserver.h"
#include "../common/repositories/character_expedition_lockouts_repository.h"
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
#include <cereal/types/utility.hpp>
extern WorldServer worldserver;
@ -162,14 +163,28 @@ void DynamicZone::CacheAllFromDatabase()
}
}
dz->UpdateMembers();
zone->dynamic_zone_cache.emplace(dz_id, std::move(dz));
}
if (!zone->dynamic_zone_cache.empty())
{
RequestMemberStatuses();
}
LogInfo("Loaded [{}] dynamic zone(s)", Strings::Commify(zone->dynamic_zone_cache.size()));
LogDynamicZones("Caching [{}] dynamic zone(s) took [{}s]", zone->dynamic_zone_cache.size(), bench.elapsed());
}
void DynamicZone::RequestMemberStatuses()
{
ServerPacket pack(ServerOP_DzGetBulkMemberStatuses, sizeof(ServerDzCerealData_Struct));
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack.pBuffer);
buf->zone_id = static_cast<uint16_t>(zone->GetZoneID());
buf->inst_id = static_cast<uint16_t>(zone->GetInstanceID());
worldserver.SendPacket(&pack);
}
template <typename T>
DynamicZone* FindDynamicZone(T pred)
{
@ -849,6 +864,34 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack)
}
break;
}
case ServerOP_DzGetBulkMemberStatuses:
{
if (zone)
{
std::vector<std::pair<uint32_t, std::vector<DynamicZoneMember>>> dzs;
dzs.reserve(zone->dynamic_zone_cache.size());
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack->pBuffer);
EQ::Util::MemoryStreamReader ss(buf->cereal_data, buf->cereal_size);
{
cereal::BinaryInputArchive archive(ss);
archive(dzs);
}
for (const auto& [dz_id, members] : dzs)
{
if (auto dz = DynamicZone::FindDynamicZoneByID(dz_id))
{
for (const auto& member : members)
{
dz->SetInternalMemberStatus(member.id, member.status);
}
dz->m_has_member_statuses = true;
}
}
}
break;
}
case ServerOP_DzUpdateMemberStatus:
{
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);

View File

@ -92,13 +92,13 @@ public:
void SendMemberNameToZoneMembers(const std::string& char_name, bool remove);
void SendMemberStatusToZoneMembers(const DynamicZoneMember& member);
void SetLocked(bool lock, bool update_db = false, DzLockMsg lock_msg = DzLockMsg::None, uint32_t color = Chat::Yellow);
void UpdateMembers();
std::string GetLootEvent(uint32_t id, DzLootEvent::Type type) const;
void SetLootEvent(uint32_t id, const std::string& event, DzLootEvent::Type type);
private:
static void StartAllClientRemovalTimers();
static void RequestMemberStatuses();
uint16_t GetCurrentInstanceID() const override;
uint16_t GetCurrentZoneID() const override;
@ -125,6 +125,7 @@ private:
void SendWorldPlayerInvite(const std::string& inviter, const std::string& swap_name, const std::string& add_name, bool pending = false);
void SetUpdatedDuration(uint32_t seconds);
void TryAddClient(Client* add_client, const std::string& inviter, const std::string& swap_name, Client* leader = nullptr);
void UpdateMembers();
std::unique_ptr<EQApplicationPacket> CreateExpireWarningPacket(uint32_t minutes_remaining);
std::unique_ptr<EQApplicationPacket> CreateInfoPacket(bool clear = false);

View File

@ -3410,6 +3410,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
case ServerOP_DzRemoveAllMembers:
case ServerOP_DzDurationUpdate:
case ServerOP_DzGetMemberStatuses:
case ServerOP_DzGetBulkMemberStatuses:
case ServerOP_DzSetCompass:
case ServerOP_DzSetSafeReturn:
case ServerOP_DzSetZoneIn: