From 55155ff8002f641cec6b2f43e5452675a12b0d0d Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sat, 15 Feb 2025 19:40:35 -0500 Subject: [PATCH] [Expeditions] Move expedition code into DynamicZone (#4672) This removes the separate Expedition class and moves lockout code and /dz command handlers into DynamicZone classes. It also refactors some code to reduce bloat and some database usage. This completes the effort of moving everything to DynamicZone that started when implementing shared tasks. It also makes sense to do this since expeditions are just dynamic zones internally despite dzs being used for other types. Expedition specific things are just handled with dz type checks. Functionally nothing should change. This is mainly internal refactoring and moving code around along with some bug fixes and reduced database usage. Main changes: - The `expeditions` database table has been removed - Expeditions no longer use a separate id, the expedition id is just the dz id - Expedition lock state and replay timer option were moved to the `dynamic_zones` table - Expeditions no longer have a separate cache from dynamic zones - Expedition creation no longer has every zone query the database to cache it - Expedition internal lockouts are now stored on DynamicZone - The `expedition_lockouts` table has been renamed to `dynamic_zone_lockouts` - Fixed a small bug with the UpdateLockoutDuration api where the internal lockout would get the time added twice in memory in the initiating zone (this api is likely rarely used) - Fixed an issue where use of the group/raid DoesAnyMemberHaveExpeditionLockout api would query once for every out of zone character. - This api now checks all members in the current zone first and only performs a single bulk query for out of zone members if that check is exhausted - Deprecated the max_check_count param of DoesAnyMemberHaveExpeditionLockout, the quest api still exists to avoid api break but a passed arg has no effect --- common/CMakeLists.txt | 10 +- common/database/database_update_manifest.cpp | 27 +- common/database_schema.h | 3 +- common/dynamic_zone_base.cpp | 357 +++- common/dynamic_zone_base.h | 82 +- common/dynamic_zone_lockout.cpp | 92 + common/dynamic_zone_lockout.h | 56 + common/eq_constants.h | 19 - common/expedition_lockout_timer.cpp | 101 -- common/expedition_lockout_timer.h | 76 - ...> base_dynamic_zone_lockouts_repository.h} | 88 +- .../base/base_dynamic_zones_repository.h | 24 + .../base/base_expeditions_repository.h | 415 ----- ...character_expedition_lockouts_repository.h | 201 ++- .../dynamic_zone_lockouts_repository.h | 84 + .../dynamic_zone_members_repository.h | 65 +- .../repositories/dynamic_zones_repository.h | 75 + .../expedition_lockouts_repository.h | 102 -- common/repositories/expeditions_repository.h | 134 -- .../instance_list_player_repository.h | 35 +- common/servertalk.h | 65 +- common/version.h | 2 +- world/CMakeLists.txt | 4 - world/cliententry.h | 6 +- world/clientlist.cpp | 11 +- world/dynamic_zone.cpp | 130 +- world/dynamic_zone.h | 3 +- world/dynamic_zone_manager.cpp | 319 +++- world/dynamic_zone_manager.h | 3 +- world/expedition_database.cpp | 78 - world/expedition_database.h | 35 - world/expedition_message.cpp | 151 -- world/expedition_message.h | 35 - world/main.cpp | 4 +- world/shared_task_manager.cpp | 2 +- world/shared_task_world_messaging.cpp | 2 +- world/world_boot.cpp | 7 +- world/zoneserver.cpp | 25 +- zone/CMakeLists.txt | 4 - zone/client.cpp | 228 ++- zone/client.h | 60 +- zone/client_packet.cpp | 16 +- zone/client_process.cpp | 2 +- zone/command.cpp | 1 - zone/common.h | 7 - zone/corpse.cpp | 2 +- zone/dynamic_zone.cpp | 1025 +++++++++-- zone/dynamic_zone.h | 100 +- zone/embparser_api.cpp | 72 +- zone/entity.cpp | 2 +- zone/expedition.cpp | 1502 ----------------- zone/expedition.h | 169 -- zone/expedition_database.cpp | 409 ----- zone/expedition_database.h | 62 - zone/expedition_request.cpp | 151 +- zone/expedition_request.h | 36 +- zone/gm_commands/dz.cpp | 281 +-- zone/gm_commands/dzkickplayers.cpp | 8 +- zone/groups.cpp | 27 +- zone/groups.h | 2 +- zone/lua_client.cpp | 40 +- zone/lua_expedition.cpp | 75 +- zone/lua_expedition.h | 13 +- zone/lua_general.cpp | 52 +- zone/lua_group.cpp | 4 +- zone/lua_raid.cpp | 4 +- zone/perl_client.cpp | 58 +- zone/perl_expedition.cpp | 165 +- zone/perl_groups.cpp | 4 +- zone/perl_raids.cpp | 4 +- zone/raids.cpp | 36 +- zone/raids.h | 2 +- zone/string_ids.h | 52 +- zone/task_client_state.cpp | 27 +- zone/worldserver.cpp | 21 +- zone/zone.cpp | 5 +- zone/zone.h | 2 - zone/zoning.cpp | 9 +- 78 files changed, 2889 insertions(+), 4778 deletions(-) create mode 100644 common/dynamic_zone_lockout.cpp create mode 100644 common/dynamic_zone_lockout.h delete mode 100644 common/expedition_lockout_timer.cpp delete mode 100644 common/expedition_lockout_timer.h rename common/repositories/base/{base_expedition_lockouts_repository.h => base_dynamic_zone_lockouts_repository.h} (81%) delete mode 100644 common/repositories/base/base_expeditions_repository.h create mode 100644 common/repositories/dynamic_zone_lockouts_repository.h delete mode 100644 common/repositories/expedition_lockouts_repository.h delete mode 100644 common/repositories/expeditions_repository.h delete mode 100644 world/expedition_database.cpp delete mode 100644 world/expedition_database.h delete mode 100644 world/expedition_message.cpp delete mode 100644 world/expedition_message.h delete mode 100644 zone/expedition.cpp delete mode 100644 zone/expedition.h delete mode 100644 zone/expedition_database.cpp delete mode 100644 zone/expedition_database.h 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()); } }