diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 457180fdf..9b16c604c 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -22,6 +22,7 @@ SET(common_sources dbcore.cpp deity.cpp dynamic_zone_base.cpp + dynamic_zone_lockout.cpp emu_constants.cpp emu_limits.cpp emu_opcodes.cpp @@ -40,7 +41,6 @@ SET(common_sources events/player_event_logs.cpp events/player_event_discord_formatter.cpp evolving_items.cpp - expedition_lockout_timer.cpp extprofile.cpp discord/discord_manager.cpp faction.cpp @@ -213,10 +213,9 @@ SET(repositories repositories/base/base_discovered_items_repository.h repositories/base/base_doors_repository.h repositories/base/base_dynamic_zones_repository.h + repositories/base/base_dynamic_zone_lockouts_repository.h repositories/base/base_dynamic_zone_members_repository.h repositories/base/base_dynamic_zone_templates_repository.h - repositories/base/base_expeditions_repository.h - repositories/base/base_expedition_lockouts_repository.h repositories/base/base_faction_association_repository.h repositories/base/base_faction_base_data_repository.h repositories/base/base_faction_list_repository.h @@ -410,10 +409,9 @@ SET(repositories repositories/discovered_items_repository.h repositories/doors_repository.h repositories/dynamic_zones_repository.h + repositories/dynamic_zone_lockouts_repository.h repositories/dynamic_zone_members_repository.h repositories/dynamic_zone_templates_repository.h - repositories/expeditions_repository.h - repositories/expedition_lockouts_repository.h repositories/faction_association_repository.h repositories/faction_base_data_repository.h repositories/faction_list_repository.h @@ -561,6 +559,7 @@ SET(common_headers discord/discord.h discord/discord_manager.h dynamic_zone_base.h + dynamic_zone_lockout.h emu_constants.h emu_limits.h emu_opcodes.h @@ -587,7 +586,6 @@ SET(common_headers events/player_events.h event_sub.h evolving_items.h - expedition_lockout_timer.h extprofile.h faction.h file.h diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 4cf689bb7..856852e88 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6812,7 +6812,32 @@ ALTER TABLE `guild_bank` ALTER TABLE `guild_bank` ADD INDEX `guild_id` (`guild_id`); )" - } + }, + ManifestEntry{ + .version = 9305, + .description = "2024_12_01_expedition_dz_merge.sql", + .check = "SHOW COLUMNS FROM `dynamic_zones` LIKE 'is_locked'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `dynamic_zones` + ADD COLUMN `is_locked` TINYINT NOT NULL DEFAULT '0' AFTER `has_zone_in`, + ADD COLUMN `add_replay` TINYINT NOT NULL DEFAULT '1' AFTER `is_locked`; + +ALTER TABLE `expedition_lockouts` + CHANGE COLUMN `expedition_id` `dynamic_zone_id` INT(10) UNSIGNED NOT NULL AFTER `id`, + DROP INDEX `expedition_id_event_name`, + ADD UNIQUE INDEX `dz_id_event_name` (`dynamic_zone_id`, `event_name`) USING BTREE; + +UPDATE expedition_lockouts lockouts + INNER JOIN expeditions ON lockouts.dynamic_zone_id = expeditions.id + SET lockouts.dynamic_zone_id = expeditions.dynamic_zone_id; + +DROP TABLE `expeditions`; + +RENAME TABLE `expedition_lockouts` TO `dynamic_zone_lockouts`; +)" + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/database_schema.h b/common/database_schema.h index 6d1243c8f..b929c5e1b 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -311,10 +311,9 @@ namespace DatabaseSchema { "completed_shared_task_members", "completed_shared_tasks", "discord_webhooks", + "dynamic_zone_lockouts", "dynamic_zone_members", "dynamic_zones", - "expedition_lockouts", - "expeditions", "gm_ips", "group_id", "group_leaders", diff --git a/common/dynamic_zone_base.cpp b/common/dynamic_zone_base.cpp index 78b38de21..e4a146b41 100644 --- a/common/dynamic_zone_base.cpp +++ b/common/dynamic_zone_base.cpp @@ -1,11 +1,13 @@ #include "dynamic_zone_base.h" #include "database.h" #include "eqemu_logsys.h" -#include "repositories/instance_list_repository.h" -#include "repositories/instance_list_player_repository.h" #include "rulesys.h" #include "servertalk.h" #include "util/uuid.h" +#include "repositories/character_expedition_lockouts_repository.h" +#include "repositories/dynamic_zone_lockouts_repository.h" +#include "repositories/instance_list_repository.h" +#include "repositories/instance_list_player_repository.h" DynamicZoneBase::DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry) { @@ -93,13 +95,15 @@ void DynamicZoneBase::LoadRepositoryResult(DynamicZonesRepository::DynamicZoneIn m_zonein.y = dz_entry.zone_in_y; m_zonein.z = dz_entry.zone_in_z; m_zonein.heading = dz_entry.zone_in_heading; - m_has_zonein = (dz_entry.has_zone_in != 0); + m_has_zonein = dz_entry.has_zone_in != 0; + m_is_locked = dz_entry.is_locked; + m_add_replay = dz_entry.add_replay; // instance_list portion m_zone_id = dz_entry.zone; m_zone_version = dz_entry.version; m_start_time = std::chrono::system_clock::from_time_t(dz_entry.start_time); m_duration = std::chrono::seconds(dz_entry.duration); - m_never_expires = (dz_entry.never_expires != 0); + m_never_expires = dz_entry.never_expires != 0; m_expire_time = m_start_time + m_duration; } @@ -119,37 +123,40 @@ void DynamicZoneBase::AddMemberFromRepositoryResult( uint32_t DynamicZoneBase::SaveToDatabase() { LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id); - - if (m_instance_id != 0) + if (m_instance_id == 0) { - auto insert_dz = DynamicZonesRepository::NewEntity(); - insert_dz.uuid = m_uuid; - insert_dz.name = m_name; - insert_dz.leader_id = m_leader.id; - insert_dz.min_players = m_min_players; - insert_dz.max_players = m_max_players; - insert_dz.instance_id = m_instance_id, - insert_dz.type = static_cast(m_type); - insert_dz.dz_switch_id = m_dz_switch_id; - insert_dz.compass_zone_id = m_compass.zone_id; - insert_dz.compass_x = m_compass.x; - insert_dz.compass_y = m_compass.y; - insert_dz.compass_z = m_compass.z; - insert_dz.safe_return_zone_id = m_safereturn.zone_id; - insert_dz.safe_return_x = m_safereturn.x; - insert_dz.safe_return_y = m_safereturn.y; - insert_dz.safe_return_z = m_safereturn.z; - insert_dz.safe_return_heading = m_safereturn.heading; - insert_dz.zone_in_x = m_zonein.x; - insert_dz.zone_in_y = m_zonein.y; - insert_dz.zone_in_z = m_zonein.z; - insert_dz.zone_in_heading = m_zonein.heading; - insert_dz.has_zone_in = m_has_zonein; - - auto inserted_dz = DynamicZonesRepository::InsertOne(GetDatabase(), insert_dz); - return inserted_dz.id; + return 0; } - return 0; + + auto dz = DynamicZonesRepository::NewEntity(); + dz.uuid = m_uuid; + dz.name = m_name; + dz.leader_id = m_leader.id; + dz.min_players = m_min_players; + dz.max_players = m_max_players; + dz.instance_id = static_cast(m_instance_id), + dz.type = static_cast(m_type); + dz.dz_switch_id = m_dz_switch_id; + dz.compass_zone_id = m_compass.zone_id; + dz.compass_x = m_compass.x; + dz.compass_y = m_compass.y; + dz.compass_z = m_compass.z; + dz.safe_return_zone_id = m_safereturn.zone_id; + dz.safe_return_x = m_safereturn.x; + dz.safe_return_y = m_safereturn.y; + dz.safe_return_z = m_safereturn.z; + dz.safe_return_heading = m_safereturn.heading; + dz.zone_in_x = m_zonein.x; + dz.zone_in_y = m_zonein.y; + dz.zone_in_z = m_zonein.z; + dz.zone_in_heading = m_zonein.heading; + dz.has_zone_in = static_cast(m_has_zonein); + dz.is_locked = static_cast(m_is_locked); + dz.add_replay = static_cast(m_add_replay); + + dz = DynamicZonesRepository::InsertOne(GetDatabase(), std::move(dz)); + + return dz.id; } bool DynamicZoneBase::AddMember(const DynamicZoneMember& add_member) @@ -196,10 +203,9 @@ bool DynamicZoneBase::RemoveMember(const DynamicZoneMember& remove_member) return true; } -bool DynamicZoneBase::SwapMember( - const DynamicZoneMember& add_member, const std::string& remove_char_name) +bool DynamicZoneBase::SwapMember(const DynamicZoneMember& add_member, const std::string& remove_name) { - auto remove_member = GetMemberData(remove_char_name); + auto remove_member = GetMemberData(remove_name); if (!add_member.IsValid() || !remove_member.IsValid()) { return false; @@ -230,9 +236,18 @@ void DynamicZoneBase::RemoveAllMembers() void DynamicZoneBase::SaveMembers(const std::vector& members) { + if (members.empty()) + { + return; + } + LogDynamicZonesDetail("Saving [{}] member(s) for dz [{}]", members.size(), m_id); m_members = members; + if (m_members.size() > m_max_players) + { + m_members.resize(m_max_players); + } // the lower level instance_list_players needs to be kept updated as well std::vector insert_members; @@ -242,12 +257,12 @@ void DynamicZoneBase::SaveMembers(const std::vector& members) DynamicZoneMembersRepository::DynamicZoneMembers member_entry{}; member_entry.dynamic_zone_id = m_id; member_entry.character_id = member.id; - insert_members.emplace_back(member_entry); + insert_members.push_back(member_entry); - InstanceListPlayerRepository::InstanceListPlayer player_entry; - player_entry.id = static_cast(m_instance_id); - player_entry.charid = static_cast(member.id); - insert_players.emplace_back(player_entry); + InstanceListPlayerRepository::InstanceListPlayer player_entry{}; + player_entry.id = m_instance_id; + player_entry.charid = member.id; + insert_players.push_back(player_entry); } DynamicZoneMembersRepository::InsertOrUpdateMany(GetDatabase(), insert_members); @@ -339,6 +354,44 @@ void DynamicZoneBase::SetLeader(const DynamicZoneMember& new_leader, bool update } } +void DynamicZoneBase::SetLocked(bool lock, bool update_db, DzLockMsg lock_msg, uint32_t color) +{ + m_is_locked = lock; + + if (update_db) + { + DynamicZonesRepository::UpdateLocked(GetDatabase(), m_id, lock); + + ServerPacket pack(ServerOP_DzLock, sizeof(ServerDzLock_Struct)); + auto buf = reinterpret_cast(pack.pBuffer); + buf->dz_id = GetID(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->lock = m_is_locked; + buf->lock_msg = static_cast(lock_msg); + buf->color = color; + SendServerPacket(&pack); + } +} + +void DynamicZoneBase::SetReplayOnJoin(bool enabled, bool update_db) +{ + m_add_replay = enabled; + + if (update_db) + { + DynamicZonesRepository::UpdateReplayOnJoin(GetDatabase(), m_id, enabled); + + ServerPacket pack(ServerOP_DzReplayOnJoin, sizeof(ServerDzBool_Struct)); + auto buf = reinterpret_cast(pack.pBuffer); + buf->dz_id = GetID(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->enabled = enabled; + SendServerPacket(&pack); + } +} + uint32_t DynamicZoneBase::GetSecondsRemaining() const { auto remaining = std::chrono::duration_cast(GetDurationRemaining()).count(); @@ -478,13 +531,13 @@ void DynamicZoneBase::RemoveInternalMember(uint32_t character_id) ), m_members.end()); } -bool DynamicZoneBase::HasMember(uint32_t character_id) +bool DynamicZoneBase::HasMember(uint32_t character_id) const { return std::any_of(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { return member.id == character_id; }); } -bool DynamicZoneBase::HasMember(const std::string& character_name) +bool DynamicZoneBase::HasMember(const std::string& character_name) const { return std::any_of(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { @@ -590,35 +643,34 @@ std::string DynamicZoneBase::GetDynamicZoneTypeName(DynamicZoneType dz_type) } } -EQ::Net::DynamicPacket DynamicZoneBase::GetSerializedDzPacket() +std::unique_ptr DynamicZoneBase::CreateServerPacket(uint16_t zone_id, uint16_t instance_id) { - EQ::Net::DynamicPacket dyn_pack; - dyn_pack.PutSerialize(0, *this); + std::ostringstream ss = GetSerialized(); + std::string_view sv = ss.view(); - LogDynamicZonesDetail("Serialized server dz size [{}]", dyn_pack.Length()); - return dyn_pack; -} - -std::unique_ptr DynamicZoneBase::CreateServerDzCreatePacket( - uint16_t origin_zone_id, uint16_t origin_instance_id) -{ - EQ::Net::DynamicPacket dyn_pack = GetSerializedDzPacket(); - - auto pack_size = sizeof(ServerDzCreateSerialized_Struct) + dyn_pack.Length(); + auto pack_size = sizeof(ServerDzCreate_Struct) + sv.size(); auto pack = std::make_unique(ServerOP_DzCreated, static_cast(pack_size)); - auto buf = reinterpret_cast(pack->pBuffer); - buf->origin_zone_id = origin_zone_id; - buf->origin_instance_id = origin_instance_id; - buf->cereal_size = static_cast(dyn_pack.Length()); - memcpy(buf->cereal_data, dyn_pack.Data(), dyn_pack.Length()); + auto buf = reinterpret_cast(pack->pBuffer); + buf->origin_zone_id = zone_id; + buf->origin_instance_id = instance_id; + buf->dz_id = GetID(); + buf->cereal_size = static_cast(sv.size()); + memcpy(buf->cereal_data, sv.data(), sv.size()); return pack; } -void DynamicZoneBase::LoadSerializedDzPacket(char* cereal_data, uint32_t cereal_size) +std::ostringstream DynamicZoneBase::GetSerialized() { - LogDynamicZonesDetail("Deserializing server dz size [{}]", cereal_size); - EQ::Util::MemoryStreamReader ss(cereal_data, cereal_size); + std::ostringstream ss; + cereal::BinaryOutputArchive archive(ss); + archive(*this); + return ss; +} + +void DynamicZoneBase::Unserialize(std::span buf) +{ + EQ::Util::MemoryStreamReader ss(buf.data(), buf.size()); cereal::BinaryInputArchive archive(ss); archive(*this); } @@ -647,3 +699,180 @@ void DynamicZoneBase::LoadTemplate(const DynamicZoneTemplatesRepository::Dynamic m_zonein.z = dz_template.zone_in_z; m_zonein.heading = dz_template.zone_in_h; } + +std::vector DynamicZoneBase::GetMemberIds() +{ + std::vector ids; + ids.reserve(m_members.size()); + for (const auto& member : m_members) + { + ids.push_back(member.id); + } + return ids; +} + +bool DynamicZoneBase::HasLockout(const std::string& event) +{ + return std::ranges::any_of(m_lockouts, [&](const auto& l) { return l.IsEvent(event); }); +} + +bool DynamicZoneBase::HasReplayLockout() +{ + return HasLockout(DzLockout::ReplayTimer); +} + +void DynamicZoneBase::AddLockout(const std::string& event, uint32_t seconds) +{ + auto lockout = DzLockout::Create(m_name, event, seconds, m_uuid); + AddLockout(lockout); +} + +void DynamicZoneBase::AddLockout(const DzLockout& lockout, bool members_only) +{ + if (!members_only) + { + DynamicZoneLockoutsRepository::InsertLockouts(GetDatabase(), GetID(), { lockout }); + } + + CharacterExpeditionLockoutsRepository::InsertLockout(GetDatabase(), GetMemberIds(), lockout); + + HandleLockoutUpdate(lockout, false, members_only); + SendServerPacket(CreateLockoutPacket(lockout, false, members_only).get()); +} + +void DynamicZoneBase::AddLockoutDuration(const std::string& event, int seconds, bool members_only) +{ + auto lockout = DzLockout::Create(m_name, event, std::max(0, seconds), m_uuid); + + // lockout has unsigned duration, pass original seconds to support reducing existing timers + int secs = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); + CharacterExpeditionLockoutsRepository::AddLockoutDuration(GetDatabase(), GetMemberIds(), lockout, secs); + + HandleLockoutDuration(lockout, seconds, members_only, true); + SendServerPacket(CreateLockoutDurationPacket(lockout, seconds, members_only).get()); +} + +void DynamicZoneBase::UpdateLockoutDuration(const std::string& event, uint32_t seconds, bool members_only) +{ + // some live expeditions update existing lockout timers during progression + auto it = std::ranges::find_if(m_lockouts, [&](const auto& l) { return l.IsEvent(event); }); + if (it != m_lockouts.end()) + { + seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); + DzLockout lockout(m_uuid, m_name, event, it->GetStartTime() + seconds, seconds); + AddLockout(lockout, members_only); + } +} + +void DynamicZoneBase::RemoveLockout(const std::string& event) +{ + DynamicZoneLockoutsRepository::DeleteWhere(GetDatabase(), fmt::format( + "dynamic_zone_id = {} AND event_name = '{}'", GetID(), Strings::Escape(event))); + + CharacterExpeditionLockoutsRepository::DeleteWhere(GetDatabase(), fmt::format( + "character_id IN ({}) AND expedition_name = '{}' AND event_name = '{}'", + fmt::join(GetMemberIds(), ","), Strings::Escape(m_name), Strings::Escape(event))); + + DzLockout lockout{m_uuid, m_name, event, 0, 0}; + HandleLockoutUpdate(lockout, true, false); + SendServerPacket(CreateLockoutPacket(lockout, true).get()); +} + +void DynamicZoneBase::HandleLockoutUpdate(const DzLockout& lockout, bool remove, bool members_only) +{ + if (!members_only) + { + std::erase_if(m_lockouts, [&](const auto& l) { return l.IsEvent(lockout.Event()); }); + if (!remove) + { + m_lockouts.push_back(lockout); + } + } +} + +void DynamicZoneBase::HandleLockoutDuration(const DzLockout& lockout, int seconds, bool members_only, bool insert_db) +{ + if (!members_only) + { + auto it = std::ranges::find_if(m_lockouts, [&](const auto& l) { return l.IsEvent(lockout.Event()); }); + if (it != m_lockouts.end()) + { + it->AddLockoutTime(seconds); + } + else + { + it = m_lockouts.insert(m_lockouts.end(), lockout); + } + + if (insert_db) + { + DynamicZoneLockoutsRepository::InsertLockouts(GetDatabase(), GetID(), { *it }); + } + } +} + +std::unique_ptr DynamicZoneBase::CreateLockoutPacket(const DzLockout& lockout, bool remove, bool members_only) const +{ + uint32_t pack_size = sizeof(ServerDzLockout_Struct); + auto pack = std::make_unique(ServerOP_DzLockout, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->expire_time = lockout.GetExpireTime(); + buf->duration = lockout.GetDuration(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->remove = remove; + buf->members_only = members_only; + strn0cpy(buf->event_name, lockout.Event().c_str(), sizeof(buf->event_name)); + return pack; +} + +std::unique_ptr DynamicZoneBase::CreateLockoutDurationPacket(const DzLockout& lockout, int seconds, bool members_only) const +{ + uint32_t pack_size = sizeof(ServerDzLockout_Struct); + auto pack = std::make_unique(ServerOP_DzLockoutDuration, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->expire_time = lockout.GetExpireTime(); + buf->duration = lockout.GetDuration(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->members_only = members_only; + buf->seconds = seconds; + strn0cpy(buf->event_name, lockout.Event().c_str(), sizeof(buf->event_name)); + return pack; +} + +void DynamicZoneBase::SyncCharacterLockouts(uint32_t char_id, std::vector& lockouts) +{ + // adds missing event lockouts to client for this expedition and updates + // client timers that are both shorter and from another expedition + bool modified = false; + + for (const auto& lockout : m_lockouts) + { + if (lockout.IsReplay() || lockout.IsExpired() || lockout.UUID() != m_uuid) + { + continue; + } + + auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const DzLockout& l) { return l.IsSame(lockout); }); + if (it == lockouts.end()) + { + modified = true; + lockouts.push_back(lockout); // insert missing + } + else if (it->GetSecondsRemaining() < lockout.GetSecondsRemaining() && it->UUID() != m_uuid) + { + // only update lockout timer not uuid so loot event apis still work + modified = true; + it->SetDuration(lockout.GetDuration()); + it->SetExpireTime(lockout.GetExpireTime()); + } + } + + if (modified) + { + CharacterExpeditionLockoutsRepository::InsertLockouts(GetDatabase(), char_id, lockouts); + } +} diff --git a/common/dynamic_zone_base.h b/common/dynamic_zone_base.h index b92f18ec2..6a43449f4 100644 --- a/common/dynamic_zone_base.h +++ b/common/dynamic_zone_base.h @@ -1,8 +1,8 @@ #ifndef COMMON_DYNAMIC_ZONE_BASE_H #define COMMON_DYNAMIC_ZONE_BASE_H +#include "dynamic_zone_lockout.h" #include "eq_constants.h" -#include "net/packet.h" #include "repositories/dynamic_zones_repository.h" #include "repositories/dynamic_zone_members_repository.h" #include "repositories/dynamic_zone_templates_repository.h" @@ -10,12 +10,40 @@ #include #include #include +#include #include #include class Database; class ServerPacket; +// message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks) +inline constexpr char DzNotAllAdded[] = "Not all players in your {0} were added to the {1}. The {1} can take a maximum of {2} players, and your {0} has {3}."; + +enum class DzLockMsg : uint8_t +{ + None = 0, Close, Begin +}; + +enum class DynamicZoneType +{ + None = 0, + Expedition, + Tutorial, + Task, + Mission, // Shared Task + Quest +}; + +enum class DynamicZoneMemberStatus +{ + Unknown = 0, + Online, + Offline, + InDynamicZone, + LinkDead +}; + struct DynamicZoneMember { uint32_t id = 0; @@ -93,6 +121,7 @@ public: const std::string& GetName() const { return m_name; } const std::string& GetUUID() const { return m_uuid; } const DynamicZoneMember& GetLeader() const { return m_leader; } + const std::vector& GetLockouts() const { return m_lockouts; } const std::vector& GetMembers() const { return m_members; } const DynamicZoneLocation& GetCompassLocation() const { return m_compass; } const DynamicZoneLocation& GetSafeReturnLocation() const { return m_safereturn; } @@ -104,31 +133,34 @@ public: uint32_t GetDatabaseMemberCount(); DynamicZoneMember GetMemberData(uint32_t character_id); DynamicZoneMember GetMemberData(const std::string& character_name); - EQ::Net::DynamicPacket GetSerializedDzPacket(); + std::vector GetMemberIds(); + std::ostringstream GetSerialized(); bool HasDatabaseMember(uint32_t character_id); - bool HasMember(uint32_t character_id); - bool HasMember(const std::string& character_name); + bool HasMember(uint32_t character_id) const; + bool HasMember(const std::string& character_name) const; bool HasMembers() const { return !m_members.empty(); } bool HasZoneInLocation() const { return m_has_zonein; } + bool IsExpedition() const { return m_type == DynamicZoneType::Expedition; } bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); } bool IsInstanceID(uint32_t instance_id) const { return (m_instance_id != 0 && m_instance_id == instance_id); } + bool IsLocked() const { return m_is_locked; } bool IsValid() const { return m_instance_id != 0; } bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const { return zone_id == m_zone_id && instance_id == m_instance_id; } - void LoadSerializedDzPacket(char* cereal_data, uint32_t cereal_size); void LoadTemplate(const DynamicZoneTemplatesRepository::DynamicZoneTemplates& dz_template); void RemoveAllMembers(); bool RemoveMember(uint32_t character_id); bool RemoveMember(const std::string& character_name); bool RemoveMember(const DynamicZoneMember& remove_member); - void SaveMembers(const std::vector& members); void SetCompass(const DynamicZoneLocation& location, bool update_db = false); void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false); void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); } void SetLeader(const DynamicZoneMember& leader, bool update_db = false); + void SetLocked(bool lock, bool update_db = false, DzLockMsg lock_msg = DzLockMsg::None, uint32_t color = Chat::Yellow); void SetMaxPlayers(uint32_t max_players) { m_max_players = max_players; } void SetMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); void SetMinPlayers(uint32_t min_players) { m_min_players = min_players; } void SetName(const std::string& name) { m_name = name; } + void SetReplayOnJoin(bool enabled, bool update_db = false); void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false); void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false); void SetSwitchID(int dz_switch_id, bool update_db = false); @@ -136,34 +168,48 @@ public: void SetUUID(std::string uuid) { m_uuid = std::move(uuid); } void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false); void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false); - bool SwapMember(const DynamicZoneMember& add_member, const std::string& remove_char_name); + bool SwapMember(const DynamicZoneMember& add_member, const std::string& remove_name); + + void AddLockout(const std::string& event, uint32_t seconds); + void AddLockoutDuration(const std::string& event, int seconds, bool members_only = true); + bool HasLockout(const std::string& event); + bool HasReplayLockout(); + void RemoveLockout(const std::string& event); + void SyncCharacterLockouts(uint32_t char_id, std::vector& lockouts); + void UpdateLockoutDuration(const std::string& event, uint32_t seconds, bool members_only = true); protected: - virtual uint16_t GetCurrentInstanceID() { return 0; } - virtual uint16_t GetCurrentZoneID() { return 0; } + virtual uint16_t GetCurrentInstanceID() const { return 0; } + virtual uint16_t GetCurrentZoneID() const { return 0; } virtual Database& GetDatabase() = 0; + virtual void HandleLockoutDuration(const DzLockout& lockout, int seconds, bool members_only, bool insert_db); + virtual void HandleLockoutUpdate(const DzLockout& lockout, bool remove, bool members_only); virtual void ProcessCompassChange(const DynamicZoneLocation& location) { m_compass = location; } virtual void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed); - virtual bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status); - virtual void ProcessRemoveAllMembers(bool silent = false) { m_members.clear(); } + virtual bool ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status); + virtual void ProcessRemoveAllMembers() { m_members.clear(); } virtual void ProcessSetSwitchID(int dz_switch_id) { m_dz_switch_id = dz_switch_id; } virtual bool SendServerPacket(ServerPacket* packet) = 0; + void AddLockout(const DzLockout& lockout, bool members_only = false); void AddInternalMember(const DynamicZoneMember& member); uint32_t Create(); uint32_t CreateInstance(); void LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry); void RemoveInternalMember(uint32_t character_id); + void SaveMembers(const std::vector& members); uint32_t SaveToDatabase(); bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); - std::unique_ptr CreateServerDzCreatePacket(uint16_t origin_zone_id, uint16_t origin_instance_id); + std::unique_ptr CreateServerPacket(uint16_t zone_id, uint16_t instance_id); std::unique_ptr CreateServerDzLocationPacket(uint16_t server_opcode, const DynamicZoneLocation& location); std::unique_ptr CreateServerDzSwitchIDPacket(); std::unique_ptr CreateServerMemberAddRemovePacket(const DynamicZoneMember& member, bool removed); std::unique_ptr CreateServerMemberStatusPacket(uint32_t character_id, DynamicZoneMemberStatus status); std::unique_ptr CreateServerMemberSwapPacket(const DynamicZoneMember& remove_member, const DynamicZoneMember& add_member); std::unique_ptr CreateServerRemoveAllMembersPacket(); + std::unique_ptr CreateLockoutPacket(const DzLockout& lockout, bool remove, bool members_only = false) const; + std::unique_ptr CreateLockoutDurationPacket(const DzLockout& lockout, int seconds, bool members_only = false) const; uint32_t m_id = 0; uint32_t m_zone_id = 0; @@ -175,6 +221,8 @@ protected: bool m_never_expires = false; bool m_has_zonein = false; bool m_has_member_statuses = false; + bool m_is_locked = false; + bool m_add_replay = true; std::string m_name; std::string m_uuid; DynamicZoneMember m_leader; @@ -182,12 +230,15 @@ protected: DynamicZoneLocation m_compass; DynamicZoneLocation m_safereturn; DynamicZoneLocation m_zonein; - std::chrono::seconds m_duration; + std::chrono::seconds m_duration = {}; std::chrono::time_point m_start_time; std::chrono::time_point m_expire_time; std::vector m_members; + std::vector m_lockouts; public: + void Unserialize(std::span buf); + template void serialize(Archive& archive) { @@ -202,6 +253,8 @@ public: m_never_expires, m_has_zonein, m_has_member_statuses, + m_is_locked, + m_add_replay, m_name, m_uuid, m_leader, @@ -212,7 +265,8 @@ public: m_duration, m_start_time, m_expire_time, - m_members + m_members, + m_lockouts ); } }; diff --git a/common/dynamic_zone_lockout.cpp b/common/dynamic_zone_lockout.cpp new file mode 100644 index 000000000..a47a9e205 --- /dev/null +++ b/common/dynamic_zone_lockout.cpp @@ -0,0 +1,92 @@ +#include "dynamic_zone_lockout.h" +#include "strings.h" +#include "rulesys.h" +#include "util/uuid.h" +#include +#include + +DzLockout::DzLockout(std::string uuid, std::string expedition, std::string event, uint64_t expire_time, uint32_t duration) + : m_uuid(std::move(uuid)) + , m_name(std::move(expedition)) + , m_event(std::move(event)) + , m_expire_time(std::chrono::system_clock::from_time_t(expire_time)) + , m_duration(duration) +{ + m_is_replay = m_event == ReplayTimer; +} + +DzLockout::DzLockout(std::string_view name, BaseDynamicZoneLockoutsRepository::DynamicZoneLockouts&& lockout) + : m_uuid(std::move(lockout.from_expedition_uuid)) + , m_name(name) + , m_event(std::move(lockout.event_name)) + , m_expire_time(std::chrono::system_clock::from_time_t(lockout.expire_time)) + , m_duration(lockout.duration) +{ + m_is_replay = m_event == ReplayTimer; +} + +DzLockout DzLockout::Create(const std::string& expedition, const std::string& event, uint32_t seconds, std::string uuid) +{ + seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); + + if (uuid.empty()) + { + uuid = EQ::Util::UUID::Generate().ToString(); + } + + DzLockout lockout{uuid, expedition, event, 0, seconds}; + lockout.Reset(); // sets expire time + return lockout; +} + +uint32_t DzLockout::GetSecondsRemaining() const +{ + auto now = std::chrono::system_clock::now(); + if (m_expire_time > now) + { + auto remaining = m_expire_time - now; + return static_cast(std::chrono::duration_cast(remaining).count()); + } + return 0; +} + +DzLockout::TimeStrings DzLockout::GetTimeRemainingStrs() const +{ + auto seconds = GetSecondsRemaining(); + return DzLockout::TimeStrings{ + fmt::format_int(seconds / 86400).str(), // days + fmt::format_int(seconds / 3600 % 24).str(), // hours + fmt::format_int(seconds / 60 % 60).str(), // minutes + fmt::format_int(seconds % 60).str() // seconds + }; +} + +bool DzLockout::IsSame(const DzLockout& other) const +{ + return other.IsSame(m_name, m_event); +} + +bool DzLockout::IsSame(const std::string& expedition, const std::string& event) const +{ + return m_name == expedition && m_event == event; +} + +void DzLockout::AddLockoutTime(int seconds) +{ + seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); + + auto new_duration = std::max(0, static_cast(m_duration.count()) + seconds); + + auto start_time = m_expire_time - m_duration; + m_duration = std::chrono::seconds(new_duration); + m_expire_time = start_time + m_duration; +} + +template +void DzLockout::serialize(T& archive) +{ + archive(m_is_replay, m_uuid, m_name, m_event, m_duration, m_expire_time); +} + +template void DzLockout::serialize(cereal::BinaryOutputArchive&); +template void DzLockout::serialize(cereal::BinaryInputArchive&); diff --git a/common/dynamic_zone_lockout.h b/common/dynamic_zone_lockout.h new file mode 100644 index 000000000..72dc132bd --- /dev/null +++ b/common/dynamic_zone_lockout.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include "repositories/base/base_dynamic_zone_lockouts_repository.h" + +class DzLockout +{ +public: + DzLockout() = default; + DzLockout(std::string uuid, std::string expedition, std::string event, uint64_t expire_time, uint32_t duration); + DzLockout(std::string_view name, BaseDynamicZoneLockoutsRepository::DynamicZoneLockouts&& lockout); + + static constexpr char ReplayTimer[] = "Replay Timer"; + + static DzLockout Create(const std::string& expedition, const std::string& event, uint32_t seconds, std::string uuid = {}); + + struct TimeStrings + { + std::string days; + std::string hours; + std::string mins; + std::string secs; + }; + + void AddLockoutTime(int seconds); + uint32_t GetDuration() const { return static_cast(m_duration.count()); } + uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } + uint64_t GetStartTime() const { return std::chrono::system_clock::to_time_t(m_expire_time - m_duration); } + uint32_t GetSecondsRemaining() const; + TimeStrings GetTimeRemainingStrs() const; + const std::string& DzName() const { return m_name; } + const std::string& Event() const { return m_event; } + const std::string& UUID() const { return m_uuid; } + bool IsEvent(std::string_view event) const { return m_event == event; } + bool IsExpired() const { return GetSecondsRemaining() == 0; } + bool IsReplay() const { return m_is_replay; } + bool IsSame(const DzLockout& other) const; + bool IsSame(const std::string& expedition, const std::string& event) const; + bool IsUUID(const std::string& uuid) const { return uuid == m_uuid; } + void Reset() { m_expire_time = std::chrono::system_clock::now() + m_duration; } + void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); } + void SetExpireTime(uint64_t expire_time) { m_expire_time = std::chrono::system_clock::from_time_t(expire_time); } + void SetUUID(const std::string& uuid) { m_uuid = uuid; } + + template + void serialize(T& archive); + +private: + bool m_is_replay = false; + std::string m_uuid; // dz received in + std::string m_name; + std::string m_event; + std::chrono::seconds m_duration = {}; + std::chrono::time_point m_expire_time; +}; diff --git a/common/eq_constants.h b/common/eq_constants.h index e048a9321..f67618bac 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -974,25 +974,6 @@ namespace ZoneBlockedSpellTypes { const uint8 Region = 2; }; -enum class DynamicZoneType -{ - None = 0, - Expedition, - Tutorial, - Task, - Mission, // Shared Task - Quest -}; - -enum class DynamicZoneMemberStatus : uint8_t -{ - Unknown = 0, - Online, - Offline, - InDynamicZone, - LinkDead -}; - enum StartZoneIndex { Odus = 0, Qeynos, diff --git a/common/expedition_lockout_timer.cpp b/common/expedition_lockout_timer.cpp deleted file mode 100644 index 6422ad55a..000000000 --- a/common/expedition_lockout_timer.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "expedition_lockout_timer.h" -#include "../common/strings.h" -#include "../common/rulesys.h" -#include "../common/util/uuid.h" -#include - -const char* const DZ_REPLAY_TIMER_NAME = "Replay Timer"; // see December 14, 2016 patch notes - -ExpeditionLockoutTimer::ExpeditionLockoutTimer( - std::string expedition_uuid, std::string expedition_name, - std::string event_name, uint64_t expire_time, uint32_t duration -) : - m_expedition_uuid{std::move(expedition_uuid)}, - m_expedition_name{std::move(expedition_name)}, - m_event_name{std::move(event_name)}, - m_expire_time(std::chrono::system_clock::from_time_t(expire_time)), - m_duration(duration) -{ - if (m_event_name == DZ_REPLAY_TIMER_NAME) - { - m_is_replay_timer = true; - } -} - -ExpeditionLockoutTimer ExpeditionLockoutTimer::CreateLockout( - const std::string& expedition_name, const std::string& event_name, uint32_t seconds, std::string uuid) -{ - seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); - - if (uuid.empty()) - { - uuid = EQ::Util::UUID::Generate().ToString(); - } - - ExpeditionLockoutTimer lockout{uuid, expedition_name, event_name, 0, seconds}; - lockout.Reset(); // sets expire time - return lockout; -} - -uint32_t ExpeditionLockoutTimer::GetSecondsRemaining() const -{ - auto now = std::chrono::system_clock::now(); - if (m_expire_time > now) - { - auto remaining = m_expire_time - now; - return static_cast(std::chrono::duration_cast(remaining).count()); - } - return 0; -} - -ExpeditionLockoutTimer::DaysHoursMinutes ExpeditionLockoutTimer::GetDaysHoursMinutesRemaining() const -{ - auto seconds = GetSecondsRemaining(); - return ExpeditionLockoutTimer::DaysHoursMinutes{ - fmt::format_int(seconds / 86400).str(), // days - fmt::format_int((seconds / 3600) % 24).str(), // hours - fmt::format_int((seconds / 60) % 60).str() // minutes - }; -} - -bool ExpeditionLockoutTimer::IsSameLockout(const ExpeditionLockoutTimer& compare_lockout) const -{ - return compare_lockout.IsSameLockout(GetExpeditionName(), GetEventName()); -} - -bool ExpeditionLockoutTimer::IsSameLockout( - const std::string& expedition_name, const std::string& event_name) const -{ - return GetExpeditionName() == expedition_name && GetEventName() == event_name; -} - -void ExpeditionLockoutTimer::AddLockoutTime(int seconds) -{ - seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); - - auto new_duration = std::max(0, static_cast(m_duration.count()) + seconds); - - auto start_time = m_expire_time - m_duration; - m_duration = std::chrono::seconds(new_duration); - m_expire_time = start_time + m_duration; -} diff --git a/common/expedition_lockout_timer.h b/common/expedition_lockout_timer.h deleted file mode 100644 index 8cbf3da54..000000000 --- a/common/expedition_lockout_timer.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef EXPEDITION_LOCKOUT_TIMER_H -#define EXPEDITION_LOCKOUT_TIMER_H - -#include -#include - -extern const char* const DZ_REPLAY_TIMER_NAME; - -class ExpeditionLockoutTimer -{ -public: - ExpeditionLockoutTimer() = default; - ExpeditionLockoutTimer( - std::string expedition_uuid, std::string expedition_name, - std::string event_name, uint64_t expire_time, uint32_t duration); - - static ExpeditionLockoutTimer CreateLockout( - const std::string& expedition_name, const std::string& event_name, - uint32_t seconds, std::string uuid = {}); - - struct DaysHoursMinutes - { - std::string days; - std::string hours; - std::string mins; - }; - - void AddLockoutTime(int seconds); - uint32_t GetDuration() const { return static_cast(m_duration.count()); } - uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } - uint64_t GetStartTime() const { return std::chrono::system_clock::to_time_t(m_expire_time - m_duration); } - uint32_t GetSecondsRemaining() const; - DaysHoursMinutes GetDaysHoursMinutesRemaining() const; - const std::string& GetExpeditionName() const { return m_expedition_name; } - const std::string& GetExpeditionUUID() const { return m_expedition_uuid; } - const std::string& GetEventName() const { return m_event_name; } - bool IsExpired() const { return GetSecondsRemaining() == 0; } - bool IsFromExpedition(const std::string& uuid) const { return uuid == m_expedition_uuid; } - bool IsReplayTimer() const { return m_is_replay_timer; } - bool IsSameLockout(const ExpeditionLockoutTimer& compare_lockout) const; - bool IsSameLockout(const std::string& expedition_name, const std::string& event_name) const; - void Reset() { m_expire_time = std::chrono::system_clock::now() + m_duration; } - void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); } - void SetExpireTime(uint64_t expire_time) { m_expire_time = std::chrono::system_clock::from_time_t(expire_time); } - void SetUUID(const std::string& uuid) { m_expedition_uuid = uuid; } - -private: - bool m_is_replay_timer = false; - std::string m_expedition_uuid; // expedition received in - std::string m_expedition_name; - std::string m_event_name; - std::chrono::seconds m_duration; - std::chrono::time_point m_expire_time; -}; - -#endif diff --git a/common/repositories/base/base_expedition_lockouts_repository.h b/common/repositories/base/base_dynamic_zone_lockouts_repository.h similarity index 81% rename from common/repositories/base/base_expedition_lockouts_repository.h rename to common/repositories/base/base_dynamic_zone_lockouts_repository.h index b4ec21804..b787d6c3b 100644 --- a/common/repositories/base/base_expedition_lockouts_repository.h +++ b/common/repositories/base/base_dynamic_zone_lockouts_repository.h @@ -9,18 +9,18 @@ * @docs https://docs.eqemu.io/developer/repositories */ -#ifndef EQEMU_BASE_EXPEDITION_LOCKOUTS_REPOSITORY_H -#define EQEMU_BASE_EXPEDITION_LOCKOUTS_REPOSITORY_H +#ifndef EQEMU_BASE_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H +#define EQEMU_BASE_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H #include "../../database.h" #include "../../strings.h" #include -class BaseExpeditionLockoutsRepository { +class BaseDynamicZoneLockoutsRepository { public: - struct ExpeditionLockouts { + struct DynamicZoneLockouts { uint32_t id; - uint32_t expedition_id; + uint32_t dynamic_zone_id; std::string event_name; time_t expire_time; uint32_t duration; @@ -36,7 +36,7 @@ public: { return { "id", - "expedition_id", + "dynamic_zone_id", "event_name", "expire_time", "duration", @@ -48,7 +48,7 @@ public: { return { "id", - "expedition_id", + "dynamic_zone_id", "event_name", "UNIX_TIMESTAMP(expire_time)", "duration", @@ -68,7 +68,7 @@ public: static std::string TableName() { - return std::string("expedition_lockouts"); + return std::string("dynamic_zone_lockouts"); } static std::string BaseSelect() @@ -89,12 +89,12 @@ public: ); } - static ExpeditionLockouts NewEntity() + static DynamicZoneLockouts NewEntity() { - ExpeditionLockouts e{}; + DynamicZoneLockouts e{}; e.id = 0; - e.expedition_id = 0; + e.dynamic_zone_id = 0; e.event_name = ""; e.expire_time = std::time(nullptr); e.duration = 0; @@ -103,23 +103,23 @@ public: return e; } - static ExpeditionLockouts GetExpeditionLockouts( - const std::vector &expedition_lockoutss, - int expedition_lockouts_id + static DynamicZoneLockouts GetDynamicZoneLockouts( + const std::vector &dynamic_zone_lockoutss, + int dynamic_zone_lockouts_id ) { - for (auto &expedition_lockouts : expedition_lockoutss) { - if (expedition_lockouts.id == expedition_lockouts_id) { - return expedition_lockouts; + for (auto &dynamic_zone_lockouts : dynamic_zone_lockoutss) { + if (dynamic_zone_lockouts.id == dynamic_zone_lockouts_id) { + return dynamic_zone_lockouts; } } return NewEntity(); } - static ExpeditionLockouts FindOne( + static DynamicZoneLockouts FindOne( Database& db, - int expedition_lockouts_id + int dynamic_zone_lockouts_id ) { auto results = db.QueryDatabase( @@ -127,16 +127,16 @@ public: "{} WHERE {} = {} LIMIT 1", BaseSelect(), PrimaryKey(), - expedition_lockouts_id + dynamic_zone_lockouts_id ) ); auto row = results.begin(); if (results.RowCount() == 1) { - ExpeditionLockouts e{}; + DynamicZoneLockouts e{}; e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.expedition_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.dynamic_zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.event_name = row[2] ? row[2] : ""; e.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); e.duration = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; @@ -150,7 +150,7 @@ public: static int DeleteOne( Database& db, - int expedition_lockouts_id + int dynamic_zone_lockouts_id ) { auto results = db.QueryDatabase( @@ -158,7 +158,7 @@ public: "DELETE FROM {} WHERE {} = {}", TableName(), PrimaryKey(), - expedition_lockouts_id + dynamic_zone_lockouts_id ) ); @@ -167,14 +167,14 @@ public: static int UpdateOne( Database& db, - const ExpeditionLockouts &e + const DynamicZoneLockouts &e ) { std::vector v; auto columns = Columns(); - v.push_back(columns[1] + " = " + std::to_string(e.expedition_id)); + v.push_back(columns[1] + " = " + std::to_string(e.dynamic_zone_id)); v.push_back(columns[2] + " = '" + Strings::Escape(e.event_name) + "'"); v.push_back(columns[3] + " = FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")"); v.push_back(columns[4] + " = " + std::to_string(e.duration)); @@ -193,15 +193,15 @@ public: return (results.Success() ? results.RowsAffected() : 0); } - static ExpeditionLockouts InsertOne( + static DynamicZoneLockouts InsertOne( Database& db, - ExpeditionLockouts e + DynamicZoneLockouts e ) { std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.expedition_id)); + v.push_back(std::to_string(e.dynamic_zone_id)); v.push_back("'" + Strings::Escape(e.event_name) + "'"); v.push_back("FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")"); v.push_back(std::to_string(e.duration)); @@ -227,7 +227,7 @@ public: static int InsertMany( Database& db, - const std::vector &entries + const std::vector &entries ) { std::vector insert_chunks; @@ -236,7 +236,7 @@ public: std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.expedition_id)); + v.push_back(std::to_string(e.dynamic_zone_id)); v.push_back("'" + Strings::Escape(e.event_name) + "'"); v.push_back("FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")"); v.push_back(std::to_string(e.duration)); @@ -258,9 +258,9 @@ public: return (results.Success() ? results.RowsAffected() : 0); } - static std::vector All(Database& db) + static std::vector All(Database& db) { - std::vector all_entries; + std::vector all_entries; auto results = db.QueryDatabase( fmt::format( @@ -272,10 +272,10 @@ public: all_entries.reserve(results.RowCount()); for (auto row = results.begin(); row != results.end(); ++row) { - ExpeditionLockouts e{}; + DynamicZoneLockouts e{}; e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.expedition_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.dynamic_zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.event_name = row[2] ? row[2] : ""; e.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); e.duration = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; @@ -287,9 +287,9 @@ public: return all_entries; } - static std::vector GetWhere(Database& db, const std::string &where_filter) + static std::vector GetWhere(Database& db, const std::string &where_filter) { - std::vector all_entries; + std::vector all_entries; auto results = db.QueryDatabase( fmt::format( @@ -302,10 +302,10 @@ public: all_entries.reserve(results.RowCount()); for (auto row = results.begin(); row != results.end(); ++row) { - ExpeditionLockouts e{}; + DynamicZoneLockouts e{}; e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.expedition_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.dynamic_zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.event_name = row[2] ? row[2] : ""; e.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); e.duration = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; @@ -379,13 +379,13 @@ public: static int ReplaceOne( Database& db, - const ExpeditionLockouts &e + const DynamicZoneLockouts &e ) { std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.expedition_id)); + v.push_back(std::to_string(e.dynamic_zone_id)); v.push_back("'" + Strings::Escape(e.event_name) + "'"); v.push_back("FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")"); v.push_back(std::to_string(e.duration)); @@ -404,7 +404,7 @@ public: static int ReplaceMany( Database& db, - const std::vector &entries + const std::vector &entries ) { std::vector insert_chunks; @@ -413,7 +413,7 @@ public: std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.expedition_id)); + v.push_back(std::to_string(e.dynamic_zone_id)); v.push_back("'" + Strings::Escape(e.event_name) + "'"); v.push_back("FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")"); v.push_back(std::to_string(e.duration)); @@ -436,4 +436,4 @@ public: } }; -#endif //EQEMU_BASE_EXPEDITION_LOCKOUTS_REPOSITORY_H +#endif //EQEMU_BASE_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H diff --git a/common/repositories/base/base_dynamic_zones_repository.h b/common/repositories/base/base_dynamic_zones_repository.h index 8fe5266ab..d9231c54e 100644 --- a/common/repositories/base/base_dynamic_zones_repository.h +++ b/common/repositories/base/base_dynamic_zones_repository.h @@ -42,6 +42,8 @@ public: float zone_in_z; float zone_in_heading; uint8_t has_zone_in; + int8_t is_locked; + int8_t add_replay; }; static std::string PrimaryKey() @@ -75,6 +77,8 @@ public: "zone_in_z", "zone_in_heading", "has_zone_in", + "is_locked", + "add_replay", }; } @@ -104,6 +108,8 @@ public: "zone_in_z", "zone_in_heading", "has_zone_in", + "is_locked", + "add_replay", }; } @@ -167,6 +173,8 @@ public: e.zone_in_z = 0; e.zone_in_heading = 0; e.has_zone_in = 0; + e.is_locked = 0; + e.add_replay = 1; return e; } @@ -226,6 +234,8 @@ public: e.zone_in_z = row[20] ? strtof(row[20], nullptr) : 0; e.zone_in_heading = row[21] ? strtof(row[21], nullptr) : 0; e.has_zone_in = row[22] ? static_cast(strtoul(row[22], nullptr, 10)) : 0; + e.is_locked = row[23] ? static_cast(atoi(row[23])) : 0; + e.add_replay = row[24] ? static_cast(atoi(row[24])) : 1; return e; } @@ -281,6 +291,8 @@ public: v.push_back(columns[20] + " = " + std::to_string(e.zone_in_z)); v.push_back(columns[21] + " = " + std::to_string(e.zone_in_heading)); v.push_back(columns[22] + " = " + std::to_string(e.has_zone_in)); + v.push_back(columns[23] + " = " + std::to_string(e.is_locked)); + v.push_back(columns[24] + " = " + std::to_string(e.add_replay)); auto results = db.QueryDatabase( fmt::format( @@ -325,6 +337,8 @@ public: v.push_back(std::to_string(e.zone_in_z)); v.push_back(std::to_string(e.zone_in_heading)); v.push_back(std::to_string(e.has_zone_in)); + v.push_back(std::to_string(e.is_locked)); + v.push_back(std::to_string(e.add_replay)); auto results = db.QueryDatabase( fmt::format( @@ -377,6 +391,8 @@ public: v.push_back(std::to_string(e.zone_in_z)); v.push_back(std::to_string(e.zone_in_heading)); v.push_back(std::to_string(e.has_zone_in)); + v.push_back(std::to_string(e.is_locked)); + v.push_back(std::to_string(e.add_replay)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -433,6 +449,8 @@ public: e.zone_in_z = row[20] ? strtof(row[20], nullptr) : 0; e.zone_in_heading = row[21] ? strtof(row[21], nullptr) : 0; e.has_zone_in = row[22] ? static_cast(strtoul(row[22], nullptr, 10)) : 0; + e.is_locked = row[23] ? static_cast(atoi(row[23])) : 0; + e.add_replay = row[24] ? static_cast(atoi(row[24])) : 1; all_entries.push_back(e); } @@ -480,6 +498,8 @@ public: e.zone_in_z = row[20] ? strtof(row[20], nullptr) : 0; e.zone_in_heading = row[21] ? strtof(row[21], nullptr) : 0; e.has_zone_in = row[22] ? static_cast(strtoul(row[22], nullptr, 10)) : 0; + e.is_locked = row[23] ? static_cast(atoi(row[23])) : 0; + e.add_replay = row[24] ? static_cast(atoi(row[24])) : 1; all_entries.push_back(e); } @@ -577,6 +597,8 @@ public: v.push_back(std::to_string(e.zone_in_z)); v.push_back(std::to_string(e.zone_in_heading)); v.push_back(std::to_string(e.has_zone_in)); + v.push_back(std::to_string(e.is_locked)); + v.push_back(std::to_string(e.add_replay)); auto results = db.QueryDatabase( fmt::format( @@ -622,6 +644,8 @@ public: v.push_back(std::to_string(e.zone_in_z)); v.push_back(std::to_string(e.zone_in_heading)); v.push_back(std::to_string(e.has_zone_in)); + v.push_back(std::to_string(e.is_locked)); + v.push_back(std::to_string(e.add_replay)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/base/base_expeditions_repository.h b/common/repositories/base/base_expeditions_repository.h deleted file mode 100644 index b4dff8502..000000000 --- a/common/repositories/base/base_expeditions_repository.h +++ /dev/null @@ -1,415 +0,0 @@ -/** - * DO NOT MODIFY THIS FILE - * - * This repository was automatically generated and is NOT to be modified directly. - * Any repository modifications are meant to be made to the repository extending the base. - * Any modifications to base repositories are to be made by the generator only - * - * @generator ./utils/scripts/generators/repository-generator.pl - * @docs https://docs.eqemu.io/developer/repositories - */ - -#ifndef EQEMU_BASE_EXPEDITIONS_REPOSITORY_H -#define EQEMU_BASE_EXPEDITIONS_REPOSITORY_H - -#include "../../database.h" -#include "../../strings.h" -#include - -class BaseExpeditionsRepository { -public: - struct Expeditions { - uint32_t id; - uint32_t dynamic_zone_id; - uint8_t add_replay_on_join; - uint8_t is_locked; - }; - - static std::string PrimaryKey() - { - return std::string("id"); - } - - static std::vector Columns() - { - return { - "id", - "dynamic_zone_id", - "add_replay_on_join", - "is_locked", - }; - } - - static std::vector SelectColumns() - { - return { - "id", - "dynamic_zone_id", - "add_replay_on_join", - "is_locked", - }; - } - - static std::string ColumnsRaw() - { - return std::string(Strings::Implode(", ", Columns())); - } - - static std::string SelectColumnsRaw() - { - return std::string(Strings::Implode(", ", SelectColumns())); - } - - static std::string TableName() - { - return std::string("expeditions"); - } - - static std::string BaseSelect() - { - return fmt::format( - "SELECT {} FROM {}", - SelectColumnsRaw(), - TableName() - ); - } - - static std::string BaseInsert() - { - return fmt::format( - "INSERT INTO {} ({}) ", - TableName(), - ColumnsRaw() - ); - } - - static Expeditions NewEntity() - { - Expeditions e{}; - - e.id = 0; - e.dynamic_zone_id = 0; - e.add_replay_on_join = 1; - e.is_locked = 0; - - return e; - } - - static Expeditions GetExpeditions( - const std::vector &expeditionss, - int expeditions_id - ) - { - for (auto &expeditions : expeditionss) { - if (expeditions.id == expeditions_id) { - return expeditions; - } - } - - return NewEntity(); - } - - static Expeditions FindOne( - Database& db, - int expeditions_id - ) - { - auto results = db.QueryDatabase( - fmt::format( - "{} WHERE {} = {} LIMIT 1", - BaseSelect(), - PrimaryKey(), - expeditions_id - ) - ); - - auto row = results.begin(); - if (results.RowCount() == 1) { - Expeditions e{}; - - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.dynamic_zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.add_replay_on_join = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; - e.is_locked = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - - return e; - } - - return NewEntity(); - } - - static int DeleteOne( - Database& db, - int expeditions_id - ) - { - auto results = db.QueryDatabase( - fmt::format( - "DELETE FROM {} WHERE {} = {}", - TableName(), - PrimaryKey(), - expeditions_id - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static int UpdateOne( - Database& db, - const Expeditions &e - ) - { - std::vector v; - - auto columns = Columns(); - - v.push_back(columns[1] + " = " + std::to_string(e.dynamic_zone_id)); - v.push_back(columns[2] + " = " + std::to_string(e.add_replay_on_join)); - v.push_back(columns[3] + " = " + std::to_string(e.is_locked)); - - auto results = db.QueryDatabase( - fmt::format( - "UPDATE {} SET {} WHERE {} = {}", - TableName(), - Strings::Implode(", ", v), - PrimaryKey(), - e.id - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static Expeditions InsertOne( - Database& db, - Expeditions e - ) - { - std::vector v; - - v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.dynamic_zone_id)); - v.push_back(std::to_string(e.add_replay_on_join)); - v.push_back(std::to_string(e.is_locked)); - - auto results = db.QueryDatabase( - fmt::format( - "{} VALUES ({})", - BaseInsert(), - Strings::Implode(",", v) - ) - ); - - if (results.Success()) { - e.id = results.LastInsertedID(); - return e; - } - - e = NewEntity(); - - return e; - } - - static int InsertMany( - Database& db, - const std::vector &entries - ) - { - std::vector insert_chunks; - - for (auto &e: entries) { - std::vector v; - - v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.dynamic_zone_id)); - v.push_back(std::to_string(e.add_replay_on_join)); - v.push_back(std::to_string(e.is_locked)); - - insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); - } - - std::vector v; - - auto results = db.QueryDatabase( - fmt::format( - "{} VALUES {}", - BaseInsert(), - Strings::Implode(",", insert_chunks) - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static std::vector All(Database& db) - { - std::vector all_entries; - - auto results = db.QueryDatabase( - fmt::format( - "{}", - BaseSelect() - ) - ); - - all_entries.reserve(results.RowCount()); - - for (auto row = results.begin(); row != results.end(); ++row) { - Expeditions e{}; - - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.dynamic_zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.add_replay_on_join = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; - e.is_locked = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - - all_entries.push_back(e); - } - - return all_entries; - } - - static std::vector GetWhere(Database& db, const std::string &where_filter) - { - std::vector all_entries; - - auto results = db.QueryDatabase( - fmt::format( - "{} WHERE {}", - BaseSelect(), - where_filter - ) - ); - - all_entries.reserve(results.RowCount()); - - for (auto row = results.begin(); row != results.end(); ++row) { - Expeditions e{}; - - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.dynamic_zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.add_replay_on_join = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; - e.is_locked = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - - all_entries.push_back(e); - } - - return all_entries; - } - - static int DeleteWhere(Database& db, const std::string &where_filter) - { - auto results = db.QueryDatabase( - fmt::format( - "DELETE FROM {} WHERE {}", - TableName(), - where_filter - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static int Truncate(Database& db) - { - auto results = db.QueryDatabase( - fmt::format( - "TRUNCATE TABLE {}", - TableName() - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static int64 GetMaxId(Database& db) - { - auto results = db.QueryDatabase( - fmt::format( - "SELECT COALESCE(MAX({}), 0) FROM {}", - PrimaryKey(), - TableName() - ) - ); - - return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); - } - - static int64 Count(Database& db, const std::string &where_filter = "") - { - auto results = db.QueryDatabase( - fmt::format( - "SELECT COUNT(*) FROM {} {}", - TableName(), - (where_filter.empty() ? "" : "WHERE " + where_filter) - ) - ); - - return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); - } - - static std::string BaseReplace() - { - return fmt::format( - "REPLACE INTO {} ({}) ", - TableName(), - ColumnsRaw() - ); - } - - static int ReplaceOne( - Database& db, - const Expeditions &e - ) - { - std::vector v; - - v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.dynamic_zone_id)); - v.push_back(std::to_string(e.add_replay_on_join)); - v.push_back(std::to_string(e.is_locked)); - - auto results = db.QueryDatabase( - fmt::format( - "{} VALUES ({})", - BaseReplace(), - Strings::Implode(",", v) - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static int ReplaceMany( - Database& db, - const std::vector &entries - ) - { - std::vector insert_chunks; - - for (auto &e: entries) { - std::vector v; - - v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.dynamic_zone_id)); - v.push_back(std::to_string(e.add_replay_on_join)); - v.push_back(std::to_string(e.is_locked)); - - insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); - } - - std::vector v; - - auto results = db.QueryDatabase( - fmt::format( - "{} VALUES {}", - BaseReplace(), - Strings::Implode(",", insert_chunks) - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } -}; - -#endif //EQEMU_BASE_EXPEDITIONS_REPOSITORY_H diff --git a/common/repositories/character_expedition_lockouts_repository.h b/common/repositories/character_expedition_lockouts_repository.h index bf16e49a4..54fcc8704 100644 --- a/common/repositories/character_expedition_lockouts_repository.h +++ b/common/repositories/character_expedition_lockouts_repository.h @@ -2,7 +2,7 @@ #define EQEMU_CHARACTER_EXPEDITION_LOCKOUTS_REPOSITORY_H #include "../database.h" -#include "../expedition_lockout_timer.h" +#include "../dynamic_zone_lockout.h" #include "../strings.h" #include "base/base_character_expedition_lockouts_repository.h" #include @@ -47,33 +47,8 @@ public: // Custom extended repository methods here - struct CharacterExpeditionLockoutsTimeStamp { - int id; - int character_id; - std::string expedition_name; - std::string event_name; - time_t expire_time; - int duration; - std::string from_expedition_uuid; - }; - - static ExpeditionLockoutTimer GetExpeditionLockoutTimerFromEntry( - CharacterExpeditionLockoutsTimeStamp&& entry) - { - ExpeditionLockoutTimer lockout_timer{ - std::move(entry.from_expedition_uuid), - std::move(entry.expedition_name), - std::move(entry.event_name), - static_cast(entry.expire_time), - static_cast(entry.duration) - }; - - return lockout_timer; - } - - static std::unordered_map> GetManyCharacterLockoutTimers( - Database& db, const std::vector& character_ids, - const std::string& expedition_name, const std::string& ordered_event_name) + static std::unordered_map> GetLockouts( + Database& db, const std::vector& char_ids, const std::string& expedition) { auto results = db.QueryDatabase(fmt::format(SQL( SELECT @@ -84,39 +59,171 @@ public: from_expedition_uuid FROM character_expedition_lockouts WHERE - character_id IN ({}) + character_id IN ({0}) AND expire_time > NOW() - AND expedition_name = '{}' + AND expedition_name = '{1}' ORDER BY - FIELD(character_id, {}), - FIELD(event_name, '{}') DESC + FIELD(character_id, {0}), + FIELD(event_name, '{2}') DESC ), - fmt::join(character_ids, ","), - Strings::Escape(expedition_name), - fmt::join(character_ids, ","), - Strings::Escape(ordered_event_name) + fmt::join(char_ids, ","), + Strings::Escape(expedition), + Strings::Escape(DzLockout::ReplayTimer) )); - std::unordered_map> lockouts; + std::unordered_map> lockouts; for (auto row = results.begin(); row != results.end(); ++row) { - CharacterExpeditionLockoutsTimeStamp entry{}; - int col = 0; - entry.character_id = std::strtoul(row[col++], nullptr, 10); - entry.expire_time = std::strtoull(row[col++], nullptr, 10); - entry.duration = std::strtoul(row[col++], nullptr, 10); - entry.event_name = row[col++]; - entry.expedition_name = expedition_name; - entry.from_expedition_uuid = row[col++]; + uint32_t char_id = std::strtoul(row[col++], nullptr, 10); + time_t expire_time = std::strtoull(row[col++], nullptr, 10); + uint32_t duration = std::strtoul(row[col++], nullptr, 10); + std::string event = row[col++]; + std::string uuid = row[col++]; - auto lockout = GetExpeditionLockoutTimerFromEntry(std::move(entry)); - lockouts[entry.character_id].emplace_back(std::move(lockout)); + lockouts[char_id].emplace_back(std::move(uuid), expedition, std::move(event), expire_time, duration); } return lockouts; } + + static std::vector GetLockouts(Database& db, uint32_t char_id) + { + std::vector lockouts; + + auto rows = GetWhere(db, fmt::format("character_id = {} AND expire_time > NOW()", char_id)); + lockouts.reserve(rows.size()); + + for (auto& row : rows) + { + lockouts.emplace_back( + std::move(row.from_expedition_uuid), + std::move(row.expedition_name), + std::move(row.event_name), + row.expire_time, + row.duration + ); + } + + return lockouts; + } + + static std::vector GetLockouts(Database& db, const std::vector& names, const std::string& expedition, const std::string& event) + { + if (names.empty()) + { + return {}; + } + + return GetWhere(db, fmt::format( + "character_id IN (select id from character_data where name IN ('{}')) AND expire_time > NOW() AND expedition_name = '{}' AND event_name = '{}' LIMIT 1", + fmt::join(names, "','"), Strings::Escape(expedition), Strings::Escape(event))); + } + + static void InsertLockouts(Database& db, uint32_t char_id, const std::vector& lockouts) + { + std::string insert_values; + for (const auto& lockout : lockouts) + { + fmt::format_to(std::back_inserter(insert_values), + "({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),", + char_id, + lockout.GetExpireTime(), + lockout.GetDuration(), + Strings::Escape(lockout.UUID()), + Strings::Escape(lockout.DzName()), + Strings::Escape(lockout.Event()) + ); + } + + if (!insert_values.empty()) + { + insert_values.pop_back(); // trailing comma + + auto query = fmt::format(SQL( + INSERT INTO character_expedition_lockouts + (character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name) + VALUES {} + ON DUPLICATE KEY UPDATE + from_expedition_uuid = VALUES(from_expedition_uuid), + expire_time = VALUES(expire_time), + duration = VALUES(duration); + ), insert_values); + + db.QueryDatabase(query); + } + } + + static void InsertLockout(Database& db, const std::vector& char_ids, const DzLockout& lockout) + { + std::string insert_values; + for (const auto& char_id : char_ids) + { + fmt::format_to(std::back_inserter(insert_values), + "({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),", + char_id, + lockout.GetExpireTime(), + lockout.GetDuration(), + Strings::Escape(lockout.UUID()), + Strings::Escape(lockout.DzName()), + Strings::Escape(lockout.Event()) + ); + } + + if (!insert_values.empty()) + { + insert_values.pop_back(); // trailing comma + + auto query = fmt::format(SQL( + INSERT INTO character_expedition_lockouts + (character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name) + VALUES {} + ON DUPLICATE KEY UPDATE + from_expedition_uuid = VALUES(from_expedition_uuid), + expire_time = VALUES(expire_time), + duration = VALUES(duration); + ), insert_values); + + db.QueryDatabase(query); + } + } + + // inserts a new lockout or updates existing lockout with seconds added to current time + static void AddLockoutDuration(Database& db, const std::vector& char_ids, const DzLockout& lockout, int seconds) + { + std::string insert_values; + for (const auto& char_id : char_ids) + { + fmt::format_to(std::back_inserter(insert_values), + "({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),", + char_id, + lockout.GetExpireTime(), + lockout.GetDuration(), + Strings::Escape(lockout.UUID()), + Strings::Escape(lockout.DzName()), + Strings::Escape(lockout.Event()) + ); + } + + if (!insert_values.empty()) + { + insert_values.pop_back(); // trailing comma + + auto query = fmt::format(SQL( + INSERT INTO character_expedition_lockouts + (character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name) + VALUES {0} + ON DUPLICATE KEY UPDATE + from_expedition_uuid = VALUES(from_expedition_uuid), + expire_time = DATE_ADD(expire_time, INTERVAL {1} SECOND), + duration = GREATEST(0, CAST(duration AS SIGNED) + {1}); + ), insert_values, seconds); + + db.QueryDatabase(query); + } + } + }; #endif //EQEMU_CHARACTER_EXPEDITION_LOCKOUTS_REPOSITORY_H diff --git a/common/repositories/dynamic_zone_lockouts_repository.h b/common/repositories/dynamic_zone_lockouts_repository.h new file mode 100644 index 000000000..f7babc2f0 --- /dev/null +++ b/common/repositories/dynamic_zone_lockouts_repository.h @@ -0,0 +1,84 @@ +#ifndef EQEMU_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H +#define EQEMU_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "../dynamic_zone_lockout.h" +#include "base/base_dynamic_zone_lockouts_repository.h" + +class DynamicZoneLockoutsRepository: public BaseDynamicZoneLockoutsRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * DynamicZoneLockoutsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * DynamicZoneLockoutsRepository::GetWhereNeverExpires() + * DynamicZoneLockoutsRepository::GetWhereXAndY() + * DynamicZoneLockoutsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + + static void InsertLockouts(Database& db, uint32_t dz_id, const std::vector& lockouts) + { + std::string insert_values; + for (const auto& lockout : lockouts) + { + fmt::format_to(std::back_inserter(insert_values), + "({}, '{}', '{}', FROM_UNIXTIME({}), {}),", + dz_id, + Strings::Escape(lockout.UUID()), + Strings::Escape(lockout.Event()), + lockout.GetExpireTime(), + lockout.GetDuration() + ); + } + + if (!insert_values.empty()) + { + insert_values.pop_back(); // trailing comma + + auto query = fmt::format(SQL( + INSERT INTO dynamic_zone_lockouts + (dynamic_zone_id, from_expedition_uuid, event_name, expire_time, duration) + VALUES {} + ON DUPLICATE KEY UPDATE + from_expedition_uuid = VALUES(from_expedition_uuid), + expire_time = VALUES(expire_time), + duration = VALUES(duration); + ), insert_values); + + db.QueryDatabase(query); + } + } + +}; + +#endif //EQEMU_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H diff --git a/common/repositories/dynamic_zone_members_repository.h b/common/repositories/dynamic_zone_members_repository.h index b1afe41ca..51b5121f2 100644 --- a/common/repositories/dynamic_zone_members_repository.h +++ b/common/repositories/dynamic_zone_members_repository.h @@ -65,7 +65,7 @@ public: )); } - static std::vector GetAllWithNames(Database& db) + static std::vector AllWithNames(Database& db) { std::vector all_entries; @@ -146,65 +146,34 @@ public: static void RemoveMember(Database& db, uint32_t dynamic_zone_id, uint32_t character_id) { - db.QueryDatabase(fmt::format(SQL( - DELETE FROM {} - WHERE dynamic_zone_id = {} AND character_id = {}; - ), - TableName(), dynamic_zone_id, character_id - )); + DeleteWhere(db, fmt::format("dynamic_zone_id = {} AND character_id = {}", dynamic_zone_id, character_id)); } static void RemoveAllMembers(Database& db, uint32_t dynamic_zone_id) { - db.QueryDatabase(fmt::format(SQL( - DELETE FROM {} - WHERE dynamic_zone_id = {}; - ), - TableName(), dynamic_zone_id - )); + DeleteWhere(db, fmt::format("dynamic_zone_id = {}", dynamic_zone_id)); } - static void RemoveAllMembers(Database& db, std::vector dynamic_zone_ids) + static uint32_t InsertOrUpdateMany(Database& db, const std::vector& entries) { - if (!dynamic_zone_ids.empty()) + if (entries.empty()) { - db.QueryDatabase(fmt::format(SQL( - DELETE FROM {} - WHERE dynamic_zone_id IN ({}); - ), - TableName(), Strings::Join(dynamic_zone_ids, ",") - )); - } - } - - static int InsertOrUpdateMany(Database& db, - const std::vector& dynamic_zone_members_entries) - { - std::vector insert_chunks; - - for (auto &dynamic_zone_members_entry: dynamic_zone_members_entries) - { - std::vector insert_values; - - insert_values.push_back(std::to_string(dynamic_zone_members_entry.id)); - insert_values.push_back(std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); - insert_values.push_back(std::to_string(dynamic_zone_members_entry.character_id)); - - insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")"); + return 0; } - std::vector insert_values; + std::vector values; + values.reserve(entries.size()); - auto results = db.QueryDatabase( - fmt::format( - "INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = id;", - TableName(), - ColumnsRaw(), - Strings::Implode(",", insert_chunks) - ) - ); + for (const auto& entry : entries) + { + values.push_back(fmt::format("({},{},{})", entry.id, entry.dynamic_zone_id, entry.character_id)); + } - return (results.Success() ? results.RowsAffected() : 0); + auto results = db.QueryDatabase(fmt::format( + "INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = id;", + TableName(), ColumnsRaw(), fmt::join(values, ","))); + + return results.Success() ? results.RowsAffected() : 0; } }; diff --git a/common/repositories/dynamic_zones_repository.h b/common/repositories/dynamic_zones_repository.h index a64aef8cc..2065a10d5 100644 --- a/common/repositories/dynamic_zones_repository.h +++ b/common/repositories/dynamic_zones_repository.h @@ -70,6 +70,8 @@ public: float zone_in_z; float zone_in_heading; int has_zone_in; + int8_t is_locked; + int8_t add_replay; int zone; int version; int is_global; @@ -105,6 +107,8 @@ public: dynamic_zones.zone_in_z, dynamic_zones.zone_in_heading, dynamic_zones.has_zone_in, + dynamic_zones.is_locked, + dynamic_zones.add_replay, instance_list.zone, instance_list.version, instance_list.is_global, @@ -144,6 +148,8 @@ public: entry.zone_in_z = strtof(row[col++], nullptr); entry.zone_in_heading = strtof(row[col++], nullptr); entry.has_zone_in = strtol(row[col++], nullptr, 10) != 0; + entry.is_locked = static_cast(strtol(row[col++], nullptr, 10)); + entry.add_replay = static_cast(strtol(row[col++], nullptr, 10)); // from instance_list entry.zone = strtol(row[col++], nullptr, 10); entry.version = strtol(row[col++], nullptr, 10); @@ -244,6 +250,22 @@ public: } } + static void UpdateLocked(Database& db, uint32_t dz_id, bool lock) + { + if (dz_id != 0) + { + db.QueryDatabase(fmt::format("UPDATE dynamic_zones SET is_locked = {} WHERE id = {}", lock, dz_id)); + } + } + + static void UpdateReplayOnJoin(Database& db, uint32_t dz_id, bool enabled) + { + if (dz_id != 0) + { + db.QueryDatabase(fmt::format("UPDATE dynamic_zones SET add_replay = {} WHERE id = {}", enabled, dz_id)); + } + } + static void UpdateSwitchID(Database& db, uint32_t dz_id, int dz_switch_id) { if (dz_id != 0) @@ -351,6 +373,59 @@ public: return all_entries; } + + struct CharacterDz + { + uint32_t id; + std::string name; + uint32_t dz_id; + }; + + // get character ids with possible active dz id by type + static std::vector GetCharactersWithDz(Database& db, const std::vector& names, int type) + { + if (names.empty()) + { + return {}; + } + + std::vector entries; + entries.reserve(names.size()); + + auto results = db.QueryDatabase(fmt::format(SQL( + SELECT + character_data.id, + character_data.name, + MAX(dynamic_zones.id) + FROM character_data + LEFT JOIN dynamic_zone_members + ON character_data.id = dynamic_zone_members.character_id + LEFT JOIN dynamic_zones + ON dynamic_zone_members.dynamic_zone_id = dynamic_zones.id + AND dynamic_zones.`type` = {0} + WHERE character_data.name IN ('{1}') + GROUP BY character_data.id + ORDER BY FIELD(character_data.name, '{1}') + ), + type, + fmt::join(names, "','") + )); + + if (results.Success()) + { + for (auto row = results.begin(); row != results.end(); ++row) + { + CharacterDz entry{}; + entry.id = std::strtoul(row[0], nullptr, 10); + entry.name = row[1]; + entry.dz_id = row[2] ? std::strtoul(row[2], nullptr, 10) : 0; + + entries.push_back(std::move(entry)); + } + } + + return entries; + } }; #endif //EQEMU_DYNAMIC_ZONES_REPOSITORY_H diff --git a/common/repositories/expedition_lockouts_repository.h b/common/repositories/expedition_lockouts_repository.h deleted file mode 100644 index 4ccae0d39..000000000 --- a/common/repositories/expedition_lockouts_repository.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef EQEMU_EXPEDITION_LOCKOUTS_REPOSITORY_H -#define EQEMU_EXPEDITION_LOCKOUTS_REPOSITORY_H - -#include "../database.h" -#include "../strings.h" -#include "base/base_expedition_lockouts_repository.h" - -class ExpeditionLockoutsRepository: public BaseExpeditionLockoutsRepository { -public: - - /** - * This file was auto generated and can be modified and extended upon - * - * Base repository methods are automatically - * generated in the "base" version of this repository. The base repository - * is immutable and to be left untouched, while methods in this class - * are used as extension methods for more specific persistence-layer - * accessors or mutators. - * - * Base Methods (Subject to be expanded upon in time) - * - * Note: Not all tables are designed appropriately to fit functionality with all base methods - * - * InsertOne - * UpdateOne - * DeleteOne - * FindOne - * GetWhere(std::string where_filter) - * DeleteWhere(std::string where_filter) - * InsertMany - * All - * - * Example custom methods in a repository - * - * ExpeditionLockoutsRepository::GetByZoneAndVersion(int zone_id, int zone_version) - * ExpeditionLockoutsRepository::GetWhereNeverExpires() - * ExpeditionLockoutsRepository::GetWhereXAndY() - * ExpeditionLockoutsRepository::DeleteWhereXAndY() - * - * Most of the above could be covered by base methods, but if you as a developer - * find yourself re-using logic for other parts of the code, its best to just make a - * method that can be re-used easily elsewhere especially if it can use a base repository - * method and encapsulate filters there - */ - - // 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 ({}) - ), - Strings::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/expeditions_repository.h b/common/repositories/expeditions_repository.h deleted file mode 100644 index aab6acf5f..000000000 --- a/common/repositories/expeditions_repository.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef EQEMU_EXPEDITIONS_REPOSITORY_H -#define EQEMU_EXPEDITIONS_REPOSITORY_H - -#include "../database.h" -#include "../strings.h" -#include "base/base_expeditions_repository.h" - -class ExpeditionsRepository: public BaseExpeditionsRepository { -public: - - /** - * This file was auto generated and can be modified and extended upon - * - * Base repository methods are automatically - * generated in the "base" version of this repository. The base repository - * is immutable and to be left untouched, while methods in this class - * are used as extension methods for more specific persistence-layer - * accessors or mutators. - * - * Base Methods (Subject to be expanded upon in time) - * - * Note: Not all tables are designed appropriately to fit functionality with all base methods - * - * InsertOne - * UpdateOne - * DeleteOne - * FindOne - * GetWhere(std::string where_filter) - * DeleteWhere(std::string where_filter) - * InsertMany - * All - * - * Example custom methods in a repository - * - * ExpeditionsRepository::GetByZoneAndVersion(int zone_id, int zone_version) - * ExpeditionsRepository::GetWhereNeverExpires() - * ExpeditionsRepository::GetWhereXAndY() - * ExpeditionsRepository::DeleteWhereXAndY() - * - * Most of the above could be covered by base methods, but if you as a developer - * find yourself re-using logic for other parts of the code, its best to just make a - * method that can be re-used easily elsewhere especially if it can use a base repository - * method and encapsulate filters there - */ - - // Custom extended repository methods here - - struct CharacterExpedition - { - uint32_t id; - std::string name; - uint32_t expedition_id; - }; - - static std::vector GetCharactersWithExpedition( - Database& db, const std::vector& character_names) - { - if (character_names.empty()) - { - return {}; - } - - std::vector entries; - - auto joined_character_names = fmt::format("'{}'", Strings::Join(character_names, "','")); - - auto results = db.QueryDatabase(fmt::format(SQL( - SELECT - character_data.id, - character_data.name, - MAX(expeditions.id) - FROM character_data - LEFT JOIN dynamic_zone_members - ON character_data.id = dynamic_zone_members.character_id - LEFT JOIN expeditions - ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id - WHERE character_data.name IN ({}) - GROUP BY character_data.id - ORDER BY FIELD(character_data.name, {}) - ), - joined_character_names, - joined_character_names - )); - - if (results.Success()) - { - entries.reserve(results.RowCount()); - - for (auto row = results.begin(); row != results.end(); ++row) - { - CharacterExpedition entry{}; - entry.id = std::strtoul(row[0], nullptr, 10); - entry.name = row[1]; - entry.expedition_id = row[2] ? std::strtoul(row[2], nullptr, 10) : 0; - - entries.emplace_back(std::move(entry)); - } - } - - return entries; - } - - static uint32_t GetIDByMemberID(Database& db, uint32_t character_id) - { - if (character_id == 0) - { - return 0; - } - - uint32_t expedition_id = 0; - - auto results = db.QueryDatabase(fmt::format(SQL( - SELECT - expeditions.id - FROM expeditions - INNER JOIN dynamic_zone_members - ON expeditions.dynamic_zone_id = dynamic_zone_members.dynamic_zone_id - WHERE - dynamic_zone_members.character_id = {} - ), - character_id - )); - - if (results.Success() && results.RowCount() > 0) - { - auto row = results.begin(); - expedition_id = std::strtoul(row[0], nullptr, 10); - } - - return expedition_id; - } -}; - -#endif //EQEMU_EXPEDITIONS_REPOSITORY_H diff --git a/common/repositories/instance_list_player_repository.h b/common/repositories/instance_list_player_repository.h index 8a8e31c0f..348ddc625 100644 --- a/common/repositories/instance_list_player_repository.h +++ b/common/repositories/instance_list_player_repository.h @@ -45,33 +45,26 @@ public: // Custom extended repository methods here - static int InsertOrUpdateMany(Database& db, - const std::vector& instance_list_player_entries) + static uint32_t InsertOrUpdateMany(Database& db, const std::vector& entries) { - std::vector insert_chunks; - - for (auto &instance_list_player_entry: instance_list_player_entries) + if (entries.empty()) { - std::vector insert_values; - - insert_values.push_back(std::to_string(instance_list_player_entry.id)); - insert_values.push_back(std::to_string(instance_list_player_entry.charid)); - - insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")"); + return 0; } - std::vector insert_values; + std::vector values; + values.reserve(entries.size()); - auto results = db.QueryDatabase( - fmt::format( - "INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = VALUES(id)", - TableName(), - ColumnsRaw(), - Strings::Implode(",", insert_chunks) - ) - ); + for (const auto& entry : entries) + { + values.push_back(fmt::format("({},{})", entry.id, entry.charid)); + } - return (results.Success() ? results.RowsAffected() : 0); + auto results = db.QueryDatabase(fmt::format( + "INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = VALUES(id)", + TableName(), ColumnsRaw(), fmt::join(values, ","))); + + return results.Success() ? results.RowsAffected() : 0; } static bool ReplaceOne(Database& db, InstanceListPlayer e) diff --git a/common/servertalk.h b/common/servertalk.h index d493313ef..d20309c21 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -170,18 +170,6 @@ #define ServerOP_LFPMatches 0x0214 #define ServerOP_ClientVersionSummary 0x0215 -// expedition -#define ServerOP_ExpeditionCreate 0x0400 -#define ServerOP_ExpeditionLockout 0x0403 -#define ServerOP_ExpeditionDzAddPlayer 0x0408 -#define ServerOP_ExpeditionDzMakeLeader 0x0409 -#define ServerOP_ExpeditionCharacterLockout 0x040d -#define ServerOP_ExpeditionSaveInvite 0x040e -#define ServerOP_ExpeditionRequestInvite 0x040f -#define ServerOP_ExpeditionReplayOnJoin 0x0410 -#define ServerOP_ExpeditionLockState 0x0411 -#define ServerOP_ExpeditionLockoutDuration 0x0414 - // dz #define ServerOP_DzAddRemoveMember 0x0450 #define ServerOP_DzRemoveAllMembers 0x0451 @@ -199,6 +187,15 @@ #define ServerOP_DzDeleted 0x045d #define ServerOP_DzSetSwitchID 0x045e #define ServerOP_DzMovePC 0x045f +#define ServerOP_DzLock 0x0460 +#define ServerOP_DzReplayOnJoin 0x0461 +#define ServerOP_DzLockout 0x0462 +#define ServerOP_DzLockoutDuration 0x0463 +#define ServerOP_DzCharacterLockout 0x0464 +#define ServerOP_DzAddPlayer 0x0465 +#define ServerOP_DzSaveInvite 0x0466 +#define ServerOP_DzRequestInvite 0x0467 +#define ServerOP_DzMakeLeader 0x0468 #define ServerOP_LSInfo 0x1000 #define ServerOP_LSStatus 0x1001 @@ -1543,10 +1540,8 @@ struct UCSServerStatus_Struct { }; }; -struct ServerExpeditionID_Struct { - uint32 expedition_id; - uint32 sender_zone_id; - uint32 sender_instance_id; +struct ServerCharacterID_Struct { + uint32_t char_id; }; struct ServerDzLeaderID_Struct { @@ -1580,45 +1575,42 @@ struct ServerDzMovePC_Struct { uint32 character_id; }; -struct ServerExpeditionLockout_Struct { - uint32 expedition_id; +struct ServerDzLockout_Struct { + uint32 dz_id; uint64 expire_time; uint32 duration; uint32 sender_zone_id; uint16 sender_instance_id; uint8 remove; uint8 members_only; - int seconds_adjust; + int seconds; char event_name[256]; }; -struct ServerExpeditionLockState_Struct { - uint32 expedition_id; +struct ServerDzLock_Struct { + uint32 dz_id; uint32 sender_zone_id; uint16 sender_instance_id; - uint8 enabled; + bool lock; uint8 lock_msg; // 0: none, 1: closing 2: trial begin + uint32 color; }; -struct ServerExpeditionSetting_Struct { - uint32 expedition_id; +struct ServerDzBool_Struct { + uint32 dz_id; uint32 sender_zone_id; uint16 sender_instance_id; - uint8 enabled; + bool enabled; }; -struct ServerExpeditionCharacterLockout_Struct { +struct ServerDzCharacterLockout_Struct { uint8 remove; - uint32 character_id; + uint32 char_id; uint64 expire_time; uint32 duration; char uuid[37]; - char expedition_name[128]; - char event_name[256]; -}; - -struct ServerExpeditionCharacterID_Struct { - uint32_t character_id; + char expedition[128]; + char event[256]; }; struct ServerDzExpireWarning_Struct { @@ -1627,7 +1619,7 @@ struct ServerDzExpireWarning_Struct { }; struct ServerDzCommand_Struct { - uint32 expedition_id; + uint32 dz_id; uint8 is_char_online; // 0: target name is offline, 1: online char requester_name[64]; char target_name[64]; @@ -1696,11 +1688,12 @@ struct ServerDzSetDuration_Struct { uint32 seconds; }; -struct ServerDzCreateSerialized_Struct { +struct ServerDzCreate_Struct { uint16_t origin_zone_id; uint16_t origin_instance_id; + uint32_t dz_id; uint32_t cereal_size; - char cereal_data[0]; + char cereal_data[1]; }; struct ServerSendPlayerEvent_Struct { diff --git a/common/version.h b/common/version.h index 9cb1866c1..56989f56c 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9304 +#define CURRENT_BINARY_DATABASE_VERSION 9305 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #endif diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index 2d4c6ebaa..1b85ad1ed 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -11,8 +11,6 @@ SET(world_sources dynamic_zone_manager.cpp eql_config.cpp eqemu_api_world_data_service.cpp - expedition_database.cpp - expedition_message.cpp launcher_link.cpp launcher_list.cpp lfplist.cpp @@ -48,8 +46,6 @@ SET(world_headers dynamic_zone_manager.h eql_config.h eqemu_api_world_data_service.h - expedition_database.h - expedition_message.h launcher_link.h launcher_list.h lfplist.h diff --git a/world/cliententry.h b/world/cliententry.h index f11bdcc1a..c421660dc 100644 --- a/world/cliententry.h +++ b/world/cliententry.h @@ -127,8 +127,8 @@ public: inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); } void ProcessTellQueue(); - void SetPendingExpeditionInvite(ServerPacket* pack) { p_pending_expedition_invite.reset(pack->Copy()); }; - std::unique_ptr GetPendingExpeditionInvite() { return std::move(p_pending_expedition_invite); } + void SetPendingDzInvite(ServerPacket* pack) { m_dz_invite.reset(pack->Copy()); }; + std::unique_ptr GetPendingDzInvite() { return std::move(m_dz_invite); } private: void ClearVars(bool iAll = false); @@ -176,7 +176,7 @@ private: // Tell Queue -- really a vector :D std::vector tell_queue; - std::unique_ptr p_pending_expedition_invite = nullptr; + std::unique_ptr m_dz_invite; }; #endif /*CLIENTENTRY_H_*/ diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 5e02d805e..7c18c8b17 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -1717,13 +1717,13 @@ void ClientList::SendCharacterMessageID(ClientListEntry* character, return; } - SerializeBuffer serialized_args; + SerializeBuffer argbuf; for (const auto& arg : args) { - serialized_args.WriteString(arg); + argbuf.WriteString(arg); } - uint32_t args_size = static_cast(serialized_args.size()); + uint32_t args_size = static_cast(argbuf.size()); uint32_t pack_size = sizeof(CZClientMessageString_Struct) + args_size; auto pack = std::make_unique(ServerOP_CZClientMessageString, pack_size); auto buf = reinterpret_cast(pack->pBuffer); @@ -1731,7 +1731,10 @@ void ClientList::SendCharacterMessageID(ClientListEntry* character, buf->chat_type = chat_type; strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name)); buf->args_size = args_size; - memcpy(buf->args, serialized_args.buffer(), serialized_args.size()); + if (argbuf.size() > 0) + { + memcpy(buf->args, argbuf.buffer(), argbuf.size()); + } character->Server()->SendPacket(pack.get()); } diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp index 3a23c186d..0b60b17ec 100644 --- a/world/dynamic_zone.cpp +++ b/world/dynamic_zone.cpp @@ -164,132 +164,6 @@ void DynamicZone::SendZonesLeaderChanged() zoneserver_list.SendPacket(pack.get()); } -void DynamicZone::HandleZoneMessage(ServerPacket* pack) -{ - switch (pack->opcode) - { - case ServerOP_DzCreated: - { - dynamic_zone_manager.CacheNewDynamicZone(pack); - break; - } - case ServerOP_DzSetCompass: - case ServerOP_DzSetSafeReturn: - case ServerOP_DzSetZoneIn: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz) - { - if (pack->opcode == ServerOP_DzSetCompass) - { - dz->SetCompass(buf->zone_id, buf->x, buf->y, buf->z, false); - } - else if (pack->opcode == ServerOP_DzSetSafeReturn) - { - dz->SetSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false); - } - else if (pack->opcode == ServerOP_DzSetZoneIn) - { - dz->SetZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false); - } - } - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_DzSetSwitchID: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz) - { - dz->ProcessSetSwitchID(buf->dz_switch_id); - } - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_DzAddRemoveMember: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz) - { - auto status = static_cast(buf->character_status); - dz->ProcessMemberAddRemove({ buf->character_id, buf->character_name, status }, buf->removed); - } - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_DzSwapMembers: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz) - { - // we add first in world so new member can be chosen if leader is removed - auto status = static_cast(buf->add_character_status); - dz->ProcessMemberAddRemove({ buf->add_character_id, buf->add_character_name, status }, false); - dz->ProcessMemberAddRemove({ buf->remove_character_id, buf->remove_character_name }, true); - } - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_DzRemoveAllMembers: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz) - { - dz->ProcessRemoveAllMembers(); - } - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_DzSetSecondsRemaining: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz) - { - dz->SetSecondsRemaining(buf->seconds); - } - break; - } - case ServerOP_DzGetMemberStatuses: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz) - { - dz->SendZoneMemberStatuses(buf->sender_zone_id, buf->sender_instance_id); - } - break; - } - case ServerOP_DzUpdateMemberStatus: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz) - { - auto status = static_cast(buf->status); - dz->ProcessMemberStatusChange(buf->character_id, status); - } - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_DzMovePC: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz && dz->HasMember(buf->character_id)) - { - zoneserver_list.SendPacket(buf->sender_zone_id, buf->sender_instance_id, pack); - } - break; - } - }; -} - void DynamicZone::ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) { DynamicZoneBase::ProcessMemberAddRemove(member, removed); @@ -345,13 +219,13 @@ void DynamicZone::SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id) void DynamicZone::CacheMemberStatuses() { - if (m_has_member_statuses) + if (m_has_member_statuses || m_members.empty()) { return; } // called when a new dz is cached to fill member statuses - std::string zone_name{}; + std::string zone_name; std::vector all_clients; all_clients.reserve(client_list.GetClientCount()); client_list.GetClients(zone_name.c_str(), all_clients); diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h index 6a3d5c953..48c702789 100644 --- a/world/dynamic_zone.h +++ b/world/dynamic_zone.h @@ -22,7 +22,6 @@ public: using DynamicZoneBase::DynamicZoneBase; // inherit base constructors static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); - static void HandleZoneMessage(ServerPacket* pack); void SetSecondsRemaining(uint32_t seconds_remaining) override; @@ -33,7 +32,7 @@ public: protected: Database& GetDatabase() override; void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) override; - bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status) override; + bool ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status) override; bool SendServerPacket(ServerPacket* packet) override; private: diff --git a/world/dynamic_zone_manager.cpp b/world/dynamic_zone_manager.cpp index 751246b2d..6e7e2d68e 100644 --- a/world/dynamic_zone_manager.cpp +++ b/world/dynamic_zone_manager.cpp @@ -1,12 +1,14 @@ #include "dynamic_zone_manager.h" #include "dynamic_zone.h" +#include "cliententry.h" +#include "clientlist.h" #include "worlddb.h" #include "zonelist.h" #include "zoneserver.h" #include "../common/rulesys.h" -#include "../common/repositories/expeditions_repository.h" -#include "../common/repositories/expedition_lockouts_repository.h" +#include "../common/repositories/dynamic_zone_lockouts_repository.h" +extern ClientList client_list; extern ZSList zoneserver_list; DynamicZoneManager dynamic_zone_manager; @@ -27,6 +29,8 @@ void DynamicZoneManager::PurgeExpiredDynamicZones() { LogDynamicZones("Purging [{}] dynamic zone(s)", dz_ids.size()); + DynamicZoneLockoutsRepository::DeleteWhere(database, + fmt::format("dynamic_zone_id IN ({})", fmt::join(dz_ids, ","))); DynamicZoneMembersRepository::DeleteWhere(database, fmt::format("dynamic_zone_id IN ({})", Strings::Join(dz_ids, ","))); DynamicZonesRepository::DeleteWhere(database, @@ -34,8 +38,7 @@ void DynamicZoneManager::PurgeExpiredDynamicZones() } } -DynamicZone* DynamicZoneManager::CreateNew( - DynamicZone& dz_request, const std::vector& members) +DynamicZone* DynamicZoneManager::TryCreate(DynamicZone& dz_request, const std::vector& members) { // this creates a new dz instance and saves it to both db and cache uint32_t dz_id = dz_request.Create(); @@ -46,15 +49,12 @@ DynamicZone* DynamicZoneManager::CreateNew( } auto dz = std::make_unique(dz_request); - if (!members.empty()) - { - dz->SaveMembers(members); - dz->CacheMemberStatuses(); - } + dz->SaveMembers(members); + dz->CacheMemberStatuses(); LogDynamicZones("Created new dz [{}] for zone [{}]", dz_id, dz_request.GetZoneID()); - auto pack = dz->CreateServerDzCreatePacket(0, 0); + auto pack = dz->CreateServerPacket(0, 0); zoneserver_list.SendPacket(pack.get()); auto inserted = dynamic_zone_cache.emplace(dz_id, std::move(dz)); @@ -63,18 +63,17 @@ DynamicZone* DynamicZoneManager::CreateNew( void DynamicZoneManager::CacheNewDynamicZone(ServerPacket* pack) { - auto buf = reinterpret_cast(pack->pBuffer); + auto buf = reinterpret_cast(pack->pBuffer); auto new_dz = std::make_unique(); - new_dz->LoadSerializedDzPacket(buf->cereal_data, buf->cereal_size); + new_dz->Unserialize({ buf->cereal_data, buf->cereal_size }); new_dz->CacheMemberStatuses(); // reserialize with member statuses cached before forwarding (restore origin zone) - auto repack = new_dz->CreateServerDzCreatePacket(buf->origin_zone_id, buf->origin_instance_id); + auto repack = new_dz->CreateServerPacket(buf->origin_zone_id, buf->origin_instance_id); - uint32_t dz_id = new_dz->GetID(); - dynamic_zone_cache.emplace(dz_id, std::move(new_dz)); - LogDynamicZones("Cached new dynamic zone [{}]", dz_id); + dynamic_zone_cache.emplace(buf->dz_id, std::move(new_dz)); + LogDynamicZones("Cached new dynamic zone [{}]", buf->dz_id); zoneserver_list.SendPacket(repack.get()); } @@ -83,18 +82,19 @@ void DynamicZoneManager::CacheAllFromDatabase() { BenchTimer bench; - auto dynamic_zones = DynamicZonesRepository::AllWithInstanceNotExpired(database); - auto dynamic_zone_members = DynamicZoneMembersRepository::GetAllWithNames(database); + auto dzs = DynamicZonesRepository::AllWithInstanceNotExpired(database); + auto members = DynamicZoneMembersRepository::AllWithNames(database); + auto lockouts = DynamicZoneLockoutsRepository::All(database); dynamic_zone_cache.clear(); - dynamic_zone_cache.reserve(dynamic_zones.size()); + dynamic_zone_cache.reserve(dzs.size()); - for (auto& entry : dynamic_zones) + for (auto& entry : dzs) { uint32_t dz_id = entry.id; auto dz = std::make_unique(std::move(entry)); - for (auto& member : dynamic_zone_members) + for (auto& member : members) { if (member.dynamic_zone_id == dz_id) { @@ -102,6 +102,14 @@ void DynamicZoneManager::CacheAllFromDatabase() } } + for (auto& lockout : lockouts) + { + if (lockout.dynamic_zone_id == dz->GetID()) + { + dz->m_lockouts.emplace_back(dz->GetName(), std::move(lockout)); + } + } + // note leader status won't be updated here until leader is set by owning system (expeditions) dz->CacheMemberStatuses(); @@ -142,24 +150,10 @@ void DynamicZoneManager::Process() dynamic_zone_cache.erase(dz_id); } - // need to look up expedition ids until lockouts are moved to dynamic zones - std::vector expedition_ids; - auto expeditions = ExpeditionsRepository::GetWhere(database, - fmt::format("dynamic_zone_id IN ({})", Strings::Join(dynamic_zone_ids, ","))); - - if (!expeditions.empty()) - { - for (const auto& expedition : expeditions) - { - expedition_ids.emplace_back(expedition.id); - } - ExpeditionLockoutsRepository::DeleteWhere(database, - fmt::format("expedition_id IN ({})", Strings::Join(expedition_ids, ","))); - } - - ExpeditionsRepository::DeleteWhere(database, - fmt::format("dynamic_zone_id IN ({})", Strings::Join(dynamic_zone_ids, ","))); - DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids); + DynamicZoneLockoutsRepository::DeleteWhere(database, + fmt::format("dynamic_zone_id IN ({})", fmt::join(dynamic_zone_ids, ","))); + DynamicZoneMembersRepository::DeleteWhere(database, + fmt::format("dynamic_zone_id IN ({})", fmt::join(dynamic_zone_ids, ","))); DynamicZonesRepository::DeleteWhere(database, fmt::format("id IN ({})", Strings::Join(dynamic_zone_ids, ","))); } @@ -174,3 +168,250 @@ void DynamicZoneManager::LoadTemplates() m_dz_templates[dz_template.id] = dz_template; } } + +void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack) +{ + switch (pack->opcode) + { + case ServerOP_DzCreated: + { + CacheNewDynamicZone(pack); + break; + } + case ServerOP_DzAddPlayer: + { + auto buf = reinterpret_cast(pack->pBuffer); + + ClientListEntry* cle = client_list.FindCharacter(buf->target_name); + if (cle && cle->Server()) + { + // continue in the add target's zone + buf->is_char_online = true; + cle->Server()->SendPacket(pack); + } + else + { + // add target not online, return to inviter + ClientListEntry* inviter_cle = client_list.FindCharacter(buf->requester_name); + if (inviter_cle && inviter_cle->Server()) + { + inviter_cle->Server()->SendPacket(pack); + } + } + break; + } + case ServerOP_DzSaveInvite: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (ClientListEntry* cle = client_list.FindCharacter(buf->target_name)) + { + // store packet on cle and re-send it when client requests it + buf->is_char_online = true; + pack->opcode = ServerOP_DzAddPlayer; + cle->SetPendingDzInvite(pack); + } + break; + } + case ServerOP_DzRequestInvite: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (ClientListEntry* cle = client_list.FindCLEByCharacterID(buf->char_id)) + { + auto invite_pack = cle->GetPendingDzInvite(); + if (invite_pack && cle->Server()) + { + cle->Server()->SendPacket(invite_pack.get()); + } + } + break; + } + case ServerOP_DzMakeLeader: + { + auto buf = reinterpret_cast(pack->pBuffer); + + // notify requester (old leader) and new leader of the result + ZoneServer* new_leader_zs = nullptr; + ClientListEntry* leader_cle = client_list.FindCharacter(buf->new_leader_name); + if (leader_cle && leader_cle->Server()) + { + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz && dz->GetLeaderID() == buf->requester_id) + { + buf->is_success = dz->SetNewLeader(leader_cle->CharID()); + } + + buf->is_online = true; + new_leader_zs = leader_cle->Server(); + new_leader_zs->SendPacket(pack); + } + + // if old and new leader are in the same zone only send one message + ClientListEntry* requester_cle = client_list.FindCLEByCharacterID(buf->requester_id); + if (requester_cle && requester_cle->Server() && requester_cle->Server() != new_leader_zs) + { + requester_cle->Server()->SendPacket(pack); + } + break; + } + case ServerOP_DzSetCompass: + case ServerOP_DzSetSafeReturn: + case ServerOP_DzSetZoneIn: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + if (pack->opcode == ServerOP_DzSetCompass) + { + dz->SetCompass(buf->zone_id, buf->x, buf->y, buf->z, false); + } + else if (pack->opcode == ServerOP_DzSetSafeReturn) + { + dz->SetSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false); + } + else if (pack->opcode == ServerOP_DzSetZoneIn) + { + dz->SetZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false); + } + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzSetSwitchID: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->ProcessSetSwitchID(buf->dz_switch_id); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzAddRemoveMember: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + auto status = static_cast(buf->character_status); + dz->ProcessMemberAddRemove({ buf->character_id, buf->character_name, status }, buf->removed); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzSwapMembers: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + // we add first in world so new member can be chosen if leader is removed + auto status = static_cast(buf->add_character_status); + dz->ProcessMemberAddRemove({ buf->add_character_id, buf->add_character_name, status }, false); + dz->ProcessMemberAddRemove({ buf->remove_character_id, buf->remove_character_name }, true); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzRemoveAllMembers: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->ProcessRemoveAllMembers(); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzSetSecondsRemaining: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->SetSecondsRemaining(buf->seconds); + } + break; + } + case ServerOP_DzGetMemberStatuses: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->SendZoneMemberStatuses(buf->sender_zone_id, buf->sender_instance_id); + } + break; + } + case ServerOP_DzUpdateMemberStatus: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + auto status = static_cast(buf->status); + dz->ProcessMemberStatusChange(buf->character_id, status); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzMovePC: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz && dz->HasMember(buf->character_id)) + { + zoneserver_list.SendPacket(buf->sender_zone_id, buf->sender_instance_id, pack); + } + break; + } + case ServerOP_DzLock: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->SetLocked(buf->lock); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzReplayOnJoin: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->SetReplayOnJoin(buf->enabled); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzLockout: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + DzLockout lockout{ dz->GetUUID(), dz->GetName(), buf->event_name, buf->expire_time, buf->duration }; + dz->HandleLockoutUpdate(lockout, buf->remove, buf->members_only); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzLockoutDuration: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + DzLockout lockout{ dz->GetUUID(), dz->GetName(), buf->event_name, buf->expire_time, buf->duration }; + dz->HandleLockoutDuration(lockout, buf->seconds, buf->members_only, false); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzCharacterLockout: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto cle = client_list.FindCLEByCharacterID(buf->char_id); + if (cle && cle->Server()) + { + cle->Server()->SendPacket(pack); + } + break; + } + default: + break; + }; +} diff --git a/world/dynamic_zone_manager.h b/world/dynamic_zone_manager.h index 8db6bfd06..7b85957dc 100644 --- a/world/dynamic_zone_manager.h +++ b/world/dynamic_zone_manager.h @@ -20,7 +20,8 @@ public: void CacheAllFromDatabase(); void CacheNewDynamicZone(ServerPacket* pack); - DynamicZone* CreateNew(DynamicZone& dz_request, const std::vector& members); + DynamicZone* TryCreate(DynamicZone& dz_request, const std::vector& members); + void HandleZoneMessage(ServerPacket* pack); void LoadTemplates(); void Process(); void PurgeExpiredDynamicZones(); diff --git a/world/expedition_database.cpp b/world/expedition_database.cpp deleted file mode 100644 index 995be8c6e..000000000 --- a/world/expedition_database.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "expedition_database.h" -#include "worlddb.h" -#include "../common/repositories/expeditions_repository.h" -#include "../common/repositories/expedition_lockouts_repository.h" -#include "../common/repositories/dynamic_zone_members_repository.h" - -void ExpeditionDatabase::PurgeExpiredExpeditions() -{ - std::string query = SQL( - SELECT - expeditions.id, - expeditions.dynamic_zone_id - FROM expeditions - LEFT JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id - LEFT JOIN instance_list ON dynamic_zones.instance_id = instance_list.id - LEFT JOIN - ( - SELECT dynamic_zone_id, COUNT(*) member_count - FROM dynamic_zone_members - GROUP BY dynamic_zone_id - ) dynamic_zone_members - ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id - WHERE - instance_list.id IS NULL - OR dynamic_zone_members.member_count IS NULL - OR dynamic_zone_members.member_count = 0 - OR (instance_list.start_time + instance_list.duration) <= UNIX_TIMESTAMP(); - ); - - auto results = database.QueryDatabase(query); - if (results.Success()) - { - std::vector expedition_ids; - std::vector dynamic_zone_ids; - for (auto row = results.begin(); row != results.end(); ++row) - { - expedition_ids.emplace_back(row[0]); - dynamic_zone_ids.emplace_back(static_cast(strtoul(row[1], nullptr, 10))); - } - - if (!expedition_ids.empty()) - { - ExpeditionsRepository::DeleteWhere(database, fmt::format("id IN ({})", fmt::join(expedition_ids, ","))); - ExpeditionLockoutsRepository::DeleteWhere(database, fmt::format("expedition_id IN ({})", fmt::join(expedition_ids, ","))); - DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids); - } - } -} - -void ExpeditionDatabase::PurgeExpiredCharacterLockouts() -{ - std::string query = SQL( - DELETE FROM character_expedition_lockouts - WHERE expire_time <= NOW(); - ); - - database.QueryDatabase(query); -} diff --git a/world/expedition_database.h b/world/expedition_database.h deleted file mode 100644 index a885dfc83..000000000 --- a/world/expedition_database.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef WORLD_EXPEDITION_DATABASE_H -#define WORLD_EXPEDITION_DATABASE_H - -#include -#include - -class Expedition; - -namespace ExpeditionDatabase -{ - void PurgeExpiredExpeditions(); - void PurgeExpiredCharacterLockouts(); -}; - -#endif diff --git a/world/expedition_message.cpp b/world/expedition_message.cpp deleted file mode 100644 index 93f6ccd6e..000000000 --- a/world/expedition_message.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "dynamic_zone.h" -#include "expedition_message.h" -#include "cliententry.h" -#include "clientlist.h" -#include "zonelist.h" -#include "zoneserver.h" -#include "../common/servertalk.h" -#include - -extern ClientList client_list; -extern ZSList zoneserver_list; - -void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack) -{ - switch (pack->opcode) - { - case ServerOP_ExpeditionCreate: - { - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_ExpeditionDzAddPlayer: - { - ExpeditionMessage::AddPlayer(pack); - break; - } - case ServerOP_ExpeditionDzMakeLeader: - { - ExpeditionMessage::MakeLeader(pack); - break; - } - case ServerOP_ExpeditionCharacterLockout: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto cle = client_list.FindCLEByCharacterID(buf->character_id); - if (cle && cle->Server()) - { - cle->Server()->SendPacket(pack); - } - break; - } - case ServerOP_ExpeditionSaveInvite: - { - ExpeditionMessage::SaveInvite(pack); - break; - } - case ServerOP_ExpeditionRequestInvite: - { - ExpeditionMessage::RequestInvite(pack); - break; - } - } -} - -void ExpeditionMessage::AddPlayer(ServerPacket* pack) -{ - auto buf = reinterpret_cast(pack->pBuffer); - - ClientListEntry* invited_cle = client_list.FindCharacter(buf->target_name); - if (invited_cle && invited_cle->Server()) - { - // continue in the add target's zone - buf->is_char_online = true; - invited_cle->Server()->SendPacket(pack); - } - else - { - // add target not online, return to inviter - ClientListEntry* inviter_cle = client_list.FindCharacter(buf->requester_name); - if (inviter_cle && inviter_cle->Server()) - { - inviter_cle->Server()->SendPacket(pack); - } - } -} - -void ExpeditionMessage::MakeLeader(ServerPacket* pack) -{ - auto buf = reinterpret_cast(pack->pBuffer); - - // notify requester (old leader) and new leader of the result - ZoneServer* new_leader_zs = nullptr; - ClientListEntry* new_leader_cle = client_list.FindCharacter(buf->new_leader_name); - if (new_leader_cle && new_leader_cle->Server()) - { - auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); - if (dz && dz->GetLeaderID() == buf->requester_id) - { - buf->is_success = dz->SetNewLeader(new_leader_cle->CharID()); - } - - buf->is_online = true; - new_leader_zs = new_leader_cle->Server(); - new_leader_zs->SendPacket(pack); - } - - // if old and new leader are in the same zone only send one message - ClientListEntry* requester_cle = client_list.FindCLEByCharacterID(buf->requester_id); - if (requester_cle && requester_cle->Server() && requester_cle->Server() != new_leader_zs) - { - requester_cle->Server()->SendPacket(pack); - } -} - -void ExpeditionMessage::SaveInvite(ServerPacket* pack) -{ - auto buf = reinterpret_cast(pack->pBuffer); - - ClientListEntry* invited_cle = client_list.FindCharacter(buf->target_name); - if (invited_cle) - { - // store packet on cle and re-send it when client requests it - buf->is_char_online = true; - pack->opcode = ServerOP_ExpeditionDzAddPlayer; - invited_cle->SetPendingExpeditionInvite(pack); - } -} - -void ExpeditionMessage::RequestInvite(ServerPacket* pack) -{ - auto buf = reinterpret_cast(pack->pBuffer); - ClientListEntry* cle = client_list.FindCLEByCharacterID(buf->character_id); - if (cle) - { - auto invite_pack = cle->GetPendingExpeditionInvite(); - if (invite_pack && cle->Server()) - { - cle->Server()->SendPacket(invite_pack.get()); - } - } -} diff --git a/world/expedition_message.h b/world/expedition_message.h deleted file mode 100644 index b97651dbc..000000000 --- a/world/expedition_message.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef WORLD_EXPEDITION_MESSAGE_H -#define WORLD_EXPEDITION_MESSAGE_H - -class ServerPacket; - -namespace ExpeditionMessage -{ - void AddPlayer(ServerPacket* pack); - void HandleZoneMessage(ServerPacket* pack); - void MakeLeader(ServerPacket* pack); - void RequestInvite(ServerPacket* pack); - void SaveInvite(ServerPacket* pack); -}; - -#endif diff --git a/world/main.cpp b/world/main.cpp index 2b4a2c8f4..f0cb8e702 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -76,10 +76,10 @@ #include "web_interface.h" #include "console.h" #include "dynamic_zone_manager.h" -#include "expedition_database.h" #include "world_server_cli.h" #include "../common/content/world_content_service.h" +#include "../common/repositories/character_expedition_lockouts_repository.h" #include "../common/repositories/character_task_timers_repository.h" #include "../common/zone_store.h" #include "world_event_scheduler.h" @@ -449,7 +449,7 @@ int main(int argc, char **argv) if (PurgeInstanceTimer.Check()) { database.PurgeExpiredInstances(); database.PurgeAllDeletedDataBuckets(); - ExpeditionDatabase::PurgeExpiredCharacterLockouts(); + CharacterExpeditionLockoutsRepository::DeleteWhere(database, "expire_time <= NOW()"); CharacterTaskTimersRepository::DeleteWhere(database, "expire_time <= NOW()"); } diff --git a/world/shared_task_manager.cpp b/world/shared_task_manager.cpp index 15925daed..f63ab0ac7 100644 --- a/world/shared_task_manager.cpp +++ b/world/shared_task_manager.cpp @@ -1172,7 +1172,7 @@ void SharedTaskManager::CreateDynamicZone(SharedTask *shared_task, DynamicZone & } } - auto new_dz = dynamic_zone_manager.CreateNew(dz_request, dz_members); + auto new_dz = dynamic_zone_manager.TryCreate(dz_request, dz_members); if (new_dz) { auto shared_task_dz = SharedTaskDynamicZonesRepository::NewEntity(); shared_task_dz.shared_task_id = shared_task->GetDbSharedTask().id; diff --git a/world/shared_task_world_messaging.cpp b/world/shared_task_world_messaging.cpp index 01584c495..e6271a458 100644 --- a/world/shared_task_world_messaging.cpp +++ b/world/shared_task_world_messaging.cpp @@ -283,7 +283,7 @@ void SharedTaskWorldMessaging::HandleZoneMessage(ServerPacket *pack) auto t = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(buf->task_id, buf->source_character_id); if (t) { DynamicZone dz; - dz.LoadSerializedDzPacket(buf->cereal_data, buf->cereal_size); + dz.Unserialize({ buf->cereal_data, buf->cereal_size }); shared_task_manager.CreateDynamicZone(t, dz); } diff --git a/world/world_boot.cpp b/world/world_boot.cpp index 831babdb7..6bd9eed7a 100644 --- a/world/world_boot.cpp +++ b/world/world_boot.cpp @@ -5,12 +5,12 @@ #include "../common/http/uri.h" #include "../common/net/console_server.h" #include "../common/net/servertalk_server.h" +#include "../common/repositories/character_expedition_lockouts_repository.h" #include "../common/repositories/character_task_timers_repository.h" #include "../common/rulesys.h" #include "../common/strings.h" #include "adventure_manager.h" #include "dynamic_zone_manager.h" -#include "expedition_database.h" #include "login_server_list.h" #include "shared_task_manager.h" #include "ucs.h" @@ -367,9 +367,8 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv) LogInfo("Purging expired dynamic zones and members"); dynamic_zone_manager.PurgeExpiredDynamicZones(); - LogInfo("Purging expired expeditions"); - ExpeditionDatabase::PurgeExpiredExpeditions(); - ExpeditionDatabase::PurgeExpiredCharacterLockouts(); + LogInfo("Purging expired character expedition lockouts"); + CharacterExpeditionLockoutsRepository::DeleteWhere(database, "expire_time <= NOW()"); LogInfo("Purging expired character task timers"); CharacterTaskTimersRepository::DeleteWhere(database, "expire_time <= NOW()"); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 554075e83..b5497da9b 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -39,7 +39,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/zone_store.h" #include "dynamic_zone.h" #include "dynamic_zone_manager.h" -#include "expedition_message.h" #include "shared_task_world_messaging.h" #include "../common/shared_tasks.h" #include "shared_task_manager.h" @@ -1375,10 +1374,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_Consent: case ServerOP_DepopAllPlayersCorpses: case ServerOP_DepopPlayerCorpse: - case ServerOP_ExpeditionLockState: - case ServerOP_ExpeditionLockout: - case ServerOP_ExpeditionLockoutDuration: - case ServerOP_ExpeditionReplayOnJoin: case ServerOP_GuildRankUpdate: case ServerOP_ItemStatus: case ServerOP_KickPlayer: @@ -1532,16 +1527,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { SharedTaskWorldMessaging::HandleZoneMessage(pack); break; } - case ServerOP_ExpeditionCreate: - case ServerOP_ExpeditionDzAddPlayer: - case ServerOP_ExpeditionDzMakeLeader: - case ServerOP_ExpeditionCharacterLockout: - case ServerOP_ExpeditionSaveInvite: - case ServerOP_ExpeditionRequestInvite: { - ExpeditionMessage::HandleZoneMessage(pack); - break; - } case ServerOP_DzCreated: + case ServerOP_DzAddPlayer: + case ServerOP_DzSaveInvite: + case ServerOP_DzRequestInvite: + case ServerOP_DzMakeLeader: case ServerOP_DzAddRemoveMember: case ServerOP_DzSwapMembers: case ServerOP_DzRemoveAllMembers: @@ -1552,8 +1542,13 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_DzSetZoneIn: case ServerOP_DzSetSwitchID: case ServerOP_DzMovePC: + case ServerOP_DzLock: + case ServerOP_DzReplayOnJoin: + case ServerOP_DzLockout: + case ServerOP_DzLockoutDuration: + case ServerOP_DzCharacterLockout: case ServerOP_DzUpdateMemberStatus: { - DynamicZone::HandleZoneMessage(pack); + dynamic_zone_manager.HandleZoneMessage(pack); break; } case ServerOP_GuildTributeUpdate: { diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index dd9ef09d7..7f3ecb5a8 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -36,8 +36,6 @@ SET(zone_sources encounter.cpp entity.cpp exp.cpp - expedition.cpp - expedition_database.cpp expedition_request.cpp fastmath.cpp fearpath.cpp @@ -205,8 +203,6 @@ SET(zone_headers encounter.h entity.h event_codes.h - expedition.h - expedition_database.h expedition_request.h fastmath.h forage.h diff --git a/zone/client.cpp b/zone/client.cpp index 8c61c04e8..cfd6ddb1c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -40,8 +40,7 @@ extern volatile bool RunLoops; #include "../common/data_verification.h" #include "../common/profanity_manager.h" #include "data_bucket.h" -#include "expedition.h" -#include "expedition_database.h" +#include "dynamic_zone.h" #include "expedition_request.h" #include "position.h" #include "worldserver.h" @@ -61,6 +60,7 @@ extern volatile bool RunLoops; #include "lua_parser.h" #include "../common/repositories/character_alternate_abilities_repository.h" +#include "../common/repositories/character_expedition_lockouts_repository.h" #include "../common/repositories/account_flags_repository.h" #include "../common/repositories/bug_reports_repository.h" #include "../common/repositories/char_recipe_list_repository.h" @@ -10024,13 +10024,13 @@ void Client::SendCrossZoneMessageString( return; } - SerializeBuffer argument_buffer; + SerializeBuffer argbuf; for (const auto& argument : arguments) { - argument_buffer.WriteString(argument); + argbuf.WriteString(argument); } - uint32_t args_size = static_cast(argument_buffer.size()); + uint32_t args_size = static_cast(argbuf.size()); uint32_t pack_size = sizeof(CZClientMessageString_Struct) + args_size; auto pack = std::make_unique(ServerOP_CZClientMessageString, pack_size); auto buf = reinterpret_cast(pack->pBuffer); @@ -10038,7 +10038,10 @@ void Client::SendCrossZoneMessageString( buf->chat_type = chat_type; strn0cpy(buf->client_name, character_name.c_str(), sizeof(buf->client_name)); buf->args_size = args_size; - memcpy(buf->args, argument_buffer.buffer(), argument_buffer.size()); + if (argbuf.size() > 0) + { + memcpy(buf->args, argbuf.buffer(), argbuf.size()); + } if (client) { @@ -10056,56 +10059,52 @@ void Client::SendDynamicZoneUpdates() SendDzCompassUpdate(); SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Online); - m_expedition_lockouts = ExpeditionDatabase::LoadCharacterLockouts(CharacterID()); + m_dz_lockouts = CharacterExpeditionLockoutsRepository::GetLockouts(database, CharacterID()); // expeditions are the only dz type that keep the window updated - auto expedition = GetExpedition(); - if (expedition) + if (DynamicZone* dz = GetExpedition()) { - expedition->GetDynamicZone()->SendClientWindowUpdate(this); + dz->SendClientWindowUpdate(this); // live synchronizes lockouts obtained during the active expedition to // members once they zone into the expedition's dynamic zone instance - if (expedition->GetDynamicZone()->IsCurrentZoneDzInstance()) + if (dz->IsCurrentZoneDz()) { - expedition->SyncCharacterLockouts(CharacterID(), m_expedition_lockouts); + dz->SyncCharacterLockouts(CharacterID(), m_dz_lockouts); } } - SendExpeditionLockoutTimers(); + SendDzLockoutTimers(); // ask world for any pending invite we saved from a previous zone - RequestPendingExpeditionInvite(); + RequestPendingDzInvite(); } -Expedition* Client::CreateExpedition(DynamicZone& dz, bool disable_messages) +DynamicZone* Client::CreateExpedition(DynamicZone& dz, bool silent) { - return Expedition::TryCreate(this, dz, disable_messages); + return DynamicZone::TryCreate(*this, dz, silent); } -Expedition* Client::CreateExpedition( - const std::string& zone_name, uint32 version, uint32 duration, const std::string& expedition_name, - uint32 min_players, uint32 max_players, bool disable_messages) +DynamicZone* Client::CreateExpedition(uint32 zone, uint32 version, uint32 duration, const std::string& name, uint32 min_players, uint32 max_players, bool silent) { - DynamicZone dz{ ZoneID(zone_name), version, duration, DynamicZoneType::Expedition }; - dz.SetName(expedition_name); + DynamicZone dz{ zone, version, duration, DynamicZoneType::Expedition }; + dz.SetName(name); dz.SetMinPlayers(min_players); dz.SetMaxPlayers(max_players); - return Expedition::TryCreate(this, dz, disable_messages); + return DynamicZone::TryCreate(*this, dz, silent); } -Expedition* Client::CreateExpeditionFromTemplate(uint32_t dz_template_id) +DynamicZone* Client::CreateExpeditionFromTemplate(uint32_t dz_template_id) { - Expedition* expedition = nullptr; auto it = zone->dz_template_cache.find(dz_template_id); if (it != zone->dz_template_cache.end()) { DynamicZone dz(DynamicZoneType::Expedition); dz.LoadTemplate(it->second); - expedition = Expedition::TryCreate(this, dz, false); + return DynamicZone::TryCreate(*this, dz, false); } - return expedition; + return nullptr; } void Client::CreateTaskDynamicZone(int task_id, DynamicZone& dz_request) @@ -10116,57 +10115,58 @@ void Client::CreateTaskDynamicZone(int task_id, DynamicZone& dz_request) } } -Expedition* Client::GetExpedition() const +DynamicZone* Client::GetExpedition() const { - if (zone && m_expedition_id) + if (zone) { - auto expedition_cache_iter = zone->expedition_cache.find(m_expedition_id); - if (expedition_cache_iter != zone->expedition_cache.end()) + for (uint32_t dz_id : m_dynamic_zone_ids) { - return expedition_cache_iter->second.get(); + auto it = zone->dynamic_zone_cache.find(dz_id); + if (it != zone->dynamic_zone_cache.end() && it->second->IsExpedition()) + { + return it->second.get(); + } } } return nullptr; } -void Client::AddExpeditionLockout(const ExpeditionLockoutTimer& lockout, bool update_db) +uint32_t Client::GetExpeditionID() const +{ + if (const DynamicZone* dz = GetExpedition()) + { + return dz->GetID(); + } + return 0; +} + +void Client::AddDzLockout(const DzLockout& lockout, bool update_db) { // todo: support for account based lockouts like live AoC expeditions // if client already has this lockout, we're replacing it with the new one - m_expedition_lockouts.erase(std::remove_if(m_expedition_lockouts.begin(), m_expedition_lockouts.end(), - [&](const ExpeditionLockoutTimer& existing_lockout) { - return existing_lockout.IsSameLockout(lockout); - } - ), m_expedition_lockouts.end()); + std::erase_if(m_dz_lockouts, [&](const DzLockout& l) { return l.IsSame(lockout); }); - m_expedition_lockouts.emplace_back(lockout); + m_dz_lockouts.push_back(lockout); if (update_db) // for quest api { - ExpeditionDatabase::InsertCharacterLockouts(CharacterID(), { lockout }); + CharacterExpeditionLockoutsRepository::InsertLockouts(database, CharacterID(), { lockout }); } - SendExpeditionLockoutTimers(); + SendDzLockoutTimers(); } -void Client::AddNewExpeditionLockout( - const std::string& expedition_name, const std::string& event_name, uint32_t seconds, std::string uuid) +void Client::AddDzLockout(const std::string& expedition, const std::string& event, uint32_t seconds, std::string uuid) { - auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid); - AddExpeditionLockout(lockout, true); + auto lockout = DzLockout::Create(expedition, event, seconds, uuid); + AddDzLockout(lockout, true); } -void Client::AddExpeditionLockoutDuration( - const std::string& expedition_name, const std::string& event_name, int seconds, - const std::string& uuid, bool update_db) +void Client::AddDzLockoutDuration(const DzLockout& lockout, int seconds, const std::string& uuid, bool update_db) { - auto it = std::find_if(m_expedition_lockouts.begin(), m_expedition_lockouts.end(), - [&](const ExpeditionLockoutTimer& lockout) { - return lockout.IsSameLockout(expedition_name, event_name); - }); - - if (it != m_expedition_lockouts.end()) + auto it = std::ranges::find_if(m_dz_lockouts, [&](const DzLockout& l) { return l.IsSame(lockout); }); + if (it != m_dz_lockouts.end()) { it->AddLockoutTime(seconds); @@ -10177,98 +10177,86 @@ void Client::AddExpeditionLockoutDuration( if (update_db) { - ExpeditionDatabase::InsertCharacterLockouts(CharacterID(), { *it }); + CharacterExpeditionLockoutsRepository::InsertLockouts(database, CharacterID(), { *it }); } - SendExpeditionLockoutTimers(); + SendDzLockoutTimers(); } else if (seconds > 0) // missing lockouts inserted for reductions would be instantly expired { - auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid); - AddExpeditionLockout(lockout, update_db); + AddDzLockout(lockout, update_db); } } -void Client::RemoveExpeditionLockout( - const std::string& expedition_name, const std::string& event_name, bool update_db) +void Client::RemoveDzLockout(const std::string& expedition, const std::string& event, bool update_db) { - m_expedition_lockouts.erase(std::remove_if(m_expedition_lockouts.begin(), m_expedition_lockouts.end(), - [&](const ExpeditionLockoutTimer& lockout) { - return lockout.IsSameLockout(expedition_name, event_name); - } - ), m_expedition_lockouts.end()); + std::erase_if(m_dz_lockouts, [&](const DzLockout& l) { return l.IsSame(expedition, event); }); if (update_db) // for quest api { - ExpeditionDatabase::DeleteCharacterLockout(CharacterID(), expedition_name, event_name); + CharacterExpeditionLockoutsRepository::DeleteWhere(database, fmt::format( + "character_id = {} AND expedition_name = '{}' AND event_name = '{}'", + CharacterID(), Strings::Escape(expedition), Strings::Escape(event))); } - SendExpeditionLockoutTimers(); + SendDzLockoutTimers(); } -void Client::RemoveAllExpeditionLockouts(const std::string& expedition_name, bool update_db) +void Client::RemoveDzLockouts(const std::string& expedition, bool update_db) { - if (expedition_name.empty()) + if (expedition.empty()) { if (update_db) { - ExpeditionDatabase::DeleteAllCharacterLockouts(CharacterID()); + CharacterExpeditionLockoutsRepository::DeleteWhere(database, fmt::format( + "character_id = {}", CharacterID())); } - m_expedition_lockouts.clear(); + m_dz_lockouts.clear(); } else { if (update_db) { - ExpeditionDatabase::DeleteAllCharacterLockouts(CharacterID(), expedition_name); + CharacterExpeditionLockoutsRepository::DeleteWhere(database, fmt::format( + "character_id = {} AND expedition_name = '{}'", CharacterID(), Strings::Escape(expedition))); } - - m_expedition_lockouts.erase(std::remove_if(m_expedition_lockouts.begin(), m_expedition_lockouts.end(), - [&](const ExpeditionLockoutTimer& lockout) { - return lockout.GetExpeditionName() == expedition_name; - } - ), m_expedition_lockouts.end()); + std::erase_if(m_dz_lockouts, [&](const DzLockout& l) { return l.DzName() == expedition; }); } - SendExpeditionLockoutTimers(); + SendDzLockoutTimers(); } -const ExpeditionLockoutTimer* Client::GetExpeditionLockout( - const std::string& expedition_name, const std::string& event_name, bool include_expired) const +const DzLockout* Client::GetDzLockout(const std::string& expedition, const std::string& event) const { - for (const auto& expedition_lockout : m_expedition_lockouts) + for (const auto& lockout : m_dz_lockouts) { - if ((include_expired || !expedition_lockout.IsExpired()) && - expedition_lockout.IsSameLockout(expedition_name, event_name)) + if (!lockout.IsExpired() && lockout.IsSame(expedition, event)) { - return &expedition_lockout; + return &lockout; } } return nullptr; } -std::vector Client::GetExpeditionLockouts( - const std::string& expedition_name, bool include_expired) +std::vector Client::GetDzLockouts(const std::string& expedition) { - std::vector lockouts; - for (const auto& lockout : m_expedition_lockouts) + std::vector lockouts; + for (const auto& lockout : m_dz_lockouts) { - if ((include_expired || !lockout.IsExpired()) && - lockout.GetExpeditionName() == expedition_name) + if (!lockout.IsExpired() && lockout.DzName() == expedition) { - lockouts.emplace_back(lockout); + lockouts.push_back(lockout); } } return lockouts; } -bool Client::HasExpeditionLockout( - const std::string& expedition_name, const std::string& event_name, bool include_expired) +bool Client::HasDzLockout(const std::string& expedition, const std::string& event) const { - return (GetExpeditionLockout(expedition_name, event_name, include_expired) != nullptr); + return GetDzLockout(expedition, event) != nullptr; } -void Client::SendExpeditionLockoutTimers() +void Client::SendDzLockoutTimers() { std::vector lockout_entries; @@ -10277,20 +10265,20 @@ void Client::SendExpeditionLockoutTimers() constexpr uint32_t rounding_seconds = 60; // erases expired lockouts while building lockout timer list - for (auto it = m_expedition_lockouts.begin(); it != m_expedition_lockouts.end();) + for (auto it = m_dz_lockouts.begin(); it != m_dz_lockouts.end();) { uint32_t seconds_remaining = it->GetSecondsRemaining(); if (seconds_remaining == 0) { - it = m_expedition_lockouts.erase(it); + it = m_dz_lockouts.erase(it); } else { - ExpeditionLockoutTimerEntry_Struct lockout; - strn0cpy(lockout.expedition_name, it->GetExpeditionName().c_str(), sizeof(lockout.expedition_name)); + ExpeditionLockoutTimerEntry_Struct lockout{}; + strn0cpy(lockout.expedition_name, it->DzName().c_str(), sizeof(lockout.expedition_name)); lockout.seconds_remaining = seconds_remaining + rounding_seconds; - lockout.event_type = it->IsReplayTimer() ? Expedition::REPLAY_TIMER_ID : Expedition::EVENT_TIMER_ID; - strn0cpy(lockout.event_name, it->GetEventName().c_str(), sizeof(lockout.event_name)); + lockout.event_type = it->IsReplay() ? DynamicZone::ReplayTimerID : DynamicZone::EventTimerID; + strn0cpy(lockout.event_name, it->Event().c_str(), sizeof(lockout.event_name)); lockout_entries.emplace_back(lockout); ++it; @@ -10310,38 +10298,31 @@ void Client::SendExpeditionLockoutTimers() QueuePacket(outapp.get()); } -void Client::RequestPendingExpeditionInvite() +void Client::RequestPendingDzInvite() const { - uint32_t packsize = sizeof(ServerExpeditionCharacterID_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionRequestInvite, packsize); - auto packbuf = reinterpret_cast(pack->pBuffer); - packbuf->character_id = CharacterID(); - worldserver.SendPacket(pack.get()); + ServerPacket pack(ServerOP_DzRequestInvite, static_cast(sizeof(ServerCharacterID_Struct))); + auto packbuf = reinterpret_cast(pack.pBuffer); + packbuf->char_id = CharacterID(); + worldserver.SendPacket(&pack); } void Client::DzListTimers() { // only lists player's current replay timer lockouts, not all event lockouts bool found = false; - for (const auto& lockout : m_expedition_lockouts) + for (const auto& lockout : m_dz_lockouts) { - if (lockout.IsReplayTimer()) + if (lockout.IsReplay()) { found = true; - auto time_remaining = lockout.GetDaysHoursMinutesRemaining(); - MessageString( - Chat::Yellow, DZLIST_REPLAY_TIMER, - time_remaining.days.c_str(), - time_remaining.hours.c_str(), - time_remaining.mins.c_str(), - lockout.GetExpeditionName().c_str() - ); + auto time = lockout.GetTimeRemainingStrs(); + MessageString(Chat::Yellow, DZ_TIMER, time.days.c_str(), time.hours.c_str(), time.mins.c_str(), lockout.DzName().c_str()); } } if (!found) { - MessageString(Chat::Yellow, EXPEDITION_NO_TIMERS); + MessageString(Chat::Yellow, DZ_NO_TIMERS); } } @@ -10447,7 +10428,10 @@ std::unique_ptr Client::CreateCompassPacket( auto outapp = std::make_unique(OP_DzCompass, outsize); auto outbuf = reinterpret_cast(outapp->pBuffer); outbuf->count = count; - memcpy(outbuf->entries, compass_entries.data(), entries_size); + if (!compass_entries.empty()) + { + memcpy(outbuf->entries, compass_entries.data(), entries_size); + } return outapp; } @@ -10456,7 +10440,7 @@ void Client::GoToDzSafeReturnOrBind(const DynamicZone* dynamic_zone) { if (dynamic_zone) { - auto safereturn = dynamic_zone->GetSafeReturnLocation(); + const auto& safereturn = dynamic_zone->GetSafeReturnLocation(); if (safereturn.zone_id != 0) { LogDynamicZonesDetail("Sending [{}] to safereturn zone [{}]", CharacterID(), safereturn.zone_id); @@ -10513,7 +10497,7 @@ void Client::SetDynamicZoneMemberStatus(DynamicZoneMemberStatus status) for (auto& dz : GetDynamicZones()) { // the rule to disable this status is handled internally by the dz - if (status == DynamicZoneMemberStatus::Online && dz->IsCurrentZoneDzInstance()) + if (status == DynamicZoneMemberStatus::Online && dz->IsCurrentZoneDz()) { status = DynamicZoneMemberStatus::InDynamicZone; } @@ -10534,7 +10518,7 @@ void Client::MovePCDynamicZone(uint32 zone_id, int zone_version, bool msg_if_inv { if (msg_if_invalid) { - MessageString(Chat::Red, DYNAMICZONE_WAY_IS_BLOCKED); // unconfirmed message + MessageString(Chat::Red, DZ_WAY_IS_BLOCKED); // unconfirmed message } } else if (client_dzs.size() == 1) diff --git a/zone/client.h b/zone/client.h index 3501a2326..1e332c17e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -21,8 +21,7 @@ class Client; class EQApplicationPacket; class DynamicZone; -class Expedition; -class ExpeditionLockoutTimer; +class DzLockout; class ExpeditionRequest; class Group; class NPC; @@ -32,6 +31,7 @@ class Seperator; class ServerPacket; struct DynamicZoneLocation; enum WaterRegionType : int; +enum class DynamicZoneMemberStatus; namespace EQ { @@ -248,6 +248,13 @@ struct ClientReward uint32 amount; }; +struct ExpeditionInvite +{ + uint32_t dz_id; + std::string inviter_name; + std::string swap_name; +}; + class Client : public Mob { public: @@ -1565,32 +1572,24 @@ public: Client* client, const std::string& client_name, uint16_t chat_type, uint32_t string_id, const std::initializer_list& arguments = {}); - void AddExpeditionLockout(const ExpeditionLockoutTimer& lockout, bool update_db = false); - void AddExpeditionLockoutDuration(const std::string& expedition_name, - const std::string& event_Name, int seconds, const std::string& uuid = {}, bool update_db = false); - void AddNewExpeditionLockout(const std::string& expedition_name, - const std::string& event_name, uint32_t duration, std::string uuid = {}); - Expedition* CreateExpedition(DynamicZone& dz, bool disable_messages = false); - Expedition* CreateExpedition(const std::string& zone_name, - uint32 version, uint32 duration, const std::string& expedition_name, - uint32 min_players, uint32 max_players, bool disable_messages = false); - Expedition* CreateExpeditionFromTemplate(uint32_t dz_template_id); - Expedition* GetExpedition() const; - uint32 GetExpeditionID() const { return m_expedition_id; } - const ExpeditionLockoutTimer* GetExpeditionLockout( - const std::string& expedition_name, const std::string& event_name, bool include_expired = false) const; - const std::vector& GetExpeditionLockouts() const { return m_expedition_lockouts; }; - std::vector GetExpeditionLockouts(const std::string& expedition_name, bool include_expired = false); - uint32 GetPendingExpeditionInviteID() const { return m_pending_expedition_invite.expedition_id; } - bool HasExpeditionLockout(const std::string& expedition_name, const std::string& event_name, bool include_expired = false); - bool IsInExpedition() const { return m_expedition_id != 0; } - void RemoveAllExpeditionLockouts(const std::string& expedition_name, bool update_db = false); - void RemoveExpeditionLockout(const std::string& expedition_name, - const std::string& event_name, bool update_db = false); - void RequestPendingExpeditionInvite(); - void SendExpeditionLockoutTimers(); - void SetExpeditionID(uint32 expedition_id) { m_expedition_id = expedition_id; }; - void SetPendingExpeditionInvite(ExpeditionInvite&& invite) { m_pending_expedition_invite = invite; } + void AddDzLockout(const DzLockout& lockout, bool update_db = false); + void AddDzLockout(const std::string& expedition, const std::string& event, uint32_t duration, std::string uuid = {}); + void AddDzLockoutDuration(const DzLockout& lockout, int seconds, const std::string& uuid = {}, bool update_db = false); + DynamicZone* CreateExpedition(DynamicZone& dz, bool silent = false); + DynamicZone* CreateExpedition(uint32 zone_id, uint32 version, uint32 duration, const std::string& name, uint32 min_players, uint32 max_players, bool silent = false); + DynamicZone* CreateExpeditionFromTemplate(uint32_t dz_template_id); + DynamicZone* GetExpedition() const; + uint32 GetExpeditionID() const; + const DzLockout* GetDzLockout(const std::string& expedition, const std::string& event) const; + const std::vector& GetDzLockouts() const { return m_dz_lockouts; }; + std::vector GetDzLockouts(const std::string& expedition); + uint32 GetPendingDzInviteID() const { return m_dz_invite.dz_id; } + void SetPendingDzInvite(const ExpeditionInvite& invite) { m_dz_invite = invite; } + void RequestPendingDzInvite() const; + bool HasDzLockout(const std::string& expedition, const std::string& event) const; + void RemoveDzLockouts(const std::string& expedition, bool update_db = false); + void RemoveDzLockout(const std::string& expedition, const std::string& event, bool update_db = false); + void SendDzLockoutTimers(); void DzListTimers(); void SetDzRemovalTimer(bool enable_timer); void SendDzCompassUpdate(); @@ -2285,9 +2284,8 @@ private: uint8 client_max_level; - uint32 m_expedition_id = 0; - ExpeditionInvite m_pending_expedition_invite { 0 }; - std::vector m_expedition_lockouts; + ExpeditionInvite m_dz_invite = {}; + std::vector m_dz_lockouts; glm::vec3 m_quest_compass; bool m_has_quest_compass = false; std::vector m_dynamic_zone_ids; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2a372b93c..82f0f7d2a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -43,8 +43,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/data_verification.h" #include "../common/rdtsc.h" #include "data_bucket.h" +#include "dynamic_zone.h" #include "event_codes.h" -#include "expedition.h" #include "guild_mgr.h" #include "merc.h" #include "petitions.h" @@ -1781,8 +1781,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_dynamic_zone_ids.emplace_back(entry.dynamic_zone_id); } - m_expedition_id = ExpeditionsRepository::GetIDByMemberID(database, CharacterID()); - auto dz = zone->GetDynamicZone(); if (dz && dz->GetSafeReturnLocation().zone_id != 0) { @@ -6055,8 +6053,8 @@ void Client::Handle_OP_DzAddPlayer(const EQApplicationPacket *app) } else { - // the only /dz command that sends an error message if no active expedition - Message(Chat::System, DZ_YOU_NOT_ASSIGNED); + // message string 8271 (not in emu clients) is the only /dz command that sends an error if no active expedition + Message(Chat::System, "You could not use this command because you are not currently assigned to a dynamic zone."); } } @@ -6089,14 +6087,14 @@ void Client::Handle_OP_DzChooseZoneReply(const EQApplicationPacket *app) void Client::Handle_OP_DzExpeditionInviteResponse(const EQApplicationPacket *app) { - auto expedition = Expedition::FindCachedExpeditionByID(m_pending_expedition_invite.expedition_id); - std::string swap_remove_name = m_pending_expedition_invite.swap_remove_name; - m_pending_expedition_invite = { 0 }; // clear before re-validating + auto expedition = DynamicZone::FindDynamicZoneByID(m_dz_invite.dz_id); + std::string swap_name = m_dz_invite.swap_name; + m_dz_invite = {}; // clear before re-validating if (expedition) { auto dzmsg = reinterpret_cast(app->pBuffer); - expedition->DzInviteResponse(this, dzmsg->accepted, swap_remove_name); + expedition->DzInviteResponse(this, dzmsg->accepted, swap_name); } } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 172d8ca2a..0639fabf8 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -39,8 +39,8 @@ #include "../common/skills.h" #include "../common/spdat.h" #include "../common/strings.h" +#include "dynamic_zone.h" #include "event_codes.h" -#include "expedition.h" #include "guild_mgr.h" #include "map.h" #include "petitions.h" diff --git a/zone/command.cpp b/zone/command.cpp index f99a6b5a3..c34e37c7c 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -22,7 +22,6 @@ #include "data_bucket.h" #include "command.h" #include "dynamic_zone.h" -#include "expedition.h" #include "queryserv.h" #include "quest_parser_collection.h" #include "titles.h" diff --git a/zone/common.h b/zone/common.h index 98d0eda6d..a900b9738 100644 --- a/zone/common.h +++ b/zone/common.h @@ -862,13 +862,6 @@ struct DamageHitInfo { EQ::skills::SkillType skill; }; -struct ExpeditionInvite -{ - uint32_t expedition_id; - std::string inviter_name; - std::string swap_remove_name; -}; - struct DataBucketCache { uint64_t bucket_id; diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 8b634b8a2..e614a2942 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -16,8 +16,8 @@ #include "../common/say_link.h" #include "corpse.h" +#include "dynamic_zone.h" #include "entity.h" -#include "expedition.h" #include "groups.h" #include "mob.h" #include "raids.h" diff --git a/zone/dynamic_zone.cpp b/zone/dynamic_zone.cpp index 65087f9ac..b8ac6ab4e 100644 --- a/zone/dynamic_zone.cpp +++ b/zone/dynamic_zone.cpp @@ -20,14 +20,19 @@ #include "dynamic_zone.h" #include "client.h" -#include "expedition.h" +#include "expedition_request.h" #include "string_ids.h" #include "worldserver.h" +#include "../common/repositories/character_expedition_lockouts_repository.h" +#include "../common/repositories/dynamic_zone_lockouts_repository.h" extern WorldServer worldserver; -DynamicZone::DynamicZone( - uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type) +// various expeditions use these strings when locking +constexpr char LockClose[] = "Your expedition is nearing its close. You cannot bring any additional people into your expedition at this time."; +constexpr char LockBegin[] = "The trial has begun. You cannot bring any additional people into your expedition at this time."; + +DynamicZone::DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type) { m_zone_id = zone_id; m_zone_version = version; @@ -45,72 +50,79 @@ bool DynamicZone::SendServerPacket(ServerPacket* packet) return worldserver.SendPacket(packet); } -uint16_t DynamicZone::GetCurrentInstanceID() +uint16_t DynamicZone::GetCurrentInstanceID() const { return zone ? static_cast(zone->GetInstanceID()) : 0; } -uint16_t DynamicZone::GetCurrentZoneID() +uint16_t DynamicZone::GetCurrentZoneID() const { return zone ? static_cast(zone->GetZoneID()) : 0; } -DynamicZone* DynamicZone::CreateNew(DynamicZone& dz_request, const std::vector& members) +DynamicZone* DynamicZone::TryCreate(Client& client, DynamicZone& dzinfo, bool silent) { - if (!zone || dz_request.GetID() != 0) + // only expedition types are currently created in zone + if (!zone || dzinfo.GetID() != 0 || !dzinfo.IsExpedition()) { return nullptr; } + // request parses leader, members list, and lockouts while validating + ExpeditionRequest request(dzinfo, client, silent); + if (!request.Validate()) + { + LogExpeditions("[{}] request by [{}] denied", dzinfo.GetName(), client.GetName()); + return nullptr; + } + + dzinfo.SetLeader({ request.GetLeaderID(), request.GetLeaderName(), DynamicZoneMemberStatus::Online }); + // this creates a new dz instance and saves it to both db and cache - uint32_t dz_id = dz_request.Create(); + uint32_t dz_id = dzinfo.Create(); if (dz_id == 0) { - LogDynamicZones("Failed to create dynamic zone for zone [{}]", dz_request.GetZoneID()); + // live uses this message when trying to enter an instance that isn't ready + // for now we can use it as a client error message if instance creation fails + client.MessageString(Chat::Red, DZ_PREVENT_ENTERING); + LogDynamicZones("Failed to create dynamic zone for zone [{}]", dzinfo.GetZoneID()); return nullptr; } - auto dz = std::make_unique(dz_request); - if (!members.empty()) - { - dz->SaveMembers(members); - } + auto [it, ok] = zone->dynamic_zone_cache.try_emplace(dz_id, std::make_unique(dzinfo)); - LogDynamicZones("Created new dz [{}] for zone [{}]", dz_id, dz_request.GetZoneID()); + DynamicZone* dz = it->second.get(); + dz->SaveMembers(request.GetMembers()); + dz->SaveLockouts(request.GetLockouts()); + + dz->SendLeaderMessage(request.GetLeaderClient(), Chat::System, DZ_AVAILABLE, { dz->GetName() }); + if (dz->GetMemberCount() < request.GetMembers().size()) + { + dz->SendLeaderMessage(request.GetLeaderClient(), Chat::System, fmt::format(DzNotAllAdded, + request.IsRaid() ? "raid" : "group", "expedition", dz->GetMaxPlayers(), request.GetMembers().size())); + } // world must be notified before we request async member updates - auto pack = dz->CreateServerDzCreatePacket(zone->GetZoneID(), zone->GetInstanceID()); + auto pack = dz->CreateServerPacket(zone->GetZoneID(), zone->GetInstanceID()); worldserver.SendPacket(pack.get()); - auto inserted = zone->dynamic_zone_cache.emplace(dz_id, std::move(dz)); + dz->UpdateMembers(); - // expeditions invoke their own updates after installing client update callbacks - if (inserted.first->second->GetType() != DynamicZoneType::Expedition) - { - inserted.first->second->DoAsyncZoneMemberUpdates(); - } + LogDynamicZones("Created new dz [{}] for zone [{}]", dz_id, dz->GetZoneID()); - return inserted.first->second.get(); + return dz; } void DynamicZone::CacheNewDynamicZone(ServerPacket* pack) { - auto buf = reinterpret_cast(pack->pBuffer); + auto buf = reinterpret_cast(pack->pBuffer); // caching new dz created in world or another zone (has member statuses set by world) - auto dz = std::make_unique(); - dz->LoadSerializedDzPacket(buf->cereal_data, buf->cereal_size); + auto [it, ok] = zone->dynamic_zone_cache.try_emplace(buf->dz_id, std::make_unique()); + it->second->Unserialize({ buf->cereal_data, buf->cereal_size }); + it->second->UpdateMembers(); - uint32_t dz_id = dz->GetID(); - auto inserted = zone->dynamic_zone_cache.emplace(dz_id, std::move(dz)); - - // expeditions invoke their own updates after installing client update callbacks - if (inserted.first->second->GetType() != DynamicZoneType::Expedition) - { - inserted.first->second->DoAsyncZoneMemberUpdates(); - } - - LogDynamicZones("Cached new dynamic zone [{}]", dz_id); + LogDynamicZones("Cached new dynamic zone [{}]", buf->dz_id); } void DynamicZone::CacheAllFromDatabase() @@ -122,18 +134,19 @@ void DynamicZone::CacheAllFromDatabase() BenchTimer bench; - auto dynamic_zones = DynamicZonesRepository::AllWithInstanceNotExpired(database); - auto dynamic_zone_members = DynamicZoneMembersRepository::GetAllWithNames(database); + auto dzs = DynamicZonesRepository::AllWithInstanceNotExpired(database); + auto members = DynamicZoneMembersRepository::AllWithNames(database); + auto lockouts = DynamicZoneLockoutsRepository::All(database); zone->dynamic_zone_cache.clear(); - zone->dynamic_zone_cache.reserve(dynamic_zones.size()); + zone->dynamic_zone_cache.reserve(dzs.size()); - for (auto& entry : dynamic_zones) + for (auto& entry : dzs) { uint32_t dz_id = entry.id; auto dz = std::make_unique(std::move(entry)); - for (auto& member : dynamic_zone_members) + for (auto& member : members) { if (member.dynamic_zone_id == dz_id) { @@ -141,6 +154,15 @@ void DynamicZone::CacheAllFromDatabase() } } + for (auto& lockout : lockouts) + { + if (lockout.dynamic_zone_id == dz->GetID()) + { + dz->m_lockouts.emplace_back(dz->GetName(), std::move(lockout)); + } + } + + dz->UpdateMembers(); zone->dynamic_zone_cache.emplace(dz_id, std::move(dz)); } @@ -148,25 +170,43 @@ void DynamicZone::CacheAllFromDatabase() LogDynamicZones("Caching [{}] dynamic zone(s) took [{}s]", zone->dynamic_zone_cache.size(), bench.elapsed()); } -DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) +template +DynamicZone* FindDynamicZone(T pred) { - if (!zone) + if (zone) { - return nullptr; + for (const auto& [id_, dz] : zone->dynamic_zone_cache) + { + if (pred(*dz.get())) + { + return dz.get(); + } + } } - - auto dz = zone->dynamic_zone_cache.find(dz_id); - if (dz != zone->dynamic_zone_cache.end()) - { - return dz->second.get(); - } - return nullptr; } -void DynamicZone::RegisterOnClientAddRemove(std::function on_client_addremove) +DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id, DynamicZoneType type) { - m_on_client_addremove = std::move(on_client_addremove); + if (zone) + { + auto it = zone->dynamic_zone_cache.find(dz_id); + if (it != zone->dynamic_zone_cache.end() && (type == DynamicZoneType::None || it->second->GetType() == type)) + { + return it->second.get(); + } + } + return nullptr; +} + +DynamicZone* DynamicZone::FindExpeditionByCharacter(uint32_t char_id) +{ + return FindDynamicZone([&](const DynamicZone& dz) { return dz.IsExpedition() && dz.HasMember(char_id); }); +} + +DynamicZone* DynamicZone::FindExpeditionByZone(uint32_t zone_id, uint32_t instance_id) +{ + return FindDynamicZone([&](const DynamicZone& dz) { return dz.IsExpedition() && dz.IsSameDz(zone_id, instance_id); }); } void DynamicZone::StartAllClientRemovalTimers() @@ -180,9 +220,9 @@ void DynamicZone::StartAllClientRemovalTimers() } } -bool DynamicZone::IsCurrentZoneDzInstance() const +bool DynamicZone::IsCurrentZoneDz() const { - return (zone && zone->GetInstanceID() != 0 && zone->GetInstanceID() == GetInstanceID()); + return zone && zone->GetInstanceID() != 0 && zone->GetInstanceID() == GetInstanceID(); } void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining) @@ -205,19 +245,410 @@ void DynamicZone::SetUpdatedDuration(uint32_t new_duration) LogDynamicZones("Updated dz [{}] zone [{}]:[{}] seconds remaining: [{}]", m_id, m_zone_id, m_instance_id, GetSecondsRemaining()); - if (zone && IsCurrentZoneDzInstance()) + if (zone && IsCurrentZoneDz()) { zone->SetInstanceTimer(GetSecondsRemaining()); } } +void DynamicZone::SendClientInvite(Client* client, const std::string& inviter, const std::string& swap_name) +{ + if (!client) + { + return; + } + + LogExpeditions("Invite [{}] to [{}] by [{}] swap [{}]", client->GetName(), GetID(), inviter, swap_name); + + client->SetPendingDzInvite({ GetID(), inviter, swap_name }); + client->MessageString(Chat::System, DZ_INVITED, GetLeaderName().c_str(), GetName().c_str()); + + // live (as of March 11 2020 patch) warns for lockouts added during current + // expedition that client would receive upon entering (sent in invite packet) + std::string events; + for (const auto& lockout : m_lockouts) + { + // live doesn't issue a warning for the dz's replay timer + if (!lockout.IsReplay() && !lockout.IsExpired() && lockout.IsUUID(GetUUID()) && + !client->HasDzLockout(GetName(), lockout.Event())) + { + auto time = lockout.GetTimeRemainingStrs(); + events += fmt::format("\n{} - {}D:{}H:{}M", lockout.Event(), time.days, time.hours, time.mins); + } + } + + if (!events.empty()) + { + client->SendColoredText(Chat::System, fmt::format( + "Warning! You will be given replay timers for the following events if you enter {}:{}", GetName(), events)); + } + + auto outapp = CreateInvitePacket(inviter, swap_name).release(); + client->FastQueuePacket(&outapp); +} + +bool DynamicZone::ConfirmLeaderCommand(Client* client) +{ + if (!client) + { + return false; + } + + if (!GetLeader().IsValid()) + { + client->MessageString(Chat::Red, DZ_NO_LEADER_INFO); // unconfirmed message + return false; + } + + if (GetLeaderID() != client->CharacterID()) + { + client->MessageString(Chat::System, DZ_NOT_LEADER, GetLeaderName().c_str()); + return false; + } + + return true; +} + +void DynamicZone::SendLeaderMessage(Client* leader, uint16_t type, const std::string& msg) +{ + Client::SendCrossZoneMessage(leader, GetLeaderName(), type, msg); +} + +void DynamicZone::SendLeaderMessage(Client* leader, uint16_t type, uint32_t str_id, std::initializer_list args) +{ + Client::SendCrossZoneMessageString(leader, GetLeaderName(), type, str_id, args); +} + +bool DynamicZone::ProcessAddConflicts(Client* leader, Client* client, bool swapping) +{ + if (!client) // a null leader client is handled by SendLeaderMessage fallback + { + return true; + } + + bool has_conflict = false; + + if (IsCurrentZoneDz()) + { + SendLeaderMessage(leader, Chat::Red, DZADD_LEAVE_ZONE, { client->GetName() }); + has_conflict = true; + } + + auto dz_id = client->GetExpeditionID(); + if (dz_id) + { + int string_id = dz_id == GetID() ? DZADD_ALREADY_PART : DZADD_ALREADY_OTHER; + SendLeaderMessage(leader, Chat::Red, string_id, { client->GetName() }); + has_conflict = true; + } + + // check any extra event lockouts for this expedition that the client has and expedition doesn't + auto lockouts = client->GetDzLockouts(GetName()); + for (const auto& lockout : lockouts) + { + // client with a replay lockout is allowed only if the replay timer was from this expedition + if (lockout.IsReplay() && lockout.UUID() != m_uuid) + { + has_conflict = true; + + auto time = lockout.GetTimeRemainingStrs(); + SendLeaderMessage(leader, Chat::Red, DZADD_REPLAY_TIMER, { client->GetName(), time.days, time.hours, time.mins }); + } + else if (!lockout.IsReplay() && !HasLockout(lockout.Event())) + { + has_conflict = true; + + auto time = lockout.GetTimeRemainingStrs(); + SendLeaderMessage(leader, Chat::Red, DZADD_EVENT_TIMER, { client->GetName(), lockout.Event(), time.days, time.hours, time.mins }); + } + } + + // member swapping integrity is handled by invite response + if (!swapping) + { + auto member_count = GetDatabaseMemberCount(); + if (member_count == 0) + { + has_conflict = true; + } + else if (member_count >= GetMaxPlayers()) + { + SendLeaderMessage(leader, Chat::Red, DZADD_EXCEED_MAX, { fmt::format_int(GetMaxPlayers()).str() }); + has_conflict = true; + } + } + + auto invite_id = client->GetPendingDzInviteID(); + if (invite_id) + { + int string_id = invite_id == GetID() ? DZADD_PENDING : DZADD_PENDING_OTHER; + SendLeaderMessage(leader, Chat::Red, string_id, { client->GetName() }); + has_conflict = true; + } + + return has_conflict; +} + +void DynamicZone::TryAddClient(Client* client, const std::string& inviter, const std::string& swap_name, Client* leader) +{ + if (!client) + { + return; + } + + LogExpeditions("Adding [{}] to [{}] by [{}] swap [{}]", client->GetName(), GetID(), inviter, swap_name); + + // null leader client handled by ProcessAddConflicts/SendLeaderMessage fallbacks + if (!leader) + { + leader = entity_list.GetClientByName(inviter.c_str()); + } + + bool has_conflicts = ProcessAddConflicts(leader, client, !swap_name.empty()); + if (!has_conflicts) + { + // live uses the original unsanitized input string in invite messages + uint32_t string_id = swap_name.empty() ? DZADD_INVITE : DZSWAP_INVITE; + SendLeaderMessage(leader, Chat::Yellow, string_id, { client->GetName() }); + SendClientInvite(client, inviter, swap_name); + } + else if (swap_name.empty()) // swap command doesn't result in this message + { + SendLeaderMessage(leader, Chat::Red, DZADD_INVITE_FAIL, { client->GetName() }); + } +} + +void DynamicZone::DzAddPlayer(Client* client, const std::string& add_name, const std::string& swap_name) +{ + if (!client || !ConfirmLeaderCommand(client)) + { + return; + } + + bool invite_failed = false; + + if (IsLocked()) + { + client->MessageString(Chat::Red, DZADD_NOT_ALLOWING); + invite_failed = true; + } + else if (add_name.empty()) + { + client->MessageString(Chat::Red, DZADD_NOT_ONLINE, add_name.c_str()); + invite_failed = true; + } + else + { + auto member_data = GetMemberData(add_name); + if (member_data.IsValid()) + { + // live prioritizes offline message before already a member message + if (member_data.status == DynamicZoneMemberStatus::Offline) + { + client->MessageString(Chat::Red, DZADD_NOT_ONLINE, add_name.c_str()); + } + else + { + client->MessageString(Chat::Red, DZADD_ALREADY_PART, add_name.c_str()); + } + invite_failed = true; + } + } + + if (invite_failed) + { + client->MessageString(Chat::Red, DZADD_INVITE_FAIL, FormatName(add_name).c_str()); + return; + } + + if (Client* add_client = entity_list.GetClientByName(add_name.c_str())) + { + // client is online in this zone + TryAddClient(add_client, client->GetName(), swap_name, client); + } + else + { + // forward to world to check if client is online and perform cross-zone invite + SendWorldPlayerInvite(client->GetName(), swap_name, FormatName(add_name)); + } +} + +void DynamicZone::DzAddPlayerContinue(std::string inviter, std::string add_name, std::string swap_name) +{ + // continuing expedition invite from leader in another zone + if (Client* add_client = entity_list.GetClientByName(add_name.c_str())) + { + TryAddClient(add_client, inviter, swap_name); + } +} + +void DynamicZone::DzInviteResponse(Client* client, bool accepted, const std::string& swap_name) +{ + if (!client) + { + return; + } + + LogExpeditions("[{}] accepted invite [{}] swap [{}]", client->GetName(), accepted, swap_name); + + // a null leader client is handled by SendLeaderMessage fallbacks + // note current leader receives invite reply messages (if leader changed) + Client* leader = entity_list.GetClientByCharID(GetLeaderID()); + + if (!accepted) + { + SendLeaderMessage(leader, Chat::Red, DZ_INVITE_DECLINED, { client->GetName() }); + return; + } + + bool is_swap = !swap_name.empty(); + bool has_conflicts = IsLocked(); + + if (IsLocked()) + { + SendLeaderMessage(leader, Chat::Red, DZADD_NOT_ALLOWING); + } + else + { + has_conflicts = ProcessAddConflicts(leader, client, is_swap); + } + + // error if swapping and character was already removed before the accept + if (is_swap) + { + auto swap_member = GetMemberData(swap_name); + if (!swap_member.IsValid() || !HasDatabaseMember(swap_member.id)) + { + has_conflicts = true; + } + } + + if (has_conflicts) + { + SendLeaderMessage(leader, Chat::Red, DZ_INVITE_ERROR, { client->GetName() }); + } + else + { + SendLeaderMessage(leader, Chat::Yellow, DZ_INVITE_ACCEPTED, { client->GetName() }); + + // replay timers are optionally added to new members on join with fresh expire time + if (m_add_replay) + { + auto it = std::ranges::find_if(m_lockouts, [&](const auto& l) { return l.IsReplay(); }); + if (it != m_lockouts.end() && it->IsUUID(GetUUID()) && !client->HasDzLockout(GetName(), DzLockout::ReplayTimer)) + { + DzLockout replay_timer = *it; // copy + replay_timer.Reset(); + client->AddDzLockout(replay_timer, true); + } + } + + DynamicZoneMember add_member(client->CharacterID(), client->GetName(), DynamicZoneMemberStatus::Online); + + bool success = is_swap ? SwapMember(add_member, swap_name) : AddMember(add_member); + if (success) + { + SendLeaderMessage(leader, Chat::Yellow, DZ_ADDED, { client->GetName(), GetName() }); + } + } +} + +void DynamicZone::DzMakeLeader(Client* client, std::string leader_name) +{ + if (!client || !ConfirmLeaderCommand(client)) + { + return; + } + + if (leader_name.empty()) + { + client->MessageString(Chat::Red, DZ_LEADER_OFFLINE, leader_name.c_str()); + return; + } + + // leader can only be changed by world + SendWorldMakeLeaderRequest(client->CharacterID(), FormatName(leader_name)); +} + +void DynamicZone::DzRemovePlayer(Client* client, std::string name) +{ + if (!client || !ConfirmLeaderCommand(client)) + { + return; + } + + // live only seems to enforce min_players for requesting expeditions, no need to check here + // note: on live members removed when inside a dz instance remain "temporary" + // members for kick timer duration and still receive lockouts across zones (unimplemented) + bool removed = RemoveMember(name); + if (!removed) + { + client->MessageString(Chat::Red, DZ_NOT_MEMBER, FormatName(name).c_str()); + } + else + { + client->MessageString(Chat::Yellow, DZ_REMOVED, FormatName(name).c_str(), m_name.c_str()); + } +} + +void DynamicZone::DzQuit(Client* client) +{ + if (client) + { + RemoveMember(client->GetName()); + } +} + +void DynamicZone::DzSwapPlayer(Client* client, std::string rem_name, std::string add_name) +{ + if (!client || !ConfirmLeaderCommand(client)) + { + return; + } + + if (rem_name.empty() || !HasMember(rem_name)) + { + client->MessageString(Chat::Red, DZSWAP_CANNOT_REMOVE, FormatName(rem_name).c_str()); + return; + } + + DzAddPlayer(client, add_name, rem_name); +} + +void DynamicZone::DzPlayerList(Client* client) +{ + if (client) + { + client->MessageString(Chat::Yellow, DZ_LEADER, GetLeaderName().c_str()); + + std::vector names; + for (const auto& member : m_members) + { + names.push_back(member.name); + } + + client->MessageString(Chat::Yellow, DZ_MEMBERS, fmt::format("{}", fmt::join(names, ", ")).c_str()); + } +} + +void DynamicZone::DzKickPlayers(Client* client) +{ + if (!client || !ConfirmLeaderCommand(client)) + { + return; + } + + RemoveAllMembers(); + client->MessageString(Chat::Red, DZ_REMOVED, "Everyone", m_name.c_str()); +} + void DynamicZone::HandleWorldMessage(ServerPacket* pack) { switch (pack->opcode) { case ServerOP_DzCreated: { - auto buf = reinterpret_cast(pack->pBuffer); + auto buf = reinterpret_cast(pack->pBuffer); if (zone && !zone->IsZone(buf->origin_zone_id, buf->origin_instance_id)) { DynamicZone::CacheNewDynamicZone(pack); @@ -232,24 +663,55 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); if (zone && dz) { - dz->SendUpdatesToZoneMembers(true, true); // members silently removed - - // manually handle expeditions to remove any references before the dz is deleted - if (dz->GetType() == DynamicZoneType::Expedition) - { - auto expedition = Expedition::FindCachedExpeditionByDynamicZoneID(dz->GetID()); - if (expedition) - { - LogExpeditionsDetail("Deleting expedition [{}] from zone cache", expedition->GetID()); - zone->expedition_cache.erase(expedition->GetID()); - } - } - LogDynamicZonesDetail("Deleting dynamic zone [{}] from zone cache", buf->dz_id); + dz->SendUpdatesToZoneMembers(true, true); // members silently removed zone->dynamic_zone_cache.erase(buf->dz_id); } break; } + case ServerOP_DzAddPlayer: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (buf->is_char_online) + { + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->DzAddPlayerContinue(buf->requester_name, buf->target_name, buf->remove_name); + } + } + else if (Client* leader = entity_list.GetClientByName(buf->requester_name)) + { + std::string target_name = FormatName(buf->target_name); + leader->MessageString(Chat::Red, DZADD_NOT_ONLINE, target_name.c_str()); + leader->MessageString(Chat::Red, DZADD_INVITE_FAIL, target_name.c_str()); + } + break; + } + case ServerOP_DzMakeLeader: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto old_leader = entity_list.GetClientByCharID(buf->requester_id); + + // success flag is set by world to indicate new leader set to an online member + if (old_leader && buf->is_success) + { + old_leader->MessageString(Chat::Yellow, DZ_LEADER_NAME, buf->new_leader_name); + } + else if (old_leader) + { + uint32_t str_id = buf->is_online ? DZ_NOT_MEMBER : DZ_LEADER_OFFLINE; + old_leader->MessageString(Chat::Red, str_id, buf->new_leader_name); + } + + if (buf->is_success && !RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) + { + if (auto new_leader = entity_list.GetClientByName(buf->new_leader_name)) + { + new_leader->MessageString(Chat::Yellow, DZ_LEADER_YOU); + } + } + break; + } case ServerOP_DzAddRemoveMember: { auto buf = reinterpret_cast(pack->pBuffer); @@ -435,6 +897,73 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) } break; } + case ServerOP_DzLock: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) + { + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->SetLocked(buf->lock, false, static_cast(buf->lock_msg), buf->color); + } + } + break; + } + case ServerOP_DzReplayOnJoin: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) + { + if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + dz->SetReplayOnJoin(buf->enabled); + } + } + break; + } + case ServerOP_DzLockout: + case ServerOP_DzLockoutDuration: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) + { + if (DynamicZone* dz = DynamicZone::FindDynamicZoneByID(buf->dz_id)) + { + DzLockout lockout{ dz->GetUUID(), dz->GetName(), buf->event_name, buf->expire_time, buf->duration }; + if (pack->opcode == ServerOP_DzLockout) + { + dz->HandleLockoutUpdate(lockout, buf->remove, buf->members_only); + } + else if (pack->opcode == ServerOP_DzLockoutDuration) + { + dz->HandleLockoutDuration(lockout, buf->seconds, buf->members_only, false); + } + } + } + break; + } + case ServerOP_DzCharacterLockout: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (Client* client = entity_list.GetClientByCharID(buf->char_id)) + { + if (!buf->remove) + { + client->AddDzLockout(DzLockout{ buf->uuid, buf->expedition, buf->event, buf->expire_time, buf->duration }); + } + else if (buf->event[0] != '\0') + { + client->RemoveDzLockout(buf->expedition, buf->event); + } + else + { + client->RemoveDzLockouts(buf->expedition); + } + } + break; + } + default: + break; } } @@ -484,19 +1013,17 @@ std::unique_ptr DynamicZone::CreateMemberListPacket(bool cl return outapp; } -std::unique_ptr DynamicZone::CreateMemberListNamePacket( - const std::string& name, bool remove_name) +std::unique_ptr DynamicZone::CreateMemberNamePacket(const std::string& name, bool remove) { constexpr uint32_t outsize = sizeof(DynamicZoneMemberListName_Struct); auto outapp = std::make_unique(OP_DzMemberListName, outsize); auto buf = reinterpret_cast(outapp->pBuffer); - buf->add_name = !remove_name; + buf->add_name = !remove; strn0cpy(buf->name, name.c_str(), sizeof(buf->name)); return outapp; } -std::unique_ptr DynamicZone::CreateMemberListStatusPacket( - const std::string& name, DynamicZoneMemberStatus status) +std::unique_ptr DynamicZone::CreateMemberStatusPacket(const std::string& name, DynamicZoneMemberStatus status) { // member list status uses member list struct with a single entry constexpr uint32_t outsize = sizeof(DynamicZoneMemberList_Struct) + sizeof(DynamicZoneMemberEntry_Struct); @@ -557,26 +1084,25 @@ void DynamicZone::SendLeaderNameToZoneMembers() if (member.id == m_leader.id && RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) { - member_client->MessageString(Chat::Yellow, DZMAKELEADER_YOU); + member_client->MessageString(Chat::Yellow, DZ_LEADER_YOU); } } } } -void DynamicZone::SendMembersExpireWarning(uint32_t minutes_remaining) +void DynamicZone::SendMembersExpireWarning(uint32_t minutes) { // expeditions warn members in all zones not just the dz - auto outapp = CreateExpireWarningPacket(minutes_remaining); + auto outapp = CreateExpireWarningPacket(minutes); for (const auto& member : GetMembers()) { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) + Client* client = entity_list.GetClientByCharID(member.id); + if (client) { - member_client->QueuePacket(outapp.get()); + client->QueuePacket(outapp.get()); // live doesn't actually send the chat message with it - member_client->MessageString(Chat::Yellow, EXPEDITION_MIN_REMAIN, - fmt::format_int(minutes_remaining).c_str()); + client->MessageString(Chat::Yellow, DZ_MINUTES_REMAIN, fmt::format_int(minutes).c_str()); } } } @@ -595,30 +1121,30 @@ void DynamicZone::SendMemberListToZoneMembers() } } -void DynamicZone::SendMemberListNameToZoneMembers(const std::string& char_name, bool remove) +void DynamicZone::SendMemberNameToZoneMembers(const std::string& char_name, bool remove) { - auto outapp_member_name = CreateMemberListNamePacket(char_name, remove); + auto outapp = CreateMemberNamePacket(char_name, remove); for (const auto& member : m_members) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { - member_client->QueuePacket(outapp_member_name.get()); + member_client->QueuePacket(outapp.get()); } } } -void DynamicZone::SendMemberListStatusToZoneMembers(const DynamicZoneMember& update_member) +void DynamicZone::SendMemberStatusToZoneMembers(const DynamicZoneMember& update) { - auto outapp_member_status = CreateMemberListStatusPacket(update_member.name, update_member.status); + auto outapp = CreateMemberStatusPacket(update.name, update.status); for (const auto& member : m_members) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { - member_client->QueuePacket(outapp_member_status.get()); + member_client->QueuePacket(outapp.get()); } } } @@ -676,10 +1202,9 @@ void DynamicZone::SendUpdatesToZoneMembers(bool removing_all, bool silent) client->QueuePacket(outapp_members.get()); } - // callback to the dz system so it can perform any messages or set client data - if (m_on_client_addremove) + if (m_type == DynamicZoneType::Expedition && removing_all && !silent) { - m_on_client_addremove(client, removing_all, silent); + client->MessageString(Chat::Yellow, DZ_REMOVED, client->GetCleanName(), GetName().c_str()); } } } @@ -706,11 +1231,7 @@ void DynamicZone::ProcessMemberAddRemove(const DynamicZoneMember& member, bool r { // sending clear info also clears member list for removed members client->QueuePacket(CreateInfoPacket(removed).get()); - } - - if (m_on_client_addremove) - { - m_on_client_addremove(client, removed, false); + client->MessageString(Chat::Yellow, removed ? DZ_REMOVED : DZ_ADDED, client->GetCleanName(), GetName().c_str()); } } @@ -720,18 +1241,18 @@ void DynamicZone::ProcessMemberAddRemove(const DynamicZoneMember& member, bool r if (!removed) { SendMemberListToZoneMembers(); } else { - SendMemberListNameToZoneMembers(member.name, true); + SendMemberNameToZoneMembers(member.name, true); } } } -void DynamicZone::ProcessRemoveAllMembers(bool silent) +void DynamicZone::ProcessRemoveAllMembers() { - SendUpdatesToZoneMembers(true, silent); - DynamicZoneBase::ProcessRemoveAllMembers(silent); + SendUpdatesToZoneMembers(true, false); + DynamicZoneBase::ProcessRemoveAllMembers(); } -void DynamicZone::DoAsyncZoneMemberUpdates() +void DynamicZone::UpdateMembers() { // gets member statuses from world and performs zone member updates on reply // if we've already received member statuses we can just update immediately @@ -750,16 +1271,16 @@ void DynamicZone::DoAsyncZoneMemberUpdates() worldserver.SendPacket(pack.get()); } -bool DynamicZone::ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status) +bool DynamicZone::ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status) { - bool changed = DynamicZoneBase::ProcessMemberStatusChange(member_id, status); + bool changed = DynamicZoneBase::ProcessMemberStatusChange(character_id, status); if (changed && m_type == DynamicZoneType::Expedition) { - auto member = GetMemberData(member_id); + auto member = GetMemberData(character_id); if (member.IsValid()) { - SendMemberListStatusToZoneMembers(member); + SendMemberStatusToZoneMembers(member); } } @@ -795,10 +1316,22 @@ bool DynamicZone::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint // expeditions may prevent looting based on client's lockouts if (GetType() == DynamicZoneType::Expedition) { - auto expedition = Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID()); - if (expedition && !expedition->CanClientLootCorpse(client, npc_type_id, entity_id)) + // entity id takes priority, falls back to checking by npc type if not set + std::string event = GetLootEvent(entity_id, DzLootEvent::Type::Entity); + if (event.empty()) { - return false; + event = GetLootEvent(npc_type_id, DzLootEvent::Type::NpcType); + } + + if (!event.empty()) + { + auto lockout = client->GetDzLockout(GetName(), event); + if (!lockout || lockout->UUID() != m_uuid) + { + // client lockout not received in this expedition, prevent looting + LogExpeditions("Character [{}] denied looting npc [{}] for [{}]", client->CharacterID(), npc_type_id, event); + return false; + } } } @@ -824,3 +1357,277 @@ void DynamicZone::MovePCInto(Client* client, bool world_verify) const worldserver.SendPacket(&pack); } } + +void DynamicZone::SetLocked(bool lock, bool update_db, DzLockMsg lock_msg, uint32_t color) +{ + DynamicZoneBase::SetLocked(lock, update_db, lock_msg, color); + + if (m_is_locked && lock_msg != DzLockMsg::None && IsCurrentZoneDz()) + { + auto msg = lock_msg == DzLockMsg::Close ? LockClose : LockBegin; + for (const auto& it : entity_list.GetClientList()) + { + if (it.second) + { + it.second->Message(color, msg); + } + } + } +} + +void DynamicZone::SaveLockouts(const std::vector& lockouts) +{ + m_lockouts = lockouts; + DynamicZoneLockoutsRepository::InsertLockouts(database, m_id, m_lockouts); +} + +static void SendWorldCharacterLockout(uint32_t char_id, const DzLockout& lockout, bool remove) +{ + uint32_t pack_size = sizeof(ServerDzCharacterLockout_Struct); + ServerPacket pack(ServerOP_DzCharacterLockout, pack_size); + auto buf = reinterpret_cast(pack.pBuffer); + buf->remove = remove; + buf->char_id = char_id; + buf->expire_time = lockout.GetExpireTime(); + buf->duration = lockout.GetDuration(); + strn0cpy(buf->uuid, lockout.UUID().c_str(), sizeof(buf->uuid)); + strn0cpy(buf->expedition, lockout.DzName().c_str(), sizeof(buf->expedition)); + strn0cpy(buf->event, lockout.Event().c_str(), sizeof(buf->event)); + worldserver.SendPacket(&pack); +} + +void DynamicZone::AddCharacterLockout( + uint32_t char_id, const std::string& expedition, const std::string& event, uint32_t seconds, const std::string& uuid) +{ + if (char_id) + { + auto lockout = DzLockout::Create(expedition, event, seconds, uuid); + CharacterExpeditionLockoutsRepository::InsertLockouts(database, char_id, { lockout }); + SendWorldCharacterLockout(char_id, lockout, false); + } +} + +void DynamicZone::AddCharacterLockout( + const std::string& char_name, const std::string& expedition, const std::string& event, uint32_t seconds, const std::string& uuid) +{ + if (!char_name.empty()) + { + uint32_t char_id = database.GetCharacterID(char_name); + AddCharacterLockout(char_id, expedition, event, seconds, uuid); + } +} + +bool DynamicZone::HasCharacterLockout(uint32_t char_id, const std::string& expedition, const std::string& event) +{ + auto lockouts = GetCharacterLockouts(char_id); + return std::any_of(lockouts.begin(), lockouts.end(), + [&](const auto& l) { return !l.IsExpired() && l.IsSame(expedition, event); }); +} + +bool DynamicZone::HasCharacterLockout(const std::string& char_name, const std::string& expedition, const std::string& event) +{ + if (!char_name.empty()) + { + return HasCharacterLockout(database.GetCharacterID(char_name), expedition, event); + } + return false; +} + +void DynamicZone::RemoveCharacterLockouts(uint32_t char_id, const std::string& expedition, const std::string& event) +{ + if (char_id) + { + std::string where = fmt::format("character_id = {}", char_id); + if (!event.empty()) + { + where += fmt::format(" AND expedition_name = '{}' AND event_name = '{}'", Strings::Escape(expedition), Strings::Escape(event)); + } + else if (!expedition.empty()) + { + where += fmt::format(" AND expedition_name = '{}'", Strings::Escape(expedition)); + } + CharacterExpeditionLockoutsRepository::DeleteWhere(database, where); + + DzLockout lockout{{}, expedition, event, 0, 0}; + SendWorldCharacterLockout(char_id, lockout, true); + } +} + +void DynamicZone::RemoveCharacterLockouts(const std::string& char_name, const std::string& expedition, const std::string& event) +{ + if (!char_name.empty()) + { + uint32_t char_id = database.GetCharacterID(char_name); + RemoveCharacterLockouts(char_id, expedition, event); + } +} + +std::vector DynamicZone::GetCharacterLockouts(uint32_t char_id) +{ + std::vector lockouts; + if (char_id == 0) + { + return lockouts; + } + + auto client = entity_list.GetClientByCharID(char_id); + if (client) + { + lockouts = client->GetDzLockouts(); + } + else + { + lockouts = CharacterExpeditionLockoutsRepository::GetLockouts(database, char_id); + } + + return lockouts; +} + +void DynamicZone::AddClientsLockout(const DzLockout& lockout) +{ + std::vector char_ids; + for (const auto& it : entity_list.GetClientList()) + { + char_ids.push_back(it.second->CharacterID()); + it.second->AddDzLockout(lockout); + } + + if (!char_ids.empty()) + { + CharacterExpeditionLockoutsRepository::InsertLockout(database, char_ids, lockout); + } +} + +void DynamicZone::HandleLockoutUpdate(const DzLockout& lockout, bool remove, bool members_only) +{ + DynamicZoneBase::HandleLockoutUpdate(lockout, remove, members_only); + + std::vector char_ids; + for (const auto& it : entity_list.GetClientList()) + { + Client* client = it.second; + if (std::ranges::any_of(m_members, [&](const auto& m) { return m.id == client->CharacterID(); })) + { + if (!remove) + { + client->AddDzLockout(lockout); + } + else + { + client->RemoveDzLockout(GetName(), lockout.Event()); + } + } + else if (!remove && IsCurrentZoneDz()) // non-member client inside the dz + { + // all clients inside the dz instance receive added lockouts to avoid exploits + // where members quit the expedition but haven't been kicked from zone yet + char_ids.push_back(client->CharacterID()); + client->AddDzLockout(lockout); + } + } + + if (!char_ids.empty()) + { + CharacterExpeditionLockoutsRepository::InsertLockout(database, char_ids, lockout); + } +} + +void DynamicZone::HandleLockoutDuration(const DzLockout& lockout, int seconds, bool members_only, bool insert_db) +{ + DynamicZoneBase::HandleLockoutDuration(lockout, seconds, members_only, insert_db); + + std::vector char_ids; + for (const auto& it : entity_list.GetClientList()) + { + Client* client = it.second; + if (std::ranges::any_of(m_members, [&](const auto& m) { return m.id == client->CharacterID(); })) + { + client->AddDzLockoutDuration(lockout, seconds, m_uuid); + } + else if (IsCurrentZoneDz()) // non-member client inside the dz + { + char_ids.push_back(client->CharacterID()); + client->AddDzLockoutDuration(lockout, seconds, m_uuid); + } + } + + if (!char_ids.empty()) // always update db for non-members in dz (call may be from another zone) + { + int secs = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); + CharacterExpeditionLockoutsRepository::AddLockoutDuration(database, char_ids, lockout, secs); + } +} + +void DynamicZone::SetLootEvent(uint32_t id, const std::string& event, DzLootEvent::Type type) +{ + if (id != 0 && IsCurrentZoneDz()) + { + LogExpeditions("Setting loot event [{}] for id [{}] type [{}]", event, id, static_cast(type)); + auto it = std::ranges::find_if(m_loot_events, [&](const auto& le) { return le.id == id && le.type == type; }); + if (it != m_loot_events.end()) + { + it->event = event; + } + else + { + m_loot_events.push_back({id, event, type}); + } + } +} + +std::string DynamicZone::GetLootEvent(uint32_t id, DzLootEvent::Type type) const +{ + std::string event_name; + if (id != 0 && IsCurrentZoneDz()) + { + auto it = std::ranges::find_if(m_loot_events, [&](const auto& le) { return le.id == id && le.type == type; }); + if (it != m_loot_events.end()) + { + event_name = it->event; + } + } + return event_name; +} + +std::unique_ptr DynamicZone::CreateInvitePacket(const std::string& inviter, const std::string& swap_name) +{ + uint32_t outsize = sizeof(ExpeditionInvite_Struct); + auto outapp = std::make_unique(OP_DzExpeditionInvite, outsize); + auto outbuf = reinterpret_cast(outapp->pBuffer); + strn0cpy(outbuf->inviter_name, inviter.c_str(), sizeof(outbuf->inviter_name)); + strn0cpy(outbuf->expedition_name, GetName().c_str(), sizeof(outbuf->expedition_name)); + strn0cpy(outbuf->swap_name, swap_name.c_str(), sizeof(outbuf->swap_name)); + outbuf->swapping = !swap_name.empty(); + outbuf->dz_zone_id = GetZoneID(); + outbuf->dz_instance_id = GetInstanceID(); + return outapp; +} + +void DynamicZone::SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name) +{ + LogExpeditions("[{}] saving invite from [{}] to [{}]", add_name, invite.inviter_name, invite.dz_id); + SendWorldPlayerInvite(invite.inviter_name, invite.swap_name, add_name, true); +} + +void DynamicZone::SendWorldPlayerInvite(const std::string& inviter, const std::string& swap_name, const std::string& add_name, bool pending) +{ + auto opcode = pending ? ServerOP_DzSaveInvite : ServerOP_DzAddPlayer; + ServerPacket pack(opcode, static_cast(sizeof(ServerDzCommand_Struct))); + auto buf = reinterpret_cast(pack.pBuffer); + buf->dz_id = GetID(); + buf->is_char_online = false; + strn0cpy(buf->requester_name, inviter.c_str(), sizeof(buf->requester_name)); + strn0cpy(buf->target_name, add_name.c_str(), sizeof(buf->target_name)); + strn0cpy(buf->remove_name, swap_name.c_str(), sizeof(buf->remove_name)); + worldserver.SendPacket(&pack); +} + +void DynamicZone::SendWorldMakeLeaderRequest(uint32_t char_id, const std::string& leader_name) +{ + ServerPacket pack(ServerOP_DzMakeLeader, static_cast(sizeof(ServerDzCommandMakeLeader_Struct))); + auto buf = reinterpret_cast(pack.pBuffer); + buf->dz_id = GetID(); + buf->requester_id = char_id; + strn0cpy(buf->new_leader_name, leader_name.c_str(), sizeof(buf->new_leader_name)); + worldserver.SendPacket(&pack); +} diff --git a/zone/dynamic_zone.h b/zone/dynamic_zone.h index 45d4e93ea..2aeaca30c 100644 --- a/zone/dynamic_zone.h +++ b/zone/dynamic_zone.h @@ -31,6 +31,15 @@ class Client; class Database; class EQApplicationPacket; class ServerPacket; +struct ExpeditionInvite; + +struct DzLootEvent +{ + enum class Type { NpcType = 0, Entity }; + uint32_t id = 0; + std::string event; + Type type = Type::NpcType; +}; class DynamicZone : public DynamicZoneBase { @@ -40,53 +49,92 @@ public: DynamicZone() = default; DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type); + static constexpr int32_t EventTimerID = 1; + static constexpr int32_t ReplayTimerID = -1; + static void CacheAllFromDatabase(); static void CacheNewDynamicZone(ServerPacket* pack); - static DynamicZone* CreateNew(DynamicZone& dz_details, const std::vector& members); - static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); + static DynamicZone* TryCreate(Client& client, DynamicZone& dzinfo, bool silent = false); + static DynamicZone* FindDynamicZoneByID(uint32_t dz_id, DynamicZoneType type = DynamicZoneType::None); + static DynamicZone* FindExpeditionByCharacter(uint32_t char_id); + static DynamicZone* FindExpeditionByZone(uint32_t zone_id, uint32_t instance_id); static void HandleWorldMessage(ServerPacket* pack); + static void AddClientsLockout(const DzLockout& lockout); + static void AddCharacterLockout(uint32_t char_id, const std::string& expedition, const std::string& event, uint32_t seconds, const std::string& uuid = {}); + static void AddCharacterLockout(const std::string& char_name, const std::string& expedition, const std::string& event, uint32_t seconds, const std::string& uuid = {}); + static bool HasCharacterLockout(uint32_t char_id, const std::string& expedition, const std::string& event); + static bool HasCharacterLockout(const std::string& char_name, const std::string& expedition, const std::string& event); + static void RemoveCharacterLockouts(uint32_t char_id, const std::string& expedition = {}, const std::string& event = {}); + static void RemoveCharacterLockouts(const std::string& char_name, const std::string& expedition = {}, const std::string& event = {}); + static std::vector GetCharacterLockouts(uint32_t char_id); + + void DzAddPlayer(Client* client, const std::string& add_name, const std::string& swap_name = {}); + void DzAddPlayerContinue(std::string inviter, std::string add_name, std::string swap_name = {}); + void DzInviteResponse(Client* client, bool accepted, const std::string& swap_name); + void DzMakeLeader(Client* client, std::string leader_name); + void DzPlayerList(Client* client); + void DzRemovePlayer(Client* client, std::string name); + void DzSwapPlayer(Client* client, std::string rem_name, std::string add_name); + void DzQuit(Client* client); + void DzKickPlayers(Client* client); + void SendWorldMakeLeaderRequest(uint32_t char_id, const std::string& leader_name); + void SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name); + void SetSecondsRemaining(uint32_t seconds_remaining) override; - void DoAsyncZoneMemberUpdates(); bool CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t entity_id); - bool IsCurrentZoneDzInstance() const; + bool IsCurrentZoneDz() const; void MovePCInto(Client* client, bool world_verify = false) const; - void RegisterOnClientAddRemove(std::function on_client_addremove); void SendClientWindowUpdate(Client* client); void SendLeaderNameToZoneMembers(); void SendMemberListToZoneMembers(); - void SendMemberListNameToZoneMembers(const std::string& char_name, bool remove); - void SendMemberListStatusToZoneMembers(const DynamicZoneMember& member); - void SendRemoveAllMembersToZoneMembers(bool silent) { ProcessRemoveAllMembers(silent); } + void SendMemberNameToZoneMembers(const std::string& char_name, bool remove); + void SendMemberStatusToZoneMembers(const DynamicZoneMember& member); + void SetLocked(bool lock, bool update_db = false, DzLockMsg lock_msg = DzLockMsg::None, uint32_t color = Chat::Yellow); + void UpdateMembers(); - std::unique_ptr CreateExpireWarningPacket(uint32_t minutes_remaining); - std::unique_ptr CreateInfoPacket(bool clear = false); - std::unique_ptr CreateLeaderNamePacket(); - std::unique_ptr CreateMemberListPacket(bool clear = false); - std::unique_ptr CreateMemberListNamePacket(const std::string& name, bool remove_name); - std::unique_ptr CreateMemberListStatusPacket(const std::string& name, DynamicZoneMemberStatus status); - -protected: - uint16_t GetCurrentInstanceID() override; - uint16_t GetCurrentZoneID() override; - Database& GetDatabase() override; - void ProcessCompassChange(const DynamicZoneLocation& location) override; - void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) override; - bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status) override; - void ProcessRemoveAllMembers(bool silent = false) override; - void ProcessSetSwitchID(int dz_switch_id) override; - bool SendServerPacket(ServerPacket* packet) override; + std::string GetLootEvent(uint32_t id, DzLootEvent::Type type) const; + void SetLootEvent(uint32_t id, const std::string& event, DzLootEvent::Type type); private: static void StartAllClientRemovalTimers(); + + uint16_t GetCurrentInstanceID() const override; + uint16_t GetCurrentZoneID() const override; + Database& GetDatabase() override; + void HandleLockoutDuration(const DzLockout& lockout, int seconds, bool members_only, bool insert_db) override; + void HandleLockoutUpdate(const DzLockout& lockout, bool remove, bool members_only) override; + void ProcessCompassChange(const DynamicZoneLocation& location) override; + void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) override; + bool ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status) override; + void ProcessRemoveAllMembers() override; + void ProcessSetSwitchID(int dz_switch_id) override; + bool SendServerPacket(ServerPacket* packet) override; + + bool ConfirmLeaderCommand(Client* client); + bool ProcessAddConflicts(Client* leader, Client* client, bool swapping); void ProcessLeaderChanged(uint32_t new_leader_id); + void SaveLockouts(const std::vector& lockouts); + void SendClientInvite(Client* client, const std::string& inviter, const std::string& swap_name); void SendCompassUpdateToZoneMembers(); + void SendLeaderMessage(Client* leader, uint16_t type, const std::string& msg); + void SendLeaderMessage(Client* leader, uint16_t type, uint32_t str_id, std::initializer_list args = {}); void SendMembersExpireWarning(uint32_t minutes); void SendUpdatesToZoneMembers(bool removing_all = false, bool silent = true); + void SendWorldPlayerInvite(const std::string& inviter, const std::string& swap_name, const std::string& add_name, bool pending = false); void SetUpdatedDuration(uint32_t seconds); + void TryAddClient(Client* add_client, const std::string& inviter, const std::string& swap_name, Client* leader = nullptr); - std::function m_on_client_addremove; + std::unique_ptr CreateExpireWarningPacket(uint32_t minutes_remaining); + std::unique_ptr CreateInfoPacket(bool clear = false); + std::unique_ptr CreateInvitePacket(const std::string& inviter, const std::string& swap_name); + std::unique_ptr CreateLeaderNamePacket(); + std::unique_ptr CreateMemberListPacket(bool clear = false); + std::unique_ptr CreateMemberNamePacket(const std::string& name, bool remove); + std::unique_ptr CreateMemberStatusPacket(const std::string& name, DynamicZoneMemberStatus status); + + std::vector m_loot_events; // only valid inside dz zone }; #endif diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 95246fd9d..97800a396 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -27,9 +27,9 @@ #include "../common/misc_functions.h" #include "dialogue_window.h" +#include "dynamic_zone.h" #include "embperl.h" #include "entity.h" -#include "expedition.h" #include "queryserv.h" #include "questmgr.h" #include "zone.h" @@ -2779,45 +2779,45 @@ void Perl__SetContentFlag(std::string flag_name, bool enabled) zone->ReloadContentFlags(); } -Expedition* Perl__get_expedition() +DynamicZone* Perl__get_expedition() { if (zone && zone->GetInstanceID() != 0) { - return Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID()); + return DynamicZone::FindExpeditionByZone(zone->GetZoneID(), zone->GetInstanceID()); } return nullptr; } -Expedition* Perl__get_expedition_by_char_id(uint32 char_id) +DynamicZone* Perl__get_expedition_by_char_id(uint32 char_id) { - return Expedition::FindCachedExpeditionByCharacterID(char_id); + return DynamicZone::FindExpeditionByCharacter(char_id); } -Expedition* Perl__get_expedition_by_dz_id(uint32 dz_id) +DynamicZone* Perl__get_expedition_by_dz_id(uint32 dz_id) { - return Expedition::FindCachedExpeditionByDynamicZoneID(dz_id); + return DynamicZone::FindDynamicZoneByID(dz_id, DynamicZoneType::Expedition); } -Expedition* Perl__get_expedition_by_zone_instance(uint32 zone_id, uint32 instance_id) +DynamicZone* Perl__get_expedition_by_zone_instance(uint32 zone_id, uint32 instance_id) { - return Expedition::FindCachedExpeditionByZoneInstance(zone_id, instance_id); + return DynamicZone::FindExpeditionByZone(zone_id, instance_id); } perl::reference Perl__get_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name) { perl::hash table; - auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id); + auto lockouts = DynamicZone::GetCharacterLockouts(char_id); - auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const ExpeditionLockoutTimer& lockout) { - return lockout.IsSameLockout(expedition_name, event_name); + auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const DzLockout& lockout) { + return lockout.IsSame(expedition_name, event_name); }); if (it != lockouts.end()) { table["remaining"] = it->GetSecondsRemaining(); - table["uuid"] = it->GetExpeditionUUID(); + table["uuid"] = it->UUID(); } return perl::reference(table); @@ -2827,23 +2827,23 @@ perl::reference Perl__get_expedition_lockouts_by_char_id(uint32 char_id) { perl::hash table; - auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id); + auto lockouts = DynamicZone::GetCharacterLockouts(char_id); for (const auto& lockout : lockouts) { - if (!table.exists(lockout.GetExpeditionName())) + if (!table.exists(lockout.DzName())) { - table[lockout.GetExpeditionName()] = perl::reference(perl::hash()); + table[lockout.DzName()] = perl::reference(perl::hash()); } - perl::hash expedition_table = table[lockout.GetExpeditionName()]; - if (!expedition_table.exists(lockout.GetEventName())) + perl::hash expedition_table = table[lockout.DzName()]; + if (!expedition_table.exists(lockout.Event())) { - expedition_table[lockout.GetEventName()] = perl::reference(perl::hash()); + expedition_table[lockout.Event()] = perl::reference(perl::hash()); } - perl::hash event_table = expedition_table[lockout.GetEventName()]; + perl::hash event_table = expedition_table[lockout.Event()]; event_table["remaining"] = lockout.GetSecondsRemaining(); - event_table["uuid"] = lockout.GetExpeditionUUID(); + event_table["uuid"] = lockout.UUID(); } return perl::reference(table); @@ -2853,18 +2853,18 @@ perl::reference Perl__get_expedition_lockouts_by_char_id(uint32 char_id, std::st { perl::hash table; - auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id); + auto lockouts = DynamicZone::GetCharacterLockouts(char_id); for (const auto& lockout : lockouts) { - if (lockout.GetExpeditionName() == expedition_name) + if (lockout.DzName() == expedition_name) { - if (!table.exists(lockout.GetEventName())) + if (!table.exists(lockout.Event())) { - table[lockout.GetEventName()] = perl::reference(perl::hash()); + table[lockout.Event()] = perl::reference(perl::hash()); } - perl::hash event_table = table[lockout.GetEventName()]; + perl::hash event_table = table[lockout.Event()]; event_table["remaining"] = lockout.GetSecondsRemaining(); - event_table["uuid"] = lockout.GetExpeditionUUID(); + event_table["uuid"] = lockout.UUID(); } } @@ -2873,39 +2873,39 @@ perl::reference Perl__get_expedition_lockouts_by_char_id(uint32 char_id, std::st void Perl__add_expedition_lockout_all_clients(std::string expedition_name, std::string event_name, uint32 seconds) { - auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds); - Expedition::AddLockoutClients(lockout); + auto lockout = DzLockout::Create(expedition_name, event_name, seconds); + DynamicZone::AddClientsLockout(lockout); } void Perl__add_expedition_lockout_all_clients(std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) { - auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid); - Expedition::AddLockoutClients(lockout); + auto lockout = DzLockout::Create(expedition_name, event_name, seconds, uuid); + DynamicZone::AddClientsLockout(lockout); } void Perl__add_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name, uint32 seconds) { - Expedition::AddLockoutByCharacterID(char_id, expedition_name, event_name, seconds); + DynamicZone::AddCharacterLockout(char_id, expedition_name, event_name, seconds); } void Perl__add_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) { - Expedition::AddLockoutByCharacterID(char_id, expedition_name, event_name, seconds, uuid); + DynamicZone::AddCharacterLockout(char_id, expedition_name, event_name, seconds, uuid); } void Perl__remove_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name) { - Expedition::RemoveLockoutsByCharacterID(char_id, expedition_name, event_name); + DynamicZone::RemoveCharacterLockouts(char_id, expedition_name, event_name); } void Perl__remove_all_expedition_lockouts_by_char_id(uint32 char_id) { - Expedition::RemoveLockoutsByCharacterID(char_id); + DynamicZone::RemoveCharacterLockouts(char_id); } void Perl__remove_all_expedition_lockouts_by_char_id(uint32 char_id, std::string expedition_name) { - Expedition::RemoveLockoutsByCharacterID(char_id, expedition_name); + DynamicZone::RemoveCharacterLockouts(char_id, expedition_name); } EQ::ItemInstance* Perl__createitem(uint32 item_id) diff --git a/zone/entity.cpp b/zone/entity.cpp index bbc20fab0..98d237de5 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5506,7 +5506,7 @@ void EntityList::ExpeditionWarning(uint32 minutes_left) auto it = client_list.begin(); while (it != client_list.end()) { - it->second->MessageString(Chat::Yellow, EXPEDITION_MIN_REMAIN, itoa((int)minutes_left)); + it->second->MessageString(Chat::Yellow, DZ_MINUTES_REMAIN, itoa((int)minutes_left)); it->second->QueuePacket(outapp); ++it; } diff --git a/zone/expedition.cpp b/zone/expedition.cpp deleted file mode 100644 index 436601126..000000000 --- a/zone/expedition.cpp +++ /dev/null @@ -1,1502 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "expedition.h" -#include "expedition_database.h" -#include "expedition_request.h" -#include "client.h" -#include "string_ids.h" -#include "worldserver.h" -#include "zonedb.h" -#include "../common/repositories/expedition_lockouts_repository.h" - -extern WorldServer worldserver; -extern Zone* zone; - -// message string 8271 (not in emu clients) -const char* const DZ_YOU_NOT_ASSIGNED = "You could not use this command because you are not currently assigned to a dynamic zone."; -// lockout warnings were added to live in March 11 2020 patch -const char* const DZADD_INVITE_WARNING = "Warning! You will be given replay timers for the following events if you enter %s:"; -const char* const DZADD_INVITE_WARNING_TIMER = "%s - %sD:%sH:%sM"; -// various expeditions re-use these strings when locking -constexpr char LOCK_CLOSE[] = "Your expedition is nearing its close. You cannot bring any additional people into your expedition at this time."; -constexpr char LOCK_BEGIN[] = "The trial has begun. You cannot bring any additional people into your expedition at this time."; - -const int32_t Expedition::REPLAY_TIMER_ID = -1; -const int32_t Expedition::EVENT_TIMER_ID = 1; - -Expedition::Expedition(DynamicZone* dz) : - m_dynamic_zone(dz) -{ - assert(m_dynamic_zone != nullptr); -} - -Expedition::Expedition(DynamicZone* dz, uint32_t id, uint32_t dz_id) : - m_dynamic_zone(dz), - m_id(id), - m_dynamic_zone_id(dz_id) -{ - assert(m_dynamic_zone != nullptr); // dz must remain valid for lifetime of expedition -} - -void Expedition::LoadRepositoryResult(const ExpeditionsRepository::Expeditions& entry) -{ - m_id = entry.id; - m_dynamic_zone_id = entry.dynamic_zone_id; - m_add_replay_on_join = entry.add_replay_on_join; - m_is_locked = entry.is_locked; -} - -void Expedition::RegisterDynamicZoneCallbacks() -{ - uint32_t expedition_id = GetID(); - - GetDynamicZone()->RegisterOnClientAddRemove( - [expedition_id](Client* client, bool removed, bool silent) { - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) { - expedition->OnClientAddRemove(client, removed, silent); - } - }); -} - -Expedition* Expedition::TryCreate(Client* requester, DynamicZone& dz_request, bool disable_messages) -{ - if (!requester || !zone) - { - return nullptr; - } - - ExpeditionRequest request{ dz_request, disable_messages }; - - // request parses leader, members list, and lockouts while validating - if (!request.Validate(requester)) - { - LogExpeditionsDetail("[{}] request by [{}] denied", request.GetExpeditionName(), requester->GetName()); - return nullptr; - } - - dz_request.SetLeader({ request.GetLeaderID(), request.GetLeaderName(), DynamicZoneMemberStatus::Online }); - - auto dz = DynamicZone::CreateNew(dz_request, request.GetMembers()); - if (!dz) - { - // live uses this message when trying to enter an instance that isn't ready - // we can use it as the client error message if instance creation fails - requester->MessageString(Chat::Red, DZ_PREVENT_ENTERING); - LogExpeditions("Failed to create a dynamic zone instance for expedition"); - return nullptr; - } - - // unique expedition ids are created from database via auto-increment column - auto expedition_id = ExpeditionDatabase::InsertExpedition(dz->GetID()); - - if (expedition_id) - { - auto expedition = std::make_unique(dz, expedition_id, dz->GetID()); - - LogExpeditions( - "Created [{}] [{}] dz: [{}] instance id: [{}] leader: [{}] minplayers: [{}] maxplayers: [{}]", - expedition->GetID(), - expedition->GetName(), - dz->GetID(), - dz->GetInstanceID(), - expedition->GetLeaderName(), - dz->GetMinPlayers(), - dz->GetMaxPlayers() - ); - - expedition->SaveLockouts(request); - expedition->RegisterDynamicZoneCallbacks(); - - auto inserted = zone->expedition_cache.emplace(expedition_id, std::move(expedition)); - - inserted.first->second->SendWorldExpeditionUpdate(ServerOP_ExpeditionCreate); // cache in other zones - inserted.first->second->SendLeaderMessage(request.GetLeaderClient(), - Chat::System, EXPEDITION_AVAILABLE, { request.GetExpeditionName() }); - - if (!request.GetNotAllAddedMessage().empty()) - { - Client::SendCrossZoneMessage(request.GetLeaderClient(), request.GetLeaderName(), - Chat::System, request.GetNotAllAddedMessage()); - } - - dz->DoAsyncZoneMemberUpdates(); - - return inserted.first->second.get(); - } - - return nullptr; -} - -void Expedition::CacheExpeditions( - std::vector&& expedition_entries) -{ - if (!zone) - { - return; - } - - // bulk load expedition internal lockouts before caching - std::vector expedition_ids; - for (const auto& entry : expedition_entries) - { - expedition_ids.emplace_back(entry.id); - } - - // todo: lockouts need be moved to dynamic zone - auto expedition_lockouts = ExpeditionLockoutsRepository::GetWithTimestamp(database, expedition_ids); - - for (auto& entry : expedition_entries) - { - // the expedition's dynamic zone and members should already be in cache - auto dynamic_zone = DynamicZone::FindDynamicZoneByID(entry.dynamic_zone_id); - if (!dynamic_zone) - { - LogExpeditions("[Warning] Expedition [{}] dz [{}] not found during caching", entry.id, entry.dynamic_zone_id); - continue; - } - - auto expedition = std::make_unique(dynamic_zone); - expedition->LoadRepositoryResult(entry); - expedition->RegisterDynamicZoneCallbacks(); - - 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)); - } - } - - zone->expedition_cache.emplace(entry.id, std::move(expedition)); - dynamic_zone->DoAsyncZoneMemberUpdates(); - } -} - -void Expedition::CacheFromDatabase(uint32_t expedition_id) -{ - if (zone && expedition_id != 0) - { - BenchTimer benchmark; - - auto expedition = ExpeditionsRepository::GetWhere(database, fmt::format("id = {}", expedition_id)); - CacheExpeditions({ std::move(expedition) }); - - LogExpeditions("Caching new expedition [{}] took [{}s]", expedition_id, benchmark.elapsed()); - } -} - -bool Expedition::CacheAllFromDatabase() -{ - if (!zone) - { - return false; - } - - BenchTimer benchmark; - - auto expeditions = ExpeditionsRepository::All(database); - zone->expedition_cache.clear(); - zone->expedition_cache.reserve(expeditions.size()); - - CacheExpeditions(std::move(expeditions)); - - LogInfo("Loaded [{}] expedition(s)", Strings::Commify(zone->expedition_cache.size())); - - LogExpeditions("Caching [{}] expedition(s) took [{}s]", zone->expedition_cache.size(), benchmark.elapsed()); - - return true; -} - -void Expedition::SaveLockouts(ExpeditionRequest& request) -{ - m_lockouts = request.GetLockouts(); - ExpeditionDatabase::InsertLockouts(m_id, m_lockouts); -} - -Expedition* Expedition::FindCachedExpeditionByCharacterID(uint32_t character_id) -{ - if (zone) - { - for (const auto& expedition : zone->expedition_cache) - { - auto expedition_dz = expedition.second->GetDynamicZone(); - if (expedition_dz && expedition_dz->HasMember(character_id)) - { - return expedition.second.get(); - } - } - } - return nullptr; -} - -Expedition* Expedition::FindCachedExpeditionByCharacterName(const std::string& char_name) -{ - if (zone) - { - for (const auto& expedition : zone->expedition_cache) - { - auto expedition_dz = expedition.second->GetDynamicZone(); - if (expedition_dz && expedition_dz->HasMember(char_name)) - { - return expedition.second.get(); - } - } - } - return nullptr; -} - -Expedition* Expedition::FindCachedExpeditionByDynamicZoneID(uint32_t dz_id) -{ - if (zone && dz_id != 0) - { - for (const auto& cached_expedition : zone->expedition_cache) - { - if (cached_expedition.second->GetDynamicZoneID() == dz_id) - { - return cached_expedition.second.get(); - } - } - } - return nullptr; -} - -Expedition* Expedition::FindCachedExpeditionByID(uint32_t expedition_id) -{ - if (zone && expedition_id) - { - auto expedition_cache_iter = zone->expedition_cache.find(expedition_id); - if (expedition_cache_iter != zone->expedition_cache.end()) - { - return expedition_cache_iter->second.get(); - } - } - return nullptr; -} - -Expedition* Expedition::FindCachedExpeditionByZoneInstance(uint32_t zone_id, uint32_t instance_id) -{ - if (zone && zone_id != 0 && instance_id != 0) - { - for (const auto& cached_expedition : zone->expedition_cache) - { - auto expedition_dz = cached_expedition.second->GetDynamicZone(); - if (expedition_dz && expedition_dz->IsSameDz(zone_id, instance_id)) - { - return cached_expedition.second.get(); - } - } - } - return nullptr; -} - -bool Expedition::HasLockout(const std::string& event_name) -{ - return (m_lockouts.find(event_name) != m_lockouts.end()); -} - -bool Expedition::HasReplayLockout() -{ - return HasLockout(DZ_REPLAY_TIMER_NAME); -} - -void Expedition::SetReplayLockoutOnMemberJoin(bool add_on_join, bool update_db) -{ - m_add_replay_on_join = add_on_join; - - if (update_db) - { - ExpeditionDatabase::UpdateReplayLockoutOnJoin(m_id, add_on_join); - SendWorldSettingChanged(ServerOP_ExpeditionReplayOnJoin, m_add_replay_on_join); - } -} - -void Expedition::AddReplayLockout(uint32_t seconds) -{ - AddLockout(DZ_REPLAY_TIMER_NAME, seconds); -} - -void Expedition::AddLockout(const std::string& event_name, uint32_t seconds) -{ - auto lockout = ExpeditionLockoutTimer::CreateLockout(GetName(), - event_name, seconds, GetDynamicZone()->GetUUID()); - AddLockout(lockout); -} - -void Expedition::AddLockout(const ExpeditionLockoutTimer& lockout, bool members_only) -{ - if (!members_only) - { - ExpeditionDatabase::InsertLockout(m_id, lockout); - } - ExpeditionDatabase::InsertMembersLockout(GetDynamicZone()->GetMembers(), lockout); - - ProcessLockoutUpdate(lockout, false, members_only); - SendWorldLockoutUpdate(lockout, false, members_only); -} - -void Expedition::AddLockoutDuration(const std::string& event_name, int seconds, bool members_only) -{ - // lockout timers use unsigned durations to define intent but we may need - // to insert a new lockout while still supporting timer reductions - auto lockout = ExpeditionLockoutTimer::CreateLockout(GetName(), - event_name, std::max(0, seconds), GetDynamicZone()->GetUUID()); - - if (!members_only) - { - auto it = m_lockouts.find(event_name); - if (it != m_lockouts.end()) - { - it->second.AddLockoutTime(seconds); - ExpeditionDatabase::InsertLockout(m_id, it->second); // replaces current one - } - else - { - ExpeditionDatabase::InsertLockout(m_id, lockout); - } - } - - // processing lockout duration applies multiplier again in client methods, - // update database with modified value now but pass original on - int modified_seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); - ExpeditionDatabase::AddLockoutDuration(GetDynamicZone()->GetMembers(), lockout, modified_seconds); - - ProcessLockoutDuration(lockout, seconds, members_only); - SendWorldLockoutDuration(lockout, seconds, members_only); -} - -void Expedition::AddReplayLockoutDuration(int seconds, bool members_only) -{ - AddLockoutDuration(DZ_REPLAY_TIMER_NAME, seconds, members_only); -} - -void Expedition::UpdateLockoutDuration( - const std::string& event_name, uint32_t seconds, bool members_only) -{ - // some live expeditions update existing lockout timers during progression - auto it = m_lockouts.find(event_name); - if (it != m_lockouts.end()) - { - seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); - - uint64_t expire_time = it->second.GetStartTime() + seconds; - AddLockout({ GetDynamicZone()->GetUUID(), GetName(), event_name, expire_time, seconds }, members_only); - } -} - -void Expedition::RemoveLockout(const std::string& event_name) -{ - ExpeditionDatabase::DeleteLockout(m_id, event_name); - ExpeditionDatabase::DeleteMembersLockout(GetDynamicZone()->GetMembers(), GetName(), event_name); - - ExpeditionLockoutTimer lockout{GetDynamicZone()->GetUUID(), GetName(), event_name, 0, 0}; - ProcessLockoutUpdate(lockout, true); - SendWorldLockoutUpdate(lockout, true); -} - -void Expedition::SendClientExpeditionInvite( - Client* client, const std::string& inviter_name, const std::string& swap_remove_name) -{ - if (!client) { - return; - } - - LogExpeditionsDetail( - "Sending expedition [{}] invite to player [{}] inviter [{}] swap name [{}]", - m_id, client->GetName(), inviter_name, swap_remove_name - ); - - client->SetPendingExpeditionInvite({ m_id, inviter_name, swap_remove_name }); - - client->MessageString(Chat::System, EXPEDITION_ASKED_TO_JOIN, - GetLeaderName().c_str(), GetName().c_str()); - - // live (as of March 11 2020 patch) sends warnings for lockouts added - // during current expedition that client would receive on entering dz - bool warned = false; - for (const auto& lockout_iter : m_lockouts) - { - // live doesn't issue a warning for the dz's replay timer - const ExpeditionLockoutTimer& lockout = lockout_iter.second; - if (!lockout.IsReplayTimer() && !lockout.IsExpired() && - lockout.IsFromExpedition(GetDynamicZone()->GetUUID()) && - !client->HasExpeditionLockout(GetName(), lockout.GetEventName())) - { - if (!warned) - { - client->Message(Chat::System, DZADD_INVITE_WARNING, GetName().c_str()); - warned = true; - } - - auto time_remaining = lockout.GetDaysHoursMinutesRemaining(); - client->Message( - Chat::System, DZADD_INVITE_WARNING_TIMER, - lockout.GetEventName().c_str(), - time_remaining.days.c_str(), - time_remaining.hours.c_str(), - time_remaining.mins.c_str() - ); - } - } - - auto outapp = CreateInvitePacket(inviter_name, swap_remove_name); - client->QueuePacket(outapp.get()); -} - -void Expedition::SendLeaderMessage( - Client* leader_client, uint16_t chat_type, uint32_t string_id, const std::initializer_list& args) -{ - Client::SendCrossZoneMessageString(leader_client, GetLeaderName(), chat_type, string_id, args); -} - -bool Expedition::ProcessAddConflicts(Client* leader_client, Client* add_client, bool swapping) -{ - if (!add_client) // a null leader_client is handled by SendLeaderMessage fallback - { - return true; - } - - bool has_conflict = false; - - if (GetDynamicZone()->IsCurrentZoneDzInstance()) - { - SendLeaderMessage(leader_client, Chat::Red, DZADD_LEAVE_ZONE_FIRST, { add_client->GetName() }); - has_conflict = true; - } - - auto expedition_id = add_client->GetExpeditionID(); - if (expedition_id) - { - auto string_id = (expedition_id == GetID()) ? DZADD_ALREADY_PART : DZADD_ALREADY_ASSIGNED; - SendLeaderMessage(leader_client, Chat::Red, string_id, { add_client->GetName() }); - has_conflict = true; - } - - // check any extra event lockouts for this expedition that the client has and expedition doesn't - auto client_lockouts = add_client->GetExpeditionLockouts(GetName()); - for (const auto& client_lockout : client_lockouts) - { - if (client_lockout.IsReplayTimer()) - { - // client with a replay lockout is allowed only if the replay timer was from this expedition - if (client_lockout.GetExpeditionUUID() != GetDynamicZone()->GetUUID()) - { - has_conflict = true; - - auto time_remaining = client_lockout.GetDaysHoursMinutesRemaining(); - SendLeaderMessage(leader_client, Chat::Red, DZADD_REPLAY_TIMER, { - add_client->GetName(), - time_remaining.days, - time_remaining.hours, - time_remaining.mins - }); - } - } - else - { - bool is_missing_lockout = (m_lockouts.find(client_lockout.GetEventName()) == m_lockouts.end()); - if (is_missing_lockout) - { - has_conflict = true; - - auto time_remaining = client_lockout.GetDaysHoursMinutesRemaining(); - SendLeaderMessage(leader_client, Chat::Red, DZADD_EVENT_TIMER, { - add_client->GetName(), - client_lockout.GetEventName(), - time_remaining.days, - time_remaining.hours, - time_remaining.mins, - client_lockout.GetEventName() - }); - } - } - } - - // member swapping integrity is handled by invite response - if (!swapping) - { - auto member_count = GetDynamicZone()->GetDatabaseMemberCount(); - if (member_count == 0) - { - has_conflict = true; - } - else if (member_count >= GetDynamicZone()->GetMaxPlayers()) - { - SendLeaderMessage(leader_client, Chat::Red, DZADD_EXCEED_MAX, { - fmt::format_int(GetDynamicZone()->GetMaxPlayers()).str() }); - has_conflict = true; - } - } - - auto invite_id = add_client->GetPendingExpeditionInviteID(); - if (invite_id) - { - auto string_id = (invite_id == GetID()) ? DZADD_PENDING : DZADD_PENDING_OTHER; - SendLeaderMessage(leader_client, Chat::Red, string_id, { add_client->GetName() }); - has_conflict = true; - } - - return has_conflict; -} - -void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std::string& swap_remove_name) -{ - if (!add_client) - { - return; - } - - LogExpeditionsDetail("Invite response by [{}] accepted [{}] swap_name [{}]", - add_client->GetName(), accepted, swap_remove_name); - - // a null leader_client is handled by SendLeaderMessage fallbacks - // note current leader receives invite reply messages (if leader changed) - Client* leader_client = entity_list.GetClientByCharID(GetLeaderID()); - - if (!accepted) - { - SendLeaderMessage(leader_client, Chat::Red, EXPEDITION_INVITE_DECLINED, { add_client->GetName() }); - return; - } - - bool was_swap_invite = !swap_remove_name.empty(); - bool has_conflicts = m_is_locked; - - if (m_is_locked) - { - SendLeaderMessage(leader_client, Chat::Red, DZADD_NOT_ALLOWING); - } - else - { - has_conflicts = ProcessAddConflicts(leader_client, add_client, was_swap_invite); - } - - // error if swapping and character was already removed before the accept - if (was_swap_invite) - { - auto swap_member = GetDynamicZone()->GetMemberData(swap_remove_name); - if (!swap_member.IsValid() || !GetDynamicZone()->HasDatabaseMember(swap_member.id)) - { - has_conflicts = true; - } - } - - if (has_conflicts) - { - SendLeaderMessage(leader_client, Chat::Red, EXPEDITION_INVITE_ERROR, { add_client->GetName() }); - } - else - { - SendLeaderMessage(leader_client, Chat::Yellow, EXPEDITION_INVITE_ACCEPTED, { add_client->GetName() }); - - // replay timers are optionally added to new members on join with fresh expire time - if (m_add_replay_on_join) - { - auto replay_lockout = m_lockouts.find(DZ_REPLAY_TIMER_NAME); - if (replay_lockout != m_lockouts.end() && - replay_lockout->second.IsFromExpedition(GetDynamicZone()->GetUUID()) && - !add_client->HasExpeditionLockout(GetName(), DZ_REPLAY_TIMER_NAME)) - { - ExpeditionLockoutTimer replay_timer = replay_lockout->second; // copy - replay_timer.Reset(); - add_client->AddExpeditionLockout(replay_timer, true); - } - } - - auto status = DynamicZoneMemberStatus::Online; - DynamicZoneMember add_member{ add_client->CharacterID(), add_client->GetName(), status }; - - bool success = false; - if (was_swap_invite) { - success = GetDynamicZone()->SwapMember(add_member, swap_remove_name); - } else { - success = GetDynamicZone()->AddMember(add_member); - } - - if (success) - { - SendLeaderMessage(leader_client, Chat::Yellow, EXPEDITION_MEMBER_ADDED, - { add_client->GetName(), GetName().c_str() }); - } - } -} - -bool Expedition::ConfirmLeaderCommand(Client* requester) -{ - if (!requester) - { - return false; - } - - if (!GetLeader().IsValid()) - { - requester->MessageString(Chat::Red, UNABLE_RETRIEVE_LEADER); // unconfirmed message - return false; - } - - if (GetLeaderID() != requester->CharacterID()) - { - requester->MessageString(Chat::System, EXPEDITION_NOT_LEADER, GetLeaderName().c_str()); - return false; - } - - return true; -} - -void Expedition::TryAddClient( - Client* add_client, const std::string& inviter_name, const std::string& swap_remove_name, - Client* leader_client) -{ - if (!add_client) - { - return; - } - - LogExpeditionsDetail( - "Add player request for expedition [{}] by inviter [{}] add name [{}] swap name [{}]", - m_id, inviter_name, add_client->GetName(), swap_remove_name - ); - - // null leader client handled by ProcessAddConflicts/SendLeaderMessage fallbacks - if (!leader_client) - { - leader_client = entity_list.GetClientByName(inviter_name.c_str()); - } - - bool has_conflicts = ProcessAddConflicts(leader_client, add_client, !swap_remove_name.empty()); - if (!has_conflicts) - { - // live uses the original unsanitized input string in invite messages - uint32_t string_id = swap_remove_name.empty() ? DZADD_INVITE : DZSWAP_INVITE; - SendLeaderMessage(leader_client, Chat::Yellow, string_id, { add_client->GetName() }); - SendClientExpeditionInvite(add_client, inviter_name.c_str(), swap_remove_name); - } - else if (swap_remove_name.empty()) // swap command doesn't result in this message - { - SendLeaderMessage(leader_client, Chat::Red, DZADD_INVITE_FAIL, { add_client->GetName() }); - } -} - -void Expedition::DzAddPlayer( - Client* requester, const std::string& add_char_name, const std::string& swap_remove_name) -{ - if (!requester || !ConfirmLeaderCommand(requester)) - { - return; - } - - bool invite_failed = false; - - if (m_is_locked) - { - requester->MessageString(Chat::Red, DZADD_NOT_ALLOWING); - invite_failed = true; - } - else if (add_char_name.empty()) - { - requester->MessageString(Chat::Red, DZADD_NOT_ONLINE, add_char_name.c_str()); - invite_failed = true; - } - else - { - auto member_data = GetDynamicZone()->GetMemberData(add_char_name); - if (member_data.IsValid()) - { - // live prioritizes offline message before already a member message - if (member_data.status == DynamicZoneMemberStatus::Offline) - { - requester->MessageString(Chat::Red, DZADD_NOT_ONLINE, add_char_name.c_str()); - } - else - { - requester->MessageString(Chat::Red, DZADD_ALREADY_PART, add_char_name.c_str()); - } - invite_failed = true; - } - } - - if (invite_failed) - { - requester->MessageString(Chat::Red, DZADD_INVITE_FAIL, FormatName(add_char_name).c_str()); - return; - } - - Client* add_client = entity_list.GetClientByName(add_char_name.c_str()); - if (add_client) - { - // client is online in this zone - TryAddClient(add_client, requester->GetName(), swap_remove_name, requester); - } - else - { - // forward to world to check if client is online and perform cross-zone invite - SendWorldAddPlayerInvite(requester->GetName(), swap_remove_name, FormatName(add_char_name)); - } -} - -void Expedition::DzAddPlayerContinue( - std::string inviter_name, std::string add_name, std::string swap_remove_name) -{ - // continuing expedition invite from leader in another zone - Client* add_client = entity_list.GetClientByName(add_name.c_str()); - if (add_client) - { - TryAddClient(add_client, inviter_name, swap_remove_name); - } -} - -void Expedition::DzMakeLeader(Client* requester, std::string new_leader_name) -{ - if (!requester || !ConfirmLeaderCommand(requester)) - { - return; - } - - if (new_leader_name.empty()) - { - requester->MessageString(Chat::Red, DZMAKELEADER_NOT_ONLINE, new_leader_name.c_str()); - return; - } - - // leader can only be changed by world - SendWorldMakeLeaderRequest(requester->CharacterID(), FormatName(new_leader_name)); -} - -void Expedition::DzRemovePlayer(Client* requester, std::string char_name) -{ - if (!requester || !ConfirmLeaderCommand(requester)) - { - return; - } - - // live only seems to enforce min_players for requesting expeditions, no need to check here - // note: on live members removed when inside a dz instance remain "temporary" - // members for kick timer duration and still receive lockouts across zones (unimplemented) - bool removed = GetDynamicZone()->RemoveMember(char_name); - if (!removed) - { - requester->MessageString(Chat::Red, EXPEDITION_NOT_MEMBER, FormatName(char_name).c_str()); - } - else - { - requester->MessageString(Chat::Yellow, EXPEDITION_REMOVED, FormatName(char_name).c_str(), GetName().c_str()); - } -} - -void Expedition::DzQuit(Client* requester) -{ - if (requester) - { - GetDynamicZone()->RemoveMember(requester->GetName()); - } -} - -void Expedition::DzSwapPlayer( - Client* requester, std::string remove_char_name, std::string add_char_name) -{ - if (!requester || !ConfirmLeaderCommand(requester)) - { - return; - } - - if (remove_char_name.empty() || !GetDynamicZone()->HasMember(remove_char_name)) - { - requester->MessageString(Chat::Red, DZSWAP_CANNOT_REMOVE, FormatName(remove_char_name).c_str()); - return; - } - - DzAddPlayer(requester, add_char_name, remove_char_name); -} - -void Expedition::DzPlayerList(Client* requester) -{ - if (requester) - { - requester->MessageString(Chat::Yellow, EXPEDITION_LEADER, GetLeaderName().c_str()); - - std::string member_names; - for (const auto& member : GetDynamicZone()->GetMembers()) - { - fmt::format_to(std::back_inserter(member_names), "{}, ", member.name); - } - - if (member_names.size() > 1) - { - member_names.erase(member_names.length() - 2); // trailing comma and space - } - - requester->MessageString(Chat::Yellow, EXPEDITION_MEMBERS, member_names.c_str()); - } -} - -void Expedition::DzKickPlayers(Client* requester) -{ - if (!requester || !ConfirmLeaderCommand(requester)) - { - return; - } - - GetDynamicZone()->RemoveAllMembers(); - requester->MessageString(Chat::Red, EXPEDITION_REMOVED, "Everyone", GetName().c_str()); -} - -void Expedition::SetLocked( - bool lock_expedition, ExpeditionLockMessage lock_msg, bool update_db, uint32_t msg_color) -{ - m_is_locked = lock_expedition; - - if (m_is_locked && lock_msg != ExpeditionLockMessage::None && GetDynamicZone()->IsCurrentZoneDzInstance()) - { - auto msg = (lock_msg == ExpeditionLockMessage::Close) ? LOCK_CLOSE : LOCK_BEGIN; - for (const auto& client_iter : entity_list.GetClientList()) - { - if (client_iter.second) - { - client_iter.second->Message(msg_color, msg); - } - } - } - - if (update_db) - { - ExpeditionDatabase::UpdateLockState(m_id, lock_expedition); - SendWorldSettingChanged(ServerOP_ExpeditionLockState, m_is_locked); - } -} - -void Expedition::OnClientAddRemove(Client* client, bool removed, bool silent) -{ - if (client) - { - client->SetExpeditionID(removed ? 0 : GetID()); - - if (!silent) - { - auto string_id = removed ? EXPEDITION_REMOVED : EXPEDITION_MEMBER_ADDED; - client->MessageString(Chat::Yellow, string_id, client->GetCleanName(), GetName().c_str()); - } - } -} - -void Expedition::ProcessLockoutDuration( - const ExpeditionLockoutTimer& lockout, int seconds, bool members_only) -{ - if (!members_only) - { - auto it = m_lockouts.find(lockout.GetEventName()); - if (it != m_lockouts.end()) - { - it->second.AddLockoutTime(seconds); - } - else - { - m_lockouts[lockout.GetEventName()] = lockout; - } - } - - for (const auto& member : GetDynamicZone()->GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - member_client->AddExpeditionLockoutDuration(GetName(), - lockout.GetEventName(), seconds, GetDynamicZone()->GetUUID()); - } - } - - if (GetDynamicZone()->IsCurrentZoneDzInstance()) - { - AddLockoutDurationClients(lockout, seconds, GetID()); - } -} - -void Expedition::AddLockoutDurationClients( - const ExpeditionLockoutTimer& lockout, int seconds, uint32_t exclude_id) -{ - std::vector lockout_clients; - for (const auto& client_iter : entity_list.GetClientList()) - { - Client* client = client_iter.second; - if (client && (exclude_id == 0 || client->GetExpeditionID() != exclude_id)) - { - lockout_clients.emplace_back(client->CharacterID(), client->GetName()); - client->AddExpeditionLockoutDuration(GetName(), - lockout.GetEventName(), seconds, GetDynamicZone()->GetUUID()); - } - } - - if (!lockout_clients.empty()) - { - int modified_seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); - ExpeditionDatabase::AddLockoutDuration(lockout_clients, lockout, modified_seconds); - } -} - -void Expedition::ProcessLockoutUpdate( - const ExpeditionLockoutTimer& lockout, bool remove, bool members_only) -{ - if (!members_only) - { - if (!remove) - { - m_lockouts[lockout.GetEventName()] = lockout; - } - else - { - m_lockouts.erase(lockout.GetEventName()); - } - } - - for (const auto& member : GetDynamicZone()->GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - if (!remove) - { - member_client->AddExpeditionLockout(lockout); - } - else - { - member_client->RemoveExpeditionLockout(GetName(), lockout.GetEventName()); - } - } - } - - // if this is the expedition's dz instance, all clients inside the zone need - // to receive added lockouts. this is done on live to avoid exploits where - // members leave the expedition but haven't been kicked from zone yet - if (!remove && GetDynamicZone()->IsCurrentZoneDzInstance()) - { - AddLockoutClients(lockout, GetID()); - } -} - -void Expedition::AddLockoutClients( - const ExpeditionLockoutTimer& lockout, uint32_t exclude_expedition_id) -{ - std::vector lockout_clients; - for (const auto& client_iter : entity_list.GetClientList()) - { - Client* client = client_iter.second; - if (client && (exclude_expedition_id == 0 || client->GetExpeditionID() != exclude_expedition_id)) - { - lockout_clients.emplace_back(client->CharacterID(), client->GetName()); - client->AddExpeditionLockout(lockout); - } - } - - if (!lockout_clients.empty()) - { - ExpeditionDatabase::InsertMembersLockout(lockout_clients, lockout); - } -} - -void Expedition::SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name) -{ - LogExpeditions( - "Character [{}] saving pending invite from [{}] to expedition [{}] in world", - add_name, invite.inviter_name, invite.expedition_id - ); - - SendWorldAddPlayerInvite(invite.inviter_name, invite.swap_remove_name, add_name, true); -} - -std::unique_ptr Expedition::CreateInvitePacket( - const std::string& inviter_name, const std::string& swap_remove_name) -{ - uint32_t outsize = sizeof(ExpeditionInvite_Struct); - auto outapp = std::make_unique(OP_DzExpeditionInvite, outsize); - auto outbuf = reinterpret_cast(outapp->pBuffer); - strn0cpy(outbuf->inviter_name, inviter_name.c_str(), sizeof(outbuf->inviter_name)); - strn0cpy(outbuf->expedition_name, GetName().c_str(), sizeof(outbuf->expedition_name)); - strn0cpy(outbuf->swap_name, swap_remove_name.c_str(), sizeof(outbuf->swap_name)); - outbuf->swapping = !swap_remove_name.empty(); - outbuf->dz_zone_id = GetDynamicZone()->GetZoneID(); - outbuf->dz_instance_id = GetDynamicZone()->GetInstanceID(); - return outapp; -} - -void Expedition::SendWorldExpeditionUpdate(uint16_t server_opcode) -{ - uint32_t pack_size = sizeof(ServerExpeditionID_Struct); - auto pack = std::make_unique(server_opcode, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - worldserver.SendPacket(pack.get()); -} - -void Expedition::SendWorldAddPlayerInvite( - const std::string& inviter_name, const std::string& swap_remove_name, const std::string& add_name, bool pending) -{ - auto server_opcode = pending ? ServerOP_ExpeditionSaveInvite : ServerOP_ExpeditionDzAddPlayer; - uint32_t pack_size = sizeof(ServerDzCommand_Struct); - auto pack = std::make_unique(server_opcode, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->is_char_online = false; - strn0cpy(buf->requester_name, inviter_name.c_str(), sizeof(buf->requester_name)); - strn0cpy(buf->target_name, add_name.c_str(), sizeof(buf->target_name)); - strn0cpy(buf->remove_name, swap_remove_name.c_str(), sizeof(buf->remove_name)); - worldserver.SendPacket(pack.get()); -} - -void Expedition::SendWorldLockoutDuration( - const ExpeditionLockoutTimer& lockout, int seconds, bool members_only) -{ - uint32_t pack_size = sizeof(ServerExpeditionLockout_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionLockoutDuration, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->expire_time = lockout.GetExpireTime(); - buf->duration = lockout.GetDuration(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->members_only = members_only; - buf->seconds_adjust = seconds; - strn0cpy(buf->event_name, lockout.GetEventName().c_str(), sizeof(buf->event_name)); - worldserver.SendPacket(pack.get()); -} - -void Expedition::SendWorldLockoutUpdate( - const ExpeditionLockoutTimer& lockout, bool remove, bool members_only) -{ - uint32_t pack_size = sizeof(ServerExpeditionLockout_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionLockout, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->expire_time = lockout.GetExpireTime(); - buf->duration = lockout.GetDuration(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->remove = remove; - buf->members_only = members_only; - strn0cpy(buf->event_name, lockout.GetEventName().c_str(), sizeof(buf->event_name)); - worldserver.SendPacket(pack.get()); -} - -void Expedition::SendWorldMakeLeaderRequest(uint32_t requester_id, const std::string& new_leader_name) -{ - uint32_t pack_size = sizeof(ServerDzCommandMakeLeader_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionDzMakeLeader, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->dz_id = GetDynamicZone()->GetID(); - buf->requester_id = requester_id; - strn0cpy(buf->new_leader_name, new_leader_name.c_str(), sizeof(buf->new_leader_name)); - worldserver.SendPacket(pack.get()); -} - -void Expedition::SendWorldSettingChanged(uint16_t server_opcode, bool setting_value) -{ - uint32_t pack_size = sizeof(ServerExpeditionSetting_Struct); - auto pack = std::make_unique(server_opcode, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->enabled = setting_value; - worldserver.SendPacket(pack.get()); -} - -void Expedition::SendWorldCharacterLockout( - uint32_t character_id, const ExpeditionLockoutTimer& lockout, bool remove) -{ - uint32_t pack_size = sizeof(ServerExpeditionCharacterLockout_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionCharacterLockout, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->remove = remove; - buf->character_id = character_id; - buf->expire_time = lockout.GetExpireTime(); - buf->duration = lockout.GetDuration(); - strn0cpy(buf->uuid, lockout.GetExpeditionUUID().c_str(), sizeof(buf->uuid)); - strn0cpy(buf->expedition_name, lockout.GetExpeditionName().c_str(), sizeof(buf->expedition_name)); - strn0cpy(buf->event_name, lockout.GetEventName().c_str(), sizeof(buf->event_name)); - worldserver.SendPacket(pack.get()); -} - -void Expedition::AddLockoutByCharacterID( - uint32_t character_id, const std::string& expedition_name, const std::string& event_name, - uint32_t seconds, const std::string& uuid) -{ - if (character_id) - { - auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid); - ExpeditionDatabase::InsertCharacterLockouts(character_id, { lockout }); - SendWorldCharacterLockout(character_id, lockout, false); - } -} - -void Expedition::AddLockoutByCharacterName( - const std::string& character_name, const std::string& expedition_name, const std::string& event_name, - uint32_t seconds, const std::string& uuid) -{ - if (!character_name.empty()) - { - uint32_t character_id = database.GetCharacterID(character_name); - AddLockoutByCharacterID(character_id, expedition_name, event_name, seconds, uuid); - } -} - -bool Expedition::HasLockoutByCharacterID( - uint32_t character_id, const std::string& expedition_name, const std::string& event_name) -{ - auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(character_id); - return std::any_of(lockouts.begin(), lockouts.end(), [&](const ExpeditionLockoutTimer& lockout) { - return !lockout.IsExpired() && lockout.IsSameLockout(expedition_name, event_name); - }); -} - -bool Expedition::HasLockoutByCharacterName( - const std::string& character_name, const std::string& expedition_name, const std::string& event_name) -{ - if (!character_name.empty()) - { - uint32_t character_id = database.GetCharacterID(character_name); - return HasLockoutByCharacterID(character_id, expedition_name, event_name); - } - return false; -} - -void Expedition::RemoveLockoutsByCharacterID( - uint32_t character_id, const std::string& expedition_name, const std::string& event_name) -{ - if (character_id) - { - if (!event_name.empty()) - { - ExpeditionDatabase::DeleteCharacterLockout(character_id, expedition_name, event_name); - } - else if (!expedition_name.empty()) - { - ExpeditionDatabase::DeleteAllCharacterLockouts(character_id, expedition_name); - } - else - { - ExpeditionDatabase::DeleteAllCharacterLockouts(character_id); - } - - ExpeditionLockoutTimer lockout{{}, expedition_name, event_name, 0, 0}; - SendWorldCharacterLockout(character_id, lockout, true); - } -} - -void Expedition::RemoveLockoutsByCharacterName( - const std::string& character_name, const std::string& expedition_name, const std::string& event_name) -{ - if (!character_name.empty()) - { - uint32_t character_id = database.GetCharacterID(character_name); - RemoveLockoutsByCharacterID(character_id, expedition_name, event_name); - } -} - -void Expedition::HandleWorldMessage(ServerPacket* pack) -{ - switch (pack->opcode) - { - case ServerOP_ExpeditionCreate: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - Expedition::CacheFromDatabase(buf->expedition_id); - } - break; - } - case ServerOP_ExpeditionLockout: - case ServerOP_ExpeditionLockoutDuration: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - ExpeditionLockoutTimer lockout{ expedition->GetDynamicZone()->GetUUID(), expedition->GetName(), - buf->event_name, buf->expire_time, buf->duration }; - - if (pack->opcode == ServerOP_ExpeditionLockout) - { - expedition->ProcessLockoutUpdate(lockout, buf->remove, buf->members_only); - } - else if (pack->opcode == ServerOP_ExpeditionLockoutDuration) - { - expedition->ProcessLockoutDuration(lockout, buf->seconds_adjust, buf->members_only); - } - } - } - break; - } - case ServerOP_ExpeditionLockState: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - expedition->SetLocked(buf->enabled, static_cast(buf->lock_msg)); - } - } - break; - } - case ServerOP_ExpeditionReplayOnJoin: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - expedition->SetReplayLockoutOnMemberJoin(buf->enabled); - } - } - break; - } - case ServerOP_ExpeditionDzAddPlayer: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (buf->is_char_online) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - expedition->DzAddPlayerContinue(buf->requester_name, buf->target_name, buf->remove_name); - } - } - else - { - Client* leader = entity_list.GetClientByName(buf->requester_name); - if (leader) - { - std::string target_name = FormatName(buf->target_name); - leader->MessageString(Chat::Red, DZADD_NOT_ONLINE, target_name.c_str()); - leader->MessageString(Chat::Red, DZADD_INVITE_FAIL, target_name.c_str()); - } - } - break; - } - case ServerOP_ExpeditionDzMakeLeader: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto old_leader_client = entity_list.GetClientByCharID(buf->requester_id); - auto new_leader_client = entity_list.GetClientByName(buf->new_leader_name); - - if (old_leader_client) - { - // success flag is set by world to indicate new leader set to an online member - if (buf->is_success) { - old_leader_client->MessageString(Chat::Yellow, DZMAKELEADER_NAME, buf->new_leader_name); - } else if (!buf->is_online) { - old_leader_client->MessageString(Chat::Red, DZMAKELEADER_NOT_ONLINE, buf->new_leader_name); - } else { - old_leader_client->MessageString(Chat::Red, EXPEDITION_NOT_MEMBER, buf->new_leader_name); - } - } - - if (buf->is_success && new_leader_client && !RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) - { - new_leader_client->MessageString(Chat::Yellow, DZMAKELEADER_YOU); - } - break; - } - case ServerOP_ExpeditionCharacterLockout: - { - auto buf = reinterpret_cast(pack->pBuffer); - Client* client = entity_list.GetClientByCharID(buf->character_id); - if (client) - { - if (!buf->remove) - { - client->AddExpeditionLockout(ExpeditionLockoutTimer{ - buf->uuid, buf->expedition_name, buf->event_name, buf->expire_time, buf->duration - }); - } - else if (buf->event_name[0] != '\0') - { - client->RemoveExpeditionLockout(buf->expedition_name, buf->event_name); - } - else - { - client->RemoveAllExpeditionLockouts(buf->expedition_name); - } - } - break; - } - } -} - -bool Expedition::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id) -{ - if (client && GetDynamicZone()->IsCurrentZoneDzInstance()) - { - // entity id takes priority, falls back to checking by npc type if not set - std::string event_name = GetLootEventBySpawnID(spawn_id); - if (event_name.empty()) - { - event_name = GetLootEventByNPCTypeID(npc_type_id); - } - - if (!event_name.empty()) - { - auto client_lockout = client->GetExpeditionLockout(GetName(), event_name); - if (!client_lockout || client_lockout->GetExpeditionUUID() != GetDynamicZone()->GetUUID()) - { - // client lockout not received in this expedition, prevent looting - LogExpeditions( - "Character [{}] denied looting npc [{}] spawn [{}] for lockout event [{}]", - client->CharacterID(), npc_type_id, spawn_id, event_name - ); - return false; - } - } - } - - return true; -} - -void Expedition::SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string& event_name) -{ - if (npc_type_id && GetDynamicZone()->IsCurrentZoneDzInstance()) - { - LogExpeditions("Setting loot event [{}] for npc type id [{}]", event_name, npc_type_id); - m_npc_loot_events[npc_type_id] = event_name; - } -} - -void Expedition::SetLootEventBySpawnID(uint32_t spawn_id, const std::string& event_name) -{ - if (spawn_id && GetDynamicZone()->IsCurrentZoneDzInstance()) - { - LogExpeditions("Setting loot event [{}] for entity id [{}]", event_name, spawn_id); - m_spawn_loot_events[spawn_id] = event_name; - } -} - -std::string Expedition::GetLootEventByNPCTypeID(uint32_t npc_type_id) -{ - std::string event_name; - - if (npc_type_id && GetDynamicZone()->IsCurrentZoneDzInstance()) - { - auto it = m_npc_loot_events.find(npc_type_id); - if (it != m_npc_loot_events.end()) - { - event_name = it->second; - } - } - - return event_name; -} - -std::string Expedition::GetLootEventBySpawnID(uint32_t spawn_id) -{ - std::string event_name; - - if (spawn_id && GetDynamicZone()->IsCurrentZoneDzInstance()) - { - auto it = m_spawn_loot_events.find(spawn_id); - if (it != m_spawn_loot_events.end()) - { - event_name = it->second; - } - } - - return event_name; -} - -std::vector Expedition::GetExpeditionLockoutsByCharacterID(uint32_t character_id) -{ - std::vector lockouts; - if (character_id == 0) - { - return lockouts; - } - - auto client = entity_list.GetClientByCharID(character_id); - if (client) - { - lockouts = client->GetExpeditionLockouts(); - } - else - { - lockouts = ExpeditionDatabase::LoadCharacterLockouts(character_id); - } - - return lockouts; -} - -void Expedition::SyncCharacterLockouts( - uint32_t character_id, std::vector& client_lockouts) -{ - // adds missing event lockouts to client for this expedition and updates - // client timers that are both shorter and from another expedition - BenchTimer benchmark; - - bool modified = false; - - for (const auto& lockout_iter : m_lockouts) - { - const ExpeditionLockoutTimer& lockout = lockout_iter.second; - if (lockout.IsReplayTimer() || lockout.IsExpired() || - lockout.GetExpeditionUUID() != GetDynamicZone()->GetUUID()) - { - continue; - } - - auto client_lockout_iter = std::find_if(client_lockouts.begin(), client_lockouts.end(), - [&](const ExpeditionLockoutTimer& client_lockout) { - return client_lockout.IsSameLockout(lockout); - }); - - if (client_lockout_iter == client_lockouts.end()) - { - modified = true; - client_lockouts.emplace_back(lockout); // insert missing - } - else if (client_lockout_iter->GetSecondsRemaining() < lockout.GetSecondsRemaining() && - client_lockout_iter->GetExpeditionUUID() != GetDynamicZone()->GetUUID()) - { - // only update lockout timer not uuid so loot event apis still work - modified = true; - client_lockout_iter->SetDuration(lockout.GetDuration()); - client_lockout_iter->SetExpireTime(lockout.GetExpireTime()); - } - } - - if (modified) - { - ExpeditionDatabase::InsertCharacterLockouts(character_id, client_lockouts); - } - - LogExpeditionsDetail("Syncing character lockouts with expedition took [{}] s", benchmark.elapsed()); -} diff --git a/zone/expedition.h b/zone/expedition.h deleted file mode 100644 index 1379b724d..000000000 --- a/zone/expedition.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef EXPEDITION_H -#define EXPEDITION_H - -#include "dynamic_zone.h" -#include "../common/expedition_lockout_timer.h" -#include "../common/repositories/expeditions_repository.h" -#include -#include -#include -#include -#include -#include - -class Client; -class EQApplicationPacket; -struct ExpeditionInvite; -class ExpeditionRequest; -class ServerPacket; - -extern const char* const DZ_YOU_NOT_ASSIGNED; - -enum class ExpeditionLockMessage : uint8_t -{ - None = 0, - Close, - Begin -}; - -class Expedition -{ -public: - Expedition() = delete; - Expedition(DynamicZone* dz); - Expedition(DynamicZone* dz, uint32_t id, uint32_t dz_id); - - static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, bool disable_messages); - - static void CacheFromDatabase(uint32_t expedition_id); - static bool CacheAllFromDatabase(); - static Expedition* FindCachedExpeditionByCharacterID(uint32_t character_id); - static Expedition* FindCachedExpeditionByCharacterName(const std::string& char_name); - static Expedition* FindCachedExpeditionByDynamicZoneID(uint32_t dz_id); - static Expedition* FindCachedExpeditionByID(uint32_t expedition_id); - static Expedition* FindCachedExpeditionByZoneInstance(uint32_t zone_id, uint32_t instance_id); - static std::vector GetExpeditionLockoutsByCharacterID(uint32_t character_id); - static void HandleWorldMessage(ServerPacket* pack); - static void AddLockoutByCharacterID(uint32_t character_id, const std::string& expedition_name, - const std::string& event_name, uint32_t seconds, const std::string& uuid = {}); - static void AddLockoutByCharacterName(const std::string& character_name, const std::string& expedition_name, - const std::string& event_name, uint32_t seconds, const std::string& uuid = {}); - static bool HasLockoutByCharacterID(uint32_t character_id, - const std::string& expedition_name, const std::string& event_name); - static bool HasLockoutByCharacterName(const std::string& character_name, - const std::string& expedition_name, const std::string& event_name); - static void RemoveLockoutsByCharacterID(uint32_t character_id, - const std::string& expedition_name = {}, const std::string& event_name = {}); - static void RemoveLockoutsByCharacterName(const std::string& character_name, - const std::string& expedition_name = {}, const std::string& event_name = {}); - static void AddLockoutClients(const ExpeditionLockoutTimer& lockout, uint32_t exclude_id = 0); - - uint32_t GetID() const { return m_id; } - uint32_t GetDynamicZoneID() const { return m_dynamic_zone_id; } - DynamicZone* GetDynamicZone() const { return m_dynamic_zone; } - const DynamicZoneMember& GetLeader() { return GetDynamicZone()->GetLeader(); } - uint32_t GetLeaderID() { return GetDynamicZone()->GetLeaderID(); } - const std::string& GetLeaderName() { return GetDynamicZone()->GetLeaderName(); } - const std::unordered_map& GetLockouts() const { return m_lockouts; } - const std::string& GetName() { return GetDynamicZone()->GetName(); } - void RegisterDynamicZoneCallbacks(); - - bool IsLocked() const { return m_is_locked; } - void SetLocked(bool lock_expedition, ExpeditionLockMessage lock_msg, - bool update_db = false, uint32_t msg_color = Chat::Yellow); - - void AddLockout(const std::string& event_name, uint32_t seconds); - void AddLockoutDuration(const std::string& event_name, int seconds, bool members_only = true); - void AddReplayLockout(uint32_t seconds); - void AddReplayLockoutDuration(int seconds, bool members_only = true); - bool HasLockout(const std::string& event_name); - bool HasReplayLockout(); - void RemoveLockout(const std::string& event_name); - void SetReplayLockoutOnMemberJoin(bool add_on_join, bool update_db = false); - void SyncCharacterLockouts(uint32_t character_id, std::vector& client_lockouts); - void UpdateLockoutDuration(const std::string& event_name, uint32_t seconds, bool members_only = true); - - bool CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id); - std::string GetLootEventByNPCTypeID(uint32_t npc_id); - std::string GetLootEventBySpawnID(uint32_t spawn_id); - void SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string& event_name); - void SetLootEventBySpawnID(uint32_t spawn_id, const std::string& event_name); - - void SendWorldMakeLeaderRequest(uint32_t requester_id, const std::string& new_leader_name); - void SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name); - - void DzAddPlayer(Client* requester, const std::string& add_char_name, const std::string& swap_remove_name = {}); - void DzAddPlayerContinue(std::string leader_name, std::string add_char_name, std::string swap_remove_name = {}); - void DzInviteResponse(Client* add_client, bool accepted, const std::string& swap_remove_name); - void DzMakeLeader(Client* requester, std::string new_leader_name); - void DzPlayerList(Client* requester); - void DzRemovePlayer(Client* requester, std::string remove_char_name); - void DzSwapPlayer(Client* requester, std::string remove_char_name, std::string add_char_name); - void DzQuit(Client* requester); - void DzKickPlayers(Client* requester); - - static const int32_t REPLAY_TIMER_ID; - static const int32_t EVENT_TIMER_ID; - -private: - static void CacheExpeditions(std::vector&& expeditions); - 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); - bool ConfirmLeaderCommand(Client* requester); - void OnClientAddRemove(Client* client, bool removed, bool silent); - void LoadRepositoryResult(const ExpeditionsRepository::Expeditions& entry); - bool ProcessAddConflicts(Client* leader_client, Client* add_client, bool swapping); - void ProcessLockoutDuration(const ExpeditionLockoutTimer& lockout, int seconds, bool members_only = false); - void ProcessLockoutUpdate(const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false); - void SaveLockouts(ExpeditionRequest& request); - void SendClientExpeditionInvite( - Client* client, const std::string& inviter_name, const std::string& swap_remove_name); - void SendLeaderMessage(Client* leader_client, uint16_t chat_type, uint32_t string_id, - const std::initializer_list& args = {}); - void SendWorldExpeditionUpdate(uint16_t server_opcode); - void SendWorldAddPlayerInvite(const std::string& inviter_name, const std::string& swap_remove_name, - const std::string& add_name, bool pending = false); - void SendWorldLockoutDuration( - const ExpeditionLockoutTimer& lockout, int seconds, bool members_only = false); - void SendWorldLockoutUpdate( - const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false); - void SendWorldSettingChanged(uint16_t server_opcode, bool setting_value); - void SetDynamicZone(DynamicZone&& dz); - void TryAddClient(Client* add_client, const std::string& inviter_name, - const std::string& swap_remove_name, Client* leader_client = nullptr); - - std::unique_ptr CreateInvitePacket(const std::string& inviter_name, const std::string& swap_remove_name); - - uint32_t m_id = 0; - uint32_t m_dynamic_zone_id = 0; - bool m_is_locked = false; - bool m_add_replay_on_join = true; - DynamicZone* m_dynamic_zone = nullptr; // should never be null, will exist for lifetime of expedition - 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 -}; - -#endif diff --git a/zone/expedition_database.cpp b/zone/expedition_database.cpp deleted file mode 100644 index 22d60a19d..000000000 --- a/zone/expedition_database.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "expedition_database.h" -#include "expedition.h" -#include "zonedb.h" -#include - -uint32_t ExpeditionDatabase::InsertExpedition(uint32_t dz_id) -{ - LogExpeditionsDetail("Inserting new expedition dz [{}]", dz_id); - - std::string query = fmt::format(SQL( - INSERT INTO expeditions - (dynamic_zone_id) - VALUES - ({}); - ), dz_id); - - auto results = database.QueryDatabase(query); - if (!results.Success()) - { - LogExpeditions("Failed to obtain an expedition id for dz [{}]", dz_id); - return 0; - } - - return results.LastInsertedID(); -} - -std::vector ExpeditionDatabase::LoadCharacterLockouts(uint32_t character_id) -{ - LogExpeditionsDetail("Loading character [{}] lockouts", character_id); - - std::vector lockouts; - - auto query = fmt::format(SQL( - SELECT - from_expedition_uuid, - expedition_name, - event_name, - UNIX_TIMESTAMP(expire_time), - duration - FROM character_expedition_lockouts - WHERE character_id = {} AND expire_time > NOW(); - ), character_id); - - auto results = database.QueryDatabase(query); - if (results.Success()) - { - for (auto row = results.begin(); row != results.end(); ++row) - { - lockouts.emplace_back( - row[0], // expedition_uuid - row[1], // expedition_name - row[2], // event_name - strtoull(row[3], nullptr, 10), // expire_time - static_cast(strtoul(row[4], nullptr, 10)) // duration - ); - } - } - - return lockouts; -} - -std::vector ExpeditionDatabase::LoadCharacterLockouts( - uint32_t character_id, const std::string& expedition_name) -{ - LogExpeditionsDetail("Loading character [{}] lockouts for [{}]", character_id, expedition_name); - - std::vector lockouts; - - auto query = fmt::format(SQL( - SELECT - from_expedition_uuid, - event_name, - UNIX_TIMESTAMP(expire_time), - duration - FROM character_expedition_lockouts - WHERE - character_id = {} - AND expire_time > NOW() - AND expedition_name = '{}'; - ), character_id, Strings::Escape(expedition_name)); - - auto results = database.QueryDatabase(query); - if (results.Success()) - { - for (auto row = results.begin(); row != results.end(); ++row) - { - lockouts.emplace_back( - row[0], // expedition_uuid - expedition_name, - row[1], // event_name - strtoull(row[2], nullptr, 10), // expire_time - static_cast(strtoul(row[3], nullptr, 10)) // duration - ); - } - } - - return lockouts; -} - -void ExpeditionDatabase::DeleteAllCharacterLockouts(uint32_t character_id) -{ - LogExpeditionsDetail("Deleting all character [{}] lockouts", character_id); - - if (character_id != 0) - { - std::string query = fmt::format(SQL( - DELETE FROM character_expedition_lockouts - WHERE character_id = {}; - ), character_id); - - database.QueryDatabase(query); - } -} - -void ExpeditionDatabase::DeleteAllCharacterLockouts( - uint32_t character_id, const std::string& expedition_name) -{ - LogExpeditionsDetail("Deleting all character [{}] lockouts for [{}]", character_id, expedition_name); - - if (character_id != 0 && !expedition_name.empty()) - { - std::string query = fmt::format(SQL( - DELETE FROM character_expedition_lockouts - WHERE character_id = {} AND expedition_name = '{}'; - ), character_id, Strings::Escape(expedition_name)); - - database.QueryDatabase(query); - } -} - -void ExpeditionDatabase::DeleteCharacterLockout( - uint32_t character_id, const std::string& expedition_name, const std::string& event_name) -{ - LogExpeditionsDetail( - "Deleting character [{}] lockout: [{}]:[{}]", character_id, expedition_name, event_name - ); - - auto query = fmt::format(SQL( - DELETE FROM character_expedition_lockouts - WHERE - character_id = {} - AND expedition_name = '{}' - AND event_name = '{}'; - ), character_id, Strings::Escape(expedition_name), Strings::Escape(event_name)); - - database.QueryDatabase(query); -} - -void ExpeditionDatabase::DeleteMembersLockout( - const std::vector& members, - const std::string& expedition_name, const std::string& event_name) -{ - LogExpeditionsDetail("Deleting members lockout: [{}]:[{}]", expedition_name, event_name); - - std::string query_character_ids; - for (const auto& member : members) - { - fmt::format_to(std::back_inserter(query_character_ids), "{},", member.id); - } - - if (!query_character_ids.empty()) - { - query_character_ids.pop_back(); // trailing comma - - auto query = fmt::format(SQL( - DELETE FROM character_expedition_lockouts - WHERE character_id - IN ({}) - AND expedition_name = '{}' - AND event_name = '{}'; - ), query_character_ids, Strings::Escape(expedition_name), Strings::Escape(event_name)); - - database.QueryDatabase(query); - } -} - -void ExpeditionDatabase::DeleteLockout(uint32_t expedition_id, const std::string& event_name) -{ - LogExpeditionsDetail("Deleting expedition [{}] lockout event [{}]", expedition_id, event_name); - - auto query = fmt::format(SQL( - DELETE FROM expedition_lockouts - WHERE expedition_id = {} AND event_name = '{}'; - ), expedition_id, Strings::Escape(event_name)); - - database.QueryDatabase(query); -} - -void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id, - const std::vector& lockouts) -{ - LogExpeditionsDetail("Inserting [{}] lockouts for character [{}]", lockouts.size(), character_id); - - std::string insert_values; - for (const auto& lockout : lockouts) - { - fmt::format_to(std::back_inserter(insert_values), - "({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),", - character_id, - lockout.GetExpireTime(), - lockout.GetDuration(), - lockout.GetExpeditionUUID(), - Strings::Escape(lockout.GetExpeditionName()), - Strings::Escape(lockout.GetEventName()) - ); - } - - if (!insert_values.empty()) - { - insert_values.pop_back(); // trailing comma - - auto query = fmt::format(SQL( - INSERT INTO character_expedition_lockouts - (character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name) - VALUES {} - ON DUPLICATE KEY UPDATE - from_expedition_uuid = VALUES(from_expedition_uuid), - expire_time = VALUES(expire_time), - duration = VALUES(duration); - ), insert_values); - - database.QueryDatabase(query); - } -} - -void ExpeditionDatabase::InsertMembersLockout( - const std::vector& members, const ExpeditionLockoutTimer& lockout) -{ - LogExpeditionsDetail( - "Inserting members lockout [{}]:[{}] with expire time [{}]", - lockout.GetExpeditionName(), lockout.GetEventName(), lockout.GetExpireTime() - ); - - std::string insert_values; - for (const auto& member : members) - { - fmt::format_to(std::back_inserter(insert_values), - "({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),", - member.id, - lockout.GetExpireTime(), - lockout.GetDuration(), - lockout.GetExpeditionUUID(), - Strings::Escape(lockout.GetExpeditionName()), - Strings::Escape(lockout.GetEventName()) - ); - } - - if (!insert_values.empty()) - { - insert_values.pop_back(); // trailing comma - - auto query = fmt::format(SQL( - INSERT INTO character_expedition_lockouts - (character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name) - VALUES {} - ON DUPLICATE KEY UPDATE - from_expedition_uuid = VALUES(from_expedition_uuid), - expire_time = VALUES(expire_time), - duration = VALUES(duration); - ), insert_values); - - database.QueryDatabase(query); - } -} - -void ExpeditionDatabase::InsertLockout( - uint32_t expedition_id, const ExpeditionLockoutTimer& lockout) -{ - LogExpeditionsDetail( - "Inserting expedition [{}] lockout: [{}]:[{}] expire time: [{}]", - expedition_id, lockout.GetExpeditionName(), lockout.GetEventName(), lockout.GetExpireTime() - ); - - auto query = fmt::format(SQL( - INSERT INTO expedition_lockouts - (expedition_id, from_expedition_uuid, event_name, expire_time, duration) - VALUES - ({}, '{}', '{}', FROM_UNIXTIME({}), {}) - ON DUPLICATE KEY UPDATE - from_expedition_uuid = VALUES(from_expedition_uuid), - expire_time = VALUES(expire_time), - duration = VALUES(duration); - ), - expedition_id, - lockout.GetExpeditionUUID(), - Strings::Escape(lockout.GetEventName()), - lockout.GetExpireTime(), - lockout.GetDuration() - ); - - database.QueryDatabase(query); -} - -void ExpeditionDatabase::InsertLockouts( - uint32_t expedition_id, const std::unordered_map& lockouts) -{ - LogExpeditionsDetail("Inserting expedition [{}] lockouts", expedition_id); - - std::string insert_values; - for (const auto& lockout : lockouts) - { - fmt::format_to(std::back_inserter(insert_values), - "({}, '{}', '{}', FROM_UNIXTIME({}), {}),", - expedition_id, - lockout.second.GetExpeditionUUID(), - Strings::Escape(lockout.second.GetEventName()), - lockout.second.GetExpireTime(), - lockout.second.GetDuration() - ); - } - - if (!insert_values.empty()) - { - insert_values.pop_back(); // trailing comma - - auto query = fmt::format(SQL( - INSERT INTO expedition_lockouts - (expedition_id, from_expedition_uuid, event_name, expire_time, duration) - VALUES {} - ON DUPLICATE KEY UPDATE - from_expedition_uuid = VALUES(from_expedition_uuid), - expire_time = VALUES(expire_time), - duration = VALUES(duration); - ), insert_values); - - database.QueryDatabase(query); - } -} - -void ExpeditionDatabase::UpdateLockState(uint32_t expedition_id, bool is_locked) -{ - LogExpeditionsDetail("Updating lock state [{}] for expedition [{}]", is_locked, expedition_id); - - auto query = fmt::format(SQL( - UPDATE expeditions SET is_locked = {} WHERE id = {}; - ), is_locked, expedition_id); - - database.QueryDatabase(query); -} - -void ExpeditionDatabase::UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join) -{ - LogExpeditionsDetail("Updating replay lockout on join [{}] for expedition [{}]", add_on_join, expedition_id); - - auto query = fmt::format(SQL( - UPDATE expeditions SET add_replay_on_join = {} WHERE id = {}; - ), add_on_join, expedition_id); - - database.QueryDatabase(query); -} - -void ExpeditionDatabase::AddLockoutDuration(const std::vector& members, - const ExpeditionLockoutTimer& lockout, int seconds) -{ - LogExpeditionsDetail( - "Adding duration [{}] seconds to members lockouts [{}]:[{}]", - seconds, lockout.GetExpeditionName(), lockout.GetEventName()); - - std::string insert_values; - for (const auto& member : members) - { - fmt::format_to(std::back_inserter(insert_values), - "({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),", - member.id, - lockout.GetExpireTime(), - lockout.GetDuration(), - lockout.GetExpeditionUUID(), - Strings::Escape(lockout.GetExpeditionName()), - Strings::Escape(lockout.GetEventName()) - ); - } - - if (!insert_values.empty()) - { - insert_values.pop_back(); // trailing comma - - auto query = fmt::format(SQL( - INSERT INTO character_expedition_lockouts - (character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name) - VALUES {} - ON DUPLICATE KEY UPDATE - from_expedition_uuid = VALUES(from_expedition_uuid), - expire_time = DATE_ADD(expire_time, INTERVAL {} SECOND), - duration = GREATEST(0, CAST(duration AS SIGNED) + {}); - ), insert_values, seconds, seconds); - - database.QueryDatabase(query); - } -} diff --git a/zone/expedition_database.h b/zone/expedition_database.h deleted file mode 100644 index 5e35fb7b3..000000000 --- a/zone/expedition_database.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY except by those people which sell it, which - * are required to give you total support for your newly bought product; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef EXPEDITION_DATABASE_H -#define EXPEDITION_DATABASE_H - -#include -#include -#include -#include -#include -#include - -class Expedition; -class ExpeditionLockoutTimer; -struct DynamicZoneMember; -class MySQLRequestResult; - -namespace ExpeditionDatabase -{ - uint32_t InsertExpedition(uint32_t dz_id); - std::vector LoadCharacterLockouts(uint32_t character_id); - std::vector LoadCharacterLockouts(uint32_t character_id, - const std::string& expedition_name); - void DeleteAllCharacterLockouts(uint32_t character_id); - void DeleteAllCharacterLockouts(uint32_t character_id, const std::string& expedition_name); - void DeleteCharacterLockout(uint32_t character_id, const std::string& expedition_name, - const std::string& event_name); - void DeleteLockout(uint32_t expedition_id, const std::string& event_name); - void DeleteMembersLockout(const std::vector& members, - const std::string& expedition_name, const std::string& event_name); - void InsertCharacterLockouts(uint32_t character_id, - const std::vector& lockouts); - void InsertMembersLockout(const std::vector& members, - const ExpeditionLockoutTimer& lockout); - void InsertLockout(uint32_t expedition_id, const ExpeditionLockoutTimer& lockout); - void InsertLockouts(uint32_t expedition_id, - const std::unordered_map& lockouts); - void UpdateLockState(uint32_t expedition_id, bool is_locked); - void UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join); - void AddLockoutDuration(const std::vector& members, - const ExpeditionLockoutTimer& lockout, int seconds); -}; - -#endif diff --git a/zone/expedition_request.cpp b/zone/expedition_request.cpp index 4c00f00dc..7c7763730 100644 --- a/zone/expedition_request.cpp +++ b/zone/expedition_request.cpp @@ -20,48 +20,30 @@ #include "expedition_request.h" #include "client.h" -#include "expedition.h" #include "groups.h" #include "raids.h" #include "string_ids.h" #include "../common/repositories/character_expedition_lockouts_repository.h" -constexpr char SystemName[] = "expedition"; - -// message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks) -constexpr const char* CREATE_NOT_ALL_ADDED = "Not all players in your {} were added to the {}. The {} can take a maximum of {} players, and your {} has {}."; -// message string 9265 (not in emu clients) -constexpr const char* EXPEDITION_OTHER_BELONGS = "{} attempted to create an expedition but {} already belongs to one."; - -ExpeditionRequest::ExpeditionRequest(const DynamicZone& dz, bool disable_messages) : - m_expedition_name(dz.GetName()), - m_min_players(dz.GetMinPlayers()), - m_max_players(dz.GetMaxPlayers()), - m_disable_messages(disable_messages) +ExpeditionRequest::ExpeditionRequest(const DynamicZone& dz, Client& client, bool silent) + : m_dz(&dz), m_requester(&client), m_silent(silent) { } -bool ExpeditionRequest::Validate(Client* requester) +bool ExpeditionRequest::Validate() { - m_requester = requester; - if (!m_requester) - { - return false; - } - // a message is sent to leader for every member that fails a requirement BenchTimer benchmark; bool requirements_met = false; - Raid* raid = m_requester->GetRaid(); - Group* group = m_requester->GetGroup(); - if (raid) + if (Raid* raid = m_requester->GetRaid()) { + m_is_raid = true; requirements_met = CanRaidRequest(raid); } - else if (group) + else if (Group* group = m_requester->GetGroup()) { requirements_met = CanGroupRequest(group); } @@ -89,21 +71,15 @@ bool ExpeditionRequest::CanRaidRequest(Raid* raid) // expedition max. members are added up to the max ordered by group number. auto raid_members = raid->GetMembers(); - if (raid_members.size() > m_max_players) + if (raid_members.size() > m_dz->GetMaxPlayers()) { - // stable_sort not needed, order within a raid group may not be what is displayed + // leader first then raid group, order within a raid group may not be what is displayed std::sort(raid_members.begin(), raid_members.end(), [&](const RaidMember& lhs, const RaidMember& rhs) { - if (m_leader_name == lhs.member_name) { // leader always added first - return true; - } else if (m_leader_name == rhs.member_name) { - return false; - } + if (m_leader_name == lhs.member_name) { return true; } + if (m_leader_name == rhs.member_name) { return false; } return lhs.group_number < rhs.group_number; }); - - m_not_all_added_msg = fmt::format(CREATE_NOT_ALL_ADDED, "raid", SystemName, - SystemName, m_max_players, "raid", raid_members.size()); } // live still performs conflict checks for all members even those beyond max @@ -139,12 +115,6 @@ bool ExpeditionRequest::CanGroupRequest(Group* group) } } - if (member_names.size() > m_max_players) - { - m_not_all_added_msg = fmt::format(CREATE_NOT_ALL_ADDED, "group", SystemName, - SystemName, m_max_players, "group", member_names.size()); - } - return CanMembersJoin(member_names); } @@ -172,7 +142,7 @@ bool ExpeditionRequest::CanMembersJoin(const std::vector& member_na return requirements_met; } -bool ExpeditionRequest::SaveLeaderLockouts(const std::vector& lockouts) +bool ExpeditionRequest::SaveLeaderLockouts(const std::vector& lockouts) { bool has_replay_lockout = false; @@ -180,9 +150,10 @@ bool ExpeditionRequest::SaveLeaderLockouts(const std::vector& member_names) { // order of member_names is preserved by queries for use with max member truncation - auto entries = ExpeditionsRepository::GetCharactersWithExpedition(database, member_names); + auto entries = DynamicZonesRepository::GetCharactersWithDz(database, member_names, static_cast(DynamicZoneType::Expedition)); if (entries.empty()) { LogExpeditions("Failed to load members for expedition request"); @@ -205,10 +176,10 @@ bool ExpeditionRequest::CheckMembersForConflicts(const std::vector& bool is_solo = (member_names.size() == 1); bool has_conflicts = false; - std::vector character_ids; + std::vector char_ids; for (const auto& character : entries) { - if (is_solo && character.expedition_id != 0) + if (is_solo && character.dz_id != 0) { // live doesn't bother checking replay lockout here SendLeaderMemberInExpedition(character.name, is_solo); @@ -216,43 +187,43 @@ bool ExpeditionRequest::CheckMembersForConflicts(const std::vector& } m_members.emplace_back(character.id, character.name, DynamicZoneMemberStatus::Online); - character_ids.emplace_back(character.id); + char_ids.push_back(character.id); } - auto member_lockouts = CharacterExpeditionLockoutsRepository::GetManyCharacterLockoutTimers( - database, character_ids, m_expedition_name, DZ_REPLAY_TIMER_NAME); + auto lockouts = CharacterExpeditionLockoutsRepository::GetLockouts(database, char_ids, m_dz->GetName()); // on live if leader has a replay lockout it never checks for event conflicts - bool leader_has_replay_lockout = false; - auto lockout_iter = member_lockouts.find(m_leader_id); - if (lockout_iter != member_lockouts.end()) + bool leader_replay = false; + auto it = lockouts.find(m_leader_id); + if (it != lockouts.end()) { - leader_has_replay_lockout = SaveLeaderLockouts(lockout_iter->second); + leader_replay = SaveLeaderLockouts(it->second); } for (const auto& character : entries) { - if (character.expedition_id != 0) + if (character.dz_id != 0) { has_conflicts = true; SendLeaderMemberInExpedition(character.name, is_solo); } - auto lockout_iter = member_lockouts.find(character.id); - if (lockout_iter != member_lockouts.end()) + auto it = lockouts.find(character.id); + if (it != lockouts.end()) { - for (const auto& lockout : lockout_iter->second) + for (const auto& lockout : it->second) { if (!lockout.IsExpired()) { + auto is_event = [&](const auto& l) { return l.IsEvent(lockout.Event()); }; + // replay timers were sorted by query so they show up before event conflicts - if (lockout.IsReplayTimer()) + if (lockout.IsReplay()) { has_conflicts = true; SendLeaderMemberReplayLockout(character.name, lockout, is_solo); } - else if (!leader_has_replay_lockout && character.id != m_leader_id && - m_lockouts.find(lockout.GetEventName()) == m_lockouts.end()) + else if (!leader_replay && character.id != m_leader_id && std::ranges::none_of(m_lockouts, is_event)) { // leader doesn't have this lockout has_conflicts = true; @@ -266,73 +237,60 @@ bool ExpeditionRequest::CheckMembersForConflicts(const std::vector& return has_conflicts; } -void ExpeditionRequest::SendLeaderMessage( - uint16_t chat_type, uint32_t string_id, const std::initializer_list& args) +void ExpeditionRequest::SendLeaderMessage(uint16_t chat_type, uint32_t string_id, std::initializer_list args) { - if (!m_disable_messages) + if (!m_silent) { Client::SendCrossZoneMessageString(m_leader, m_leader_name, chat_type, string_id, args); } } -void ExpeditionRequest::SendLeaderMemberInExpedition(const std::string& member_name, bool is_solo) +void ExpeditionRequest::SendLeaderMemberInExpedition(const std::string& name, bool is_solo) { - if (m_disable_messages) + if (m_silent) { return; } if (is_solo) { - SendLeaderMessage(Chat::Red, EXPEDITION_YOU_BELONG); + SendLeaderMessage(Chat::Red, DZ_YOU_BELONG); } else if (m_requester) { - std::string message = fmt::format(EXPEDITION_OTHER_BELONGS, m_requester->GetName(), member_name); - Client::SendCrossZoneMessage(m_leader, m_leader_name, Chat::Red, message); + // message string 9265 (not in emu clients) + Client::SendCrossZoneMessage(m_leader, m_leader_name, Chat::Red, fmt::format( + "{} attempted to create an expedition but {} already belongs to one.", m_requester->GetName(), name)); } } -void ExpeditionRequest::SendLeaderMemberReplayLockout( - const std::string& member_name, const ExpeditionLockoutTimer& lockout, bool is_solo) +void ExpeditionRequest::SendLeaderMemberReplayLockout(const std::string& name, const DzLockout& lockout, bool is_solo) { - if (m_disable_messages) + if (m_silent) { return; } - auto time_remaining = lockout.GetDaysHoursMinutesRemaining(); + auto time = lockout.GetTimeRemainingStrs(); if (is_solo) { - SendLeaderMessage(Chat::Red, EXPEDITION_YOU_PLAYED_HERE, { - time_remaining.days, time_remaining.hours, time_remaining.mins - }); + SendLeaderMessage(Chat::Red, DZ_REPLAY_YOU, { time.days, time.hours, time.mins }); } else { - SendLeaderMessage(Chat::Red, EXPEDITION_REPLAY_TIMER, { - member_name, time_remaining.days, time_remaining.hours, time_remaining.mins - }); + SendLeaderMessage(Chat::Red, DZ_REPLAY_OTHER, { name, time.days, time.hours, time.mins }); } } -void ExpeditionRequest::SendLeaderMemberEventLockout( - const std::string& member_name, const ExpeditionLockoutTimer& lockout) +void ExpeditionRequest::SendLeaderMemberEventLockout(const std::string& name, const DzLockout& lockout) { - if (m_disable_messages) + if (m_silent) { return; } - auto time_remaining = lockout.GetDaysHoursMinutesRemaining(); - SendLeaderMessage(Chat::Red, EXPEDITION_EVENT_TIMER, { - member_name, - lockout.GetEventName(), - time_remaining.days, - time_remaining.hours, - time_remaining.mins, - lockout.GetEventName() - }); + auto time = lockout.GetTimeRemainingStrs(); + SendLeaderMessage(Chat::Red, DZ_EVENT_TIMER, { name, lockout.Event(), time.days, time.hours, time.mins }); } bool ExpeditionRequest::IsPlayerCountValidated() @@ -343,19 +301,14 @@ bool ExpeditionRequest::IsPlayerCountValidated() auto bypass_status = RuleI(Expedition, MinStatusToBypassPlayerCountRequirements); auto gm_bypass = (m_requester && m_requester->GetGM() && m_requester->Admin() >= bypass_status); - if (m_members.size() > m_max_players) - { - // members were sorted at start, truncate after conflict checks to act like live - m_members.resize(m_max_players); - } - else if (!gm_bypass && m_members.size() < m_min_players) + if (!gm_bypass && m_members.size() < m_dz->GetMinPlayers()) { requirements_met = false; - SendLeaderMessage(Chat::System, REQUIRED_PLAYER_COUNT, { + SendLeaderMessage(Chat::System, DZ_PLAYER_COUNT, { fmt::format_int(m_members.size()).str(), - fmt::format_int(m_min_players).str(), - fmt::format_int(m_max_players).str() + fmt::format_int(m_dz->GetMinPlayers()).str(), + fmt::format_int(m_dz->GetMaxPlayers()).str() }); } diff --git a/zone/expedition_request.h b/zone/expedition_request.h index 059aa71de..037c43ae3 100644 --- a/zone/expedition_request.h +++ b/zone/expedition_request.h @@ -21,12 +21,11 @@ #ifndef EXPEDITION_REQUEST_H #define EXPEDITION_REQUEST_H -#include "expedition.h" -#include "../common/expedition_lockout_timer.h" +#include "dynamic_zone.h" +#include "../common/dynamic_zone_lockout.h" #include #include #include -#include class Client; class Group; @@ -35,19 +34,16 @@ class Raid; class ExpeditionRequest { public: - ExpeditionRequest(const DynamicZone& dz, bool disable_messages = false); + ExpeditionRequest(const DynamicZone& dz, Client& client, bool silent = false); - bool Validate(Client* requester); + bool Validate(); - const std::string& GetExpeditionName() const { return m_expedition_name; } Client* GetLeaderClient() const { return m_leader; } uint32_t GetLeaderID() const { return m_leader_id; } const std::string& GetLeaderName() const { return m_leader_name; } - const std::string& GetNotAllAddedMessage() const { return m_not_all_added_msg; } - uint32_t GetMinPlayers() const { return m_min_players; } - uint32_t GetMaxPlayers() const { return m_max_players; } const std::vector& GetMembers() const { return m_members; } - const std::unordered_map& GetLockouts() const { return m_lockouts; } + const std::vector& GetLockouts() const { return m_lockouts; } + bool IsRaid() const { return m_is_raid; } private: bool CanMembersJoin(const std::vector& member_names); @@ -55,23 +51,21 @@ private: bool CanGroupRequest(Group* group); bool CheckMembersForConflicts(const std::vector& member_names); bool IsPlayerCountValidated(); - bool SaveLeaderLockouts(const std::vector& leader_lockouts); - void SendLeaderMemberInExpedition(const std::string& member_name, bool is_solo); - void SendLeaderMemberReplayLockout(const std::string& member_name, const ExpeditionLockoutTimer& lockout, bool is_solo); - void SendLeaderMemberEventLockout(const std::string& member_name, const ExpeditionLockoutTimer& lockout); - void SendLeaderMessage(uint16_t chat_type, uint32_t string_id, const std::initializer_list& args = {}); + bool SaveLeaderLockouts(const std::vector& leader_lockouts); + void SendLeaderMemberInExpedition(const std::string& name, bool is_solo); + void SendLeaderMemberReplayLockout(const std::string& name, const DzLockout& lockout, bool is_solo); + void SendLeaderMemberEventLockout(const std::string& name, const DzLockout& lockout); + void SendLeaderMessage(uint16_t chat_type, uint32_t string_id, std::initializer_list args = {}); + const DynamicZone* m_dz = nullptr; Client* m_requester = nullptr; Client* m_leader = nullptr; uint32_t m_leader_id = 0; - uint32_t m_min_players = 0; - uint32_t m_max_players = 0; - bool m_disable_messages = false; - std::string m_expedition_name; + bool m_silent = false; + bool m_is_raid = false; std::string m_leader_name; - std::string m_not_all_added_msg; std::vector m_members; - std::unordered_map m_lockouts; + std::vector m_lockouts; }; #endif diff --git a/zone/gm_commands/dz.cpp b/zone/gm_commands/dz.cpp index cab117b6d..fe2ce1252 100755 --- a/zone/gm_commands/dz.cpp +++ b/zone/gm_commands/dz.cpp @@ -1,5 +1,5 @@ #include "../client.h" -#include "../expedition.h" +#include "../dynamic_zone.h" void command_dz(Client *c, const Seperator *sep) { @@ -10,151 +10,43 @@ void command_dz(Client *c, const Seperator *sep) if (strcasecmp(sep->arg[1], "cache") == 0) { if (strcasecmp(sep->arg[2], "reload") == 0) { DynamicZone::CacheAllFromDatabase(); - Expedition::CacheAllFromDatabase(); - c->Message( - Chat::White, fmt::format( - "Reloaded [{}] dynamic zone(s) and [{}] expedition(s) from database", - zone->dynamic_zone_cache.size(), zone->expedition_cache.size() - ).c_str()); + c->Message(Chat::White, fmt::format("Reloaded [{}] dynamic zone(s) from database", zone->dynamic_zone_cache.size()).c_str()); } } - else if (strcasecmp(sep->arg[1], "expedition") == 0) { - if (strcasecmp(sep->arg[2], "list") == 0) { - std::vector expeditions; - for (const auto &expedition : zone->expedition_cache) { - expeditions.emplace_back(expedition.second.get()); - } - - std::sort( - expeditions.begin(), expeditions.end(), - [](const Expedition *lhs, const Expedition *rhs) { - return lhs->GetID() < rhs->GetID(); - } - ); - - c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", expeditions.size()).c_str()); - for (const auto &expedition : expeditions) { - auto dz = expedition->GetDynamicZone(); - if (!dz) { - LogExpeditions("Expedition [{}] has an invalid dz [{}] in cache", - expedition->GetID(), - expedition->GetDynamicZoneID()); - continue; - } - - auto leader_saylink = Saylink::Silent( - fmt::format( - "#goto {}", - expedition->GetLeaderName() - ), - expedition->GetLeaderName() - ); - auto zone_saylink = Saylink::Silent( - fmt::format( - "#zoneinstance {}", - dz->GetInstanceID() - ), - "zone" - ); - - auto seconds = dz->GetSecondsRemaining(); - - c->Message( - Chat::White, fmt::format( - "expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - expedition->GetID(), - expedition->GetDynamicZoneID(), - expedition->GetName(), - leader_saylink, - zone_saylink, - ZoneName(dz->GetZoneID()), - dz->GetZoneID(), - dz->GetInstanceID(), - dz->GetZoneVersion(), - dz->GetMemberCount(), - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } + else if (strcasecmp(sep->arg[1], "destroy") == 0 && sep->IsNumber(2)) { + auto dz_id = std::strtoul(sep->arg[2], nullptr, 10); + if (auto dz = DynamicZone::FindDynamicZoneByID(dz_id)) { + c->Message(Chat::White, fmt::format("Destroying dz [{}] ({})", dz_id, dz->GetName()).c_str()); + dz->RemoveAllMembers(); } - else if (strcasecmp(sep->arg[2], "reload") == 0) { - Expedition::CacheAllFromDatabase(); - c->Message( - Chat::White, fmt::format( - "Reloaded [{}] expeditions to cache from database.", zone->expedition_cache.size() - ).c_str()); - } - else if (strcasecmp(sep->arg[2], "destroy") == 0 && sep->IsNumber(3)) { - auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) { - c->Message( - Chat::White, fmt::format( - "Destroying expedition [{}] ({})", - expedition_id, expedition->GetName()).c_str()); - expedition->GetDynamicZone()->RemoveAllMembers(); - } - else { - c->Message(Chat::Red, fmt::format("Failed to destroy expedition [{}]", sep->arg[3]).c_str()); - } - } - else if (strcasecmp(sep->arg[2], "unlock") == 0 && sep->IsNumber(3)) { - auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) { - c->Message(Chat::White, fmt::format("Unlocking expedition [{}]", expedition_id).c_str()); - expedition->SetLocked(false, ExpeditionLockMessage::None, true); - } - else { - c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", sep->arg[3]).c_str()); - } + else { + c->Message(Chat::Red, fmt::format("Failed to destroy dz [{}]", sep->arg[3]).c_str()); } } else if (strcasecmp(sep->arg[1], "list") == 0) { - c->Message( - Chat::White, - fmt::format("Total Dynamic Zones (cache): [{}]", zone->dynamic_zone_cache.size()).c_str()); - - std::vector dynamic_zones; - for (const auto &dz : zone->dynamic_zone_cache) { - dynamic_zones.emplace_back(dz.second.get()); + std::vector dynamic_zones; + for (const auto& it : zone->dynamic_zone_cache) { + dynamic_zones.push_back(it.second.get()); } - std::sort( - dynamic_zones.begin(), dynamic_zones.end(), - [](const DynamicZone *lhs, const DynamicZone *rhs) { - return lhs->GetID() < rhs->GetID(); - } - ); - - for (const auto &dz : dynamic_zones) { - auto seconds = dz->GetSecondsRemaining(); - auto zone_saylink = Saylink::Silent( - fmt::format( - "#zoneinstance {}", - dz->GetInstanceID() - ), - "zone" - ); - - std::string aligned_type = fmt::format( - "[{}]", - DynamicZone::GetDynamicZoneTypeName(static_cast(dz->GetType()))); - c->Message( - Chat::White, fmt::format( - "id: [{}] type: {:>10} {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - dz->GetID(), - aligned_type, - zone_saylink, - dz->GetZoneID(), - dz->GetInstanceID(), - dz->GetZoneVersion(), - dz->GetMemberCount(), - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); + std::ranges::sort(dynamic_zones, {}, &DynamicZone::GetID); + c->Message(Chat::White, fmt::format("Total Dynamic Zones (cache): [{}]", dynamic_zones.size()).c_str()); + for (const DynamicZone* dz : dynamic_zones) { + uint32_t seconds = dz->GetSecondsRemaining(); + c->Message(Chat::White, fmt::format( + "id: [{}] - [{}] - {}: [{}:{}:{}] members: [{}] expires: [{:02}:{:02}:{:02}] leader: [{}]", + dz->GetID(), + DynamicZone::GetDynamicZoneTypeName(dz->GetType()), + Saylink::Silent(fmt::format("#zoneinstance {}", dz->GetInstanceID()), "zone"), + dz->GetZoneID(), + dz->GetInstanceID(), + dz->GetZoneVersion(), + dz->GetMemberCount(), + seconds / 3600, // hours + seconds / 60 % 60, // minutes + seconds % 60, // seconds + Saylink::Silent(fmt::format("#goto {}", dz->GetLeaderName()), dz->GetLeaderName()) + ).c_str()); } } else if (strcasecmp(sep->arg[1], "listdb") == 0) { @@ -163,97 +55,70 @@ void command_dz(Client *c, const Seperator *sep) auto now = std::chrono::system_clock::now(); - for (const auto &dz : dz_list) { - auto expire_time = std::chrono::system_clock::from_time_t(dz.start_time + dz.duration); - auto remaining = std::chrono::duration_cast(expire_time - now); - auto seconds = std::max(0, static_cast(remaining.count())); + for (const auto& dz : dz_list) { + auto expire_time = std::chrono::system_clock::from_time_t(static_cast(dz.start_time) + dz.duration); bool is_expired = now > expire_time; if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) { - auto zone_saylink = is_expired ? "zone" : Saylink::Silent( - fmt::format( - "#zoneinstance {}", - dz.instance - ), - "zone" - ); - - c->Message( - Chat::White, fmt::format( - "id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - dz.id, - DynamicZone::GetDynamicZoneTypeName(static_cast(dz.type)), - zone_saylink, - dz.zone, - dz.instance, - dz.version, - dz.member_count, - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); + auto seconds = std::max(0, static_cast(std::chrono::duration_cast(expire_time - now).count())); + c->Message(Chat::White, fmt::format( + "id: [{}] - [{}] - {}: [{}:{}:{}] members: [{}] expires: [{:02}:{:02}:{:02}]", + dz.id, + DynamicZone::GetDynamicZoneTypeName(static_cast(dz.type)), + is_expired ? "zone" : Saylink::Silent(fmt::format("#zoneinstance {}", dz.instance), "zone"), + dz.zone, + dz.instance, + dz.version, + dz.member_count, + seconds / 3600, // hours + seconds / 60 % 60, // minutes + seconds % 60 // seconds + ).c_str()); } } } else if (strcasecmp(sep->arg[1], "lockouts") == 0) { if (strcasecmp(sep->arg[2], "remove") == 0 && sep->arg[3][0] != '\0') { if (sep->arg[5][0] == '\0') { - c->Message( - Chat::White, fmt::format( - "Removing [{}] lockouts on [{}].", sep->arg[4][0] ? sep->arg[4] : "all", sep->arg[3] - ).c_str()); + c->Message(Chat::White, fmt::format("Removing [{}] lockouts on [{}].", sep->arg[4][0] ? sep->arg[4] : "all", sep->arg[3]).c_str()); } else { - c->Message( - Chat::White, fmt::format( - "Removing [{}]:[{}] lockout on [{}].", sep->arg[4], sep->arg[5], sep->arg[3] - ).c_str()); + c->Message(Chat::White, fmt::format("Removing [{}]:[{}] lockout on [{}].", sep->arg[4], sep->arg[5], sep->arg[3]).c_str()); } - Expedition::RemoveLockoutsByCharacterName(sep->arg[3], sep->arg[4], sep->arg[5]); + DynamicZone::RemoveCharacterLockouts(sep->arg[3], sep->arg[4], sep->arg[5]); } } else if (strcasecmp(sep->arg[1], "makeleader") == 0 && sep->IsNumber(2) && sep->arg[3][0] != '\0') { - auto expedition_id = std::strtoul(sep->arg[2], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) { - auto char_name = FormatName(sep->arg[3]); - c->Message( - Chat::White, - fmt::format("Setting expedition [{}] leader to [{}]", expedition_id, char_name).c_str()); - expedition->SendWorldMakeLeaderRequest(c->CharacterID(), char_name); + uint32_t dz_id = std::strtoul(sep->arg[2], nullptr, 10); + if (auto dz = DynamicZone::FindDynamicZoneByID(dz_id)) { + std::string name = FormatName(sep->arg[3]); + c->Message(Chat::White, fmt::format("Setting expedition [{}] leader to [{}]", dz_id, name).c_str()); + dz->SendWorldMakeLeaderRequest(c->CharacterID(), name); } else { - c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", expedition_id).c_str()); + c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", dz_id).c_str()); + } + } + else if (strcasecmp(sep->arg[1], "unlock") == 0 && sep->IsNumber(2)) { + uint32_t dz_id = std::strtoul(sep->arg[2], nullptr, 10); + if (auto dz = DynamicZone::FindDynamicZoneByID(dz_id)) { + c->Message(Chat::White, fmt::format("Unlocking expedition dz [{}]", dz_id).c_str()); + dz->SetLocked(false, true); + } + else { + c->Message(Chat::Red, fmt::format("Failed to find dz [{}]", sep->arg[2]).c_str()); } } else { c->Message(Chat::White, "#dz usage:"); - c->Message( - Chat::White, - "#dz cache reload - reload the current zone cache from db (also reloads expedition cache dependency)" - ); - c->Message(Chat::White, "#dz expedition list - list expeditions in current zone cache"); - c->Message(Chat::White, "#dz expedition reload - reload expedition zone cache from database"); - c->Message( - Chat::White, - "#dz expedition destroy - destroy expedition globally (must be in cache)" - ); - c->Message(Chat::White, "#dz expedition unlock - unlock expedition"); - c->Message(Chat::White, "#dz list - list all dynamic zone instances from current zone cache"); - c->Message( - Chat::White, - "#dz listdb [all] - list dynamic zone instances from database -- 'all' includes expired" - ); + c->Message(Chat::White, "#dz cache reload - reload current zone cache from db (also reloads expedition cache)"); + c->Message(Chat::White, "#dz destroy - destroy dz globally (must be in cache)"); + c->Message(Chat::White, "#dz list - list dynamic zones in current zone cache"); + c->Message(Chat::White, "#dz listdb [all] - list dynamic zones in database -- 'all' includes expired"); c->Message(Chat::White, "#dz lockouts remove - delete all of character's expedition lockouts"); - c->Message( - Chat::White, - "#dz lockouts remove \"\" - delete lockouts by expedition" - ); - c->Message( - Chat::White, - "#dz lockouts remove \"\" \"\" - delete lockout by expedition event" - ); - c->Message(Chat::White, "#dz makeleader - set new expedition leader"); + c->Message(Chat::White, "#dz lockouts remove \"\" - delete lockouts by expedition"); + c->Message(Chat::White, "#dz lockouts remove \"\" \"\" - delete expedition event lockout"); + c->Message(Chat::White, "#dz makeleader - set new expedition leader"); + c->Message(Chat::White, "#dz unlock - unlock expedition"); } } - diff --git a/zone/gm_commands/dzkickplayers.cpp b/zone/gm_commands/dzkickplayers.cpp index 149ccc0af..1f56aee44 100755 --- a/zone/gm_commands/dzkickplayers.cpp +++ b/zone/gm_commands/dzkickplayers.cpp @@ -1,13 +1,11 @@ #include "../client.h" -#include "../expedition.h" void command_dzkickplayers(Client *c, const Seperator *sep) { if (c) { - auto expedition = c->GetExpedition(); - if (expedition) { - expedition->DzKickPlayers(c); + auto dz = c->GetExpedition(); + if (dz) { + dz->DzKickPlayers(c); } } } - diff --git a/zone/groups.cpp b/zone/groups.cpp index 846c1dde9..a66e7a4bd 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -18,11 +18,12 @@ #include "../common/global_define.h" #include "../common/eqemu_logsys.h" -#include "expedition.h" +#include "dynamic_zone.h" #include "masterentity.h" #include "worldserver.h" #include "string_ids.h" #include "../common/events/player_event_logs.h" +#include "../common/repositories/character_expedition_lockouts_repository.h" #include "../common/repositories/group_id_repository.h" #include "../common/repositories/group_leaders_repository.h" #include "queryserv.h" @@ -2515,25 +2516,21 @@ void Group::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_r } } -bool Group::DoesAnyMemberHaveExpeditionLockout( - const std::string& expedition_name, const std::string& event_name, int max_check_count) +bool Group::AnyMemberHasDzLockout(const std::string& expedition, const std::string& event) { - if (max_check_count <= 0) + std::vector names; + for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { - max_check_count = MAX_GROUP_MEMBERS; - } - - for (int i = 0; i < MAX_GROUP_MEMBERS && i < max_check_count; ++i) - { - if (membername[i][0]) + if (!members[i] && membername[i][0]) { - if (Expedition::HasLockoutByCharacterName(membername[i], expedition_name, event_name)) - { - return true; - } + names.emplace_back(membername[i]); // out of zone member + } + else if (members[i] && members[i]->IsClient() && members[i]->CastToClient()->HasDzLockout(expedition, event)) + { + return true; } } - return false; + return !CharacterExpeditionLockoutsRepository::GetLockouts(database, names, expedition, event).empty(); } bool Group::IsLeader(const char* name) { diff --git a/zone/groups.h b/zone/groups.h index 91fcee13e..a07ecc61c 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -161,7 +161,7 @@ public: inline int GetMentorPercent() { return mentor_percent; } inline Client *GetMentoree() { return mentoree; } - bool DoesAnyMemberHaveExpeditionLockout(const std::string& expedition_name, const std::string& event_name, int max_check_count = 0); + bool AnyMemberHasDzLockout(const std::string& expedition, const std::string& event); Mob* members[MAX_GROUP_MEMBERS] {nullptr}; char membername[MAX_GROUP_MEMBERS][64] {""}; diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index f5e435133..ab26f45cb 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1944,12 +1944,12 @@ Lua_Expedition Lua_Client::CreateExpedition(luabind::object expedition_table) { Lua_Expedition Lua_Client::CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players) { Lua_Safe_Call_Class(Lua_Expedition); - return self->CreateExpedition(zone_name, version, duration, expedition_name, min_players, max_players); + return self->CreateExpedition(ZoneID(zone_name), version, duration, expedition_name, min_players, max_players); } Lua_Expedition Lua_Client::CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool disable_messages) { Lua_Safe_Call_Class(Lua_Expedition); - return self->CreateExpedition(zone_name, version, duration, expedition_name, min_players, max_players, disable_messages); + return self->CreateExpedition(ZoneID(zone_name), version, duration, expedition_name, min_players, max_players, disable_messages); } Lua_Expedition Lua_Client::CreateExpeditionFromTemplate(uint32_t dz_template_id) { @@ -1968,16 +1968,15 @@ luabind::object Lua_Client::GetExpeditionLockouts(lua_State* L) if (d_) { auto self = reinterpret_cast(d_); - auto lockouts = self->GetExpeditionLockouts(); - + const auto& lockouts = self->GetDzLockouts(); for (const auto& lockout : lockouts) { - auto lockout_table = lua_table[lockout.GetExpeditionName()]; + auto lockout_table = lua_table[lockout.DzName()]; if (luabind::type(lockout_table) != LUA_TTABLE) { lockout_table = luabind::newtable(L); } - lockout_table[lockout.GetEventName()] = lockout.GetSecondsRemaining(); + lockout_table[lockout.Event()] = lockout.GetSecondsRemaining(); } } return lua_table; @@ -1989,13 +1988,12 @@ luabind::object Lua_Client::GetExpeditionLockouts(lua_State* L, std::string expe if (d_) { auto self = reinterpret_cast(d_); - auto lockouts = self->GetExpeditionLockouts(); - + const auto& lockouts = self->GetDzLockouts(); for (const auto& lockout : lockouts) { - if (lockout.GetExpeditionName() == expedition_name) + if (lockout.DzName() == expedition_name) { - lua_table[lockout.GetEventName()] = lockout.GetSecondsRemaining(); + lua_table[lockout.Event()] = lockout.GetSecondsRemaining(); } } } @@ -2005,52 +2003,54 @@ luabind::object Lua_Client::GetExpeditionLockouts(lua_State* L, std::string expe std::string Lua_Client::GetLockoutExpeditionUUID(std::string expedition_name, std::string event_name) { Lua_Safe_Call_String(); std::string uuid; - auto lockout = self->GetExpeditionLockout(expedition_name, event_name); + auto lockout = self->GetDzLockout(expedition_name, event_name); if (lockout) { - uuid = lockout->GetExpeditionUUID(); + uuid = lockout->UUID(); } return uuid; } void Lua_Client::AddExpeditionLockout(std::string expedition_name, std::string event_name, uint32 seconds) { Lua_Safe_Call_Void(); - self->AddNewExpeditionLockout(expedition_name, event_name, seconds); + self->AddDzLockout(expedition_name, event_name, seconds); } void Lua_Client::AddExpeditionLockout(std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) { Lua_Safe_Call_Void(); - self->AddNewExpeditionLockout(expedition_name, event_name, seconds, uuid); + self->AddDzLockout(expedition_name, event_name, seconds, uuid); } void Lua_Client::AddExpeditionLockoutDuration(std::string expedition_name, std::string event_name, int seconds) { Lua_Safe_Call_Void(); - self->AddExpeditionLockoutDuration(expedition_name, event_name, seconds, {}, true); + auto lockout = DzLockout::Create(expedition_name, event_name, seconds); + self->AddDzLockoutDuration(lockout, seconds, {}, true); } void Lua_Client::AddExpeditionLockoutDuration(std::string expedition_name, std::string event_name, int seconds, std::string uuid) { Lua_Safe_Call_Void(); - self->AddExpeditionLockoutDuration(expedition_name, event_name, seconds, uuid, true); + auto lockout = DzLockout::Create(expedition_name, event_name, seconds, uuid); + self->AddDzLockoutDuration(lockout, seconds, uuid, true); } void Lua_Client::RemoveAllExpeditionLockouts() { Lua_Safe_Call_Void(); - self->RemoveAllExpeditionLockouts({}, true); + self->RemoveDzLockouts({}, true); } void Lua_Client::RemoveAllExpeditionLockouts(std::string expedition_name) { Lua_Safe_Call_Void(); - self->RemoveAllExpeditionLockouts(expedition_name, true); + self->RemoveDzLockouts(expedition_name, true); } void Lua_Client::RemoveExpeditionLockout(std::string expedition_name, std::string event_name) { Lua_Safe_Call_Void(); - self->RemoveExpeditionLockout(expedition_name, event_name, true); + self->RemoveDzLockout(expedition_name, event_name, true); } bool Lua_Client::HasExpeditionLockout(std::string expedition_name, std::string event_name) { Lua_Safe_Call_Bool(); - return self->HasExpeditionLockout(expedition_name, event_name); + return self->HasDzLockout(expedition_name, event_name); } void Lua_Client::MovePCDynamicZone(uint32 zone_id) { diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index fca207077..41554b41e 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -1,7 +1,7 @@ #ifdef LUA_EQEMU #include "lua_expedition.h" -#include "expedition.h" +#include "dynamic_zone.h" #include "../common/zone_store.h" #include "lua.hpp" #include @@ -24,22 +24,17 @@ void Lua_Expedition::AddLockoutDuration(std::string event_name, int seconds, boo void Lua_Expedition::AddReplayLockout(uint32_t seconds) { Lua_Safe_Call_Void(); - self->AddReplayLockout(seconds); + self->AddLockout(DzLockout::ReplayTimer, seconds); } void Lua_Expedition::AddReplayLockoutDuration(int seconds) { Lua_Safe_Call_Void(); - self->AddReplayLockoutDuration(seconds); + self->AddLockoutDuration(DzLockout::ReplayTimer, seconds); } void Lua_Expedition::AddReplayLockoutDuration(int seconds, bool members_only) { Lua_Safe_Call_Void(); - self->AddReplayLockoutDuration(seconds, members_only); -} - -uint32_t Lua_Expedition::GetDynamicZoneID() { - Lua_Safe_Call_Int(); - return self->GetDynamicZoneID(); + self->AddLockoutDuration(DzLockout::ReplayTimer, seconds, members_only); } uint32_t Lua_Expedition::GetID() { @@ -49,7 +44,7 @@ uint32_t Lua_Expedition::GetID() { int Lua_Expedition::GetInstanceID() { Lua_Safe_Call_Int(); - return self->GetDynamicZone()->GetInstanceID(); + return self->GetInstanceID(); } std::string Lua_Expedition::GetLeaderName() { @@ -63,10 +58,10 @@ luabind::object Lua_Expedition::GetLockouts(lua_State* L) { if (d_) { auto self = reinterpret_cast(d_); - auto lockouts = self->GetLockouts(); + const auto& lockouts = self->GetLockouts(); for (const auto& lockout : lockouts) { - lua_table[lockout.first] = lockout.second.GetSecondsRemaining(); + lua_table[lockout.Event()] = lockout.GetSecondsRemaining(); } } return lua_table; @@ -74,17 +69,17 @@ luabind::object Lua_Expedition::GetLockouts(lua_State* L) { std::string Lua_Expedition::GetLootEventByNPCTypeID(uint32_t npc_type_id) { Lua_Safe_Call_String(); - return self->GetLootEventByNPCTypeID(npc_type_id); + return self->GetLootEvent(npc_type_id, DzLootEvent::Type::NpcType); } std::string Lua_Expedition::GetLootEventBySpawnID(uint32_t spawn_id) { Lua_Safe_Call_String(); - return self->GetLootEventBySpawnID(spawn_id); + return self->GetLootEvent(spawn_id, DzLootEvent::Type::Entity); } uint32_t Lua_Expedition::GetMemberCount() { Lua_Safe_Call_Int(); - return self->GetDynamicZone()->GetMemberCount(); + return self->GetMemberCount(); } luabind::object Lua_Expedition::GetMembers(lua_State* L) { @@ -93,7 +88,7 @@ luabind::object Lua_Expedition::GetMembers(lua_State* L) { if (d_) { auto self = reinterpret_cast(d_); - for (const auto& member : self->GetDynamicZone()->GetMembers()) + for (const auto& member : self->GetMembers()) { lua_table[member.name] = member.id; } @@ -108,27 +103,27 @@ std::string Lua_Expedition::GetName() { int Lua_Expedition::GetSecondsRemaining() { Lua_Safe_Call_Int(); - return self->GetDynamicZone()->GetSecondsRemaining(); + return self->GetSecondsRemaining(); } std::string Lua_Expedition::GetUUID() { Lua_Safe_Call_String(); - return self->GetDynamicZone()->GetUUID(); + return self->GetUUID(); } int Lua_Expedition::GetZoneID() { Lua_Safe_Call_Int(); - return self->GetDynamicZone()->GetZoneID(); + return self->GetZoneID(); } std::string Lua_Expedition::GetZoneName() { Lua_Safe_Call_String(); - return ZoneName(self->GetDynamicZone()->GetZoneID()); + return ZoneName(self->GetZoneID()); } int Lua_Expedition::GetZoneVersion() { Lua_Safe_Call_Int(); - return self->GetDynamicZone()->GetZoneVersion(); + return self->GetZoneVersion(); } bool Lua_Expedition::HasLockout(std::string event_name) { @@ -148,7 +143,7 @@ bool Lua_Expedition::IsLocked() { void Lua_Expedition::RemoveCompass() { Lua_Safe_Call_Void(); - self->GetDynamicZone()->SetCompass(0, 0, 0, 0, true); + self->SetCompass(0, 0, 0, 0, true); } void Lua_Expedition::RemoveLockout(std::string event_name) { @@ -158,69 +153,69 @@ void Lua_Expedition::RemoveLockout(std::string event_name) { void Lua_Expedition::SetCompass(uint32_t zone_id, float x, float y, float z) { Lua_Safe_Call_Void(); - self->GetDynamicZone()->SetCompass(zone_id, x, y, z, true); + self->SetCompass(zone_id, x, y, z, true); } void Lua_Expedition::SetCompass(std::string zone_name, float x, float y, float z) { Lua_Safe_Call_Void(); - self->GetDynamicZone()->SetCompass(ZoneID(zone_name), x, y, z, true); + self->SetCompass(ZoneID(zone_name), x, y, z, true); } void Lua_Expedition::SetLocked(bool lock_expedition) { Lua_Safe_Call_Void(); - self->SetLocked(lock_expedition, ExpeditionLockMessage::None, true); + self->SetLocked(lock_expedition, true); } void Lua_Expedition::SetLocked(bool lock_expedition, int lock_msg) { Lua_Safe_Call_Void(); - self->SetLocked(lock_expedition, static_cast(lock_msg), true); + self->SetLocked(lock_expedition, true, static_cast(lock_msg)); } void Lua_Expedition::SetLocked(bool lock_expedition, int lock_msg, uint32_t msg_color) { Lua_Safe_Call_Void(); - self->SetLocked(lock_expedition, static_cast(lock_msg), true, msg_color); + self->SetLocked(lock_expedition, true, static_cast(lock_msg), msg_color); } void Lua_Expedition::SetLootEventByNPCTypeID(uint32_t npc_type_id, std::string event_name) { Lua_Safe_Call_Void(); - self->SetLootEventByNPCTypeID(npc_type_id, event_name); + self->SetLootEvent(npc_type_id, event_name, DzLootEvent::Type::NpcType); } void Lua_Expedition::SetLootEventBySpawnID(uint32_t spawn_id, std::string event_name) { Lua_Safe_Call_Void(); - self->SetLootEventBySpawnID(spawn_id, event_name); + self->SetLootEvent(spawn_id, event_name, DzLootEvent::Type::Entity); } void Lua_Expedition::SetReplayLockoutOnMemberJoin(bool enable) { Lua_Safe_Call_Void(); - self->SetReplayLockoutOnMemberJoin(enable, true); + self->SetReplayOnJoin(enable, true); } void Lua_Expedition::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->GetDynamicZone()->SetSafeReturn(zone_id, x, y, z, heading, true); + self->SetSafeReturn(zone_id, x, y, z, heading, true); } void Lua_Expedition::SetSafeReturn(std::string zone_name, float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->GetDynamicZone()->SetSafeReturn(ZoneID(zone_name), x, y, z, heading, true); + self->SetSafeReturn(ZoneID(zone_name), x, y, z, heading, true); } void Lua_Expedition::SetSecondsRemaining(uint32_t seconds_remaining) { Lua_Safe_Call_Void(); - self->GetDynamicZone()->SetSecondsRemaining(seconds_remaining); + self->SetSecondsRemaining(seconds_remaining); } void Lua_Expedition::SetSwitchID(int dz_switch_id) { Lua_Safe_Call_Void(); - self->GetDynamicZone()->SetSwitchID(dz_switch_id, true); + self->SetSwitchID(dz_switch_id, true); } void Lua_Expedition::SetZoneInLocation(float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->GetDynamicZone()->SetZoneInLocation(x, y, z, heading, true); + self->SetZoneInLocation(x, y, z, heading, true); } void Lua_Expedition::UpdateLockoutDuration(std::string event_name, uint32_t duration) { @@ -244,7 +239,7 @@ luabind::scope lua_register_expedition() { .def("AddReplayLockout", (void(Lua_Expedition::*)(uint32_t))&Lua_Expedition::AddReplayLockout) .def("AddReplayLockoutDuration", (void(Lua_Expedition::*)(int))&Lua_Expedition::AddReplayLockoutDuration) .def("AddReplayLockoutDuration", (void(Lua_Expedition::*)(int, bool))&Lua_Expedition::AddReplayLockoutDuration) - .def("GetDynamicZoneID", &Lua_Expedition::GetDynamicZoneID) + .def("GetDynamicZoneID", &Lua_Expedition::GetID) .def("GetID", (uint32_t(Lua_Expedition::*)(void))&Lua_Expedition::GetID) .def("GetInstanceID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetInstanceID) .def("GetLeaderName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetLeaderName) @@ -282,12 +277,12 @@ luabind::scope lua_register_expedition() { } luabind::scope lua_register_expedition_lock_messages() { - return luabind::class_("ExpeditionLockMessage") + return luabind::class_("ExpeditionLockMessage") .enum_("constants") [( - luabind::value("None", static_cast(ExpeditionLockMessage::None)), - luabind::value("Close", static_cast(ExpeditionLockMessage::Close)), - luabind::value("Begin", static_cast(ExpeditionLockMessage::Begin)) + luabind::value("None", static_cast(DzLockMsg::None)), + luabind::value("Close", static_cast(DzLockMsg::Close)), + luabind::value("Begin", static_cast(DzLockMsg::Begin)) )]; } diff --git a/zone/lua_expedition.h b/zone/lua_expedition.h index a338e3272..f11e3d6d6 100644 --- a/zone/lua_expedition.h +++ b/zone/lua_expedition.h @@ -26,7 +26,7 @@ #include "../common/types.h" #include -class Expedition; +class DynamicZone; class Lua_Client; struct lua_State; @@ -41,16 +41,16 @@ namespace luabind { luabind::scope lua_register_expedition(); luabind::scope lua_register_expedition_lock_messages(); -class Lua_Expedition : public Lua_Ptr +class Lua_Expedition : public Lua_Ptr { - typedef Expedition NativeType; + typedef DynamicZone NativeType; public: Lua_Expedition() : Lua_Ptr(nullptr) { } - Lua_Expedition(Expedition *d) : Lua_Ptr(d) { } + Lua_Expedition(DynamicZone* d) : Lua_Ptr(d) { } virtual ~Lua_Expedition() { } - operator Expedition*() { - return reinterpret_cast(GetLuaPtrData()); + operator DynamicZone*() { + return reinterpret_cast(GetLuaPtrData()); } void AddLockout(std::string event_name, uint32_t seconds); @@ -59,7 +59,6 @@ public: void AddReplayLockout(uint32_t seconds); void AddReplayLockoutDuration(int seconds); void AddReplayLockoutDuration(int seconds, bool members_only); - uint32_t GetDynamicZoneID(); uint32_t GetID(); int GetInstanceID(); std::string GetLeaderName(); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index bc76bb38d..c41fcd1fb 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -25,8 +25,8 @@ #include "encounter.h" #include "lua_encounter.h" #include "data_bucket.h" -#include "expedition.h" #include "dialogue_window.h" +#include "dynamic_zone.h" #include "../common/events/player_event_logs.h" #include "worldserver.h" #include "zone.h" @@ -1828,36 +1828,36 @@ void lua_set_content_flag(std::string flag_name, bool enabled){ Lua_Expedition lua_get_expedition() { if (zone && zone->GetInstanceID() != 0) { - return Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID()); + return DynamicZone::FindExpeditionByZone(zone->GetZoneID(), zone->GetInstanceID()); } return nullptr; } Lua_Expedition lua_get_expedition_by_char_id(uint32 char_id) { - return Expedition::FindCachedExpeditionByCharacterID(char_id); + return DynamicZone::FindExpeditionByCharacter(char_id); } Lua_Expedition lua_get_expedition_by_dz_id(uint32 dz_id) { - return Expedition::FindCachedExpeditionByDynamicZoneID(dz_id); + return DynamicZone::FindDynamicZoneByID(dz_id, DynamicZoneType::Expedition); } Lua_Expedition lua_get_expedition_by_zone_instance(uint32 zone_id, uint32 instance_id) { - return Expedition::FindCachedExpeditionByZoneInstance(zone_id, instance_id); + return DynamicZone::FindExpeditionByZone(zone_id, instance_id); } luabind::object lua_get_expedition_lockout_by_char_id(lua_State* L, uint32 char_id, std::string expedition_name, std::string event_name) { luabind::adl::object lua_table = luabind::newtable(L); - auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id); + auto lockouts = DynamicZone::GetCharacterLockouts(char_id); - auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const ExpeditionLockoutTimer& lockout) { - return lockout.IsSameLockout(expedition_name, event_name); + auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const DzLockout& lockout) { + return lockout.IsSame(expedition_name, event_name); }); if (it != lockouts.end()) { lua_table["remaining"] = it->GetSecondsRemaining(); - lua_table["uuid"] = it->GetExpeditionUUID(); + lua_table["uuid"] = it->UUID(); } return lua_table; @@ -1866,23 +1866,23 @@ luabind::object lua_get_expedition_lockout_by_char_id(lua_State* L, uint32 char_ luabind::object lua_get_expedition_lockouts_by_char_id(lua_State* L, uint32 char_id) { luabind::adl::object lua_table = luabind::newtable(L); - auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id); + auto lockouts = DynamicZone::GetCharacterLockouts(char_id); for (const auto& lockout : lockouts) { - auto lockout_table = lua_table[lockout.GetExpeditionName()]; + auto lockout_table = lua_table[lockout.DzName()]; if (luabind::type(lockout_table) != LUA_TTABLE) { lockout_table = luabind::newtable(L); } - auto event_table = lockout_table[lockout.GetEventName()]; + auto event_table = lockout_table[lockout.Event()]; if (luabind::type(event_table) != LUA_TTABLE) { event_table = luabind::newtable(L); } event_table["remaining"] = lockout.GetSecondsRemaining(); - event_table["uuid"] = lockout.GetExpeditionUUID(); + event_table["uuid"] = lockout.UUID(); } return lua_table; } @@ -1890,51 +1890,51 @@ luabind::object lua_get_expedition_lockouts_by_char_id(lua_State* L, uint32 char luabind::object lua_get_expedition_lockouts_by_char_id(lua_State* L, uint32 char_id, std::string expedition_name) { luabind::adl::object lua_table = luabind::newtable(L); - auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id); + auto lockouts = DynamicZone::GetCharacterLockouts(char_id); for (const auto& lockout : lockouts) { - if (lockout.GetExpeditionName() == expedition_name) + if (lockout.DzName() == expedition_name) { - auto event_table = lua_table[lockout.GetEventName()]; + auto event_table = lua_table[lockout.Event()]; if (luabind::type(event_table) != LUA_TTABLE) { event_table = luabind::newtable(L); } event_table["remaining"] = lockout.GetSecondsRemaining(); - event_table["uuid"] = lockout.GetExpeditionUUID(); + event_table["uuid"] = lockout.UUID(); } } return lua_table; } void lua_add_expedition_lockout_all_clients(std::string expedition_name, std::string event_name, uint32 seconds) { - auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds); - Expedition::AddLockoutClients(lockout); + auto lockout = DzLockout::Create(expedition_name, event_name, seconds); + DynamicZone::AddClientsLockout(lockout); } void lua_add_expedition_lockout_all_clients(std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) { - auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid); - Expedition::AddLockoutClients(lockout); + auto lockout = DzLockout::Create(expedition_name, event_name, seconds, uuid); + DynamicZone::AddClientsLockout(lockout); } void lua_add_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name, uint32 seconds) { - Expedition::AddLockoutByCharacterID(char_id, expedition_name, event_name, seconds); + DynamicZone::AddCharacterLockout(char_id, expedition_name, event_name, seconds); } void lua_add_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) { - Expedition::AddLockoutByCharacterID(char_id, expedition_name, event_name, seconds, uuid); + DynamicZone::AddCharacterLockout(char_id, expedition_name, event_name, seconds, uuid); } void lua_remove_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name) { - Expedition::RemoveLockoutsByCharacterID(char_id, expedition_name, event_name); + DynamicZone::RemoveCharacterLockouts(char_id, expedition_name, event_name); } void lua_remove_all_expedition_lockouts_by_char_id(uint32 char_id) { - Expedition::RemoveLockoutsByCharacterID(char_id); + DynamicZone::RemoveCharacterLockouts(char_id); } void lua_remove_all_expedition_lockouts_by_char_id(uint32 char_id, std::string expedition_name) { - Expedition::RemoveLockoutsByCharacterID(char_id, expedition_name); + DynamicZone::RemoveCharacterLockouts(char_id, expedition_name); } std::string lua_seconds_to_time(int duration) { diff --git a/zone/lua_group.cpp b/zone/lua_group.cpp index 049ecaa4a..a427ef3d3 100644 --- a/zone/lua_group.cpp +++ b/zone/lua_group.cpp @@ -125,13 +125,13 @@ Lua_Mob Lua_Group::GetMember(int member_index) { bool Lua_Group::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name) { Lua_Safe_Call_Bool(); - return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name); + return self->AnyMemberHasDzLockout(expedition_name, event_name); } bool Lua_Group::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name, int max_check_count) { Lua_Safe_Call_Bool(); - return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name, max_check_count); + return self->AnyMemberHasDzLockout(expedition_name, event_name); // max_check_count deprecated } uint32 Lua_Group::GetAverageLevel() { diff --git a/zone/lua_raid.cpp b/zone/lua_raid.cpp index a5f05f00f..46290a861 100644 --- a/zone/lua_raid.cpp +++ b/zone/lua_raid.cpp @@ -157,13 +157,13 @@ int Lua_Raid::GetGroupNumber(int member_index) { bool Lua_Raid::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name) { Lua_Safe_Call_Bool(); - return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name); + return self->AnyMemberHasDzLockout(expedition_name, event_name); } bool Lua_Raid::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name, int max_check_count) { Lua_Safe_Call_Bool(); - return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name, max_check_count); + return self->AnyMemberHasDzLockout(expedition_name, event_name); // max_check_count deprecated } luabind::scope lua_register_raid() { diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 03017cd38..b109bb448 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5,7 +5,7 @@ #include "../common/global_define.h" #include "embperl.h" #include "client.h" -#include "expedition.h" +#include "dynamic_zone.h" #include "titles.h" #include "dialogue_window.h" @@ -1775,7 +1775,7 @@ DynamicZoneLocation GetDynamicZoneLocationFromHash(perl::hash table) return { zone_id, x, y, z, h }; } -Expedition* Perl_Client_CreateExpedition(Client* self, perl::reference table_ref) +DynamicZone* Perl_Client_CreateExpedition(Client* self, perl::reference table_ref) { perl::hash table = table_ref; perl::hash expedition = table["expedition"]; @@ -1822,17 +1822,17 @@ Expedition* Perl_Client_CreateExpedition(Client* self, perl::reference table_ref return self->CreateExpedition(dz); } -Expedition* Perl_Client_CreateExpedition(Client* self, std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players) +DynamicZone* Perl_Client_CreateExpedition(Client* self, std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players) { - return self->CreateExpedition(zone_name, version, duration, expedition_name, min_players, max_players); + return self->CreateExpedition(ZoneID(zone_name), version, duration, expedition_name, min_players, max_players); } -Expedition* Perl_Client_CreateExpedition(Client* self, std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool disable_messages) +DynamicZone* Perl_Client_CreateExpedition(Client* self, std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool disable_messages) { - return self->CreateExpedition(zone_name, version, duration, expedition_name, min_players, max_players, disable_messages); + return self->CreateExpedition(ZoneID(zone_name), version, duration, expedition_name, min_players, max_players, disable_messages); } -Expedition* Perl_Client_CreateExpeditionFromTemplate(Client* self, uint32_t dz_template_id) +DynamicZone* Perl_Client_CreateExpeditionFromTemplate(Client* self, uint32_t dz_template_id) { return self->CreateExpeditionFromTemplate(dz_template_id); } @@ -1875,7 +1875,7 @@ void Perl_Client_CreateTaskDynamicZone(Client* self, int task_id, perl::referenc self->CreateTaskDynamicZone(task_id, dz); } -Expedition* Perl_Client_GetExpedition(Client* self) +DynamicZone* Perl_Client_GetExpedition(Client* self) { return self->GetExpedition(); } @@ -1884,15 +1884,15 @@ perl::reference Perl_Client_GetExpeditionLockouts(Client* self) { perl::hash lockout_hash; - auto lockouts = self->GetExpeditionLockouts(); + const auto& lockouts = self->GetDzLockouts(); for (const auto& lockout : lockouts) { - if (!lockout_hash.exists(lockout.GetExpeditionName())) + if (!lockout_hash.exists(lockout.DzName())) { - lockout_hash[lockout.GetExpeditionName()] = perl::reference(perl::hash()); + lockout_hash[lockout.DzName()] = perl::reference(perl::hash()); } - perl::hash events = lockout_hash[lockout.GetExpeditionName()]; // nested - events[lockout.GetEventName()] = lockout.GetSecondsRemaining(); + perl::hash events = lockout_hash[lockout.DzName()]; // nested + events[lockout.Event()] = lockout.GetSecondsRemaining(); } return perl::reference(lockout_hash); @@ -1902,10 +1902,10 @@ perl::reference Perl_Client_GetExpeditionLockouts(Client* self, std::string expe { perl::hash event_hash; - auto lockouts = self->GetExpeditionLockouts(expedition_name); + auto lockouts = self->GetDzLockouts(expedition_name); for (const auto& lockout : lockouts) { - event_hash[lockout.GetEventName()] = lockout.GetSecondsRemaining(); + event_hash[lockout.Event()] = lockout.GetSecondsRemaining(); } return perl::reference(event_hash); @@ -1913,48 +1913,50 @@ perl::reference Perl_Client_GetExpeditionLockouts(Client* self, std::string expe std::string Perl_Client_GetLockoutExpeditionUUID(Client* self, std::string expedition_name, std::string event_name) { - auto lockout = self->GetExpeditionLockout(expedition_name, event_name); - return lockout ? lockout->GetExpeditionUUID() : std::string{}; + auto lockout = self->GetDzLockout(expedition_name, event_name); + return lockout ? lockout->UUID() : std::string{}; } void Perl_Client_AddExpeditionLockout(Client* self, std::string expedition_name, std::string event_name, uint32 seconds) { - self->AddNewExpeditionLockout(expedition_name, event_name, seconds); + self->AddDzLockout(expedition_name, event_name, seconds); } void Perl_Client_AddExpeditionLockout(Client* self, std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) { - self->AddNewExpeditionLockout(expedition_name, event_name, seconds, uuid); + self->AddDzLockout(expedition_name, event_name, seconds, uuid); } void Perl_Client_AddExpeditionLockoutDuration(Client* self, std::string expedition_name, std::string event_name, int seconds) { - self->AddExpeditionLockoutDuration(expedition_name, event_name, seconds, {}, true); + auto lockout = DzLockout::Create(expedition_name, event_name, seconds); + self->AddDzLockoutDuration(lockout, seconds, {}, true); } void Perl_Client_AddExpeditionLockoutDuration(Client* self, std::string expedition_name, std::string event_name, int seconds, std::string uuid) { - self->AddExpeditionLockoutDuration(expedition_name, event_name, seconds, uuid, true); + auto lockout = DzLockout::Create(expedition_name, event_name, seconds, uuid); + self->AddDzLockoutDuration(lockout, seconds, uuid, true); } void Perl_Client_RemoveAllExpeditionLockouts(Client* self) { - self->RemoveAllExpeditionLockouts({}, true); + self->RemoveDzLockouts({}, true); } void Perl_Client_RemoveAllExpeditionLockouts(Client* self, std::string expedition_name) { - self->RemoveAllExpeditionLockouts(expedition_name, true); + self->RemoveDzLockouts(expedition_name, true); } void Perl_Client_RemoveExpeditionLockout(Client* self, std::string expedition_name, std::string event_name) { - self->RemoveExpeditionLockout(expedition_name, event_name, true); + self->RemoveDzLockout(expedition_name, event_name, true); } bool Perl_Client_HasExpeditionLockout(Client* self, std::string expedition_name, std::string event_name) { - return self->HasExpeditionLockout(expedition_name, event_name); + return self->HasDzLockout(expedition_name, event_name); } void Perl_Client_MovePCDynamicZone(Client* self, perl::scalar zone) @@ -3389,9 +3391,9 @@ void perl_register_client() package.add("CountAugmentEquippedByID", &Perl_Client_CountAugmentEquippedByID); package.add("CountItem", &Perl_Client_CountItem); package.add("CountItemEquippedByID", &Perl_Client_CountItemEquippedByID); - package.add("CreateExpedition", (Expedition*(*)(Client*, perl::reference))&Perl_Client_CreateExpedition); - package.add("CreateExpedition", (Expedition*(*)(Client*, std::string, uint32, uint32, std::string, uint32, uint32))&Perl_Client_CreateExpedition); - package.add("CreateExpedition", (Expedition*(*)(Client*, std::string, uint32, uint32, std::string, uint32, uint32, bool))&Perl_Client_CreateExpedition); + package.add("CreateExpedition", (DynamicZone*(*)(Client*, perl::reference))&Perl_Client_CreateExpedition); + package.add("CreateExpedition", (DynamicZone*(*)(Client*, std::string, uint32, uint32, std::string, uint32, uint32))&Perl_Client_CreateExpedition); + package.add("CreateExpedition", (DynamicZone*(*)(Client*, std::string, uint32, uint32, std::string, uint32, uint32, bool))&Perl_Client_CreateExpedition); package.add("CreateExpeditionFromTemplate", &Perl_Client_CreateExpeditionFromTemplate); package.add("CreateTaskDynamicZone", &Perl_Client_CreateTaskDynamicZone); package.add("DecreaseByID", &Perl_Client_DecreaseByID); diff --git a/zone/perl_expedition.cpp b/zone/perl_expedition.cpp index c46cb907c..a9b485f9a 100644 --- a/zone/perl_expedition.cpp +++ b/zone/perl_expedition.cpp @@ -3,214 +3,209 @@ #ifdef EMBPERL_XS_CLASSES #include "embperl.h" -#include "expedition.h" +#include "dynamic_zone.h" #include "../common/zone_store.h" #include "../common/global_define.h" -void Perl_Expedition_AddLockout(Expedition* self, std::string event_name, uint32_t seconds) +void Perl_Expedition_AddLockout(DynamicZone* self, std::string event_name, uint32_t seconds) { self->AddLockout(event_name, seconds); } -void Perl_Expedition_AddLockoutDuration(Expedition* self, std::string event_name, int seconds) +void Perl_Expedition_AddLockoutDuration(DynamicZone* self, std::string event_name, int seconds) { self->AddLockoutDuration(event_name, seconds); } -void Perl_Expedition_AddLockoutDuration(Expedition* self, std::string event_name, int seconds, bool members_only) +void Perl_Expedition_AddLockoutDuration(DynamicZone* self, std::string event_name, int seconds, bool members_only) { self->AddLockoutDuration(event_name, seconds, members_only); } -void Perl_Expedition_AddReplayLockout(Expedition* self, uint32_t seconds) +void Perl_Expedition_AddReplayLockout(DynamicZone* self, uint32_t seconds) { - self->AddReplayLockout(seconds); + self->AddLockout(DzLockout::ReplayTimer, seconds); } -void Perl_Expedition_AddReplayLockoutDuration(Expedition* self, int seconds) +void Perl_Expedition_AddReplayLockoutDuration(DynamicZone* self, int seconds) { - self->AddReplayLockoutDuration(seconds); + self->AddLockoutDuration(DzLockout::ReplayTimer, seconds);; } -void Perl_Expedition_AddReplayLockoutDuration(Expedition* self, int seconds, bool members_only) +void Perl_Expedition_AddReplayLockoutDuration(DynamicZone* self, int seconds, bool members_only) { - self->AddReplayLockoutDuration(seconds, members_only); + self->AddLockoutDuration(DzLockout::ReplayTimer, seconds, members_only); } -uint32_t Perl_Expedition_GetDynamicZoneID(Expedition* self) -{ - return self->GetDynamicZone()->GetID(); -} - -uint32_t Perl_Expedition_GetID(Expedition* self) +uint32_t Perl_Expedition_GetID(DynamicZone* self) { return self->GetID(); } -uint16_t Perl_Expedition_GetInstanceID(Expedition* self) +uint16_t Perl_Expedition_GetInstanceID(DynamicZone* self) { - return self->GetDynamicZone()->GetInstanceID(); + return self->GetInstanceID(); } -std::string Perl_Expedition_GetLeaderName(Expedition* self) +std::string Perl_Expedition_GetLeaderName(DynamicZone* self) { return self->GetLeaderName(); } -perl::reference Perl_Expedition_GetLockouts(Expedition* self) +perl::reference Perl_Expedition_GetLockouts(DynamicZone* self) { perl::hash table; - auto lockouts = self->GetLockouts(); + const auto& lockouts = self->GetLockouts(); for (const auto& lockout : lockouts) { - table[lockout.first] = lockout.second.GetSecondsRemaining(); + table[lockout.Event()] = lockout.GetSecondsRemaining(); } return perl::reference(table); } -std::string Perl_Expedition_GetLootEventByNPCTypeID(Expedition* self, uint32_t npc_type_id) +std::string Perl_Expedition_GetLootEventByNPCTypeID(DynamicZone* self, uint32_t npc_type_id) { - return self->GetLootEventByNPCTypeID(npc_type_id); + return self->GetLootEvent(npc_type_id, DzLootEvent::Type::NpcType); } -std::string Perl_Expedition_GetLootEventBySpawnID(Expedition* self, uint32_t spawn_id) +std::string Perl_Expedition_GetLootEventBySpawnID(DynamicZone* self, uint32_t spawn_id) { - return self->GetLootEventBySpawnID(spawn_id); + return self->GetLootEvent(spawn_id, DzLootEvent::Type::Entity); } -uint32_t Perl_Expedition_GetMemberCount(Expedition* self) +uint32_t Perl_Expedition_GetMemberCount(DynamicZone* self) { - return self->GetDynamicZone()->GetMemberCount(); + return self->GetMemberCount(); } -perl::reference Perl_Expedition_GetMembers(Expedition* self) +perl::reference Perl_Expedition_GetMembers(DynamicZone* self) { perl::hash table; - for (const auto& member : self->GetDynamicZone()->GetMembers()) + for (const auto& member : self->GetMembers()) { table[member.name] = member.id; } return perl::reference(table); } -std::string Perl_Expedition_GetName(Expedition* self) +std::string Perl_Expedition_GetName(DynamicZone* self) { return self->GetName(); } -uint32_t Perl_Expedition_GetSecondsRemaining(Expedition* self) +uint32_t Perl_Expedition_GetSecondsRemaining(DynamicZone* self) { - return self->GetDynamicZone()->GetSecondsRemaining(); + return self->GetSecondsRemaining(); } -std::string Perl_Expedition_GetUUID(Expedition* self) +std::string Perl_Expedition_GetUUID(DynamicZone* self) { - return self->GetDynamicZone()->GetUUID(); + return self->GetUUID(); } -uint16_t Perl_Expedition_GetZoneID(Expedition* self) +uint16_t Perl_Expedition_GetZoneID(DynamicZone* self) { - return self->GetDynamicZone()->GetZoneID(); + return self->GetZoneID(); } -std::string Perl_Expedition_GetZoneName(Expedition* self) +std::string Perl_Expedition_GetZoneName(DynamicZone* self) { - return ZoneName(self->GetDynamicZone()->GetZoneID()); + return ZoneName(self->GetZoneID()); } -uint32_t Perl_Expedition_GetZoneVersion(Expedition* self) +uint32_t Perl_Expedition_GetZoneVersion(DynamicZone* self) { - return self->GetDynamicZone()->GetZoneVersion(); + return self->GetZoneVersion(); } -bool Perl_Expedition_HasLockout(Expedition* self, std::string event_name) +bool Perl_Expedition_HasLockout(DynamicZone* self, std::string event_name) { return self->HasLockout(event_name); } -bool Perl_Expedition_HasReplayLockout(Expedition* self) +bool Perl_Expedition_HasReplayLockout(DynamicZone* self) { return self->HasReplayLockout(); } -bool Perl_Expedition_IsLocked(Expedition* self) +bool Perl_Expedition_IsLocked(DynamicZone* self) { return self->IsLocked(); } -void Perl_Expedition_RemoveCompass(Expedition* self) +void Perl_Expedition_RemoveCompass(DynamicZone* self) { - self->GetDynamicZone()->SetCompass(0, 0, 0, 0, true); + self->SetCompass(0, 0, 0, 0, true); } -void Perl_Expedition_RemoveLockout(Expedition* self, std::string event_name) +void Perl_Expedition_RemoveLockout(DynamicZone* self, std::string event_name) { self->RemoveLockout(event_name); } -void Perl_Expedition_SetCompass(Expedition* self, perl::scalar zone, float x, float y, float z) +void Perl_Expedition_SetCompass(DynamicZone* self, perl::scalar zone, float x, float y, float z) { uint32_t zone_id = zone.is_string() ? ZoneID(zone.c_str()) : zone.as(); - self->GetDynamicZone()->SetCompass(zone_id, x, y, z, true); + self->SetCompass(zone_id, x, y, z, true); } -void Perl_Expedition_SetLocked(Expedition* self, bool locked) +void Perl_Expedition_SetLocked(DynamicZone* self, bool locked) { - self->SetLocked(locked, ExpeditionLockMessage::None); + self->SetLocked(locked, true); } -void Perl_Expedition_SetLocked(Expedition* self, bool locked, int lock_msg) +void Perl_Expedition_SetLocked(DynamicZone* self, bool locked, int lock_msg) { - self->SetLocked(locked, static_cast(lock_msg), true); + self->SetLocked(locked, true, static_cast(lock_msg)); } -void Perl_Expedition_SetLocked(Expedition* self, bool locked, int lock_msg, uint32_t color) +void Perl_Expedition_SetLocked(DynamicZone* self, bool locked, int lock_msg, uint32_t color) { - self->SetLocked(locked, static_cast(lock_msg), true, color); + self->SetLocked(locked, true, static_cast(lock_msg), color); } -void Perl_Expedition_SetLootEventByNPCTypeID(Expedition* self, uint32_t npc_type_id, std::string event_name) +void Perl_Expedition_SetLootEventByNPCTypeID(DynamicZone* self, uint32_t npc_type_id, std::string event_name) { - self->SetLootEventByNPCTypeID(npc_type_id, event_name); + self->SetLootEvent(npc_type_id, event_name, DzLootEvent::Type::NpcType); } -void Perl_Expedition_SetLootEventBySpawnID(Expedition* self, uint32_t entity_id, std::string event_name) +void Perl_Expedition_SetLootEventBySpawnID(DynamicZone* self, uint32_t entity_id, std::string event_name) { - self->SetLootEventBySpawnID(entity_id, event_name); + self->SetLootEvent(entity_id, event_name, DzLootEvent::Type::Entity); } -void Perl_Expedition_SetReplayLockoutOnMemberJoin(Expedition* self, bool enable) +void Perl_Expedition_SetReplayLockoutOnMemberJoin(DynamicZone* self, bool enable) { - self->SetReplayLockoutOnMemberJoin(enable, true); + self->SetReplayOnJoin(enable, true); } -void Perl_Expedition_SetSafeReturn(Expedition* self, perl::scalar zone, float x, float y, float z, float heading) +void Perl_Expedition_SetSafeReturn(DynamicZone* self, perl::scalar zone, float x, float y, float z, float heading) { uint32_t zone_id = zone.is_string() ? ZoneID(zone.c_str()) : zone.as(); - self->GetDynamicZone()->SetSafeReturn(zone_id, x, y, z, heading, true); + self->SetSafeReturn(zone_id, x, y, z, heading, true); } -void Perl_Expedition_SetSecondsRemaining(Expedition* self, uint32_t seconds_remaining) +void Perl_Expedition_SetSecondsRemaining(DynamicZone* self, uint32_t seconds_remaining) { - self->GetDynamicZone()->SetSecondsRemaining(seconds_remaining); + self->SetSecondsRemaining(seconds_remaining); } -void Perl_Expedition_SetSwitchID(Expedition* self, int dz_switch_id) +void Perl_Expedition_SetSwitchID(DynamicZone* self, int dz_switch_id) { - self->GetDynamicZone()->SetSwitchID(dz_switch_id, true); + self->SetSwitchID(dz_switch_id, true); } -void Perl_Expedition_SetZoneInLocation(Expedition* self, float x, float y, float z, float heading) +void Perl_Expedition_SetZoneInLocation(DynamicZone* self, float x, float y, float z, float heading) { - self->GetDynamicZone()->SetZoneInLocation(x, y, z, heading, true); + self->SetZoneInLocation(x, y, z, heading, true); } -void Perl_Expedition_UpdateLockoutDuration(Expedition* self, std::string event_name, uint32_t seconds) +void Perl_Expedition_UpdateLockoutDuration(DynamicZone* self, std::string event_name, uint32_t seconds) { self->UpdateLockoutDuration(event_name, seconds); } -void Perl_Expedition_UpdateLockoutDuration(Expedition* self, std::string event_name, uint32_t seconds, bool members_only) +void Perl_Expedition_UpdateLockoutDuration(DynamicZone* self, std::string event_name, uint32_t seconds, bool members_only) { self->UpdateLockoutDuration(event_name, seconds, members_only); } @@ -219,14 +214,14 @@ void perl_register_expedition() { perl::interpreter perl(PERL_GET_THX); - auto package = perl.new_class("Expedition"); + auto package = perl.new_class("Expedition"); package.add("AddLockout", &Perl_Expedition_AddLockout); - package.add("AddLockoutDuration", (void(*)(Expedition*, std::string, int))&Perl_Expedition_AddLockoutDuration); - package.add("AddLockoutDuration", (void(*)(Expedition*, std::string, int, bool))&Perl_Expedition_AddLockoutDuration); + package.add("AddLockoutDuration", (void(*)(DynamicZone*, std::string, int))&Perl_Expedition_AddLockoutDuration); + package.add("AddLockoutDuration", (void(*)(DynamicZone*, std::string, int, bool))&Perl_Expedition_AddLockoutDuration); package.add("AddReplayLockout", &Perl_Expedition_AddReplayLockout); - package.add("AddReplayLockoutDuration", (void(*)(Expedition*, int))&Perl_Expedition_AddReplayLockoutDuration); - package.add("AddReplayLockoutDuration", (void(*)(Expedition*, int, bool))&Perl_Expedition_AddReplayLockoutDuration); - package.add("GetDynamicZoneID", &Perl_Expedition_GetDynamicZoneID); + package.add("AddReplayLockoutDuration", (void(*)(DynamicZone*, int))&Perl_Expedition_AddReplayLockoutDuration); + package.add("AddReplayLockoutDuration", (void(*)(DynamicZone*, int, bool))&Perl_Expedition_AddReplayLockoutDuration); + package.add("GetDynamicZoneID", &Perl_Expedition_GetID); package.add("GetID", &Perl_Expedition_GetID); package.add("GetInstanceID", &Perl_Expedition_GetInstanceID); package.add("GetLeaderName", &Perl_Expedition_GetLeaderName); @@ -247,9 +242,9 @@ void perl_register_expedition() package.add("RemoveCompass", &Perl_Expedition_RemoveCompass); package.add("RemoveLockout", &Perl_Expedition_RemoveLockout); package.add("SetCompass", &Perl_Expedition_SetCompass); - package.add("SetLocked", (void(*)(Expedition*, bool))&Perl_Expedition_SetLocked); - package.add("SetLocked", (void(*)(Expedition*, bool, int))&Perl_Expedition_SetLocked); - package.add("SetLocked", (void(*)(Expedition*, bool, int, uint32_t))&Perl_Expedition_SetLocked); + package.add("SetLocked", (void(*)(DynamicZone*, bool))&Perl_Expedition_SetLocked); + package.add("SetLocked", (void(*)(DynamicZone*, bool, int))&Perl_Expedition_SetLocked); + package.add("SetLocked", (void(*)(DynamicZone*, bool, int, uint32_t))&Perl_Expedition_SetLocked); package.add("SetLootEventByNPCTypeID", &Perl_Expedition_SetLootEventByNPCTypeID); package.add("SetLootEventBySpawnID", &Perl_Expedition_SetLootEventBySpawnID); package.add("SetReplayLockoutOnMemberJoin", &Perl_Expedition_SetReplayLockoutOnMemberJoin); @@ -257,8 +252,8 @@ void perl_register_expedition() package.add("SetSecondsRemaining", &Perl_Expedition_SetSecondsRemaining); package.add("SetSwitchID", &Perl_Expedition_SetSwitchID); package.add("SetZoneInLocation", &Perl_Expedition_SetZoneInLocation); - package.add("UpdateLockoutDuration", (void(*)(Expedition*, std::string, uint32_t))&Perl_Expedition_UpdateLockoutDuration); - package.add("UpdateLockoutDuration", (void(*)(Expedition*, std::string, uint32_t, bool))&Perl_Expedition_UpdateLockoutDuration); + package.add("UpdateLockoutDuration", (void(*)(DynamicZone*, std::string, uint32_t))&Perl_Expedition_UpdateLockoutDuration); + package.add("UpdateLockoutDuration", (void(*)(DynamicZone*, std::string, uint32_t, bool))&Perl_Expedition_UpdateLockoutDuration); } void perl_register_expedition_lock_messages() @@ -266,9 +261,9 @@ void perl_register_expedition_lock_messages() perl::interpreter perl(PERL_GET_THX); auto package = perl.new_package("ExpeditionLockMessage"); - package.add_const("None", static_cast(ExpeditionLockMessage::None)); - package.add_const("Close", static_cast(ExpeditionLockMessage::Close)); - package.add_const("Begin", static_cast(ExpeditionLockMessage::Begin)); + package.add_const("None", static_cast(DzLockMsg::None)); + package.add_const("Close", static_cast(DzLockMsg::Close)); + package.add_const("Begin", static_cast(DzLockMsg::Begin)); } #endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_groups.cpp b/zone/perl_groups.cpp index 3c87a494f..f93e31db8 100644 --- a/zone/perl_groups.cpp +++ b/zone/perl_groups.cpp @@ -129,12 +129,12 @@ Client* Perl_Group_GetMember(Group* self, int member_index) // @categories Accou bool Perl_Group_DoesAnyMemberHaveExpeditionLockout(Group* self, std::string expedition_name, std::string event_name) { - return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name); + return self->AnyMemberHasDzLockout(expedition_name, event_name); } bool Perl_Group_DoesAnyMemberHaveExpeditionLockout(Group* self, std::string expedition_name, std::string event_name, int max_check_count) { - return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name, max_check_count); + return self->AnyMemberHasDzLockout(expedition_name, event_name); // max_check_count deprecated } uint32_t Perl_Group_GetLowestLevel(Group* self) // @categories Script Utility, Group diff --git a/zone/perl_raids.cpp b/zone/perl_raids.cpp index 6b5668b6f..c01785121 100644 --- a/zone/perl_raids.cpp +++ b/zone/perl_raids.cpp @@ -139,12 +139,12 @@ Client* Perl_Raid_GetMember(Raid* self, int member_index) // @categories Raid bool Perl_Raid_DoesAnyMemberHaveExpeditionLockout(Raid* self, std::string expedition_name, std::string event_name) { - return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name); + return self->AnyMemberHasDzLockout(expedition_name, event_name); } bool Perl_Raid_DoesAnyMemberHaveExpeditionLockout(Raid* self, std::string expedition_name, std::string event_name, int max_check_count) { - return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name, max_check_count); + return self->AnyMemberHasDzLockout(expedition_name, event_name); // max_check_count deprecated } int Perl_Raid_GetGroupNumber(Raid* self, int member_index) { diff --git a/zone/raids.cpp b/zone/raids.cpp index dd127684b..26592214f 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -18,14 +18,15 @@ #include "../common/strings.h" #include "../common/events/player_event_logs.h" +#include "../common/repositories/character_expedition_lockouts_repository.h" #include "../common/repositories/raid_details_repository.h" #include "../common/repositories/raid_members_repository.h" #include "../common/raid.h" #include "client.h" +#include "dynamic_zone.h" #include "entity.h" -#include "expedition.h" #include "groups.h" #include "mob.h" #include "raids.h" @@ -2138,28 +2139,21 @@ std::vector Raid::GetMembers() const return raid_members; } -bool Raid::DoesAnyMemberHaveExpeditionLockout(const std::string& expedition_name, const std::string& event_name, int max_check_count) +bool Raid::AnyMemberHasDzLockout(const std::string& expedition, const std::string& event) { - auto raid_members = GetMembers(); - - if (max_check_count > 0) { - // priority is leader, group number, then ungrouped members - std::sort(raid_members.begin(), raid_members.end(), - [&](const RaidMember& lhs, const RaidMember& rhs) { - if (lhs.is_raid_leader) { - return true; - } else if (rhs.is_raid_leader) { - return false; - } - return lhs.group_number < rhs.group_number; - }); - - raid_members.resize(max_check_count); + std::vector names; + for (const auto& mbr : members) + { + if (!mbr.member && !mbr.is_bot && mbr.member_name[0]) + { + names.emplace_back(mbr.member_name); // out of zone member + } + else if (mbr.member && !mbr.is_bot && mbr.member->HasDzLockout(expedition, event)) + { + return true; + } } - - return std::any_of(raid_members.begin(), raid_members.end(), [&](const RaidMember& raid_member) { - return Expedition::HasLockoutByCharacterName(raid_member.member_name, expedition_name, event_name); - }); + return !CharacterExpeditionLockoutsRepository::GetLockouts(database, names, expedition, event).empty(); } Mob* Raid::GetRaidMainAssistOne() diff --git a/zone/raids.h b/zone/raids.h index 6fdf13206..5f59f1e45 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -271,7 +271,7 @@ public: void QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_required = true, bool ignore_sender = true, float distance = 0, bool group_only = true); - bool DoesAnyMemberHaveExpeditionLockout(const std::string& expedition_name, const std::string& event_name, int max_check_count = 0); + bool AnyMemberHasDzLockout(const std::string& expedition, const std::string& event); std::vector GetMembers() const; std::vector GetRaidGroupMembers(uint32 gid); diff --git a/zone/string_ids.h b/zone/string_ids.h index 34efb5ed9..5e929db02 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -328,42 +328,42 @@ #define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1! #define TASK_UPDATED 3471 //Your task '%1' has been updated. #define YOU_ASSIGNED_TASK 3472 //You have been assigned the task '%1'. -#define EXPEDITION_YOU_BELONG 3500 //You cannot create this expedition since you already belong to another. -#define EXPEDITION_YOU_PLAYED_HERE 3501 //You cannot create this expedition for another %1d:%2h:%3m since you have recently played here. -#define REQUIRED_PLAYER_COUNT 3503 //You do not meet the player count requirement. You have %1 players. You must have at least %2 and no more than %3. -#define EXPEDITION_REPLAY_TIMER 3504 //%1 cannot be added to this expedition for another %2D:%3H:%4M since they have recently played in this area. -#define EXPEDITION_AVAILABLE 3507 //%1 is now available to you. +#define DZ_YOU_BELONG 3500 //You cannot create this expedition since you already belong to another. +#define DZ_REPLAY_YOU 3501 //You cannot create this expedition for another %1d:%2h:%3m since you have recently played here. +#define DZ_PLAYER_COUNT 3503 //You do not meet the player count requirement. You have %1 players. You must have at least %2 and no more than %3. +#define DZ_REPLAY_OTHER 3504 //%1 cannot be added to this expedition for another %2D:%3H:%4M since they have recently played in this area. +#define DZ_AVAILABLE 3507 //%1 is now available to you. #define DZADD_INVITE 3508 //Sending an invitation to: %1. #define DZ_PREVENT_ENTERING 3510 //A strange magical presence prevents you from entering. It's too dangerous to enter at the moment. #define DZADD_INVITE_FAIL 3511 //%1 could not be invited to join you. -#define UNABLE_RETRIEVE_LEADER 3512 //Unable to retrieve information on the leader to check permissions. -#define EXPEDITION_NOT_LEADER 3513 //You are not the expedition leader, only %1 can issue this command. -#define EXPEDITION_NOT_MEMBER 3514 //%1 is not a member of this expedition. -#define EXPEDITION_REMOVED 3516 //%1 has been removed from %2. +#define DZ_NO_LEADER_INFO 3512 //Unable to retrieve information on the leader to check permissions. +#define DZ_NOT_LEADER 3513 //You are not the expedition leader, only %1 can issue this command. +#define DZ_NOT_MEMBER 3514 //%1 is not a member of this expedition. +#define DZ_REMOVED 3516 //%1 has been removed from %2. #define DZSWAP_INVITE 3517 //Sending an invitation to: %1. They must accept in order to swap party members. -#define DZMAKELEADER_NOT_ONLINE 3518 //%1 is not currently online. You can only transfer leadership to an online member of the expedition you are in. -#define DZLIST_REPLAY_TIMER 3519 //You have %1d:%2h:%3m remaining until you may enter %4. -#define DZMAKELEADER_NAME 3520 //%1 has been made the leader for this expedition. -#define DZMAKELEADER_YOU 3521 //You have been made the leader of this expedition. -#define EXPEDITION_INVITE_ACCEPTED 3522 //%1 has accepted your offer to join your expedition. -#define EXPEDITION_MEMBER_ADDED 3523 //%1 has been added to %2. -#define EXPEDITION_INVITE_ERROR 3524 //%1 accepted your offer to join your expedition but could not due to error(s). -#define EXPEDITION_INVITE_DECLINED 3525 //%1 has declined your offer to join your expedition. -#define EXPEDITION_ASKED_TO_JOIN 3527 //%1 has asked you to join the expedition: %2. Would you like to join? -#define DYNAMICZONE_WAY_IS_BLOCKED 3528 //The way is blocked to you. Perhaps you would be able to enter if there was a reason to come here. -#define EXPEDITION_NO_TIMERS 3529 //You have no outstanding timers. -#define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end. -#define EXPEDITION_LEADER 3552 //Expedition Leader: %1 -#define EXPEDITION_MEMBERS 3553 //Expedition Members: %1 -#define EXPEDITION_EVENT_TIMER 3561 //%1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3D:%4H:%5M until they can experience it again. They may be added to the expedition later, once %2 has been completed. +#define DZ_LEADER_OFFLINE 3518 //%1 is not currently online. You can only transfer leadership to an online member of the expedition you are in. +#define DZ_TIMER 3519 //You have %1d:%2h:%3m remaining until you may enter %4. +#define DZ_LEADER_NAME 3520 //%1 has been made the leader for this expedition. +#define DZ_LEADER_YOU 3521 //You have been made the leader of this expedition. +#define DZ_INVITE_ACCEPTED 3522 //%1 has accepted your offer to join your expedition. +#define DZ_ADDED 3523 //%1 has been added to %2. +#define DZ_INVITE_ERROR 3524 //%1 accepted your offer to join your expedition but could not due to error(s). +#define DZ_INVITE_DECLINED 3525 //%1 has declined your offer to join your expedition. +#define DZ_INVITED 3527 //%1 has asked you to join the expedition: %2. Would you like to join? +#define DZ_WAY_IS_BLOCKED 3528 //The way is blocked to you. Perhaps you would be able to enter if there was a reason to come here. +#define DZ_NO_TIMERS 3529 //You have no outstanding timers. +#define DZ_MINUTES_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end. +#define DZ_LEADER 3552 //Expedition Leader: %1 +#define DZ_MEMBERS 3553 //Expedition Members: %1 +#define DZ_EVENT_TIMER 3561 //%1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3D:%4H:%5M until they can experience it again. They may be added to the expedition later, once %2 has been completed. #define LOOT_NOT_ALLOWED 3562 //You are not allowed to loot the item: %1. #define DZ_UNABLE_RETRIEVE_LEADER 3583 //Unable to retrieve dynamic zone leader to check permissions. #define DZADD_NOT_ALLOWING 3585 //The expedition is not allowing players to be added. #define DZADD_NOT_ONLINE 3586 //%1 is not currently online. A player needs to be online to be added to a Dynamic Zone #define DZADD_EXCEED_MAX 3587 //You can not add another player since you currently have the maximum number of players allowed (%1) in this zone. #define DZADD_ALREADY_PART 3588 //You can not add %1 since they are already part of this zone. -#define DZADD_LEAVE_ZONE_FIRST 3589 //You can not add %1 since they first need to leave the zone before being allowed back in. -#define DZADD_ALREADY_ASSIGNED 3590 //%1 can not be added to this dynamic zone since they are already assigned to another dynamic zone. +#define DZADD_LEAVE_ZONE 3589 //You can not add %1 since they first need to leave the zone before being allowed back in. +#define DZADD_ALREADY_OTHER 3590 //%1 can not be added to this dynamic zone since they are already assigned to another dynamic zone. #define DZADD_REPLAY_TIMER 3591 //%1 can not be added to this dynamic zone for another %2D:%3H:%4M since they have recently played this zone. #define DZADD_EVENT_TIMER 3592 //%1 can not be added to this dynamic zone since they have recently experienced %2. They must wait for another %3D:%4H:%5M, or until event %2 has occurred. #define DZADD_PENDING 3593 //%1 currently has an outstanding invitation to join this Dynamic Zone. diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index 7fa49a180..39767a7c0 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -2288,12 +2288,10 @@ void ClientTaskState::CreateTaskDynamicZone(Client* client, int task_id, Dynamic dz_request.SetMinPlayers(task->min_players); dz_request.SetMaxPlayers(task->max_players); - // a task might create a dz from an objective so override dz duration to time remaining - // live probably creates the dz with the shared task and just adds members for objectives + // a task might create a dz from an element so override dz duration to time remaining std::chrono::seconds seconds(TaskTimeLeft(task_id)); if (task->duration == 0 || seconds.count() < 0) { - // todo: maybe add a rule for duration // cap unlimited duration tasks so instance isn't held indefinitely // expected behavior is to re-acquire any unlimited tasks that have an expired dz seconds = std::chrono::duration_cast(std::chrono::hours(24)); @@ -2301,32 +2299,21 @@ void ClientTaskState::CreateTaskDynamicZone(Client* client, int task_id, Dynamic dz_request.SetDuration(static_cast(seconds.count())); - if (task->type == TaskType::Task || task->type == TaskType::Quest) - { - if (task->type == TaskType::Task) { - dz_request.SetType(DynamicZoneType::Task); - } else { - dz_request.SetType(DynamicZoneType::Quest); - } - - // todo: can enable non-shared task dz creation when dz ids are persisted for them (db also) - //DynamicZoneMember solo_member{ client->CharacterID(), client->GetCleanName() }; - //DynamicZone::CreateNew(dz_request, { solo_member }); - } - else if (task->type == TaskType::Shared) + if (task->type == TaskType::Shared) { dz_request.SetType(DynamicZoneType::Mission); // shared task missions are created in world - EQ::Net::DynamicPacket dyn_pack = dz_request.GetSerializedDzPacket(); + std::ostringstream ss = dz_request.GetSerialized(); + std::string_view sv = ss.view(); - auto pack_size = sizeof(ServerSharedTaskCreateDynamicZone_Struct) + dyn_pack.Length(); + auto pack_size = sizeof(ServerSharedTaskCreateDynamicZone_Struct) + sv.size(); auto pack = std::make_unique(ServerOP_SharedTaskCreateDynamicZone, static_cast(pack_size)); auto buf = reinterpret_cast(pack->pBuffer); buf->source_character_id = client->CharacterID(); buf->task_id = task_id; - buf->cereal_size = static_cast(dyn_pack.Length()); - memcpy(buf->cereal_data, dyn_pack.Data(), dyn_pack.Length()); + buf->cereal_size = static_cast(sv.size()); + memcpy(buf->cereal_data, sv.data(), sv.size()); worldserver.SendPacket(pack.get()); } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 4dd61f588..50d0e9e19 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -40,8 +40,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "client.h" #include "command.h" #include "corpse.h" +#include "dynamic_zone.h" #include "entity.h" -#include "expedition.h" #include "quest_parser_collection.h" #include "guild_mgr.h" #include "mob.h" @@ -3601,20 +3601,10 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } - case ServerOP_ExpeditionCreate: - case ServerOP_ExpeditionLockout: - case ServerOP_ExpeditionLockoutDuration: - case ServerOP_ExpeditionLockState: - case ServerOP_ExpeditionReplayOnJoin: - case ServerOP_ExpeditionDzAddPlayer: - case ServerOP_ExpeditionDzMakeLeader: - case ServerOP_ExpeditionCharacterLockout: - { - Expedition::HandleWorldMessage(pack); - break; - } case ServerOP_DzCreated: case ServerOP_DzDeleted: + case ServerOP_DzAddPlayer: + case ServerOP_DzMakeLeader: case ServerOP_DzAddRemoveMember: case ServerOP_DzSwapMembers: case ServerOP_DzRemoveAllMembers: @@ -3628,6 +3618,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case ServerOP_DzLeaderChanged: case ServerOP_DzExpireWarning: case ServerOP_DzMovePC: + case ServerOP_DzLock: + case ServerOP_DzReplayOnJoin: + case ServerOP_DzLockout: + case ServerOP_DzLockoutDuration: + case ServerOP_DzCharacterLockout: { DynamicZone::HandleWorldMessage(pack); break; diff --git a/zone/zone.cpp b/zone/zone.cpp index 5488ed914..c6d029189 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -36,7 +36,7 @@ #include "../common/strings.h" #include "../common/eqemu_logsys.h" -#include "expedition.h" +#include "dynamic_zone.h" #include "guild_mgr.h" #include "map.h" #include "npc.h" @@ -1161,7 +1161,6 @@ bool Zone::Init(bool is_static) { LoadDynamicZoneTemplates(); DynamicZone::CacheAllFromDatabase(); - Expedition::CacheAllFromDatabase(); content_db.LoadGlobalLoot(); @@ -1622,7 +1621,7 @@ bool Zone::Process() { if (minutes_warning > 0) { // expedition expire warnings are handled by world - auto expedition = Expedition::FindCachedExpeditionByZoneInstance(GetZoneID(), GetInstanceID()); + auto expedition = DynamicZone::FindExpeditionByZone(GetZoneID(), GetInstanceID()); if (!expedition) { entity_list.ExpeditionWarning(minutes_warning); diff --git a/zone/zone.h b/zone/zone.h index 2d8a33b60..967d87f79 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -93,7 +93,6 @@ struct ZoneEXPModInfo { }; class Client; -class Expedition; class Map; class Mob; class WaterMap; @@ -236,7 +235,6 @@ public: std::vector zone_grid_entries; std::unordered_map> dynamic_zone_cache; - std::unordered_map> expedition_cache; std::unordered_map dz_template_cache; std::unordered_map exp_modifiers; diff --git a/zone/zoning.cpp b/zone/zoning.cpp index d515ff168..f7a32e502 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -21,7 +21,7 @@ #include "../common/rulesys.h" #include "../common/strings.h" -#include "expedition.h" +#include "dynamic_zone.h" #include "queryserv.h" #include "quest_parser_collection.h" #include "string_ids.h" @@ -495,13 +495,12 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc if(GetPet()) entity_list.RemoveFromHateLists(GetPet()); - if (GetPendingExpeditionInviteID() != 0) + if (GetPendingDzInviteID() != 0) { // live re-invites if client zoned with a pending invite, save pending invite info in world - auto expedition = Expedition::FindCachedExpeditionByID(GetPendingExpeditionInviteID()); - if (expedition) + if (auto dz = DynamicZone::FindDynamicZoneByID(GetPendingDzInviteID(), DynamicZoneType::Expedition)) { - expedition->SendWorldPendingInvite(m_pending_expedition_invite, GetName()); + dz->SendWorldPendingInvite(m_dz_invite, GetName()); } }