diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 67cf4433c..d0611ae65 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -16,6 +16,7 @@ SET(common_sources database_instances.cpp dbcore.cpp deity.cpp + dynamic_zone_base.cpp emu_constants.cpp emu_limits.cpp emu_opcodes.cpp @@ -466,6 +467,7 @@ SET(common_headers database_schema.h dbcore.h deity.h + dynamic_zone_base.h emu_constants.h emu_limits.h emu_opcodes.h diff --git a/common/dynamic_zone_base.cpp b/common/dynamic_zone_base.cpp new file mode 100644 index 000000000..8e29722d3 --- /dev/null +++ b/common/dynamic_zone_base.cpp @@ -0,0 +1,289 @@ +#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 "servertalk.h" + +DynamicZoneBase::DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry) +{ + LoadRepositoryResult(std::move(entry)); +} + +uint32_t DynamicZoneBase::Create() +{ + if (m_id != 0) + { + return m_id; + } + + if (GetInstanceID() == 0) + { + CreateInstance(); + } + + m_id = SaveToDatabase(); + + return m_id; +} + +uint32_t DynamicZoneBase::CreateInstance() +{ + if (m_instance_id) + { + LogDynamicZones("CreateInstance failed, instance id [{}] already created", m_instance_id); + return 0; + } + + if (!m_zone_id) + { + LogDynamicZones("CreateInstance failed, invalid zone id [{}]", m_zone_id); + return 0; + } + + uint16_t unused_instance_id = 0; + if (!GetDatabase().GetUnusedInstanceID(unused_instance_id)) // todo: doesn't this race with insert? + { + LogDynamicZones("Failed to find unused instance id"); + return 0; + } + + m_start_time = std::chrono::system_clock::now(); + m_expire_time = m_start_time + m_duration; + + auto insert_instance = InstanceListRepository::NewEntity(); + insert_instance.id = unused_instance_id; + insert_instance.zone = m_zone_id; + insert_instance.version = m_zone_version; + insert_instance.start_time = static_cast(std::chrono::system_clock::to_time_t(m_start_time)); + insert_instance.duration = static_cast(m_duration.count()); + insert_instance.never_expires = m_never_expires; + + auto instance = InstanceListRepository::InsertOne(GetDatabase(), insert_instance); + if (instance.id == 0) + { + LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id); + return 0; + } + + m_instance_id = instance.id; + + return m_instance_id; +} + +void DynamicZoneBase::LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry) +{ + m_id = dz_entry.id; + m_instance_id = dz_entry.instance_id; + m_type = static_cast(dz_entry.type); + m_compass.zone_id = dz_entry.compass_zone_id; + m_compass.x = dz_entry.compass_x; + m_compass.y = dz_entry.compass_y; + m_compass.z = dz_entry.compass_z; + m_safereturn.zone_id = dz_entry.safe_return_zone_id; + m_safereturn.x = dz_entry.safe_return_x; + m_safereturn.y = dz_entry.safe_return_y; + m_safereturn.z = dz_entry.safe_return_z; + m_safereturn.heading = dz_entry.safe_return_heading; + m_zonein.x = dz_entry.zone_in_x; + 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); + // 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_expire_time = m_start_time + m_duration; +} + +uint32_t DynamicZoneBase::SaveToDatabase() +{ + LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id); + + if (m_instance_id != 0) + { + auto insert_dz = DynamicZonesRepository::NewEntity(); + insert_dz.instance_id = m_instance_id, + insert_dz.type = static_cast(m_type); + 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; +} + +void DynamicZoneBase::AddCharacter(uint32_t character_id) +{ + GetDatabase().AddClientToInstance(m_instance_id, character_id); + SendInstanceAddRemoveCharacter(character_id, false); // stops client kick timer +} + +void DynamicZoneBase::RemoveCharacter(uint32_t character_id) +{ + GetDatabase().RemoveClientFromInstance(m_instance_id, character_id); + SendInstanceAddRemoveCharacter(character_id, true); // start client kick timer +} + +void DynamicZoneBase::RemoveAllCharacters(bool enable_removal_timers) +{ + if (GetInstanceID() == 0) + { + return; + } + + if (enable_removal_timers) + { + SendInstanceRemoveAllCharacters(); + } + + GetDatabase().RemoveClientsFromInstance(GetInstanceID()); +} + +void DynamicZoneBase::SaveInstanceMembersToDatabase(const std::vector& character_ids) +{ + LogDynamicZonesDetail("Saving [{}] members for instance [{}]", character_ids.size(), m_instance_id); + + std::vector insert_players; + + for (const auto& character_id : character_ids) + { + InstanceListPlayerRepository::InstanceListPlayer entry{}; + entry.id = static_cast(m_instance_id); + entry.charid = static_cast(character_id); + insert_players.emplace_back(entry); + } + + InstanceListPlayerRepository::InsertMany(GetDatabase(), insert_players); +} + +void DynamicZoneBase::SetCompass(const DynamicZoneLocation& location, bool update_db) +{ + ProcessCompassChange(location); + + if (update_db) + { + LogDynamicZonesDetail("Saving [{}] compass zone: [{}] xyz: ([{}], [{}], [{}])", + m_id, m_compass.zone_id, m_compass.x, m_compass.y, m_compass.z); + + DynamicZonesRepository::UpdateCompass(GetDatabase(), + m_id, m_compass.zone_id, m_compass.x, m_compass.y, m_compass.z); + + SendGlobalLocationChange(ServerOP_DzSetCompass, location); + } +} + +void DynamicZoneBase::SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db) +{ + SetCompass({ zone_id, x, y, z, 0.0f }, update_db); +} + +void DynamicZoneBase::SetSafeReturn(const DynamicZoneLocation& location, bool update_db) +{ + m_safereturn = location; + + if (update_db) + { + LogDynamicZonesDetail("Saving [{}] safereturn zone: [{}] xyzh: ([{}], [{}], [{}], [{}])", + m_id, m_safereturn.zone_id, m_safereturn.x, m_safereturn.y, m_safereturn.z, m_safereturn.heading); + + DynamicZonesRepository::UpdateSafeReturn(GetDatabase(), m_id, m_safereturn.zone_id, + m_safereturn.x, m_safereturn.y, m_safereturn.z, m_safereturn.heading); + + SendGlobalLocationChange(ServerOP_DzSetSafeReturn, location); + } +} + +void DynamicZoneBase::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db) +{ + SetSafeReturn({ zone_id, x, y, z, heading }, update_db); +} + +void DynamicZoneBase::SetZoneInLocation(const DynamicZoneLocation& location, bool update_db) +{ + m_zonein = location; + m_has_zonein = true; + + if (update_db) + { + LogDynamicZonesDetail("Saving [{}] zonein zone: [{}] xyzh: ([{}], [{}], [{}], [{}])", + m_id, m_zone_id, m_zonein.x, m_zonein.y, m_zonein.z, m_zonein.heading); + + DynamicZonesRepository::UpdateZoneIn(GetDatabase(), m_id, m_zone_id, + m_zonein.x, m_zonein.y, m_zonein.z, m_zonein.heading, m_has_zonein); + + SendGlobalLocationChange(ServerOP_DzSetZoneIn, location); + } +} + +void DynamicZoneBase::SetZoneInLocation(float x, float y, float z, float heading, bool update_db) +{ + SetZoneInLocation({ 0, x, y, z, heading }, update_db); +} + +uint32_t DynamicZoneBase::GetSecondsRemaining() const +{ + auto remaining = std::chrono::duration_cast(GetDurationRemaining()).count(); + return std::max(0, static_cast(remaining)); +} + +std::unique_ptr DynamicZoneBase::CreateServerAddRemoveCharacterPacket( + uint32_t character_id, bool removed) +{ + constexpr uint32_t pack_size = sizeof(ServerDzCharacter_Struct); + auto pack = std::make_unique(ServerOP_DzAddRemoveCharacter, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->zone_id = GetZoneID(); + buf->instance_id = GetInstanceID(); + buf->remove = removed; + buf->character_id = character_id; + + return pack; +} + +std::unique_ptr DynamicZoneBase::CreateServerRemoveAllCharactersPacket() +{ + constexpr uint32_t pack_size = sizeof(ServerDzCharacter_Struct); + auto pack = std::make_unique(ServerOP_DzRemoveAllCharacters, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->zone_id = GetZoneID(); + buf->instance_id = GetInstanceID(); + buf->remove = true; + buf->character_id = 0; + + return pack; +} + +std::unique_ptr DynamicZoneBase::CreateServerDzLocationPacket( + uint16_t server_opcode, const DynamicZoneLocation& location) +{ + constexpr uint32_t pack_size = sizeof(ServerDzLocation_Struct); + auto pack = std::make_unique(server_opcode, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->zone_id = location.zone_id; + buf->x = location.x; + buf->y = location.y; + buf->z = location.z; + buf->heading = location.heading; + + return pack; +} diff --git a/common/dynamic_zone_base.h b/common/dynamic_zone_base.h new file mode 100644 index 000000000..2c157b2b5 --- /dev/null +++ b/common/dynamic_zone_base.h @@ -0,0 +1,112 @@ +#ifndef COMMON_DYNAMIC_ZONE_BASE_H +#define COMMON_DYNAMIC_ZONE_BASE_H + +#include "eq_constants.h" +#include "repositories/dynamic_zones_repository.h" +#include +#include +#include +#include +#include +#include + +class Database; +class ServerPacket; + +struct DynamicZoneLocation +{ + uint32_t zone_id = 0; + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + float heading = 0.0f; + + DynamicZoneLocation() = default; + DynamicZoneLocation(uint32_t zone_id_, float x_, float y_, float z_, float heading_) + : zone_id(zone_id_), x(x_), y(y_), z(z_), heading(heading_) {} +}; + +class DynamicZoneBase +{ +public: + virtual ~DynamicZoneBase() = default; + DynamicZoneBase(const DynamicZoneBase&) = default; + DynamicZoneBase(DynamicZoneBase&&) = default; + DynamicZoneBase& operator=(const DynamicZoneBase&) = default; + DynamicZoneBase& operator=(DynamicZoneBase&&) = default; + DynamicZoneBase() = default; + DynamicZoneBase(uint32_t dz_id) : m_id(dz_id) {} + DynamicZoneBase(DynamicZoneType type) : m_type(type) {} + DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry); + + virtual void SetSecondsRemaining(uint32_t seconds_remaining) = 0; + + uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } + uint32_t GetID() const { return m_id; } + uint16_t GetInstanceID() const { return static_cast(m_instance_id); } + uint32_t GetSecondsRemaining() const; + uint16_t GetZoneID() const { return static_cast(m_zone_id); } + uint32_t GetZoneIndex() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); } + uint32_t GetZoneVersion() const { return m_zone_version; } + DynamicZoneType GetType() const { return m_type; } + const std::string& GetLeaderName() const { return m_leader_name; } + const std::string& GetName() const { return m_name; } + const DynamicZoneLocation& GetCompassLocation() const { return m_compass; } + const DynamicZoneLocation& GetSafeReturnLocation() const { return m_safereturn; } + const DynamicZoneLocation& GetZoneInLocation() const { return m_zonein; } + std::chrono::system_clock::duration GetDurationRemaining() const { return m_expire_time - std::chrono::system_clock::now(); } + + void AddCharacter(uint32_t character_id); + uint32_t Create(); + bool HasZoneInLocation() const { return m_has_zonein; } + 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 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 RemoveAllCharacters(bool enable_removal_timers = true); + void RemoveCharacter(uint32_t character_id); + void SaveInstanceMembersToDatabase(const std::vector& character_ids); + 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 SetLeaderName(const std::string& leader_name) { m_leader_name = leader_name; } + void SetName(const std::string& name) { m_name = name; } + 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 SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false); + void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false); + +protected: + virtual uint16_t GetCurrentInstanceID() { return 0; } + virtual uint16_t GetCurrentZoneID() { return 0; } + virtual Database& GetDatabase() = 0; + virtual void ProcessCompassChange(const DynamicZoneLocation& location) { m_compass = location; } + virtual void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) = 0; + virtual void SendInstanceRemoveAllCharacters() = 0; + virtual void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) = 0; + + uint32_t CreateInstance(); + void LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry); + uint32_t SaveToDatabase(); + + std::unique_ptr CreateServerAddRemoveCharacterPacket(uint32_t character_id, bool removed); + std::unique_ptr CreateServerRemoveAllCharactersPacket(); + std::unique_ptr CreateServerDzLocationPacket(uint16_t server_opcode, const DynamicZoneLocation& location); + + uint32_t m_id = 0; + uint32_t m_zone_id = 0; + uint32_t m_instance_id = 0; + uint32_t m_zone_version = 0; + bool m_never_expires = false; + bool m_has_zonein = false; + std::string m_name; + std::string m_leader_name; + DynamicZoneType m_type{ DynamicZoneType::None }; + DynamicZoneLocation m_compass; + DynamicZoneLocation m_safereturn; + DynamicZoneLocation m_zonein; + std::chrono::seconds m_duration; + std::chrono::time_point m_start_time; + std::chrono::time_point m_expire_time; +}; + +#endif diff --git a/common/repositories/dynamic_zones_repository.h b/common/repositories/dynamic_zones_repository.h index 03435457c..27fd406ca 100644 --- a/common/repositories/dynamic_zones_repository.h +++ b/common/repositories/dynamic_zones_repository.h @@ -65,6 +65,241 @@ public: // Custom extended repository methods here + struct DynamicZoneInstance + { + uint32_t id; + int instance_id; + int type; + int compass_zone_id; + float compass_x; + float compass_y; + float compass_z; + int safe_return_zone_id; + float safe_return_x; + float safe_return_y; + float safe_return_z; + float safe_return_heading; + float zone_in_x; + float zone_in_y; + float zone_in_z; + float zone_in_heading; + int has_zone_in; + int zone; + int version; + int is_global; + uint32_t start_time; + int duration; + int never_expires; + }; + + static std::string SelectDynamicZoneJoinInstance() + { + return std::string(SQL( + SELECT + dynamic_zones.id, + dynamic_zones.instance_id, + dynamic_zones.type, + dynamic_zones.compass_zone_id, + dynamic_zones.compass_x, + dynamic_zones.compass_y, + dynamic_zones.compass_z, + dynamic_zones.safe_return_zone_id, + dynamic_zones.safe_return_x, + dynamic_zones.safe_return_y, + dynamic_zones.safe_return_z, + dynamic_zones.safe_return_heading, + dynamic_zones.zone_in_x, + dynamic_zones.zone_in_y, + dynamic_zones.zone_in_z, + dynamic_zones.zone_in_heading, + dynamic_zones.has_zone_in, + instance_list.zone, + instance_list.version, + instance_list.is_global, + instance_list.start_time, + instance_list.duration, + instance_list.never_expires + FROM dynamic_zones + INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id + )); + } + + static DynamicZoneInstance FillWithInstanceFromRow(MySQLRequestRow& row) + { + DynamicZoneInstance entry{}; + + int col = 0; + entry.id = strtoul(row[col++], nullptr, 10); + entry.instance_id = strtol(row[col++], nullptr, 10); + entry.type = strtol(row[col++], nullptr, 10); + entry.compass_zone_id = strtol(row[col++], nullptr, 10); + entry.compass_x = strtof(row[col++], nullptr); + entry.compass_y = strtof(row[col++], nullptr); + entry.compass_z = strtof(row[col++], nullptr); + entry.safe_return_zone_id = strtol(row[col++], nullptr, 10); + entry.safe_return_x = strtof(row[col++], nullptr); + entry.safe_return_y = strtof(row[col++], nullptr); + entry.safe_return_z = strtof(row[col++], nullptr); + entry.safe_return_heading = strtof(row[col++], nullptr); + entry.zone_in_x = strtof(row[col++], nullptr); + entry.zone_in_y = strtof(row[col++], nullptr); + 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; + // from instance_list + entry.zone = strtol(row[col++], nullptr, 10); + entry.version = strtol(row[col++], nullptr, 10); + entry.is_global = strtol(row[col++], nullptr, 10); + entry.start_time = strtoul(row[col++], nullptr, 10); + entry.duration = strtol(row[col++], nullptr, 10); + entry.never_expires = strtol(row[col++], nullptr, 10); + + return entry; + } + + static std::vector GetWithInstance(Database& db, + const std::vector& dynamic_zone_ids) + { + if (dynamic_zone_ids.empty()) + { + return {}; + } + + std::vector all_entries; + + auto results = db.QueryDatabase(fmt::format( + "{} WHERE dynamic_zones.id IN ({}) ORDER BY dynamic_zones.id;", + SelectDynamicZoneJoinInstance(), + fmt::join(dynamic_zone_ids, ",") + )); + + if (results.Success()) + { + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + DynamicZoneInstance entry = FillWithInstanceFromRow(row); + all_entries.emplace_back(std::move(entry)); + } + } + + return all_entries; + } + + static void UpdateCompass(Database& db, uint32_t dz_id, int zone_id, float x, float y, float z) + { + if (dz_id != 0) + { + std::string query = fmt::format(SQL( + UPDATE {} SET + compass_zone_id = {}, + compass_x = {}, + compass_y = {}, + compass_z = {} + WHERE {} = {}; + ), TableName(), zone_id, x, y, z, PrimaryKey(), dz_id); + + db.QueryDatabase(query); + } + } + + static void UpdateSafeReturn(Database& db, uint32_t dz_id, int zone_id, float x, float y, float z, float heading) + { + if (dz_id != 0) + { + std::string query = fmt::format(SQL( + UPDATE {} SET + safe_return_zone_id = {}, + safe_return_x = {}, + safe_return_y = {}, + safe_return_z = {}, + safe_return_heading = {} + WHERE {} = {}; + ), TableName(), zone_id, x, y, z, heading, PrimaryKey(), dz_id); + + db.QueryDatabase(query); + } + } + + static void UpdateZoneIn(Database& db, uint32_t dz_id, uint32_t zone_id, float x, float y, float z, float heading, bool has_zone_in) + { + if (dz_id != 0) + { + std::string query = fmt::format(SQL( + UPDATE {} SET + zone_in_x = {}, + zone_in_y = {}, + zone_in_z = {}, + zone_in_heading = {}, + has_zone_in = {} + WHERE {} = {}; + ), TableName(), x, y, z, heading, has_zone_in, PrimaryKey(), dz_id); + + db.QueryDatabase(query); + } + } + + struct DynamicZoneInstancePlayerCount + { + uint32_t id; + int type; + int instance; + int zone; + int version; + uint32_t start_time; + int duration; + int player_count; + }; + + static std::string SelectDynamicZoneInstancePlayerCount() + { + return std::string(SQL( + SELECT + dynamic_zones.id, + dynamic_zones.type, + instance_list.id, + instance_list.zone, + instance_list.version, + instance_list.start_time, + instance_list.duration, + COUNT(instance_list_player.id) member_count + FROM dynamic_zones + INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id + LEFT JOIN instance_list_player ON instance_list.id = instance_list_player.id + GROUP BY instance_list.id + ORDER BY dynamic_zones.id; + )); + }; + + static std::vector AllDzInstancePlayerCounts(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase(SelectDynamicZoneInstancePlayerCount()); + if (results.Success()) + { + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + DynamicZoneInstancePlayerCount entry{}; + + int col = 0; + entry.id = strtoul(row[col++], nullptr, 10); + entry.type = strtol(row[col++], nullptr, 10); + entry.instance = strtol(row[col++], nullptr, 10); + entry.zone = strtol(row[col++], nullptr, 10); + entry.version = strtol(row[col++], nullptr, 10); + entry.start_time = strtoul(row[col++], nullptr, 10); + entry.duration = strtol(row[col++], nullptr, 10); + entry.player_count = strtol(row[col++], nullptr, 10); + + all_entries.emplace_back(std::move(entry)); + } + } + return all_entries; + } }; #endif //EQEMU_DYNAMIC_ZONES_REPOSITORY_H diff --git a/common/servertalk.h b/common/servertalk.h index e86dd2c86..384bcc15b 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -161,7 +161,7 @@ #define ServerOP_ExpeditionExpireWarning 0x0416 #define ServerOP_ExpeditionChooseNewLeader 0x0417 -#define ServerOP_DzCharacterChange 0x0450 +#define ServerOP_DzAddRemoveCharacter 0x0450 #define ServerOP_DzRemoveAllCharacters 0x0451 #define ServerOP_DzSetSecondsRemaining 0x0452 #define ServerOP_DzDurationUpdate 0x0453 diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp index 252559c29..a845c5b1c 100644 --- a/world/dynamic_zone.cpp +++ b/world/dynamic_zone.cpp @@ -12,16 +12,21 @@ extern ZSList zoneserver_list; DynamicZone::DynamicZone( uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version, uint32_t start_time, uint32_t duration, DynamicZoneType type -) : - m_id(id), - m_instance_id(instance_id), - m_zone_id(zone_id), - m_zone_version(zone_version), - m_start_time(std::chrono::system_clock::from_time_t(start_time)), - m_duration(duration), - m_type(type), - m_expire_time(m_start_time + m_duration) +) { + m_id = id; + m_instance_id = instance_id; + m_zone_id = zone_id; + m_zone_version = zone_version; + m_start_time = std::chrono::system_clock::from_time_t(start_time); + m_duration = std::chrono::seconds(duration); + m_type = type; + m_expire_time = m_start_time + m_duration; +} + +Database& DynamicZone::GetDatabase() +{ + return database; } DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) @@ -99,10 +104,27 @@ void DynamicZone::HandleZoneMessage(ServerPacket* pack) 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_DzCharacterChange: + case ServerOP_DzAddRemoveCharacter: case ServerOP_DzRemoveAllCharacters: { auto buf = reinterpret_cast(pack->pBuffer); @@ -125,3 +147,29 @@ void DynamicZone::HandleZoneMessage(ServerPacket* pack) } }; } + +void DynamicZone::SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) +{ + ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(GetInstanceID()); + if (instance_zs) + { + auto pack = CreateServerAddRemoveCharacterPacket(character_id, remove); + instance_zs->SendPacket(pack.get()); + } +} + +void DynamicZone::SendInstanceRemoveAllCharacters() +{ + ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(GetInstanceID()); + if (instance_zs) + { + auto pack = CreateServerRemoveAllCharactersPacket(); + instance_zs->SendPacket(pack.get()); + } +} + +void DynamicZone::SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) +{ + auto pack = CreateServerDzLocationPacket(server_opcode, location); + zoneserver_list.SendPacket(pack.get()); +} diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h index 84455d7eb..a82ff9d0e 100644 --- a/world/dynamic_zone.h +++ b/world/dynamic_zone.h @@ -1,9 +1,9 @@ #ifndef WORLD_DYNAMIC_ZONE_H #define WORLD_DYNAMIC_ZONE_H -#include "../common/eq_constants.h" -#include +#include "../common/dynamic_zone_base.h" +class Database; class ServerPacket; enum class DynamicZoneStatus @@ -14,9 +14,11 @@ enum class DynamicZoneStatus ExpiredEmpty, }; -class DynamicZone +class DynamicZone : public DynamicZoneBase { public: + using DynamicZoneBase::DynamicZoneBase; // inherit base constructors + DynamicZone() = default; DynamicZone(uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version, uint32_t start_time, uint32_t duration, DynamicZoneType type); @@ -24,29 +26,20 @@ public: static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); static void HandleZoneMessage(ServerPacket* pack); - uint32_t GetID() const { return m_id; } - uint16_t GetInstanceID() const { return static_cast(m_instance_id); } - uint16_t GetZoneID() const { return static_cast(m_zone_id); } - uint32_t GetZoneVersion() const { return m_zone_version; } - std::chrono::system_clock::duration GetRemainingDuration() const { - return m_expire_time - std::chrono::system_clock::now(); } + void SetSecondsRemaining(uint32_t seconds_remaining) override; DynamicZoneStatus Process(bool force_expire); - bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); } - void SetSecondsRemaining(uint32_t seconds_remaining); + +protected: + Database& GetDatabase() override; + void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) override; + void SendInstanceRemoveAllCharacters() override; + void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) override; private: void SendZonesDurationUpdate(); - uint32_t m_id = 0; - uint32_t m_instance_id = 0; - uint32_t m_zone_id = 0; - uint32_t m_zone_version = 0; bool m_is_pending_early_shutdown = false; - DynamicZoneType m_type{ DynamicZoneType::None }; - std::chrono::seconds m_duration; - std::chrono::time_point m_start_time; - std::chrono::time_point m_expire_time; }; #endif diff --git a/world/expedition.cpp b/world/expedition.cpp index 2937a20bc..a72275c94 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -142,7 +142,7 @@ void Expedition::CheckExpireWarning() if (m_warning_cooldown_timer.Check(false)) { using namespace std::chrono_literals; - auto remaining = GetDynamicZone().GetRemainingDuration(); + auto remaining = GetDynamicZone().GetDurationRemaining(); if ((remaining > 14min && remaining < 15min) || (remaining > 4min && remaining < 5min) || (remaining > 0min && remaining < 1min)) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 3f0efbf19..9a319774a 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1388,7 +1388,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { ExpeditionMessage::HandleZoneMessage(pack); break; } - case ServerOP_DzCharacterChange: + case ServerOP_DzAddRemoveCharacter: case ServerOP_DzRemoveAllCharacters: case ServerOP_DzSetSecondsRemaining: case ServerOP_DzSetCompass: diff --git a/zone/client.cpp b/zone/client.cpp index 96e18dcd9..d92129632 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9595,7 +9595,7 @@ 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 dz_instance{ zone_name, version, duration, DynamicZoneType::Expedition }; + DynamicZone dz_instance{ ZoneID(zone_name), version, duration, DynamicZoneType::Expedition }; ExpeditionRequest request{ expedition_name, min_players, max_players, disable_messages }; return Expedition::TryCreate(this, dz_instance, request); } diff --git a/zone/command.cpp b/zone/command.cpp index 62757f2b2..709bb92c4 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -57,6 +57,7 @@ #include "../common/eqemu_logsys.h" #include "../common/profanity_manager.h" #include "../common/net/eqstream.h" +#include "../common/repositories/dynamic_zones_repository.h" #include "data_bucket.h" #include "command.h" @@ -6960,58 +6961,36 @@ void command_dz(Client* c, const Seperator* sep) } else if (strcasecmp(sep->arg[1], "list") == 0) { - std::string query = SQL( - SELECT - dynamic_zones.id, - dynamic_zones.type, - instance_list.id, - instance_list.zone, - instance_list.version, - instance_list.start_time, - instance_list.duration, - COUNT(instance_list_player.id) member_count - FROM dynamic_zones - INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id - LEFT JOIN instance_list_player ON instance_list.id = instance_list_player.id - GROUP BY instance_list.id - ORDER BY dynamic_zones.id; - ); + auto dz_list = DynamicZonesRepository::AllDzInstancePlayerCounts(database); + c->Message(Chat::White, fmt::format("Total Dynamic Zones: [{}]", dz_list.size()).c_str()); - auto results = database.QueryDatabase(query); - if (results.Success()) + auto now = std::chrono::system_clock::now(); + + for (const auto& dz : dz_list) { - c->Message(Chat::White, fmt::format("Total Dynamic Zones: [{}]", results.RowCount()).c_str()); - for (auto row = results.begin(); row != results.end(); ++row) + 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())); + bool is_expired = now > expire_time; + + if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) { - auto start_time = strtoul(row[5], nullptr, 10); - auto duration = strtoul(row[6], nullptr, 10); - auto expire_time = std::chrono::system_clock::from_time_t(start_time + duration); + auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#zoneinstance {}", dz.instance), false, "zone"); - auto now = std::chrono::system_clock::now(); - auto remaining = std::chrono::duration_cast(expire_time - now); - auto seconds = std::max(0, static_cast(remaining.count())); - - bool is_expired = now > expire_time; - if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) - { - uint32_t instance_id = strtoul(row[2], nullptr, 10); - auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#zoneinstance {}", instance_id), false, "zone"); - - c->Message(Chat::White, fmt::format( - "dz id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - strtoul(row[0], nullptr, 10), // dynamic_zone_id - strtoul(row[1], nullptr, 10), // dynamic_zone_type - zone_saylink, - strtoul(row[3], nullptr, 10), // instance_zone_id - instance_id, // instance_id - strtoul(row[4], nullptr, 10), // instance_zone_version - strtoul(row[7], nullptr, 10), // instance member_count - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } + c->Message(Chat::White, fmt::format( + "dz id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + dz.id, + dz.type, + zone_saylink, + dz.zone, + dz.instance, + dz.version, + dz.player_count, + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); } } } diff --git a/zone/dynamic_zone.cpp b/zone/dynamic_zone.cpp index 9358f15a2..9d9f35012 100644 --- a/zone/dynamic_zone.cpp +++ b/zone/dynamic_zone.cpp @@ -22,30 +22,35 @@ #include "client.h" #include "expedition.h" #include "worldserver.h" -#include "zonedb.h" #include "../common/eqemu_logsys.h" extern WorldServer worldserver; -DynamicZone::DynamicZone( - uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type -) : - m_zone_id(zone_id), - m_version(version), - m_duration(duration), - m_type(type) -{ -} +// message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks) +const char* const CREATE_NOT_ALL_ADDED = "Not all players in your {} were added to the {}. The {} can take a maximum of {} players, and your {} has {}."; DynamicZone::DynamicZone( - std::string zone_name, uint32_t version, uint32_t duration, DynamicZoneType type -) : - DynamicZone(ZoneID(zone_name), version, duration, type) + uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type) { - if (!m_zone_id) - { - LogDynamicZones("Failed to get zone id for zone [{}]", zone_name); - } + m_zone_id = zone_id; + m_zone_version = version; + m_duration = std::chrono::seconds(duration); + m_type = type; +} + +Database& DynamicZone::GetDatabase() +{ + return database; +} + +uint16_t DynamicZone::GetCurrentInstanceID() +{ + return zone ? static_cast(zone->GetInstanceID()) : 0; +} + +uint16_t DynamicZone::GetCurrentZoneID() +{ + return zone ? static_cast(zone->GetZoneID()) : 0; } DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) @@ -64,366 +69,43 @@ std::unordered_map DynamicZone::LoadMultipleDzFromDatabas { LogDynamicZonesDetail("Loading dynamic zone data for [{}] instances", dynamic_zone_ids.size()); - std::string in_dynamic_zone_ids_query = fmt::format("{}", fmt::join(dynamic_zone_ids, ",")); - std::unordered_map dynamic_zones; - if (!in_dynamic_zone_ids_query.empty()) + auto entries = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); + for (auto& entry : entries) { - std::string query = fmt::format(SQL( - {} WHERE dynamic_zones.id IN ({}); - ), DynamicZoneSelectQuery(), in_dynamic_zone_ids_query); - - auto results = database.QueryDatabase(query); - if (results.Success()) - { - for (auto row = results.begin(); row != results.end(); ++row) - { - DynamicZone dz; - dz.LoadDatabaseResult(row); - dynamic_zones.emplace(dz.GetID(), dz); - } - } + dynamic_zones.emplace(entry.id, std::move(entry)); } return dynamic_zones; } -uint32_t DynamicZone::Create() +void DynamicZone::StartAllClientRemovalTimers() { - if (m_id != 0) + for (const auto& client_iter : entity_list.GetClientList()) { - return m_id; - } - - if (GetInstanceID() == 0) - { - CreateInstance(); - } - - m_id = SaveToDatabase(); - - return m_id; -} - -uint32_t DynamicZone::CreateInstance() -{ - if (m_instance_id) - { - LogDynamicZones("CreateInstance failed, instance id [{}] already created", m_instance_id); - return 0; - } - - if (!m_zone_id) - { - LogDynamicZones("CreateInstance failed, invalid zone id [{}]", m_zone_id); - return 0; - } - - uint16_t instance_id = 0; - if (!database.GetUnusedInstanceID(instance_id)) // todo: doesn't this race with insert? - { - LogDynamicZones("Failed to find unused instance id"); - return 0; - } - - m_start_time = std::chrono::system_clock::now(); - auto start_time = std::chrono::system_clock::to_time_t(m_start_time); - - std::string query = fmt::format(SQL( - INSERT INTO instance_list - (id, zone, version, start_time, duration) - VALUES - ({}, {}, {}, {}, {}) - ), instance_id, m_zone_id, m_version, start_time, m_duration.count()); - - auto results = database.QueryDatabase(query); - if (!results.Success()) - { - LogDynamicZones("Failed to create instance [{}] for Dynamic Zone [{}]", instance_id, m_zone_id); - return 0; - } - - m_instance_id = instance_id; - m_never_expires = false; - m_expire_time = m_start_time + m_duration; - - return m_instance_id; -} - -std::string DynamicZone::DynamicZoneSelectQuery() -{ - return std::string(SQL( - SELECT - instance_list.id, - instance_list.zone, - instance_list.version, - instance_list.start_time, - instance_list.duration, - instance_list.never_expires, - dynamic_zones.id, - dynamic_zones.type, - dynamic_zones.compass_zone_id, - dynamic_zones.compass_x, - dynamic_zones.compass_y, - dynamic_zones.compass_z, - dynamic_zones.safe_return_zone_id, - dynamic_zones.safe_return_x, - dynamic_zones.safe_return_y, - dynamic_zones.safe_return_z, - dynamic_zones.safe_return_heading, - dynamic_zones.zone_in_x, - dynamic_zones.zone_in_y, - dynamic_zones.zone_in_z, - dynamic_zones.zone_in_heading, - dynamic_zones.has_zone_in - FROM dynamic_zones - INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id - )); -} - -void DynamicZone::LoadDatabaseResult(MySQLRequestRow& row) -{ - m_instance_id = strtoul(row[0], nullptr, 10); - m_zone_id = strtoul(row[1], nullptr, 10); - m_version = strtoul(row[2], nullptr, 10); - m_start_time = std::chrono::system_clock::from_time_t(strtoul(row[3], nullptr, 10)); - m_duration = std::chrono::seconds(strtoul(row[4], nullptr, 10)); - m_expire_time = m_start_time + m_duration; - m_never_expires = (strtoul(row[5], nullptr, 10) != 0); - m_id = strtoul(row[6], nullptr, 10); - m_type = static_cast(strtoul(row[7], nullptr, 10)); - m_compass.zone_id = strtoul(row[8], nullptr, 10); - m_compass.x = strtof(row[9], nullptr); - m_compass.y = strtof(row[10], nullptr); - m_compass.z = strtof(row[11], nullptr); - m_safereturn.zone_id = strtoul(row[12], nullptr, 10); - m_safereturn.x = strtof(row[13], nullptr); - m_safereturn.y = strtof(row[14], nullptr); - m_safereturn.z = strtof(row[15], nullptr); - m_safereturn.heading = strtof(row[16], nullptr); - m_zonein.x = strtof(row[17], nullptr); - m_zonein.y = strtof(row[18], nullptr); - m_zonein.z = strtof(row[19], nullptr); - m_zonein.heading = strtof(row[20], nullptr); - m_has_zonein = (strtoul(row[21], nullptr, 10) != 0); -} - -uint32_t DynamicZone::SaveToDatabase() -{ - LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id); - - if (m_instance_id != 0) - { - std::string query = fmt::format(SQL( - INSERT INTO dynamic_zones - ( - instance_id, - type, - compass_zone_id, - compass_x, - compass_y, - compass_z, - safe_return_zone_id, - safe_return_x, - safe_return_y, - safe_return_z, - safe_return_heading, - zone_in_x, - zone_in_y, - zone_in_z, - zone_in_heading, - has_zone_in - ) - VALUES - ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}); - ), - m_instance_id, - static_cast(m_type), - m_compass.zone_id, - m_compass.x, - m_compass.y, - m_compass.z, - m_safereturn.zone_id, - m_safereturn.x, - m_safereturn.y, - m_safereturn.z, - m_safereturn.heading, - m_zonein.x, - m_zonein.y, - m_zonein.z, - m_zonein.heading, - m_has_zonein - ); - - auto results = database.QueryDatabase(query); - if (results.Success()) + if (client_iter.second) { - return results.LastInsertedID(); + client_iter.second->SetDzRemovalTimer(true); } } - return 0; } -void DynamicZone::SaveCompassToDatabase() +void DynamicZone::SendInstanceRemoveAllCharacters() { - LogDynamicZonesDetail( - "Instance [{}] saving compass zone: [{}] xyz: ([{}], [{}], [{}])", - m_instance_id, m_compass.zone_id, m_compass.x, m_compass.y, m_compass.z - ); - - if (m_instance_id != 0) + // just remove all clients in bulk instead of only characters assigned to the instance + if (IsCurrentZoneDzInstance()) { - std::string query = fmt::format(SQL( - UPDATE dynamic_zones SET - compass_zone_id = {}, - compass_x = {}, - compass_y = {}, - compass_z = {} - WHERE instance_id = {}; - ), - m_compass.zone_id, - m_compass.x, - m_compass.y, - m_compass.z, - m_instance_id - ); - - database.QueryDatabase(query); + DynamicZone::StartAllClientRemovalTimers(); + } + else if (GetInstanceID() != 0) + { + auto pack = CreateServerRemoveAllCharactersPacket(); + worldserver.SendPacket(pack.get()); } } -void DynamicZone::SaveSafeReturnToDatabase() -{ - LogDynamicZonesDetail( - "Instance [{}] saving safereturn zone: [{}] xyzh: ([{}], [{}], [{}], [{}])", - m_instance_id, m_safereturn.zone_id, m_safereturn.x, m_safereturn.y, m_safereturn.z, m_safereturn.heading - ); - - if (m_instance_id != 0) - { - std::string query = fmt::format(SQL( - UPDATE dynamic_zones SET - safe_return_zone_id = {}, - safe_return_x = {}, - safe_return_y = {}, - safe_return_z = {}, - safe_return_heading = {} - WHERE instance_id = {}; - ), - m_safereturn.zone_id, - m_safereturn.x, - m_safereturn.y, - m_safereturn.z, - m_safereturn.heading, - m_instance_id - ); - - database.QueryDatabase(query); - } -} - -void DynamicZone::SaveZoneInLocationToDatabase() -{ - LogDynamicZonesDetail( - "Instance [{}] saving zonein zone: [{}] xyzh: ([{}], [{}], [{}], [{}]) has: [{}]", - m_instance_id, m_zone_id, m_zonein.x, m_zonein.y, m_zonein.z, m_zonein.heading, m_has_zonein - ); - - if (m_instance_id != 0) - { - std::string query = fmt::format(SQL( - UPDATE dynamic_zones SET - zone_in_x = {}, - zone_in_y = {}, - zone_in_z = {}, - zone_in_heading = {}, - has_zone_in = {} - WHERE instance_id = {}; - ), - m_zonein.x, - m_zonein.y, - m_zonein.z, - m_zonein.heading, - m_has_zonein, - m_instance_id - ); - - database.QueryDatabase(query); - } -} - -void DynamicZone::AddCharacter(uint32_t character_id) -{ - database.AddClientToInstance(m_instance_id, character_id); - SendInstanceCharacterChange(character_id, false); // stops client kick timer -} - -void DynamicZone::RemoveCharacter(uint32_t character_id) -{ - database.RemoveClientFromInstance(m_instance_id, character_id); - SendInstanceCharacterChange(character_id, true); // start client kick timer -} - -void DynamicZone::RemoveAllCharacters(bool enable_removal_timers) -{ - if (GetInstanceID() == 0) - { - return; - } - - if (enable_removal_timers) - { - // just remove all clients in bulk instead of only characters assigned to the instance - if (IsCurrentZoneDzInstance()) - { - for (const auto& client_iter : entity_list.GetClientList()) - { - if (client_iter.second) - { - client_iter.second->SetDzRemovalTimer(true); - } - } - } - else if (GetInstanceID() != 0) - { - uint32_t packsize = sizeof(ServerDzCharacter_Struct); - auto pack = std::make_unique(ServerOP_DzRemoveAllCharacters, packsize); - auto packbuf = reinterpret_cast(pack->pBuffer); - packbuf->zone_id = GetZoneID(); - packbuf->instance_id = GetInstanceID(); - packbuf->remove = true; - packbuf->character_id = 0; - worldserver.SendPacket(pack.get()); - } - } - - database.RemoveClientsFromInstance(GetInstanceID()); -} - -void DynamicZone::SaveInstanceMembersToDatabase(const std::vector& character_ids) -{ - LogDynamicZonesDetail("Saving [{}] instance members to database", character_ids.size()); - - std::string insert_values; - for (const auto& character_id : character_ids) - { - fmt::format_to(std::back_inserter(insert_values), "({}, {}),", m_instance_id, character_id); - } - - if (!insert_values.empty()) - { - insert_values.pop_back(); // trailing comma - - std::string query = fmt::format(SQL( - REPLACE INTO instance_list_player (id, charid) VALUES {}; - ), insert_values); - - database.QueryDatabase(query); - } -} - -void DynamicZone::SendInstanceCharacterChange(uint32_t character_id, bool removed) +void DynamicZone::SendInstanceAddRemoveCharacter(uint32_t character_id, bool removed) { // if removing, sets removal timer on client inside the instance if (IsCurrentZoneDzInstance()) @@ -436,97 +118,16 @@ void DynamicZone::SendInstanceCharacterChange(uint32_t character_id, bool remove } else if (GetInstanceID() != 0) { - uint32_t packsize = sizeof(ServerDzCharacter_Struct); - auto pack = std::make_unique(ServerOP_DzCharacterChange, packsize); - auto packbuf = reinterpret_cast(pack->pBuffer); - packbuf->zone_id = GetZoneID(); - packbuf->instance_id = GetInstanceID(); - packbuf->remove = removed; - packbuf->character_id = character_id; + auto pack = CreateServerAddRemoveCharacterPacket(character_id, removed); worldserver.SendPacket(pack.get()); } } -void DynamicZone::SetCompass(const DynamicZoneLocation& location, bool update_db) -{ - m_compass = location; - - if (m_on_compass_change) - { - m_on_compass_change(); - } - - if (update_db) - { - SaveCompassToDatabase(); - SendWorldSetLocation(ServerOP_DzSetCompass, location); - } -} - -void DynamicZone::SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db) -{ - SetCompass({ zone_id, x, y, z, 0.0f }, update_db); -} - -void DynamicZone::SetSafeReturn(const DynamicZoneLocation& location, bool update_db) -{ - m_safereturn = location; - - if (update_db) - { - SaveSafeReturnToDatabase(); - SendWorldSetLocation(ServerOP_DzSetSafeReturn, location); - } -} - -void DynamicZone::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db) -{ - SetSafeReturn({ zone_id, x, y, z, heading }, update_db); -} - -void DynamicZone::SetZoneInLocation(const DynamicZoneLocation& location, bool update_db) -{ - m_zonein = location; - m_has_zonein = true; - - if (update_db) - { - SaveZoneInLocationToDatabase(); - SendWorldSetLocation(ServerOP_DzSetZoneIn, location); - } -} - -void DynamicZone::SetZoneInLocation(float x, float y, float z, float heading, bool update_db) -{ - SetZoneInLocation({ 0, x, y, z, heading }, update_db); -} - bool DynamicZone::IsCurrentZoneDzInstance() const { return (zone && zone->GetInstanceID() != 0 && zone->GetInstanceID() == GetInstanceID()); } -bool DynamicZone::IsInstanceID(uint32_t instance_id) const -{ - return (GetInstanceID() != 0 && GetInstanceID() == instance_id); -} - -bool DynamicZone::IsSameDz(uint32_t zone_id, uint32_t instance_id) const -{ - return zone_id == m_zone_id && instance_id == m_instance_id; -} - -uint32_t DynamicZone::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; -} - void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining) { // async @@ -553,19 +154,9 @@ void DynamicZone::SetUpdatedDuration(uint32_t new_duration) } } -void DynamicZone::SendWorldSetLocation(uint16_t server_opcode, const DynamicZoneLocation& location) +void DynamicZone::SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) { - uint32_t pack_size = sizeof(ServerDzLocation_Struct); - auto pack = std::make_unique(server_opcode, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->dz_id = GetID(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->zone_id = location.zone_id; - buf->x = location.x; - buf->y = location.y; - buf->z = location.z; - buf->heading = location.heading; + auto pack = CreateServerDzLocationPacket(server_opcode, location); worldserver.SendPacket(pack.get()); } @@ -573,7 +164,7 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) { switch (pack->opcode) { - case ServerOP_DzCharacterChange: + case ServerOP_DzAddRemoveCharacter: { auto buf = reinterpret_cast(pack->pBuffer); Client* client = entity_list.GetClientByCharID(buf->character_id); @@ -588,13 +179,7 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) auto buf = reinterpret_cast(pack->pBuffer); if (buf->remove) { - for (const auto& client_list_iter : entity_list.GetClientList()) - { - if (client_list_iter.second) - { - client_list_iter.second->SetDzRemovalTimer(true); - } - } + DynamicZone::StartAllClientRemovalTimers(); } break; } @@ -636,3 +221,12 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) } } } + +void DynamicZone::ProcessCompassChange(const DynamicZoneLocation& location) +{ + DynamicZoneBase::ProcessCompassChange(location); + if (m_on_compass_change) + { + m_on_compass_change(); + } +} diff --git a/zone/dynamic_zone.h b/zone/dynamic_zone.h index 583dd6958..98949953d 100644 --- a/zone/dynamic_zone.h +++ b/zone/dynamic_zone.h @@ -21,106 +21,49 @@ #ifndef DYNAMIC_ZONE_H #define DYNAMIC_ZONE_H -#include "../common/eq_constants.h" -#include +#include "../common/dynamic_zone_base.h" #include #include #include #include #include -class MySQLRequestRow; +class Database; class ServerPacket; -struct DynamicZoneLocation -{ - uint32_t zone_id = 0; - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - float heading = 0.0f; +extern const char* const CREATE_NOT_ALL_ADDED; - DynamicZoneLocation() = default; - DynamicZoneLocation(uint32_t zone_id_, float x_, float y_, float z_, float heading_) - : zone_id(zone_id_), x(x_), y(y_), z(z_), heading(heading_) {} -}; - -class DynamicZone +class DynamicZone : public DynamicZoneBase { public: + using DynamicZoneBase::DynamicZoneBase; // inherit base constructors + DynamicZone() = default; DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type); - DynamicZone(std::string zone_shortname, uint32_t version, uint32_t duration, DynamicZoneType type); - DynamicZone(uint32_t dz_id) : m_id(dz_id) {} - DynamicZone(DynamicZoneType type) : m_type(type) {} static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); static std::unordered_map LoadMultipleDzFromDatabase( const std::vector& dynamic_zone_ids); static void HandleWorldMessage(ServerPacket* pack); - uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } - uint32_t GetID() const { return m_id; } - uint16_t GetInstanceID() const { return static_cast(m_instance_id); } - uint32_t GetSecondsRemaining() const; - uint16_t GetZoneID() const { return static_cast(m_zone_id); } - uint32_t GetZoneIndex() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); } - uint32_t GetZoneVersion() const { return m_version; } - const std::string& GetLeaderName() const { return m_leader_name; } - const std::string& GetName() const { return m_name; } - DynamicZoneType GetType() const { return m_type; } - DynamicZoneLocation GetCompassLocation() const { return m_compass; } - DynamicZoneLocation GetSafeReturnLocation() const { return m_safereturn; } - DynamicZoneLocation GetZoneInLocation() const { return m_zonein; } + void SetSecondsRemaining(uint32_t seconds_remaining) override; - void AddCharacter(uint32_t character_id); - uint32_t Create(); - uint32_t CreateInstance(); - bool HasZoneInLocation() const { return m_has_zonein; } - bool IsCurrentZoneDzInstance() const; - bool IsInstanceID(uint32_t instance_id) const; - bool IsValid() const { return m_instance_id != 0; } - bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const; - void RegisterOnCompassChange(const std::function& on_change) { m_on_compass_change = on_change; } - void RemoveAllCharacters(bool enable_removal_timers = true); - void RemoveCharacter(uint32_t character_id); - void SaveInstanceMembersToDatabase(const std::vector& character_ids); - void SendInstanceCharacterChange(uint32_t character_id, bool removed); - 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 SetLeaderName(const std::string& leader_name) { m_leader_name = leader_name; } - void SetName(const std::string& name) { m_name = name; } - 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 SetSecondsRemaining(uint32_t seconds_remaining); - void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false); - void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false); - void SetUpdatedDuration(uint32_t seconds); + bool IsCurrentZoneDzInstance() const; + void RegisterOnCompassChange(const std::function& on_change) { m_on_compass_change = on_change; } + void SetUpdatedDuration(uint32_t seconds); + +protected: + uint16_t GetCurrentInstanceID() override; + uint16_t GetCurrentZoneID() override; + Database& GetDatabase() override; + void ProcessCompassChange(const DynamicZoneLocation& location) override; + void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) override; + void SendInstanceRemoveAllCharacters() override; + void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) override; private: - static std::string DynamicZoneSelectQuery(); - void LoadDatabaseResult(MySQLRequestRow& row); - void SaveCompassToDatabase(); - void SaveSafeReturnToDatabase(); - void SaveZoneInLocationToDatabase(); - void SendWorldSetLocation(uint16_t server_opcode, const DynamicZoneLocation& location); - uint32_t SaveToDatabase(); + static void StartAllClientRemovalTimers(); - uint32_t m_id = 0; - uint32_t m_zone_id = 0; - uint32_t m_instance_id = 0; - uint32_t m_version = 0; - bool m_never_expires = false; - bool m_has_zonein = false; - std::string m_name; - std::string m_leader_name; - DynamicZoneType m_type{ DynamicZoneType::None }; - DynamicZoneLocation m_compass; - DynamicZoneLocation m_safereturn; - DynamicZoneLocation m_zonein; - std::chrono::seconds m_duration; - std::chrono::time_point m_start_time; - std::chrono::time_point m_expire_time; std::function m_on_compass_change; }; diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 5bfecb8e4..27bb86079 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -39,9 +39,6 @@ const char* const EXPEDITION_OTHER_BELONGS = "{} attempted to create an expedi // 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"; -const char* const KICKPLAYERS_EVERYONE = "Everyone"; -// message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks) -const char* const CREATE_NOT_ALL_ADDED = "Not all players in your {} were added to the {}. The {} can take a maximum of {} players, and your {} has {}."; // 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."; @@ -1107,7 +1104,7 @@ void Expedition::DzKickPlayers(Client* requester) } RemoveAllMembers(); - requester->MessageString(Chat::Red, EXPEDITION_REMOVED, KICKPLAYERS_EVERYONE, m_expedition_name.c_str()); + requester->MessageString(Chat::Red, EXPEDITION_REMOVED, "Everyone", m_expedition_name.c_str()); } void Expedition::SetLocked( diff --git a/zone/expedition.h b/zone/expedition.h index c78864c6b..5b2a40043 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -39,7 +39,6 @@ class ServerPacket; extern const char* const DZ_YOU_NOT_ASSIGNED; extern const char* const EXPEDITION_OTHER_BELONGS; -extern const char* const CREATE_NOT_ALL_ADDED; enum class ExpeditionMemberStatus : uint8_t { diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 34994d88a..1b4758ff2 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -2906,7 +2906,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) Expedition::HandleWorldMessage(pack); break; } - case ServerOP_DzCharacterChange: + case ServerOP_DzAddRemoveCharacter: case ServerOP_DzRemoveAllCharacters: case ServerOP_DzDurationUpdate: case ServerOP_DzSetCompass: