[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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 580 additions and 559 deletions

View File

@ -32,6 +32,7 @@ SET(common_sources
eq_stream_proxy.cpp
eqtime.cpp
event_sub.cpp
expedition_base.cpp
expedition_lockout_timer.cpp
extprofile.cpp
faction.cpp
@ -495,6 +496,7 @@ SET(common_headers
eqtime.h
errmsg.h
event_sub.h
expedition_base.h
expedition_lockout_timer.h
extprofile.h
faction.h

View File

@ -474,4 +474,13 @@ enum class DynamicZoneType
Quest
};
enum class ExpeditionMemberStatus : uint8_t
{
Unknown = 0,
Online,
Offline,
InDynamicZone,
LinkDead
};
#endif /*COMMON_EQ_CONSTANTS_H*/

View File

@ -0,0 +1,93 @@
#include "expedition_base.h"
#include "repositories/expeditions_repository.h"
ExpeditionBase::ExpeditionBase(uint32_t id, const std::string& uuid,
const std::string& expedition_name, const ExpeditionMember& leader,
uint32_t min_players, uint32_t max_players
) :
m_id(id),
m_uuid(uuid),
m_expedition_name(expedition_name),
m_leader(leader),
m_min_players(min_players),
m_max_players(max_players)
{
}
void ExpeditionBase::LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry)
{
m_id = entry.id;
m_uuid = std::move(entry.uuid);
m_expedition_name = std::move(entry.expedition_name);
m_min_players = entry.min_players;
m_max_players = entry.max_players;
m_add_replay_on_join = entry.add_replay_on_join;
m_is_locked = entry.is_locked;
m_leader.char_id = entry.leader_id;
m_leader.name = std::move(entry.leader_name);
}
void ExpeditionBase::AddMemberFromRepositoryResult(
ExpeditionMembersRepository::MemberWithName&& entry)
{
auto status = ExpeditionMemberStatus::Unknown;
AddInternalMember({ entry.character_id, std::move(entry.character_name), status });
}
void ExpeditionBase::AddInternalMember(const ExpeditionMember& member)
{
if (!HasMember(member.char_id))
{
m_members.emplace_back(member);
}
}
void ExpeditionBase::RemoveInternalMember(uint32_t character_id)
{
m_members.erase(std::remove_if(m_members.begin(), m_members.end(),
[&](const ExpeditionMember& member) { return member.char_id == character_id; }
), m_members.end());
}
bool ExpeditionBase::HasMember(uint32_t character_id)
{
return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
return member.char_id == character_id;
});
}
bool ExpeditionBase::HasMember(const std::string& character_name)
{
return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0);
});
}
ExpeditionMember ExpeditionBase::GetMemberData(uint32_t character_id)
{
auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
return member.char_id == character_id;
});
ExpeditionMember member_data;
if (it != m_members.end())
{
member_data = *it;
}
return member_data;
}
ExpeditionMember ExpeditionBase::GetMemberData(const std::string& character_name)
{
auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0);
});
ExpeditionMember member_data;
if (it != m_members.end())
{
member_data = *it;
}
return member_data;
}

74
common/expedition_base.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef COMMON_EXPEDITION_BASE_H
#define COMMON_EXPEDITION_BASE_H
#include "eq_constants.h"
#include "repositories/expeditions_repository.h"
#include "repositories/expedition_members_repository.h"
#include <cstdint>
#include <string>
#include <vector>
struct ExpeditionMember
{
uint32_t char_id = 0;
std::string name;
ExpeditionMemberStatus status = ExpeditionMemberStatus::Online;
ExpeditionMember() = default;
ExpeditionMember(uint32_t id, const std::string& name_)
: char_id(id), name(name_) {}
ExpeditionMember(uint32_t id, const std::string& name_, ExpeditionMemberStatus status_)
: char_id(id), name(name_), status(status_) {}
bool IsValid() const { return char_id != 0 && !name.empty(); }
};
class ExpeditionBase
{
public:
virtual ~ExpeditionBase() = default;
ExpeditionBase(const ExpeditionBase&) = default;
ExpeditionBase(ExpeditionBase&&) = default;
ExpeditionBase& operator=(const ExpeditionBase&) = default;
ExpeditionBase& operator=(ExpeditionBase&&) = default;
uint32_t GetID() const { return m_id; }
uint32_t GetLeaderID() const { return m_leader.char_id; }
uint32_t GetMinPlayers() const { return m_min_players; }
uint32_t GetMaxPlayers() const { return m_max_players; }
uint32_t GetMemberCount() const { return static_cast<uint32_t>(m_members.size()); }
const std::string& GetName() const { return m_expedition_name; }
const std::string& GetLeaderName() const { return m_leader.name; }
const std::string& GetUUID() const { return m_uuid; }
const std::vector<ExpeditionMember>& GetMembers() const { return m_members; }
void AddInternalMember(const ExpeditionMember& member);
void ClearInternalMembers() { m_members.clear(); }
bool HasMember(const std::string& character_name);
bool HasMember(uint32_t character_id);
bool IsEmpty() const { return m_members.empty(); }
void RemoveInternalMember(uint32_t character_id);
void LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry);
void AddMemberFromRepositoryResult(ExpeditionMembersRepository::MemberWithName&& entry);
protected:
ExpeditionBase() = default;
ExpeditionBase(uint32_t id, const std::string& uuid, const std::string& expedition_name,
const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players);
ExpeditionMember GetMemberData(uint32_t character_id);
ExpeditionMember GetMemberData(const std::string& character_name);
uint32_t m_id = 0;
uint32_t m_min_players = 0;
uint32_t m_max_players = 0;
bool m_is_locked = false;
bool m_add_replay_on_join = true;
std::string m_uuid;
std::string m_expedition_name;
ExpeditionMember m_leader;
std::vector<ExpeditionMember> m_members;
};
#endif

View File

@ -78,7 +78,7 @@ public:
entry.id = 0;
entry.expedition_id = 0;
entry.event_name = "";
entry.expire_time = current_timestamp();
entry.expire_time = "";
entry.duration = 0;
entry.from_expedition_uuid = "";

View File

@ -65,6 +65,58 @@ public:
// Custom extended repository methods here
struct ExpeditionLockoutsWithTimestamp {
uint32_t id;
uint32_t expedition_id;
std::string event_name;
time_t expire_time;
int duration;
std::string from_expedition_uuid;
};
static std::vector<ExpeditionLockoutsWithTimestamp> GetWithTimestamp(
Database& db, const std::vector<uint32_t>& expedition_ids)
{
if (expedition_ids.empty())
{
return {};
}
std::vector<ExpeditionLockoutsWithTimestamp> all_entries;
auto results = db.QueryDatabase(fmt::format(SQL(
SELECT
id,
expedition_id,
event_name,
UNIX_TIMESTAMP(expire_time),
duration,
from_expedition_uuid
FROM expedition_lockouts
WHERE expedition_id IN ({})
),
fmt::join(expedition_ids, ",")
));
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row)
{
ExpeditionLockoutsWithTimestamp entry{};
int col = 0;
entry.id = strtoul(row[col++], nullptr, 10);
entry.expedition_id = strtoul(row[col++], nullptr, 10);
entry.event_name = row[col++];
entry.expire_time = strtoull(row[col++], nullptr, 10);
entry.duration = strtol(row[col++], nullptr, 10);
entry.from_expedition_uuid = row[col++];
all_entries.emplace_back(std::move(entry));
}
return all_entries;
}
};
#endif //EQEMU_EXPEDITION_LOCKOUTS_REPOSITORY_H

View File

@ -65,6 +65,68 @@ public:
// Custom extended repository methods here
struct MemberWithName {
uint32_t id;
uint32_t expedition_id;
uint32_t character_id;
int is_current_member;
std::string character_name;
};
static std::string SelectMembersWithNames()
{
return std::string(SQL(
SELECT
expedition_members.id,
expedition_members.expedition_id,
expedition_members.character_id,
expedition_members.is_current_member,
character_data.name
FROM expedition_members
INNER JOIN character_data ON expedition_members.character_id = character_data.id
));
}
static std::vector<MemberWithName> GetWithNames(Database& db,
const std::vector<uint32_t>& expedition_ids)
{
if (expedition_ids.empty())
{
return {};
}
std::vector<MemberWithName> all_entries;
auto results = db.QueryDatabase(fmt::format(SQL(
{}
WHERE expedition_members.expedition_id IN ({})
AND expedition_members.is_current_member = TRUE;
),
SelectMembersWithNames(),
fmt::join(expedition_ids, ",")
));
if (results.Success())
{
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row)
{
MemberWithName entry{};
int col = 0;
entry.id = strtoul(row[col++], nullptr, 10);
entry.expedition_id = strtoul(row[col++], nullptr, 10);
entry.character_id = strtoul(row[col++], nullptr, 10);
entry.is_current_member = strtoul(row[col++], nullptr, 10);
entry.character_name = row[col++];
all_entries.emplace_back(std::move(entry));
}
}
return all_entries;
}
};
#endif //EQEMU_EXPEDITION_MEMBERS_REPOSITORY_H

View File

@ -65,6 +65,100 @@ public:
// Custom extended repository methods here
struct ExpeditionWithLeader
{
uint32_t id;
std::string uuid;
uint32_t dynamic_zone_id;
std::string expedition_name;
uint32_t min_players;
uint32_t max_players;
int add_replay_on_join;
int is_locked;
uint32_t leader_id;
std::string leader_name;
};
static std::string SelectExpeditionsJoinLeader()
{
return std::string(SQL(
SELECT
expeditions.id,
expeditions.uuid,
expeditions.dynamic_zone_id,
expeditions.expedition_name,
expeditions.min_players,
expeditions.max_players,
expeditions.add_replay_on_join,
expeditions.is_locked,
expeditions.leader_id,
character_data.name leader_name
FROM expeditions
INNER JOIN character_data ON expeditions.leader_id = character_data.id
));
}
static ExpeditionWithLeader FillExpeditionWithLeaderFromRow(MySQLRequestRow& row)
{
ExpeditionWithLeader entry{};
int col = 0;
entry.id = strtoul(row[col++], nullptr, 10);
entry.uuid = row[col++];
entry.dynamic_zone_id = strtoul(row[col++], nullptr, 10);
entry.expedition_name = row[col++];
entry.min_players = strtoul(row[col++], nullptr, 10);
entry.max_players = strtoul(row[col++], nullptr, 10);
entry.add_replay_on_join = strtoul(row[col++], nullptr, 10);
entry.is_locked = strtoul(row[col++], nullptr, 10);
entry.leader_id = strtoul(row[col++], nullptr, 10);
entry.leader_name = row[col++];
return entry;
}
static std::vector<ExpeditionWithLeader> GetAllWithLeaderName(Database& db)
{
std::vector<ExpeditionWithLeader> all_entries;
auto results = db.QueryDatabase(fmt::format(
"{} ORDER BY expeditions.id;",
SelectExpeditionsJoinLeader()
));
if (results.Success())
{
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row)
{
ExpeditionWithLeader entry = FillExpeditionWithLeaderFromRow(row);
all_entries.emplace_back(std::move(entry));
}
}
return all_entries;
}
static ExpeditionWithLeader GetWithLeaderName(Database& db, uint32_t expedition_id)
{
ExpeditionWithLeader entry{};
auto results = db.QueryDatabase(fmt::format(
"{} WHERE expeditions.id = {};",
SelectExpeditionsJoinLeader(),
expedition_id
));
if (results.Success() && results.RowCount() > 0)
{
auto row = results.begin();
entry = FillExpeditionWithLeaderFromRow(row);
}
return entry;
}
struct CharacterExpedition
{
uint32_t id;

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;

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);

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());
}

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

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());

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();

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;

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);
if (expedition.IsValid())
{
auto existing_expedition = GetExpedition(expedition_id);
if (!existing_expedition)
{
m_expeditions.emplace_back(expedition);
auto expedition = ExpeditionsRepository::GetWithLeaderName(database, expedition_id);
CacheExpeditions({ std::move(expedition) });
}
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)
{
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())
{
expedition->SetDynamicZone(std::move(*dz_entry_iter));
}
for (auto& member : expedition_members)
{
if (member.expedition_id == expedition->GetID())
{
expedition->AddMemberFromRepositoryResult(std::move(member));
}
}
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.emplace_back(std::move(expedition));
}
), 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;
}

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))};
};

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();

View File

@ -6900,20 +6900,20 @@ void command_dz(Client* c, const Seperator* sep)
auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format(
"#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName());
auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format(
"#zoneinstance {}", expedition->GetInstanceID()), false, "zone");
"#zoneinstance {}", expedition->GetDynamicZone().GetInstanceID()), false, "zone");
auto seconds = expedition->GetDynamicZone().GetSecondsRemaining();
c->Message(Chat::White, fmt::format(
"expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]",
expedition->GetID(),
expedition->GetDynamicZoneID(),
expedition->GetDynamicZone().GetID(),
expedition->GetName(),
leader_saylink,
zone_saylink,
ZoneName(expedition->GetDynamicZone().GetZoneID()),
expedition->GetDynamicZone().GetZoneID(),
expedition->GetInstanceID(),
expedition->GetDynamicZone().GetInstanceID(),
expedition->GetDynamicZone().GetZoneVersion(),
expedition->GetMemberCount(),
seconds / 3600, // hours

View File

@ -64,22 +64,6 @@ DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id)
return nullptr;
}
std::unordered_map<uint32_t, DynamicZone> DynamicZone::LoadMultipleDzFromDatabase(
const std::vector<uint32_t>& dynamic_zone_ids)
{
LogDynamicZonesDetail("Loading dynamic zone data for [{}] instances", dynamic_zone_ids.size());
std::unordered_map<uint32_t, DynamicZone> dynamic_zones;
auto entries = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids);
for (auto& entry : entries)
{
dynamic_zones.emplace(entry.id, std::move(entry));
}
return dynamic_zones;
}
void DynamicZone::StartAllClientRemovalTimers()
{
for (const auto& client_iter : entity_list.GetClientList())

View File

@ -42,8 +42,6 @@ public:
DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type);
static DynamicZone* FindDynamicZoneByID(uint32_t dz_id);
static std::unordered_map<uint32_t, DynamicZone> LoadMultipleDzFromDatabase(
const std::vector<uint32_t>& dynamic_zone_ids);
static void HandleWorldMessage(ServerPacket* pack);
void SetSecondsRemaining(uint32_t seconds_remaining) override;

View File

@ -27,6 +27,8 @@
#include "zonedb.h"
#include "../common/eqemu_logsys.h"
#include "../common/expedition_lockout_timer.h"
#include "../common/repositories/expedition_lockouts_repository.h"
#include "../common/repositories/expedition_members_repository.h"
#include "../common/util/uuid.h"
extern WorldServer worldserver;
@ -49,13 +51,7 @@ const int32_t Expedition::EVENT_TIMER_ID = 1;
Expedition::Expedition(
uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name,
const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players
) :
m_id(id),
m_uuid(uuid),
m_expedition_name(expedition_name),
m_leader(leader),
m_min_players(min_players),
m_max_players(max_players)
) : ExpeditionBase(id, uuid, expedition_name, leader, min_players, max_players)
{
SetDynamicZone(std::move(dz));
}
@ -122,7 +118,7 @@ Expedition* Expedition::TryCreate(
"Created [{}] [{}] instance id: [{}] leader: [{}] minplayers: [{}] maxplayers: [{}]",
expedition->GetID(),
expedition->GetName(),
expedition->GetInstanceID(),
expedition->GetDynamicZone().GetInstanceID(),
expedition->GetLeaderName(),
expedition->GetMinPlayers(),
expedition->GetMaxPlayers()
@ -150,113 +146,88 @@ Expedition* Expedition::TryCreate(
return nullptr;
}
void Expedition::CacheExpeditions(MySQLRequestResult& results)
void Expedition::CacheExpeditions(
std::vector<ExpeditionsRepository::ExpeditionWithLeader>&& expedition_entries)
{
if (!results.Success() || !zone)
if (!zone)
{
return;
}
// bulk load expedition dzs, members, and internal lockouts before caching
std::vector<uint32_t> expedition_ids;
std::vector<uint32_t> dynamic_zone_ids;;
std::vector<std::pair<uint32_t, uint32_t>> expedition_character_ids;
using col = LoadExpeditionColumns::eLoadExpeditionColumns;
uint32_t last_expedition_id = 0;
for (auto row = results.begin(); row != results.end(); ++row)
std::vector<uint32_t> dynamic_zone_ids;
for (const auto& entry : expedition_entries)
{
auto expedition_id = strtoul(row[col::id], nullptr, 10);
if (expedition_id != last_expedition_id)
{
expedition_ids.emplace_back(expedition_id);
uint32_t leader_id = strtoul(row[col::leader_id], nullptr, 10);
uint32_t dynamic_zone_id = strtoul(row[col::dz_id], nullptr, 10);
dynamic_zone_ids.emplace_back(dynamic_zone_id);
std::unique_ptr<Expedition> expedition = std::make_unique<Expedition>(
expedition_id,
row[col::uuid], // expedition uuid
DynamicZone{ dynamic_zone_id },
row[col::expedition_name], // expedition name
ExpeditionMember{ leader_id, row[col::leader_name] }, // expedition leader id, name
strtoul(row[col::min_players], nullptr, 10), // min_players
strtoul(row[col::max_players], nullptr, 10) // max_players
);
bool add_replay_on_join = (strtoul(row[col::add_replay_on_join], nullptr, 10) != 0);
bool is_locked = (strtoul(row[col::is_locked], nullptr, 10) != 0);
expedition->SetReplayLockoutOnMemberJoin(add_replay_on_join);
expedition->SetLocked(is_locked, ExpeditionLockMessage::None);
zone->expedition_cache.emplace(expedition_id, std::move(expedition));
expedition_ids.emplace_back(entry.id);
dynamic_zone_ids.emplace_back(entry.dynamic_zone_id);
}
last_expedition_id = expedition_id;
auto dynamic_zones = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids);
auto expedition_members = ExpeditionMembersRepository::GetWithNames(database, expedition_ids);
auto expedition_lockouts = ExpeditionLockoutsRepository::GetWithTimestamp(database, expedition_ids);
// looping expedition members
auto current_expedition = Expedition::FindCachedExpeditionByID(last_expedition_id);
if (current_expedition)
std::vector<std::pair<uint32_t, uint32_t>> expedition_character_ids; // for online status request
for (auto& entry : expedition_entries)
{
auto member_id = strtoul(row[col::member_id], nullptr, 10);
current_expedition->AddInternalMember(
row[col::member_name], member_id, ExpeditionMemberStatus::Offline);
expedition_character_ids.emplace_back(expedition_id, member_id);
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())
{
expedition->SetDynamicZone(std::move(*dz_entry_iter));
}
for (auto& member : expedition_members)
{
if (member.expedition_id == expedition->GetID())
{
expedition->AddMemberFromRepositoryResult(std::move(member));
expedition_character_ids.emplace_back(expedition->GetID(), member.character_id);
}
}
for (auto& lockout_entry : expedition_lockouts)
{
if (lockout_entry.expedition_id == expedition->GetID())
{
ExpeditionLockoutTimer lockout{
std::move(lockout_entry.from_expedition_uuid),
expedition->GetName(),
std::move(lockout_entry.event_name),
static_cast<uint64_t>(lockout_entry.expire_time),
static_cast<uint32_t>(lockout_entry.duration)
};
std::string event_name = lockout.GetEventName(); // copy for key since we're moving it
expedition->m_lockouts.emplace(std::move(event_name), std::move(lockout));
}
}
auto inserted = zone->expedition_cache.emplace(entry.id, std::move(expedition));
inserted.first->second->SendUpdatesToZoneMembers();
}
// ask world for online members from all cached expeditions at once
Expedition::SendWorldGetOnlineMembers(expedition_character_ids);
// bulk load dynamic zone data and expedition lockouts for cached expeditions
auto dynamic_zones = DynamicZone::LoadMultipleDzFromDatabase(dynamic_zone_ids);
auto expedition_lockouts = ExpeditionDatabase::LoadMultipleExpeditionLockouts(expedition_ids);
for (const auto& expedition_id : expedition_ids)
{
auto expedition = Expedition::FindCachedExpeditionByID(expedition_id);
if (expedition)
{
auto dz_iter = dynamic_zones.find(expedition->GetDynamicZoneID());
if (dz_iter != dynamic_zones.end())
{
expedition->SetDynamicZone(std::move(dz_iter->second));
}
auto lockout_iter = expedition_lockouts.find(expedition->GetID());
if (lockout_iter != expedition_lockouts.end())
{
expedition->m_lockouts = lockout_iter->second;
}
// send member updates now that all data is loaded for the cached expedition
expedition->SendUpdatesToZoneMembers();
}
}
}
void Expedition::CacheFromDatabase(uint32_t expedition_id)
{
if (zone)
if (zone && expedition_id != 0)
{
BenchTimer benchmark;
auto results = ExpeditionDatabase::LoadExpedition(expedition_id);
if (!results.Success())
{
LogExpeditions("Failed to load Expedition [{}] for zone cache", expedition_id);
return;
}
auto expedition = ExpeditionsRepository::GetWithLeaderName(database, expedition_id);
CacheExpeditions({ std::move(expedition) });
CacheExpeditions(results);
auto elapsed = benchmark.elapsed();
LogExpeditions("Caching new expedition [{}] took [{}s]", expedition_id, elapsed);
LogExpeditions("Caching new expedition [{}] took [{}s]", expedition_id, benchmark.elapsed());
}
}
@ -269,20 +240,13 @@ bool Expedition::CacheAllFromDatabase()
BenchTimer benchmark;
auto expeditions = ExpeditionsRepository::GetAllWithLeaderName(database);
zone->expedition_cache.clear();
zone->expedition_cache.reserve(expeditions.size());
// load all active expeditions and members to current zone cache
auto results = ExpeditionDatabase::LoadAllExpeditions();
if (!results.Success())
{
LogExpeditions("Failed to load Expeditions for zone cache");
return false;
}
CacheExpeditions(std::move(expeditions));
CacheExpeditions(results);
auto elapsed = benchmark.elapsed();
LogExpeditions("Caching [{}] expedition(s) took [{}s]", zone->expedition_cache.size(), elapsed);
LogExpeditions("Caching [{}] expedition(s) took [{}s]", zone->expedition_cache.size(), benchmark.elapsed());
return true;
}
@ -391,48 +355,6 @@ bool Expedition::HasReplayLockout()
return HasLockout(DZ_REPLAY_TIMER_NAME);
}
bool Expedition::HasMember(uint32_t character_id)
{
return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
return member.char_id == character_id;
});
}
bool Expedition::HasMember(const std::string& character_name)
{
return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0);
});
}
ExpeditionMember Expedition::GetMemberData(uint32_t character_id)
{
auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
return member.char_id == character_id;
});
ExpeditionMember member_data;
if (it != m_members.end())
{
member_data = *it;
}
return member_data;
}
ExpeditionMember Expedition::GetMemberData(const std::string& character_name)
{
auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) {
return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0);
});
ExpeditionMember member_data;
if (it != m_members.end())
{
member_data = *it;
}
return member_data;
}
void Expedition::SetReplayLockoutOnMemberJoin(bool add_on_join, bool update_db)
{
m_add_replay_on_join = add_on_join;
@ -526,20 +448,6 @@ void Expedition::RemoveLockout(const std::string& event_name)
SendWorldLockoutUpdate(lockout, true);
}
void Expedition::AddInternalMember(
const std::string& char_name, uint32_t character_id, ExpeditionMemberStatus status)
{
auto it = std::find_if(m_members.begin(), m_members.end(),
[character_id](const ExpeditionMember& member) {
return member.char_id == character_id;
});
if (it == m_members.end())
{
m_members.emplace_back(character_id, char_name, status);
}
}
bool Expedition::AddMember(const std::string& add_char_name, uint32_t add_char_id)
{
if (HasMember(add_char_id))
@ -644,10 +552,7 @@ void Expedition::UpdateMemberStatus(uint32_t update_member_id, ExpeditionMemberS
}
// if zone already had this member status cached avoid packet update to clients
auto it = std::find_if(m_members.begin(), m_members.end(),
[&](const ExpeditionMember& member) { return member.char_id == update_member_id; });
if (it != m_members.end() && it->status == status)
if (member_data.status == status)
{
return;
}
@ -1197,7 +1102,7 @@ void Expedition::ProcessMemberAdded(const std::string& char_name, uint32_t added
leader_client->MessageString(Chat::Yellow, EXPEDITION_MEMBER_ADDED, char_name.c_str(), m_expedition_name.c_str());
}
AddInternalMember(char_name, added_char_id, ExpeditionMemberStatus::Online);
AddInternalMember({ added_char_id, char_name, ExpeditionMemberStatus::Online });
Client* member_client = entity_list.GetClientByCharID(added_char_id);
if (member_client)

View File

@ -22,8 +22,9 @@
#define EXPEDITION_H
#include "dynamic_zone.h"
#include "../common/eq_constants.h"
#include "../common/expedition_base.h"
#include "../common/expedition_lockout_timer.h"
#include "../common/repositories/expeditions_repository.h"
#include <cstdint>
#include <memory>
#include <string>
@ -34,21 +35,11 @@ class Client;
class EQApplicationPacket;
struct ExpeditionInvite;
class ExpeditionRequest;
class MySQLRequestResult;
class ServerPacket;
extern const char* const DZ_YOU_NOT_ASSIGNED;
extern const char* const EXPEDITION_OTHER_BELONGS;
enum class ExpeditionMemberStatus : uint8_t
{
Unknown = 0,
Online,
Offline,
InDynamicZone,
LinkDead
};
enum class ExpeditionLockMessage : uint8_t
{
None = 0,
@ -56,25 +47,10 @@ enum class ExpeditionLockMessage : uint8_t
Begin
};
struct ExpeditionMember
{
uint32_t char_id = 0;
std::string name;
ExpeditionMemberStatus status = ExpeditionMemberStatus::Online;
ExpeditionMember() = default;
ExpeditionMember(uint32_t char_id_, const std::string& name_)
: char_id(char_id_), name(name_) {}
ExpeditionMember(uint32_t char_id_, const std::string& name_, ExpeditionMemberStatus status_)
: char_id(char_id_), name(name_), status(status_) {}
bool IsValid() const { return char_id != 0 && !name.empty(); }
};
class Expedition
class Expedition : public ExpeditionBase
{
public:
Expedition() = delete;
Expedition() = default;
Expedition(uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name,
const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players);
@ -103,23 +79,10 @@ public:
const std::string& expedition_name = {}, const std::string& event_name = {});
static void AddLockoutClients(const ExpeditionLockoutTimer& lockout, uint32_t exclude_id = 0);
uint32_t GetDynamicZoneID() const { return m_dynamiczone.GetID(); }
uint32_t GetID() const { return m_id; }
uint16_t GetInstanceID() const { return m_dynamiczone.GetInstanceID(); }
uint32_t GetLeaderID() const { return m_leader.char_id; }
uint32_t GetMinPlayers() const { return m_min_players; }
uint32_t GetMaxPlayers() const { return m_max_players; }
uint32_t GetMemberCount() const { return static_cast<uint32_t>(m_members.size()); }
DynamicZone& GetDynamicZone() { return m_dynamiczone; }
const std::string& GetName() const { return m_expedition_name; }
const std::string& GetLeaderName() const { return m_leader.name; }
const std::string& GetUUID() const { return m_uuid; }
const std::unordered_map<std::string, ExpeditionLockoutTimer>& GetLockouts() const { return m_lockouts; }
const std::vector<ExpeditionMember>& GetMembers() const { return m_members; }
bool AddMember(const std::string& add_char_name, uint32_t add_char_id);
bool HasMember(const std::string& character_name);
bool HasMember(uint32_t character_id);
void RemoveAllMembers(bool enable_removal_timers = true);
bool RemoveMember(const std::string& remove_char_name);
void SetMemberStatus(Client* client, ExpeditionMemberStatus status);
@ -164,13 +127,12 @@ public:
static const int32_t EVENT_TIMER_ID;
private:
static void CacheExpeditions(MySQLRequestResult& results);
static void CacheExpeditions(std::vector<ExpeditionsRepository::ExpeditionWithLeader>&& expeditions);
static void SendWorldGetOnlineMembers(const std::vector<std::pair<uint32_t, uint32_t>>& expedition_character_ids);
static void SendWorldCharacterLockout(uint32_t character_id, const ExpeditionLockoutTimer& lockout, bool remove);
void AddLockout(const ExpeditionLockoutTimer& lockout, bool members_only = false);
void AddLockoutDurationClients(const ExpeditionLockoutTimer& lockout, int seconds, uint32_t exclude_id = 0);
void AddInternalMember(const std::string& char_name, uint32_t char_id, ExpeditionMemberStatus status);
bool ConfirmLeaderCommand(Client* requester);
bool ProcessAddConflicts(Client* leader_client, Client* add_client, bool swapping);
void ProcessLeaderChanged(uint32_t new_leader_id);
@ -207,8 +169,6 @@ private:
const std::string& swap_remove_name, Client* leader_client = nullptr);
void UpdateMemberStatus(uint32_t update_character_id, ExpeditionMemberStatus status);
ExpeditionMember GetMemberData(uint32_t character_id);
ExpeditionMember GetMemberData(const std::string& character_name);
std::unique_ptr<EQApplicationPacket> CreateExpireWarningPacket(uint32_t minutes_remaining);
std::unique_ptr<EQApplicationPacket> CreateInfoPacket(bool clear = false);
std::unique_ptr<EQApplicationPacket> CreateInvitePacket(const std::string& inviter_name, const std::string& swap_remove_name);
@ -217,16 +177,7 @@ private:
std::unique_ptr<EQApplicationPacket> CreateMemberListStatusPacket(const std::string& name, ExpeditionMemberStatus status);
std::unique_ptr<EQApplicationPacket> CreateLeaderNamePacket();
uint32_t m_id = 0;
uint32_t m_min_players = 0;
uint32_t m_max_players = 0;
bool m_is_locked = false;
bool m_add_replay_on_join = true;
std::string m_uuid;
std::string m_expedition_name;
DynamicZone m_dynamiczone { DynamicZoneType::Expedition };
ExpeditionMember m_leader;
std::vector<ExpeditionMember> m_members;
std::unordered_map<std::string, ExpeditionLockoutTimer> m_lockouts;
std::unordered_map<uint32_t, std::string> m_npc_loot_events; // only valid inside dz zone
std::unordered_map<uint32_t, std::string> m_spawn_loot_events; // only valid inside dz zone

View File

@ -51,52 +51,6 @@ uint32_t ExpeditionDatabase::InsertExpedition(
return results.LastInsertedID();
}
std::string ExpeditionDatabase::LoadExpeditionsSelectQuery()
{
return std::string(SQL(
SELECT
expeditions.id,
expeditions.uuid,
expeditions.dynamic_zone_id,
expeditions.expedition_name,
expeditions.leader_id,
expeditions.min_players,
expeditions.max_players,
expeditions.add_replay_on_join,
expeditions.is_locked,
character_data.name leader_name,
expedition_members.character_id,
member_data.name
FROM expeditions
INNER JOIN character_data ON expeditions.leader_id = character_data.id
INNER JOIN expedition_members ON expeditions.id = expedition_members.expedition_id
AND expedition_members.is_current_member = TRUE
INNER JOIN character_data member_data ON expedition_members.character_id = member_data.id
));
}
MySQLRequestResult ExpeditionDatabase::LoadExpedition(uint32_t expedition_id)
{
LogExpeditionsDetail("Loading expedition [{}]", expedition_id);
std::string query = fmt::format(SQL(
{} WHERE expeditions.id = {};
), LoadExpeditionsSelectQuery(), expedition_id);
return database.QueryDatabase(query);
}
MySQLRequestResult ExpeditionDatabase::LoadAllExpeditions()
{
LogExpeditionsDetail("Loading all expeditions from database");
std::string query = fmt::format(SQL(
{} ORDER BY expeditions.id;
), LoadExpeditionsSelectQuery());
return database.QueryDatabase(query);
}
std::vector<ExpeditionLockoutTimer> ExpeditionDatabase::LoadCharacterLockouts(uint32_t character_id)
{
LogExpeditionsDetail("Loading character [{}] lockouts", character_id);
@ -170,54 +124,6 @@ std::vector<ExpeditionLockoutTimer> ExpeditionDatabase::LoadCharacterLockouts(
return lockouts;
}
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>>
ExpeditionDatabase::LoadMultipleExpeditionLockouts(
const std::vector<uint32_t>& expedition_ids)
{
LogExpeditionsDetail("Loading internal lockouts for [{}] expeditions", expedition_ids.size());
std::string in_expedition_ids_query = fmt::format("{}", fmt::join(expedition_ids, ","));
// these are loaded into the same container type expeditions use to store lockouts
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>> lockouts;
if (!in_expedition_ids_query.empty())
{
std::string query = fmt::format(SQL(
SELECT
expedition_lockouts.expedition_id,
expedition_lockouts.from_expedition_uuid,
expeditions.expedition_name,
expedition_lockouts.event_name,
UNIX_TIMESTAMP(expedition_lockouts.expire_time),
expedition_lockouts.duration
FROM expedition_lockouts
INNER JOIN expeditions ON expedition_lockouts.expedition_id = expeditions.id
WHERE expedition_id IN ({})
ORDER BY expedition_id;
), in_expedition_ids_query);
auto results = database.QueryDatabase(query);
if (results.Success())
{
for (auto row = results.begin(); row != results.end(); ++row)
{
auto expedition_id = strtoul(row[0], nullptr, 10);
lockouts[expedition_id].emplace(row[3], ExpeditionLockoutTimer{
row[1], // expedition_uuid
row[2], // expedition_name
row[3], // event_name
strtoull(row[4], nullptr, 10), // expire_time
static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) // original duration
});
}
}
}
return lockouts;
}
void ExpeditionDatabase::DeleteAllCharacterLockouts(uint32_t character_id)
{
LogExpeditionsDetail("Deleting all character [{}] lockouts", character_id);

View File

@ -38,14 +38,9 @@ namespace ExpeditionDatabase
uint32_t InsertExpedition(
const std::string& uuid, uint32_t instance_id, const std::string& expedition_name,
uint32_t leader_id, uint32_t min_players, uint32_t max_players);
std::string LoadExpeditionsSelectQuery();
MySQLRequestResult LoadExpedition(uint32_t expedition_id);
MySQLRequestResult LoadAllExpeditions();
std::vector<ExpeditionLockoutTimer> LoadCharacterLockouts(uint32_t character_id);
std::vector<ExpeditionLockoutTimer> LoadCharacterLockouts(uint32_t character_id,
const std::string& expedition_name);
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>>
LoadMultipleExpeditionLockouts(const std::vector<uint32_t>& expedition_ids);
void DeleteAllMembers(uint32_t expedition_id);
void DeleteMember(uint32_t expedition_id, uint32_t character_id);
void DeleteAllCharacterLockouts(uint32_t character_id);
@ -73,23 +68,4 @@ namespace ExpeditionDatabase
const ExpeditionLockoutTimer& lockout, int seconds);
};
namespace LoadExpeditionColumns
{
enum eLoadExpeditionColumns
{
id = 0,
uuid,
dz_id,
expedition_name,
leader_id,
min_players,
max_players,
add_replay_on_join,
is_locked,
leader_name,
member_id,
member_name
};
};
#endif

View File

@ -59,7 +59,7 @@ void Lua_Expedition::AddReplayLockoutDuration(int seconds, bool members_only) {
uint32_t Lua_Expedition::GetDynamicZoneID() {
Lua_Safe_Call_Int();
return self->GetDynamicZoneID();
return self->GetDynamicZone().GetID();
}
uint32_t Lua_Expedition::GetID() {

View File

@ -142,7 +142,7 @@ XS(XS_Expedition_GetDynamicZoneID) {
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetDynamicZoneID());
XSRETURN_UV(THIS->GetDynamicZone().GetID());
}
XS(XS_Expedition_GetID);
@ -168,7 +168,7 @@ XS(XS_Expedition_GetInstanceID) {
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetInstanceID());
XSRETURN_UV(THIS->GetDynamicZone().GetInstanceID());
}
XS(XS_Expedition_GetLeaderName);