[Expeditions] Create common dz abstract class (#1312)

This creates an abstract class in common so zone and world can share
most of the implementation. World now has access to the same dz data and
api as zone.

Rename CharacterChange to AddRemoveCharacter for clarity

Rename GetRemainingDuration to GetDurationRemaining for consistency

Move dynamic zone queries to custom repository methods
This commit is contained in:
hg
2021-03-29 03:17:36 -04:00
committed by GitHub
parent f5cf566fca
commit 049fe55c7f
16 changed files with 814 additions and 623 deletions
+2
View File
@@ -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
+289
View File
@@ -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<int>(std::chrono::system_clock::to_time_t(m_start_time));
insert_instance.duration = static_cast<int>(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<DynamicZoneType>(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<int>(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<uint32_t>& character_ids)
{
LogDynamicZonesDetail("Saving [{}] members for instance [{}]", character_ids.size(), m_instance_id);
std::vector<InstanceListPlayerRepository::InstanceListPlayer> insert_players;
for (const auto& character_id : character_ids)
{
InstanceListPlayerRepository::InstanceListPlayer entry{};
entry.id = static_cast<int>(m_instance_id);
entry.charid = static_cast<int>(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<std::chrono::seconds>(GetDurationRemaining()).count();
return std::max(0, static_cast<int>(remaining));
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateServerAddRemoveCharacterPacket(
uint32_t character_id, bool removed)
{
constexpr uint32_t pack_size = sizeof(ServerDzCharacter_Struct);
auto pack = std::make_unique<ServerPacket>(ServerOP_DzAddRemoveCharacter, pack_size);
auto buf = reinterpret_cast<ServerDzCharacter_Struct*>(pack->pBuffer);
buf->zone_id = GetZoneID();
buf->instance_id = GetInstanceID();
buf->remove = removed;
buf->character_id = character_id;
return pack;
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateServerRemoveAllCharactersPacket()
{
constexpr uint32_t pack_size = sizeof(ServerDzCharacter_Struct);
auto pack = std::make_unique<ServerPacket>(ServerOP_DzRemoveAllCharacters, pack_size);
auto buf = reinterpret_cast<ServerDzCharacter_Struct*>(pack->pBuffer);
buf->zone_id = GetZoneID();
buf->instance_id = GetInstanceID();
buf->remove = true;
buf->character_id = 0;
return pack;
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateServerDzLocationPacket(
uint16_t server_opcode, const DynamicZoneLocation& location)
{
constexpr uint32_t pack_size = sizeof(ServerDzLocation_Struct);
auto pack = std::make_unique<ServerPacket>(server_opcode, pack_size);
auto buf = reinterpret_cast<ServerDzLocation_Struct*>(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;
}
+112
View File
@@ -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 <algorithm>
#include <chrono>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
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<uint16_t>(m_instance_id); }
uint32_t GetSecondsRemaining() const;
uint16_t GetZoneID() const { return static_cast<uint16_t>(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<uint32_t>& 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<ServerPacket> CreateServerAddRemoveCharacterPacket(uint32_t character_id, bool removed);
std::unique_ptr<ServerPacket> CreateServerRemoveAllCharactersPacket();
std::unique_ptr<ServerPacket> 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<std::chrono::system_clock> m_start_time;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
};
#endif
@@ -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<DynamicZoneInstance> GetWithInstance(Database& db,
const std::vector<uint32_t>& dynamic_zone_ids)
{
if (dynamic_zone_ids.empty())
{
return {};
}
std::vector<DynamicZoneInstance> 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<DynamicZoneInstancePlayerCount> AllDzInstancePlayerCounts(Database& db)
{
std::vector<DynamicZoneInstancePlayerCount> 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
+1 -1
View File
@@ -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