Skip to content

Commit

Permalink
[Expeditions] Track DZ member status in world (#1341)
Browse files Browse the repository at this point in the history
World now caches and tracks member statuses so it can send them to zones
that request them on startup. Prior to this the cle would be searched in
world for every zone startup caching request, now it's only searched once
when a new expedition is created.

Bulk loading statuses removed since it would only be needed on world
startup now and likely have no clients in the client list anyway.

This also lets world choose non-linkdead members on expedition leader
changes and better detect when a leader change needs to occur
  • Loading branch information
hgtw committed May 10, 2021
1 parent 26d374d commit 0ce7c11
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 157 deletions.
2 changes: 2 additions & 0 deletions common/dynamic_zone_base.h
Expand Up @@ -25,6 +25,8 @@ struct DynamicZoneMember
DynamicZoneMember(uint32_t id, std::string name_, DynamicZoneMemberStatus status_)
: id(id), name{std::move(name_)}, status(status_) {}

bool IsOnline() const { return status == DynamicZoneMemberStatus::Online ||
status == DynamicZoneMemberStatus::InDynamicZone; }
bool IsValid() const { return id != 0 && !name.empty(); }
};

Expand Down
25 changes: 25 additions & 0 deletions common/expedition_base.cpp
@@ -1,5 +1,6 @@
#include "expedition_base.h"
#include "repositories/expeditions_repository.h"
#include "rulesys.h"

ExpeditionBase::ExpeditionBase(uint32_t id, const std::string& uuid,
const std::string& expedition_name, const DynamicZoneMember& leader,
Expand Down Expand Up @@ -91,3 +92,27 @@ DynamicZoneMember ExpeditionBase::GetMemberData(const std::string& character_nam
}
return member_data;
}

bool ExpeditionBase::SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status)
{
if (status == DynamicZoneMemberStatus::InDynamicZone && !RuleB(Expedition, EnableInDynamicZoneStatus))
{
status = DynamicZoneMemberStatus::Online;
}

if (character_id == m_leader.id)
{
m_leader.status = status;
}

auto it = std::find_if(m_members.begin(), m_members.end(),
[&](const DynamicZoneMember& member) { return member.id == character_id; });

if (it != m_members.end() && it->status != status)
{
it->status = status;
return true;
}

return false;
}
1 change: 1 addition & 0 deletions common/expedition_base.h
Expand Up @@ -33,6 +33,7 @@ class ExpeditionBase
bool HasMember(uint32_t character_id);
bool IsEmpty() const { return m_members.empty(); }
void RemoveInternalMember(uint32_t character_id);
bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status);

void LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry);
void AddMemberFromRepositoryResult(ExpeditionMembersRepository::MemberWithName&& entry);
Expand Down
17 changes: 6 additions & 11 deletions common/servertalk.h
Expand Up @@ -148,7 +148,7 @@
#define ServerOP_ExpeditionMemberChange 0x0404
#define ServerOP_ExpeditionMemberSwap 0x0405
#define ServerOP_ExpeditionMemberStatus 0x0406
#define ServerOP_ExpeditionGetOnlineMembers 0x0407
#define ServerOP_ExpeditionGetMemberStatuses 0x0407
#define ServerOP_ExpeditionDzAddPlayer 0x0408
#define ServerOP_ExpeditionDzMakeLeader 0x0409
#define ServerOP_ExpeditionCharacterLockout 0x040d
Expand All @@ -159,7 +159,6 @@
#define ServerOP_ExpeditionMembersRemoved 0x0412
#define ServerOP_ExpeditionLockoutDuration 0x0414
#define ServerOP_ExpeditionExpireWarning 0x0416
#define ServerOP_ExpeditionChooseNewLeader 0x0417

#define ServerOP_DzAddRemoveCharacter 0x0450
#define ServerOP_DzRemoveAllCharacters 0x0451
Expand Down Expand Up @@ -2035,19 +2034,15 @@ struct ServerExpeditionMemberStatus_Struct {
uint32 character_id;
};

struct ServerExpeditionCharacterEntry_Struct {
uint32 expedition_id;
struct ServerExpeditionMemberStatusEntry_Struct {
uint32 character_id;
uint32 character_zone_id;
uint16 character_instance_id;
uint8 character_online; // 0: offline 1: online
uint8 online_status; // 0: unknown 1: Online 2: Offline 3: In Dynamic Zone 4: Link Dead
};

struct ServerExpeditionCharacters_Struct {
uint32 sender_zone_id;
uint16 sender_instance_id;
struct ServerExpeditionMemberStatuses_Struct {
uint32 expedition_id;
uint32 count;
ServerExpeditionCharacterEntry_Struct entries[0];
ServerExpeditionMemberStatusEntry_Struct entries[0];
};

struct ServerExpeditionLockout_Struct {
Expand Down
63 changes: 60 additions & 3 deletions world/expedition.cpp
Expand Up @@ -62,10 +62,8 @@ void Expedition::ChooseNewLeader()
return;
}

// we don't track expedition member status in world so may choose a linkdead member
// this is fine since it will trigger another change when that member goes offline
auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) {
if (member.id != m_leader.id) {
if (member.id != m_leader.id && member.IsOnline()) {
auto member_cle = client_list.FindCLEByCharacterID(member.id);
return (member_cle && member_cle->GetOnline() == CLE_Status::InZone);
}
Expand Down Expand Up @@ -171,3 +169,62 @@ bool Expedition::Process()

return false;
}

void Expedition::UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status)
{
SetInternalMemberStatus(character_id, status);

// any member status update will trigger a leader fix if leader was offline
if (m_leader.status == DynamicZoneMemberStatus::Offline)
{
ChooseNewLeader();
}
}

void Expedition::SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id)
{
const auto& members = GetMembers();

uint32_t members_count = static_cast<uint32_t>(members.size());
uint32_t entries_size = sizeof(ServerExpeditionMemberStatusEntry_Struct) * members_count;
uint32_t pack_size = sizeof(ServerExpeditionMemberStatuses_Struct) + entries_size;
auto pack = std::make_unique<ServerPacket>(ServerOP_ExpeditionGetMemberStatuses, pack_size);
auto buf = reinterpret_cast<ServerExpeditionMemberStatuses_Struct*>(pack->pBuffer);
buf->expedition_id = GetID();
buf->count = members_count;

for (int i = 0; i < members.size(); ++i)
{
buf->entries[i].character_id = members[i].id;
buf->entries[i].online_status = static_cast<uint8_t>(members[i].status);
}

zoneserver_list.SendPacket(zone_id, instance_id, pack.get());
}

void Expedition::CacheMemberStatuses()
{
// called when a new expedition is cached to fill member statuses
std::string zone_name{};
std::vector<ClientListEntry*> all_clients;
all_clients.reserve(client_list.GetClientCount());
client_list.GetClients(zone_name.c_str(), all_clients);

for (const auto& member : m_members)
{
auto it = std::find_if(all_clients.begin(), all_clients.end(),
[&](const ClientListEntry* cle) { return (cle && cle->CharID() == member.id); });

auto status = DynamicZoneMemberStatus::Offline;
if (it != all_clients.end())
{
status = DynamicZoneMemberStatus::Online;
if (GetDynamicZone().IsSameDz((*it)->zone(), (*it)->instance()))
{
status = DynamicZoneMemberStatus::InDynamicZone;
}
}

SetInternalMemberStatus(member.id, status);
}
}
4 changes: 3 additions & 1 deletion world/expedition.h
Expand Up @@ -32,16 +32,18 @@ class Expedition : public ExpeditionBase
Expedition();

void RemoveMember(uint32_t character_id);
void CacheMemberStatuses();
void CheckExpireWarning();
void CheckLeader();
void ChooseNewLeader();
DynamicZone& GetDynamicZone() { return m_dynamic_zone; }
bool Process();

void SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id);
void SendZonesExpeditionDeleted();
void SendZonesExpireWarning(uint32_t minutes_remaining);
void SetDynamicZone(DynamicZone&& dz);
bool SetNewLeader(const DynamicZoneMember& member);
void UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status);

private:
void SendZonesLeaderChanged();
Expand Down
58 changes: 19 additions & 39 deletions world/expedition_message.cpp
Expand Up @@ -35,11 +35,6 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack)
{
switch (pack->opcode)
{
case ServerOP_ExpeditionChooseNewLeader:
{
ExpeditionMessage::ChooseNewLeader(pack);
break;
}
case ServerOP_ExpeditionCreate:
{
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
Expand Down Expand Up @@ -69,9 +64,21 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack)
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionGetOnlineMembers:
case ServerOP_ExpeditionMemberStatus:
{
auto buf = reinterpret_cast<ServerExpeditionMemberStatus_Struct*>(pack->pBuffer);
auto expedition = expedition_state.GetExpedition(buf->expedition_id);
if (expedition)
{
auto status = static_cast<DynamicZoneMemberStatus>(buf->status);
expedition->UpdateMemberStatus(buf->character_id, status);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionGetMemberStatuses:
{
ExpeditionMessage::GetOnlineMembers(pack);
ExpeditionMessage::GetMemberStatuses(pack);
break;
}
case ServerOP_ExpeditionDzAddPlayer:
Expand Down Expand Up @@ -157,31 +164,14 @@ void ExpeditionMessage::MakeLeader(ServerPacket* pack)
}
}

void ExpeditionMessage::GetOnlineMembers(ServerPacket* pack)
void ExpeditionMessage::GetMemberStatuses(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerExpeditionCharacters_Struct*>(pack->pBuffer);

// not efficient but only requested during caching
char zone_name[64] = {0};
std::vector<ClientListEntry*> all_clients;
all_clients.reserve(client_list.GetClientCount());
client_list.GetClients(zone_name, all_clients);

for (uint32_t i = 0; i < buf->count; ++i)
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
auto expedition = expedition_state.GetExpedition(buf->expedition_id);
if (expedition)
{
auto it = std::find_if(all_clients.begin(), all_clients.end(), [&](const ClientListEntry* cle) {
return (cle && cle->CharID() == buf->entries[i].character_id);
});

if (it != all_clients.end())
{
buf->entries[i].character_zone_id = (*it)->zone();
buf->entries[i].character_instance_id = (*it)->instance();
buf->entries[i].character_online = true;
}
expedition->SendZoneMemberStatuses(buf->sender_zone_id, buf->sender_instance_id);
}

zoneserver_list.SendPacket(buf->sender_zone_id, buf->sender_instance_id, pack);
}

void ExpeditionMessage::SaveInvite(ServerPacket* pack)
Expand Down Expand Up @@ -211,13 +201,3 @@ void ExpeditionMessage::RequestInvite(ServerPacket* pack)
}
}
}

void ExpeditionMessage::ChooseNewLeader(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
auto expedition = expedition_state.GetExpedition(buf->expedition_id);
if (expedition)
{
expedition->ChooseNewLeader();
}
}
3 changes: 1 addition & 2 deletions world/expedition_message.h
Expand Up @@ -26,8 +26,7 @@ class ServerPacket;
namespace ExpeditionMessage
{
void AddPlayer(ServerPacket* pack);
void ChooseNewLeader(ServerPacket* pack);
void GetOnlineMembers(ServerPacket* pack);
void GetMemberStatuses(ServerPacket* pack);
void HandleZoneMessage(ServerPacket* pack);
void MakeLeader(ServerPacket* pack);
void RequestInvite(ServerPacket* pack);
Expand Down
2 changes: 2 additions & 0 deletions world/expedition_state.cpp
Expand Up @@ -110,6 +110,8 @@ void ExpeditionState::CacheExpeditions(
}
}

expedition->CacheMemberStatuses();

m_expeditions.emplace_back(std::move(expedition));
}
}
Expand Down
5 changes: 2 additions & 3 deletions world/zoneserver.cpp
Expand Up @@ -1366,17 +1366,16 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_ExpeditionLockout:
case ServerOP_ExpeditionLockoutDuration:
case ServerOP_ExpeditionLockState:
case ServerOP_ExpeditionMemberStatus:
case ServerOP_ExpeditionReplayOnJoin:
case ServerOP_ExpeditionExpireWarning:
{
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionChooseNewLeader:
case ServerOP_ExpeditionCreate:
case ServerOP_ExpeditionGetOnlineMembers:
case ServerOP_ExpeditionGetMemberStatuses:
case ServerOP_ExpeditionMemberChange:
case ServerOP_ExpeditionMemberStatus:
case ServerOP_ExpeditionMemberSwap:
case ServerOP_ExpeditionMembersRemoved:
case ServerOP_ExpeditionDzAddPlayer:
Expand Down

0 comments on commit 0ce7c11

Please sign in to comment.