diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index bb932490c..08db08585 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -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 diff --git a/common/eq_constants.h b/common/eq_constants.h index cb042043f..b17dcc2be 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -474,4 +474,13 @@ enum class DynamicZoneType Quest }; +enum class ExpeditionMemberStatus : uint8_t +{ + Unknown = 0, + Online, + Offline, + InDynamicZone, + LinkDead +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/expedition_base.cpp b/common/expedition_base.cpp new file mode 100644 index 000000000..fa4962706 --- /dev/null +++ b/common/expedition_base.cpp @@ -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; +} diff --git a/common/expedition_base.h b/common/expedition_base.h new file mode 100644 index 000000000..aeb072fae --- /dev/null +++ b/common/expedition_base.h @@ -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 +#include +#include + +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(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& 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 m_members; +}; + +#endif diff --git a/common/repositories/base/base_expedition_lockouts_repository.h b/common/repositories/base/base_expedition_lockouts_repository.h index f2f2bd5be..e9c5ceda2 100644 --- a/common/repositories/base/base_expedition_lockouts_repository.h +++ b/common/repositories/base/base_expedition_lockouts_repository.h @@ -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 = ""; diff --git a/common/repositories/expedition_lockouts_repository.h b/common/repositories/expedition_lockouts_repository.h index 0f33c4209..5eab18915 100644 --- a/common/repositories/expedition_lockouts_repository.h +++ b/common/repositories/expedition_lockouts_repository.h @@ -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 GetWithTimestamp( + Database& db, const std::vector& expedition_ids) + { + if (expedition_ids.empty()) + { + return {}; + } + + std::vector 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 diff --git a/common/repositories/expedition_members_repository.h b/common/repositories/expedition_members_repository.h index 87304a99b..55b5bddd7 100644 --- a/common/repositories/expedition_members_repository.h +++ b/common/repositories/expedition_members_repository.h @@ -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 GetWithNames(Database& db, + const std::vector& expedition_ids) + { + if (expedition_ids.empty()) + { + return {}; + } + + std::vector 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 diff --git a/common/repositories/expeditions_repository.h b/common/repositories/expeditions_repository.h index 3cd2f2352..47c821060 100644 --- a/common/repositories/expeditions_repository.h +++ b/common/repositories/expeditions_repository.h @@ -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 GetAllWithLeaderName(Database& db) + { + std::vector 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; diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp index a845c5b1c..dad35db82 100644 --- a/world/dynamic_zone.cpp +++ b/world/dynamic_zone.cpp @@ -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; diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h index a82ff9d0e..60eb5b65a 100644 --- a/world/dynamic_zone.h +++ b/world/dynamic_zone.h @@ -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); diff --git a/world/expedition.cpp b/world/expedition.cpp index a72275c94..723098013 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -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(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(ServerOP_ExpeditionLeaderChanged, pack_size); auto buf = reinterpret_cast(pack->pBuffer); buf->expedition_id = GetID(); - buf->leader_id = m_leader_id; + buf->leader_id = m_leader.char_id; zoneserver_list.SendPacket(pack.get()); } diff --git a/world/expedition.h b/world/expedition.h index 2f48739df..11ae083ed 100644 --- a/world/expedition.h +++ b/world/expedition.h @@ -22,43 +22,34 @@ #define WORLD_EXPEDITION_H #include "dynamic_zone.h" +#include "../common/expedition_base.h" #include "../common/timer.h" -#include #include -#include -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 m_member_ids; }; #endif diff --git a/world/expedition_database.cpp b/world/expedition_database.cpp index 2f54d7f3d..c3738901a 100644 --- a/world/expedition_database.cpp +++ b/world/expedition_database.cpp @@ -71,90 +71,6 @@ void ExpeditionDatabase::PurgeExpiredCharacterLockouts() database.QueryDatabase(query); } -std::vector ExpeditionDatabase::LoadExpeditions(uint32_t select_expedition_id) -{ - std::vector 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(strtoul(row[1], nullptr, 10)), // dz_id - static_cast(strtoul(row[3], nullptr, 10)), // dz_zone_id - static_cast(strtoul(row[2], nullptr, 10)), // dz_instance_id - static_cast(strtoul(row[4], nullptr, 10)), // dz_zone_version - static_cast(strtoul(row[5], nullptr, 10)), // start_time - static_cast(strtoul(row[6], nullptr, 10)), // duration - DynamicZoneType::Expedition - }; - - expeditions.emplace_back( - expedition_id, - dynamic_zone, - static_cast(strtoul(row[7], nullptr, 10)) // leader_id - ); - } - - last_expedition_id = expedition_id; - - uint32_t member_id = static_cast(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& expedition_ids) { LogExpeditionsDetail("Deleting [{}] expedition(s)", expedition_ids.size()); diff --git a/world/expedition_database.h b/world/expedition_database.h index 8342a292a..b86fd8b5d 100644 --- a/world/expedition_database.h +++ b/world/expedition_database.h @@ -29,8 +29,6 @@ class Expedition; namespace ExpeditionDatabase { void DeleteExpeditions(const std::vector& expedition_ids); - std::vector LoadExpeditions(uint32_t select_expedition_id = 0); - Expedition LoadExpedition(uint32_t expedition_id); void MoveMembersToSafeReturn(const std::vector& expedition_ids); void PurgeExpiredExpeditions(); void PurgeExpiredCharacterLockouts(); diff --git a/world/expedition_message.cpp b/world/expedition_message.cpp index 46ecfa9cc..999dfd44a 100644 --- a/world/expedition_message.cpp +++ b/world/expedition_message.cpp @@ -43,22 +43,22 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack) case ServerOP_ExpeditionCreate: { auto buf = reinterpret_cast(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(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(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; diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp index 815ef6c58..19693e279 100644 --- a/world/expedition_state.cpp +++ b/world/expedition_state.cpp @@ -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 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) { + 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) { + 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&& expedition_entries) +{ + // bulk load expedition dzs and members before caching + std::vector expedition_ids; + std::vector 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->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; } diff --git a/world/expedition_state.h b/world/expedition_state.h index d7fb27d09..da8cdb306 100644 --- a/world/expedition_state.h +++ b/world/expedition_state.h @@ -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 @@ -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&& 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 m_expeditions; + std::vector> m_expeditions; Timer m_process_throttle_timer{static_cast(RuleI(DynamicZone, WorldProcessRate))}; }; diff --git a/world/main.cpp b/world/main.cpp index a094c8b4f..f150db0e4 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -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(); diff --git a/zone/command.cpp b/zone/command.cpp index 709bb92c4..e49dea4d2 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -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 diff --git a/zone/dynamic_zone.cpp b/zone/dynamic_zone.cpp index 9d9f35012..0b19e5ade 100644 --- a/zone/dynamic_zone.cpp +++ b/zone/dynamic_zone.cpp @@ -64,22 +64,6 @@ DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) return nullptr; } -std::unordered_map DynamicZone::LoadMultipleDzFromDatabase( - const std::vector& dynamic_zone_ids) -{ - LogDynamicZonesDetail("Loading dynamic zone data for [{}] instances", dynamic_zone_ids.size()); - - std::unordered_map 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()) diff --git a/zone/dynamic_zone.h b/zone/dynamic_zone.h index 98949953d..3b421330a 100644 --- a/zone/dynamic_zone.h +++ b/zone/dynamic_zone.h @@ -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 LoadMultipleDzFromDatabase( - const std::vector& dynamic_zone_ids); static void HandleWorldMessage(ServerPacket* pack); void SetSecondsRemaining(uint32_t seconds_remaining) override; diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 27bb86079..0c1481d7d 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -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&& expedition_entries) { - if (!results.Success() || !zone) + if (!zone) { return; } + // bulk load expedition dzs, members, and internal lockouts before caching std::vector expedition_ids; - std::vector dynamic_zone_ids;; - std::vector> expedition_character_ids; - - using col = LoadExpeditionColumns::eLoadExpeditionColumns; - - uint32_t last_expedition_id = 0; - - for (auto row = results.begin(); row != results.end(); ++row) + std::vector dynamic_zone_ids; + for (const auto& entry : expedition_entries) { - auto expedition_id = strtoul(row[col::id], nullptr, 10); + expedition_ids.emplace_back(entry.id); + dynamic_zone_ids.emplace_back(entry.dynamic_zone_id); + } - if (expedition_id != last_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); + + std::vector> expedition_character_ids; // for online status request + + for (auto& entry : expedition_entries) + { + auto expedition = std::make_unique(); + 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_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 = std::make_unique( - 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->SetDynamicZone(std::move(*dz_entry_iter)); } - last_expedition_id = expedition_id; - - // looping expedition members - auto current_expedition = Expedition::FindCachedExpeditionByID(last_expedition_id); - if (current_expedition) + for (auto& member : expedition_members) { - 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); + 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(lockout_entry.expire_time), + static_cast(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) diff --git a/zone/expedition.h b/zone/expedition.h index 5b2a40043..d6e906fc7 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -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 #include #include @@ -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(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& GetLockouts() const { return m_lockouts; } - const std::vector& 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&& expeditions); static void SendWorldGetOnlineMembers(const std::vector>& 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 CreateExpireWarningPacket(uint32_t minutes_remaining); std::unique_ptr CreateInfoPacket(bool clear = false); std::unique_ptr CreateInvitePacket(const std::string& inviter_name, const std::string& swap_remove_name); @@ -217,16 +177,7 @@ private: std::unique_ptr CreateMemberListStatusPacket(const std::string& name, ExpeditionMemberStatus status); std::unique_ptr 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 m_members; std::unordered_map m_lockouts; std::unordered_map m_npc_loot_events; // only valid inside dz zone std::unordered_map m_spawn_loot_events; // only valid inside dz zone diff --git a/zone/expedition_database.cpp b/zone/expedition_database.cpp index d628d8fd1..e1b5c0bf6 100644 --- a/zone/expedition_database.cpp +++ b/zone/expedition_database.cpp @@ -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 ExpeditionDatabase::LoadCharacterLockouts(uint32_t character_id) { LogExpeditionsDetail("Loading character [{}] lockouts", character_id); @@ -170,54 +124,6 @@ std::vector ExpeditionDatabase::LoadCharacterLockouts( return lockouts; } -std::unordered_map> -ExpeditionDatabase::LoadMultipleExpeditionLockouts( - const std::vector& 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> 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(strtoul(row[5], nullptr, 10)) // original duration - }); - } - } - } - - return lockouts; -} - void ExpeditionDatabase::DeleteAllCharacterLockouts(uint32_t character_id) { LogExpeditionsDetail("Deleting all character [{}] lockouts", character_id); diff --git a/zone/expedition_database.h b/zone/expedition_database.h index 2c72ee9ad..9e0487b36 100644 --- a/zone/expedition_database.h +++ b/zone/expedition_database.h @@ -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 LoadCharacterLockouts(uint32_t character_id); std::vector LoadCharacterLockouts(uint32_t character_id, const std::string& expedition_name); - std::unordered_map> - LoadMultipleExpeditionLockouts(const std::vector& 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 diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index 1bd872cc2..434cc5726 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -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() { diff --git a/zone/perl_expedition.cpp b/zone/perl_expedition.cpp index 3095c3770..ccba1e6a8 100644 --- a/zone/perl_expedition.cpp +++ b/zone/perl_expedition.cpp @@ -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);