[Expeditions] Refactor expedition caching (#1315)

Add common expedition base class

Use repository for zone and world expedition caching

World now stores members and leader as Member objects instead of ids

This improves readability of the caching methods and lets world cache
expedition dzs and members like zone. World also now caches expeditions
as unique_ptr which will be necessary for future dz callback lambdas
that capture 'this' so addresses don't change on cache vector resizes.
This commit is contained in:
hg
2021-04-07 02:20:35 -04:00
committed by GitHub
parent 0534a2c6be
commit dadc1b2843
27 changed files with 580 additions and 559 deletions
-15
View File
@@ -9,21 +9,6 @@
extern ZSList zoneserver_list;
DynamicZone::DynamicZone(
uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version,
uint32_t start_time, uint32_t duration, DynamicZoneType type
)
{
m_id = id;
m_instance_id = instance_id;
m_zone_id = zone_id;
m_zone_version = zone_version;
m_start_time = std::chrono::system_clock::from_time_t(start_time);
m_duration = std::chrono::seconds(duration);
m_type = type;
m_expire_time = m_start_time + m_duration;
}
Database& DynamicZone::GetDatabase()
{
return database;
-4
View File
@@ -19,10 +19,6 @@ class DynamicZone : public DynamicZoneBase
public:
using DynamicZoneBase::DynamicZoneBase; // inherit base constructors
DynamicZone() = default;
DynamicZone(uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version,
uint32_t start_time, uint32_t duration, DynamicZoneType type);
static DynamicZone* FindDynamicZoneByID(uint32_t dz_id);
static void HandleZoneMessage(ServerPacket* pack);
+25 -33
View File
@@ -30,37 +30,25 @@
extern ClientList client_list;
extern ZSList zoneserver_list;
Expedition::Expedition(uint32_t expedition_id, const DynamicZone& dz, uint32_t leader_id
) :
m_expedition_id(expedition_id),
m_dynamic_zone(dz),
m_leader_id(leader_id),
Expedition::Expedition() :
m_choose_leader_cooldown_timer{ static_cast<uint32_t>(RuleI(Expedition, ChooseLeaderCooldownTime)) }
{
m_warning_cooldown_timer.Enable();
}
void Expedition::AddMember(uint32_t character_id)
void Expedition::SetDynamicZone(DynamicZone&& dz)
{
if (!HasMember(character_id))
{
m_member_ids.emplace_back(character_id);
}
}
dz.SetName(GetName());
dz.SetLeaderName(GetLeaderName());
bool Expedition::HasMember(uint32_t character_id)
{
return std::any_of(m_member_ids.begin(), m_member_ids.end(),
[&](uint32_t member_id) { return member_id == character_id; });
m_dynamic_zone = std::move(dz);
}
void Expedition::RemoveMember(uint32_t character_id)
{
m_member_ids.erase(std::remove_if(m_member_ids.begin(), m_member_ids.end(),
[&](uint32_t member_id) { return member_id == character_id; }
), m_member_ids.end());
RemoveInternalMember(character_id);
if (character_id == m_leader_id)
if (character_id == m_leader.char_id)
{
ChooseNewLeader();
}
@@ -68,7 +56,7 @@ void Expedition::RemoveMember(uint32_t character_id)
void Expedition::ChooseNewLeader()
{
if (m_member_ids.empty() || !m_choose_leader_cooldown_timer.Check())
if (m_members.empty() || !m_choose_leader_cooldown_timer.Check())
{
m_choose_leader_needed = true;
return;
@@ -76,34 +64,38 @@ void Expedition::ChooseNewLeader()
// 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_member_ids.begin(), m_member_ids.end(), [&](uint32_t member_id) {
auto member_cle = (member_id != m_leader_id) ? client_list.FindCLEByCharacterID(member_id) : nullptr;
return (member_id != m_leader_id && member_cle && member_cle->GetOnline() == CLE_Status::InZone);
auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
if (member.char_id != m_leader.char_id) {
auto member_cle = client_list.FindCLEByCharacterID(member.char_id);
return (member_cle && member_cle->GetOnline() == CLE_Status::InZone);
}
return false;
});
if (it == m_member_ids.end())
if (it == m_members.end())
{
// no online members found, fallback to choosing any member
it = std::find_if(m_member_ids.begin(), m_member_ids.end(),
[&](uint32_t member_id) { return (member_id != m_leader_id); });
it = std::find_if(m_members.begin(), m_members.end(),
[&](const ExpeditionMember& member) { return (member.char_id != m_leader.char_id); });
}
if (it != m_member_ids.end() && SetNewLeader(*it))
if (it != m_members.end() && SetNewLeader(*it))
{
m_choose_leader_needed = false;
}
}
bool Expedition::SetNewLeader(uint32_t character_id)
bool Expedition::SetNewLeader(const ExpeditionMember& member)
{
if (!HasMember(character_id))
if (!HasMember(member.char_id))
{
return false;
}
LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_expedition_id, m_leader_id, character_id);
ExpeditionDatabase::UpdateLeaderID(m_expedition_id, character_id);
m_leader_id = character_id;
LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_id, m_leader.name, member.name);
ExpeditionDatabase::UpdateLeaderID(m_id, member.char_id);
m_leader = member;
m_dynamic_zone.SetLeaderName(m_leader.name);
SendZonesLeaderChanged();
return true;
}
@@ -133,7 +125,7 @@ void Expedition::SendZonesLeaderChanged()
auto pack = std::make_unique<ServerPacket>(ServerOP_ExpeditionLeaderChanged, pack_size);
auto buf = reinterpret_cast<ServerExpeditionLeaderID_Struct*>(pack->pBuffer);
buf->expedition_id = GetID();
buf->leader_id = m_leader_id;
buf->leader_id = m_leader.char_id;
zoneserver_list.SendPacket(pack.get());
}
+6 -15
View File
@@ -22,43 +22,34 @@
#define WORLD_EXPEDITION_H
#include "dynamic_zone.h"
#include "../common/expedition_base.h"
#include "../common/timer.h"
#include <chrono>
#include <cstdint>
#include <vector>
class Expedition
class Expedition : public ExpeditionBase
{
public:
Expedition() = default;
Expedition(uint32_t expedition_id, const DynamicZone& dz, uint32_t leader_id);
Expedition();
void AddMember(uint32_t character_id);
void RemoveMember(uint32_t character_id);
void RemoveAllMembers() { m_member_ids.clear(); }
void CheckExpireWarning();
void CheckLeader();
void ChooseNewLeader();
DynamicZone& GetDynamicZone() { return m_dynamic_zone; }
uint32_t GetID() const { return m_expedition_id; }
bool HasMember(uint32_t character_id);
bool IsEmpty() const { return m_member_ids.empty(); }
bool IsValid() const { return m_expedition_id != 0; }
bool Process();
void SendZonesExpeditionDeleted();
void SendZonesExpireWarning(uint32_t minutes_remaining);
bool SetNewLeader(uint32_t new_leader_id);
void SetDynamicZone(DynamicZone&& dz);
bool SetNewLeader(const ExpeditionMember& member);
private:
void SendZonesLeaderChanged();
uint32_t m_expedition_id = 0;
uint32_t m_leader_id = 0;
bool m_choose_leader_needed = false;
Timer m_choose_leader_cooldown_timer;
Timer m_warning_cooldown_timer;
DynamicZone m_dynamic_zone;
std::vector<uint32_t> m_member_ids;
};
#endif
-84
View File
@@ -71,90 +71,6 @@ void ExpeditionDatabase::PurgeExpiredCharacterLockouts()
database.QueryDatabase(query);
}
std::vector<Expedition> ExpeditionDatabase::LoadExpeditions(uint32_t select_expedition_id)
{
std::vector<Expedition> expeditions;
std::string query = SQL(
SELECT
expeditions.id,
expeditions.dynamic_zone_id,
instance_list.id,
instance_list.zone,
instance_list.version,
instance_list.start_time,
instance_list.duration,
expeditions.leader_id,
expedition_members.character_id
FROM expeditions
INNER JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id
INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id
INNER JOIN expedition_members ON expedition_members.expedition_id = expeditions.id
AND expedition_members.is_current_member = TRUE
);
if (select_expedition_id != 0)
{
query.append(fmt::format(" WHERE expeditions.id = {};", select_expedition_id));
}
else
{
query.append(" ORDER BY expeditions.id;");
}
auto results = database.QueryDatabase(query);
if (results.Success())
{
uint32_t last_expedition_id = 0;
for (auto row = results.begin(); row != results.end(); ++row)
{
uint32_t expedition_id = strtoul(row[0], nullptr, 10);
if (last_expedition_id != expedition_id)
{
DynamicZone dynamic_zone{
static_cast<uint32_t>(strtoul(row[1], nullptr, 10)), // dz_id
static_cast<uint32_t>(strtoul(row[3], nullptr, 10)), // dz_zone_id
static_cast<uint32_t>(strtoul(row[2], nullptr, 10)), // dz_instance_id
static_cast<uint32_t>(strtoul(row[4], nullptr, 10)), // dz_zone_version
static_cast<uint32_t>(strtoul(row[5], nullptr, 10)), // start_time
static_cast<uint32_t>(strtoul(row[6], nullptr, 10)), // duration
DynamicZoneType::Expedition
};
expeditions.emplace_back(
expedition_id,
dynamic_zone,
static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) // leader_id
);
}
last_expedition_id = expedition_id;
uint32_t member_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
expeditions.back().AddMember(member_id);
}
}
return expeditions;
}
Expedition ExpeditionDatabase::LoadExpedition(uint32_t expedition_id)
{
LogExpeditions("Loading expedition [{}] for world cache", expedition_id);
Expedition expedition;
auto expeditions = LoadExpeditions(expedition_id);
if (!expeditions.empty())
{
expedition = expeditions.front();
}
return expedition;
}
void ExpeditionDatabase::DeleteExpeditions(const std::vector<uint32_t>& expedition_ids)
{
LogExpeditionsDetail("Deleting [{}] expedition(s)", expedition_ids.size());
-2
View File
@@ -29,8 +29,6 @@ class Expedition;
namespace ExpeditionDatabase
{
void DeleteExpeditions(const std::vector<uint32_t>& expedition_ids);
std::vector<Expedition> LoadExpeditions(uint32_t select_expedition_id = 0);
Expedition LoadExpedition(uint32_t expedition_id);
void MoveMembersToSafeReturn(const std::vector<uint32_t>& expedition_ids);
void PurgeExpiredExpeditions();
void PurgeExpiredCharacterLockouts();
+5 -5
View File
@@ -43,22 +43,22 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack)
case ServerOP_ExpeditionCreate:
{
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
expedition_state.AddExpedition(buf->expedition_id);
expedition_state.CacheFromDatabase(buf->expedition_id);
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionMemberChange:
{
auto buf = reinterpret_cast<ServerExpeditionMemberChange_Struct*>(pack->pBuffer);
expedition_state.MemberChange(buf->expedition_id, buf->char_id, buf->removed);
expedition_state.MemberChange(buf->expedition_id, { buf->char_id, buf->char_name }, buf->removed);
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionMemberSwap:
{
auto buf = reinterpret_cast<ServerExpeditionMemberSwap_Struct*>(pack->pBuffer);
expedition_state.MemberChange(buf->expedition_id, buf->add_char_id, false);
expedition_state.MemberChange(buf->expedition_id, buf->remove_char_id, true);
expedition_state.MemberChange(buf->expedition_id, { buf->add_char_id, buf->add_char_name }, false);
expedition_state.MemberChange(buf->expedition_id, { buf->remove_char_id, buf->remove_char_name }, true);
zoneserver_list.SendPacket(pack);
break;
}
@@ -141,7 +141,7 @@ void ExpeditionMessage::MakeLeader(ServerPacket* pack)
auto expedition = expedition_state.GetExpedition(buf->expedition_id);
if (expedition)
{
buf->is_success = expedition->SetNewLeader(new_leader_cle->CharID());
buf->is_success = expedition->SetNewLeader({ new_leader_cle->CharID(), new_leader_cle->name() });
}
buf->is_online = true;
+71 -36
View File
@@ -21,7 +21,9 @@
#include "expedition_state.h"
#include "expedition.h"
#include "expedition_database.h"
#include "worlddb.h"
#include "../common/eqemu_logsys.h"
#include "../common/repositories/expedition_members_repository.h"
#include <algorithm>
ExpeditionState expedition_state;
@@ -29,66 +31,99 @@ ExpeditionState expedition_state;
Expedition* ExpeditionState::GetExpedition(uint32_t expedition_id)
{
auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(),
[&](const Expedition& expedition) { return expedition.GetID() == expedition_id; });
[&](const std::unique_ptr<Expedition>& expedition) {
return expedition->GetID() == expedition_id;
});
return (it != m_expeditions.end()) ? &(*it) : nullptr;
return (it != m_expeditions.end()) ? it->get() : nullptr;
}
Expedition* ExpeditionState::GetExpeditionByDynamicZoneID(uint32_t dz_id)
{
auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(),
[&](Expedition& expedition) { return expedition.GetDynamicZone().GetID() == dz_id; });
[&](const std::unique_ptr<Expedition>& expedition) {
return expedition->GetDynamicZone().GetID() == dz_id;
});
return (it != m_expeditions.end()) ? &(*it) : nullptr;
return (it != m_expeditions.end()) ? it->get() : nullptr;
}
void ExpeditionState::LoadActiveExpeditions()
void ExpeditionState::CacheFromDatabase(uint32_t expedition_id)
{
BenchTimer benchmark;
m_expeditions = ExpeditionDatabase::LoadExpeditions();
auto elapsed = benchmark.elapsed();
LogExpeditions("World caching [{}] expeditions took [{}s]", m_expeditions.size(), elapsed);
}
void ExpeditionState::AddExpedition(uint32_t expedition_id)
{
if (expedition_id == 0)
if (expedition_id == 0 || GetExpedition(expedition_id))
{
return;
}
auto expedition = ExpeditionDatabase::LoadExpedition(expedition_id);
auto expedition = ExpeditionsRepository::GetWithLeaderName(database, expedition_id);
CacheExpeditions({ std::move(expedition) });
}
if (expedition.IsValid())
void ExpeditionState::CacheAllFromDatabase()
{
BenchTimer benchmark;
auto expeditions = ExpeditionsRepository::GetAllWithLeaderName(database);
m_expeditions.clear();
m_expeditions.reserve(expeditions.size());
CacheExpeditions(std::move(expeditions));
LogExpeditions("Caching [{}] expedition(s) took [{}s]", m_expeditions.size(), benchmark.elapsed());
}
void ExpeditionState::CacheExpeditions(
std::vector<ExpeditionsRepository::ExpeditionWithLeader>&& expedition_entries)
{
// bulk load expedition dzs and members before caching
std::vector<uint32_t> expedition_ids;
std::vector<uint32_t> dynamic_zone_ids;
for (const auto& entry : expedition_entries)
{
auto existing_expedition = GetExpedition(expedition_id);
if (!existing_expedition)
expedition_ids.emplace_back(entry.id);
dynamic_zone_ids.emplace_back(entry.dynamic_zone_id);
}
auto dynamic_zones = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids);
auto expedition_members = ExpeditionMembersRepository::GetWithNames(database, expedition_ids);
for (auto& entry : expedition_entries)
{
auto expedition = std::make_unique<Expedition>();
expedition->LoadRepositoryResult(std::move(entry));
auto dz_entry_iter = std::find_if(dynamic_zones.begin(), dynamic_zones.end(),
[&](const DynamicZonesRepository::DynamicZoneInstance& dz_entry) {
return dz_entry.id == entry.dynamic_zone_id;
});
if (dz_entry_iter != dynamic_zones.end())
{
m_expeditions.emplace_back(expedition);
expedition->SetDynamicZone(std::move(*dz_entry_iter));
}
for (auto& member : expedition_members)
{
if (member.expedition_id == expedition->GetID())
{
expedition->AddMemberFromRepositoryResult(std::move(member));
}
}
m_expeditions.emplace_back(std::move(expedition));
}
}
void ExpeditionState::RemoveExpedition(uint32_t expedition_id)
{
m_expeditions.erase(std::remove_if(m_expeditions.begin(), m_expeditions.end(),
[&](const Expedition& expedition) {
return expedition.GetID() == expedition_id;
}
), m_expeditions.end());
}
void ExpeditionState::MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove)
void ExpeditionState::MemberChange(
uint32_t expedition_id, const ExpeditionMember& member, bool remove)
{
auto expedition = GetExpedition(expedition_id);
if (expedition)
{
if (remove) {
expedition->RemoveMember(character_id);
expedition->RemoveMember(member.char_id);
} else {
expedition->AddMember(character_id);
expedition->AddInternalMember(member);
}
}
}
@@ -98,7 +133,7 @@ void ExpeditionState::RemoveAllMembers(uint32_t expedition_id)
auto expedition = GetExpedition(expedition_id);
if (expedition)
{
expedition->RemoveAllMembers();
expedition->ClearInternalMembers();
}
}
@@ -113,10 +148,10 @@ void ExpeditionState::Process()
for (auto it = m_expeditions.begin(); it != m_expeditions.end();)
{
bool is_deleted = it->Process();
bool is_deleted = (*it)->Process();
if (is_deleted)
{
expedition_ids.emplace_back(it->GetID());
expedition_ids.emplace_back((*it)->GetID());
}
it = is_deleted ? m_expeditions.erase(it) : it + 1;
}
+7 -5
View File
@@ -21,6 +21,7 @@
#ifndef WORLD_EXPEDITION_STATE_H
#define WORLD_EXPEDITION_STATE_H
#include "../common/repositories/expeditions_repository.h"
#include "../common/rulesys.h"
#include "../common/timer.h"
#include <cstdint>
@@ -29,21 +30,22 @@
extern class ExpeditionState expedition_state;
class Expedition;
struct ExpeditionMember;
class ExpeditionState
{
public:
void AddExpedition(uint32_t expedition_id);
void CacheExpeditions(std::vector<ExpeditionsRepository::ExpeditionWithLeader>&& expedition_entries);
void CacheFromDatabase(uint32_t expedition_id);
void CacheAllFromDatabase();
Expedition* GetExpedition(uint32_t expedition_id);
Expedition* GetExpeditionByDynamicZoneID(uint32_t dz_id);
void LoadActiveExpeditions();
void MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove);
void MemberChange(uint32_t expedition_id, const ExpeditionMember& member, bool remove);
void Process();
void RemoveAllMembers(uint32_t expedition_id);
void RemoveExpedition(uint32_t expedition_id);
private:
std::vector<Expedition> m_expeditions;
std::vector<std::unique_ptr<Expedition>> m_expeditions;
Timer m_process_throttle_timer{static_cast<uint32_t>(RuleI(DynamicZone, WorldProcessRate))};
};
+1 -1
View File
@@ -438,7 +438,7 @@ int main(int argc, char** argv) {
PurgeInstanceTimer.Start(450000);
LogInfo("Loading active expeditions");
expedition_state.LoadActiveExpeditions();
expedition_state.CacheAllFromDatabase();
LogInfo("Loading char create info");
content_db.LoadCharacterCreateAllocations();