diff --git a/dNet/PacketDecoder.cpp b/dNet/PacketDecoder.cpp index 48da75ec8..6c280bf6a 100644 --- a/dNet/PacketDecoder.cpp +++ b/dNet/PacketDecoder.cpp @@ -1,6 +1,11 @@ #include "PacketDecoder.h" +#include +#include #include +#include +#include +#include #include #include @@ -21,16 +26,30 @@ #include "WorldPackets.h" #include "WorldRoutePacket.h" #include "magic_enum.hpp" +#include "PacketJson.h" +#include "master/CDClientReload.h" +#include "master/DashboardMessages.h" +#include "master/DataChanged.h" +#include "master/InstanceMigration.h" +#include "master/LiveUpdate.h" +#include "master/MessageCapture.h" +#include "master/PlayerAction.h" +#include "master/Profiling.h" +#include "master/ServerTraffic.h" +#include "master/UgcModelsMade.h" +#include "master/WorldFiles.h" +#include "Profiler.h" +#include "TrafficStats.h" +#include "ZoneFileLog.h" namespace { using json = nlohmann::json; - using Fields = std::function(RakNet::BitStream&, bool fromClient)>; using Redactor = std::function; using Scrubber = std::function; - struct Entry { - Fields fields; + // Packets with secrets or text to scrub (the fields of every packet come from PacketFields.inc) + struct Protection { Redactor redact; // set for structs with secret fields Scrubber scrub; // set for structs with account names, character names or typed text }; @@ -73,19 +92,124 @@ namespace { }; } - std::string Id(LWOOBJID id) { return std::to_string(id); } - json Point(const NiPoint3& p) { return json::array({ p.x, p.y, p.z }); } - json Rotation(const NiQuaternion& q) { return json::array({ q.x, q.y, q.z, q.w }); } + // The fields of every packet: its struct's own Deserialize, then its members by name (PacketFields.inc, written by + // tools/gen_game_message_fields.py from the structs) + using PacketJson::ToJson; - template - Fields Make(std::function fill) { - return [fill](RakNet::BitStream& stream, bool) -> std::optional { - T packet; - if (!packet.Deserialize(stream)) return std::nullopt; - json out = json::object(); - fill(packet, out); + json ToJson(const TrafficStats::Histogram& histogram) { + json buckets = json::array(); + for (const auto& [bucket, count] : histogram.Sparse()) buckets.push_back(json::array({ bucket, count })); + return json{ {"count", std::to_string(histogram.Count())}, {"sumUs", std::to_string(histogram.Sum())}, {"buckets", buckets} }; + } + + template json ToJson(const std::optional& value); + template json ToJson(const std::vector& values); + template json ToJson(const std::map& values); + template json ToJson(const std::pair& value); + template json ToJson(const std::array& values); + template json ToJson(const std::set& values); + template json ToJson(const std::deque& values); + template json ToJson(const std::unordered_map& values); + + struct Read { + std::optional fields; + uint32_t unreadBits{}; + }; + using Reader = Read(*)(RakNet::BitStream&); + + struct Entry { + ServiceType service; + uint32_t id; + const char* structName; + bool received; // read by the server from its client (has a Handle) + Reader read; + }; + + template Read ReadWith(RakNet::BitStream& stream); + +#include "PacketFields.inc" + + template json ToJson(const std::optional& value) { return value ? ToJson(*value) : json(nullptr); } + template json ToJson(const std::vector& values) { + if constexpr (std::is_same_v || std::is_same_v) { + return json{ {"hex", PacketJson::Hex(std::string_view(reinterpret_cast(values.data()), values.size()))} }; + } else { + json out = json::array(); + for (const auto& value : values) out.push_back(ToJson(value)); return out; - }; + } + } + template json ToJson(const std::map& values) { + json out = json::array(); + for (const auto& [key, value] : values) out.push_back(json::array({ ToJson(key), ToJson(value) })); + return out; + } + template json ToJson(const std::pair& value) { return json::array({ ToJson(value.first), ToJson(value.second) }); } + template json ToJson(const std::array& values) { + json out = json::array(); + for (const auto& value : values) out.push_back(ToJson(value)); + return out; + } + + template json ToJson(const std::set& values) { + json out = json::array(); + for (const auto& value : values) out.push_back(ToJson(value)); + return out; + } + template json ToJson(const std::deque& values) { + json out = json::array(); + for (const auto& value : values) out.push_back(ToJson(value)); + return out; + } + template json ToJson(const std::unordered_map& values) { + json out = json::array(); + for (const auto& [key, value] : values) out.push_back(json::array({ ToJson(key), ToJson(value) })); + return out; + } + + template Read ReadWith(RakNet::BitStream& stream) { + T packet; + if (!packet.Deserialize(stream)) return {}; + return { ToJson(packet), stream.GetNumberOfUnreadBits() }; + } + + // (service, ID) -> its structs, the ones the server reads from a client first + const std::map, std::vector>& FieldIndex() { + static const auto index = [] { + std::map, std::vector> out; + for (const auto& entry : Entries()) out[{ entry.service, entry.id }].push_back(&entry); + return out; + }(); + return index; + } + + /** + * The packet's fields from the first of its structs that reads the whole of it (a struct per direction can share + * an ID: the one for this direction is tried first). A struct that reads but leaves whole bytes unread is shown + * with "(unread bits)" when no struct reads it all. + */ + std::optional ReadFields(ServiceType service, uint32_t id, bool fromClient, RakNet::BitStream& stream, bool& failed) { + const auto it = FieldIndex().find({ service, id }); + if (it == FieldIndex().end()) return std::nullopt; + auto candidates = it->second; + std::stable_sort(candidates.begin(), candidates.end(), [fromClient](const Entry* a, const Entry* b) { + return (a->received == fromClient) > (b->received == fromClient); + }); + const auto start = stream.GetReadOffset(); + std::optional partial; + for (const auto* entry : candidates) { + if (!entry->read) continue; + stream.SetReadOffset(start); + auto read = entry->read(stream); + if (!read.fields) continue; + if (read.unreadBits < 8) return read.fields; + if (!partial) { + partial = std::move(read.fields); + (*partial)["(unread bits)"] = read.unreadBits; + } + } + failed = !partial; + return partial; } // Reads T from a whole packet, lets `blank` clear its secrets, and writes it back in place @@ -106,141 +230,23 @@ namespace { using Key = std::pair; template Key K(ServiceType service, E id) { return { service, static_cast(id) }; } - const std::map& Registry() { + const std::map& Registry() { using S = ServiceType; - static const std::map registry{ - // Handshake: one ID, a struct per direction - { K(S::COMMON, MessageType::Server::VERSION_CONFIRM), { [](RakNet::BitStream& stream, bool fromClient) -> std::optional { - if (fromClient) { - CommonPackets::ClientVersionConfirm packet; - if (!packet.Deserialize(stream)) return std::nullopt; - return json{ {"netVersion", packet.netVersion}, {"serviceType", static_cast(packet.serviceType)}, {"processID", packet.processID}, {"port", packet.port} }; - } - CommonPackets::ServerVersionConfirm packet; - if (!packet.Deserialize(stream)) return std::nullopt; - return json{ {"netVersion", packet.netVersion}, {"serviceType", static_cast(packet.serviceType)} }; - } } }, - - // Auth: the username and password are blanked when recorded - { K(S::AUTH, MessageType::Auth::LOGIN_REQUEST), { - Make([](const auto& p, json& j) { - j = { {"username", p.username.GetAsString()}, {"localeID", static_cast(p.localeID)}, {"clientOS", static_cast(p.clientOS)}, - {"memoryStats", p.memoryStats.GetAsString()}, {"videoCard", p.videoCard.GetAsString()} }; - }), - Blank([](auto& p) { p.username.string.clear(); p.password.string.clear(); }), - Scrub([](auto& p) { X(p.username); }) } }, - { K(S::CLIENT, MessageType::Client::LOGIN_RESPONSE), { - Make([](const auto& p, json& j) { - json stamps = json::array(); - for (const auto& stamp : p.stamps.list) stamps.push_back({ {"type", static_cast(stamp.type)}, {"value", stamp.value}, {"timestamp", stamp.timestamp} }); - j = { {"responseCode", static_cast(p.responseCode)}, {"worldServerIP", p.worldServerIP.string}, {"worldServerPort", p.worldServerPort}, - {"errorMessage", p.errorMessage}, {"stamps", stamps} }; - }), - Blank([](auto& p) { p.userKey.string.clear(); p.cdnKey.string.clear(); }) } }, - - // World - { K(S::WORLD, MessageType::World::VALIDATION), { - Make([](const auto& p, json& j) { j = { {"username", p.username.GetAsString()}, {"fdbChecksum", p.fdbChecksum.string} }; }), - Blank([](auto& p) { p.sessionKey.string.clear(); }), - Scrub([](auto& p) { X(p.username); }) } }, - { K(S::WORLD, MessageType::World::CHARACTER_CREATE_REQUEST), { Make([](const auto& p, json& j) { - j = { {"name", p.name.GetAsString()}, {"firstNameIndex", p.firstNameIndex}, {"middleNameIndex", p.middleNameIndex}, {"lastNameIndex", p.lastNameIndex}, - {"shirtColor", p.shirtColor}, {"shirtStyle", p.shirtStyle}, {"pantsColor", p.pantsColor}, {"hairStyle", p.hairStyle}, {"hairColor", p.hairColor}, - {"eyebrows", p.eyebrows}, {"eyes", p.eyes}, {"mouth", p.mouth} }; - }), - nullptr, Scrub(nullptr, [](auto& p) { X(p.name); }) } }, - { K(S::WORLD, MessageType::World::LOGIN_REQUEST), { Make([](const auto& p, json& j) { j = { {"playerID", Id(p.playerID)} }; }) } }, - { K(S::WORLD, MessageType::World::CHARACTER_DELETE_REQUEST), { Make([](const auto& p, json& j) { j = { {"objectID", Id(p.objectID)} }; }) } }, - { K(S::WORLD, MessageType::World::CHARACTER_RENAME_REQUEST), { Make([](const auto& p, json& j) { - j = { {"objectID", Id(p.objectID)}, {"name", p.name.GetAsString()} }; - }), - nullptr, Scrub(nullptr, [](auto& p) { X(p.name); }) } }, - { K(S::WORLD, MessageType::World::LEVEL_LOAD_COMPLETE), { Make([](const auto& p, json& j) { - j = { {"mapID", p.mapID}, {"instanceID", p.instanceID}, {"cloneID", p.cloneID} }; - }) } }, - { K(S::WORLD, MessageType::World::POSITION_UPDATE), { Make([](const auto& p, json& j) { - j = { {"position", Point(p.update.position)}, {"rotation", Rotation(p.update.rotation)}, {"onGround", p.update.onGround}, {"onRail", p.update.onRail} }; - if (p.hasVelocity) j["velocity"] = Point(p.update.velocity); - if (p.hasLocalSpaceInfo) j["platform"] = Id(p.update.localSpaceInfo.objectId); - }) } }, - { K(S::WORLD, MessageType::World::GENERAL_CHAT_MESSAGE), { Make([](const auto& p, json& j) { - j = { {"chatChannel", p.chatChannel}, {"message", GeneralUtils::UTF16ToWTF8(p.message)} }; - }), - nullptr, Scrub(nullptr, [](auto& p) { X(p.message); }) } }, - { K(S::WORLD, MessageType::World::ROUTE_PACKET), { Make([](const auto& p, json& j) { - j = { {"routed", PacketDecoder::Name(p.routedService, p.routedMessageID)}, {"size", p.size} }; - }) } }, - - // To the client - { K(S::CLIENT, MessageType::Client::LOAD_STATIC_ZONE), { Make([](const auto& p, json& j) { - j = { {"mapID", p.mapID}, {"instanceID", p.instanceID}, {"cloneID", p.cloneID}, {"mapChecksum", p.mapChecksum}, {"playerPosition", Point(p.playerPosition)}, - {"instanceType", p.instanceType} }; - }) } }, - { K(S::CLIENT, MessageType::Client::CHARACTER_LIST_RESPONSE), { Make([](const auto& p, json& j) { - json characters = json::array(); - for (const auto& c : p.characters) { - characters.push_back({ {"objectID", Id(c.objectID)}, {"name", c.name.GetAsString()}, {"zoneID", c.zoneID}, {"equippedItems", c.equippedItems} }); - } - j = { {"selectedCharacterIndex", p.selectedCharacterIndex}, {"characters", characters} }; - }), - nullptr, Scrub(nullptr, [](auto& p) { for (auto& c : p.characters) { X(c.name); X(c.unapprovedName); } }) } }, - { K(S::CLIENT, MessageType::Client::CREATE_CHARACTER), { Make([](const auto& p, json& j) { - j = { {"objectID", Id(p.objectID)}, {"templateID", p.templateID}, {"name", GeneralUtils::UTF16ToWTF8(p.name)}, {"gmLevel", static_cast(p.gmLevel)}, - {"reputation", p.reputation}, {"propertyCloneID", p.propertyCloneID}, {"xmlBytes", p.xmlData.size()} }; - }), - nullptr, Scrub(nullptr, [](auto& p) { X(p.name); }) } }, - { K(S::CLIENT, MessageType::Client::CHARACTER_CREATE_RESPONSE), { Make([](const auto& p, json& j) { - j = { {"response", static_cast(p.response)} }; - }) } }, - { K(S::CLIENT, MessageType::Client::TRANSFER_TO_WORLD), { Make([](const auto& p, json& j) { - j = { {"serverIP", p.serverIP.string}, {"serverPort", p.serverPort}, {"mythranShift", p.mythranShift} }; - }) } }, - - // Chat - { K(S::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE), { Make([](const auto& p, json& j) { - j = { {"playerID", Id(p.playerID)}, {"chatChannel", static_cast(p.chatChannel)}, {"message", p.message.GetAsString()} }; - }), - nullptr, Scrub(nullptr, [](auto& p) { X(p.senderName); X(p.message); }) } }, - { K(S::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE), { Make([](const auto& p, json& j) { - j = { {"playerID", Id(p.playerID)}, {"senderName", p.senderName.GetAsString()}, {"receiverName", p.receiverName.GetAsString()}, - {"responseCode", p.responseCode}, {"message", p.message.GetAsString()} }; - }), - nullptr, Scrub(nullptr, [](auto& p) { X(p.senderName); X(p.receiverName); X(p.message); }) } }, - { K(S::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET), { Make([](const auto& p, json& j) { - j = { {"targetID", Id(p.targetID)}, {"bytes", p.routedData.size()} }; - if (p.routedData.size() >= 8 && p.routedData[0] == ID_USER_PACKET_ENUM) { - uint16_t service; - uint32_t id; - std::memcpy(&service, p.routedData.data() + 1, sizeof(service)); - std::memcpy(&id, p.routedData.data() + 3, sizeof(id)); - j["routed"] = PacketDecoder::Name(static_cast(service), id); - } - }) } }, - - // Between servers: session keys are blanked when recorded - { K(S::MASTER, MessageType::Master::REQUEST_SESSION_KEY), { Make([](const auto& p, json& j) { j = { {"username", p.username.GetAsString()} }; }), - nullptr, Scrub([](auto& p) { X(p.username); }) } }, - { K(S::MASTER, MessageType::Master::SESSION_KEY_RESPONSE), { - Make([](const auto& p, json& j) { j = { {"username", p.username.GetAsString()} }; }), - Blank([](auto& p) { p.sessionKey = 0; }), - Scrub([](auto& p) { X(p.username); }) } }, - { K(S::MASTER, MessageType::Master::SET_SESSION_KEY), { - Make([](const auto& p, json& j) { j = { {"username", p.username.string} }; }), - Blank([](auto& p) { p.sessionKey = 0; }), - Scrub([](auto& p) { X(p.username); }) } }, - { K(S::MASTER, MessageType::Master::NEW_SESSION_ALERT), { - Make([](const auto& p, json& j) { j = { {"username", p.username.string} }; }), - Blank([](auto& p) { p.sessionKey = 0; }), - Scrub([](auto& p) { X(p.username); }) } }, - { K(S::MASTER, MessageType::Master::REQUEST_ZONE_TRANSFER), { Make([](const auto& p, json& j) { - j = { {"requestID", Id(p.requestID)}, {"zoneID", p.zoneID}, {"cloneID", p.cloneID}, {"mythranShift", p.mythranShift}, {"stamps", p.stamps.size()} }; - }) } }, - { K(S::MASTER, MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE), { Make([](const auto& p, json& j) { - j = { {"requestID", Id(p.requestID)}, {"zoneID", p.zoneID}, {"zoneInstance", p.zoneInstance}, {"zoneClone", p.zoneClone}, {"serverPort", p.serverPort}, - {"stamps", p.stamps.size()} }; - }) } }, - { K(S::MASTER, MessageType::Master::PLAYER_ADDED), { Make([](const auto& p, json& j) { j = { {"zoneID", p.zoneID}, {"instanceID", p.instanceID} }; }) } }, - { K(S::MASTER, MessageType::Master::PLAYER_REMOVED), { Make([](const auto& p, json& j) { j = { {"zoneID", p.zoneID}, {"instanceID", p.instanceID} }; }) } }, + static const std::map registry{ + { K(S::AUTH, MessageType::Auth::LOGIN_REQUEST), { Blank([](auto& p) { p.username.string.clear(); p.password.string.clear(); }), Scrub([](auto& p) { X(p.username); }) } }, + { K(S::CLIENT, MessageType::Client::LOGIN_RESPONSE), { Blank([](auto& p) { p.userKey.string.clear(); p.cdnKey.string.clear(); }), nullptr } }, + { K(S::WORLD, MessageType::World::VALIDATION), { Blank([](auto& p) { p.sessionKey.string.clear(); }), Scrub([](auto& p) { X(p.username); }) } }, + { K(S::WORLD, MessageType::World::CHARACTER_CREATE_REQUEST), { nullptr, Scrub(nullptr, [](auto& p) { X(p.name); }) } }, + { K(S::WORLD, MessageType::World::CHARACTER_RENAME_REQUEST), { nullptr, Scrub(nullptr, [](auto& p) { X(p.name); }) } }, + { K(S::WORLD, MessageType::World::GENERAL_CHAT_MESSAGE), { nullptr, Scrub(nullptr, [](auto& p) { X(p.message); }) } }, + { K(S::CLIENT, MessageType::Client::CHARACTER_LIST_RESPONSE), { nullptr, Scrub(nullptr, [](auto& p) { for (auto& c : p.characters) { X(c.name); X(c.unapprovedName); } }) } }, + { K(S::CLIENT, MessageType::Client::CREATE_CHARACTER), { nullptr, Scrub(nullptr, [](auto& p) { X(p.name); }) } }, + { K(S::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE), { nullptr, Scrub(nullptr, [](auto& p) { X(p.senderName); X(p.message); }) } }, + { K(S::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE), { nullptr, Scrub(nullptr, [](auto& p) { X(p.senderName); X(p.receiverName); X(p.message); }) } }, + { K(S::MASTER, MessageType::Master::REQUEST_SESSION_KEY), { nullptr, Scrub([](auto& p) { X(p.username); }) } }, + { K(S::MASTER, MessageType::Master::SESSION_KEY_RESPONSE), { Blank([](auto& p) { p.sessionKey = 0; }), Scrub([](auto& p) { X(p.username); }) } }, + { K(S::MASTER, MessageType::Master::SET_SESSION_KEY), { Blank([](auto& p) { p.sessionKey = 0; }), Scrub([](auto& p) { X(p.username); }) } }, + { K(S::MASTER, MessageType::Master::NEW_SESSION_ALERT), { Blank([](auto& p) { p.sessionKey = 0; }), Scrub([](auto& p) { X(p.username); }) } }, }; return registry; } @@ -312,6 +318,27 @@ namespace { } } +namespace { + // Routed packets: the name of the packet they carry + void AddRoutedName(ServiceType service, uint32_t id, std::string_view bytes, json& fields) { + RakNet::BitStream stream(reinterpret_cast(const_cast(bytes.data())), static_cast(bytes.size()), false); + stream.IgnoreBytes(8); + if (service == ServiceType::WORLD && id == static_cast(MessageType::World::ROUTE_PACKET)) { + WorldPackets::RoutePacket packet; + if (packet.Deserialize(stream)) fields["routed"] = PacketDecoder::Name(packet.routedService, packet.routedMessageID); + } else if (service == ServiceType::CHAT && id == static_cast(MessageType::Chat::WORLD_ROUTE_PACKET)) { + ChatPackets::WorldRoutePacket packet; + if (packet.Deserialize(stream) && packet.routedData.size() >= 8 && packet.routedData[0] == ID_USER_PACKET_ENUM) { + uint16_t routedService; + uint32_t routedId; + std::memcpy(&routedService, packet.routedData.data() + 1, sizeof(routedService)); + std::memcpy(&routedId, packet.routedData.data() + 3, sizeof(routedId)); + fields["routed"] = PacketDecoder::Name(static_cast(routedService), routedId); + } + } + } +} + namespace PacketDecoder { std::string Name(ServiceType service, uint32_t messageId) { switch (service) { @@ -344,7 +371,7 @@ namespace PacketDecoder { int32_t lot{}; if (stream.Read(flag) && stream.Read(network) && stream.Read(object) && stream.Read(lot)) { out.objectId = object; - out.fields = json{ {"networkID", network}, {"objectID", Id(object)}, {"lot", lot} }; + out.fields = json{ {"networkID", network}, {"objectID", std::to_string(object)}, {"lot", lot} }; } } return out; @@ -380,10 +407,8 @@ namespace PacketDecoder { return out; } - const auto it = Registry().find({ service, id }); - if (it == Registry().end()) return out; - out.fields = it->second.fields(stream, fromClient); - out.failed = !out.fields.has_value(); + out.fields = ReadFields(service, id, fromClient, stream, out.failed); + if (out.fields) AddRoutedName(service, id, bytes, *out.fields); return out; } @@ -434,5 +459,7 @@ namespace PacketDecoder { return it != Registry().end() && it->second.redact; } - size_t RegisteredCount() { return Registry().size() + 1; } + size_t RegisteredCount() { return FieldIndex().size(); } + + bool HasFields(ServiceType service, uint32_t messageId) { return FieldIndex().contains({ service, messageId }); } } diff --git a/dNet/PacketDecoder.h b/dNet/PacketDecoder.h index e2bde47ff..e376e48e8 100644 --- a/dNet/PacketDecoder.h +++ b/dNet/PacketDecoder.h @@ -74,6 +74,9 @@ namespace PacketDecoder { // How many (service, message) pairs have typed decoders, for tests size_t RegisteredCount(); + + // Whether a (service, message) has a struct its fields are read with + bool HasFields(ServiceType service, uint32_t messageId); } #endif //!__PACKETDECODER__H__ diff --git a/dNet/PacketFields.inc b/dNet/PacketFields.inc new file mode 100644 index 000000000..ec6e9636b --- /dev/null +++ b/dNet/PacketFields.inc @@ -0,0 +1,1891 @@ +// Generated by tools/gen_game_message_fields.py from the packet structs. Do not edit: run the script. + +// Every struct's members, in declaration order (a base struct's first) +json ToJson(const AuthPackets::LoginRequest& m); +json ToJson(const ChatPackets::LoginSessionNotify& m); +json ToJson(const ChatPackets::UnexpectedDisconnect& m); +json ToJson(const ChatPackets::GMLevelUpdate& m); +json ToJson(const ChatPackets::GMMute& m); +json ToJson(const ChatPackets::Announcement& m); +json ToJson(const ChatPackets::CreateTeam& m); +json ToJson(const ChatPackets::TeamUpdate& m); +json ToJson(const ChatPackets::AchievementNotify& m); +json ToJson(const ChatPackets::MailNotify& m); +json ToJson(const ChatPackets::ShowAllRequest& m); +json ToJson(const ChatPackets::FindPlayerRequest& m); +json ToJson(const ChatPackets::GetFriendsList& m); +json ToJson(const ChatPackets::AddFriendRequest& m); +json ToJson(const ChatPackets::AddFriendResponse& m); +json ToJson(const ChatPackets::RemoveFriend& m); +json ToJson(const ChatPackets::GetIgnoreList& m); +json ToJson(const ChatPackets::AddIgnore& m); +json ToJson(const ChatPackets::RemoveIgnore& m); +json ToJson(const ChatPackets::GeneralChatMessage& m); +json ToJson(const ChatPackets::PrivateChatMessage& m); +json ToJson(const ChatPackets::TeamInvite& m); +json ToJson(const ChatPackets::TeamInviteResponse& m); +json ToJson(const ChatPackets::TeamLeave& m); +json ToJson(const ChatPackets::TeamKick& m); +json ToJson(const ChatPackets::TeamSetLeader& m); +json ToJson(const ChatPackets::TeamSetLoot& m); +json ToJson(const ChatPackets::TeamGetStatus& m); +json ToJson(const ChatPackets::RequestMinimumChatMode& m); +json ToJson(const ChatPackets::RequestMinimumChatModePrivate& m); +json ToJson(const ChatPackets::GuildInvite& m); +json ToJson(const ChatPackets::GuildInviteResponse& m); +json ToJson(const ChatPackets::GuildLeave& m); +json ToJson(const ChatPackets::GuildGetAll& m); +json ToJson(const ChatPackets::GuildCreate& m); +json ToJson(const ChatPackets::GuildKick& m); +json ToJson(const ChatPackets::GuildSetRank& m); +json ToJson(const ChatPackets::GuildDisband& m); +json ToJson(const ChatPackets::GuildStatus& m); +json ToJson(const ChatPackets::Client::GeneralChatMessage& m); +json ToJson(const ChatPackets::Client::PrivateChatMessage& m); +json ToJson(const ClientPackets::LoginResponse& m); +json ToJson(const ClientPackets::LoadStaticZone& m); +json ToJson(const ClientPackets::CharacterListResponse& m); +json ToJson(const ClientPackets::CharacterListResponse::Character& m); +json ToJson(const ClientPackets::CharacterCreateResponse& m); +json ToJson(const ClientPackets::CharacterRenameResponse& m); +json ToJson(const ClientPackets::DeleteCharacterResponse& m); +json ToJson(const ClientPackets::TransferToWorld& m); +json ToJson(const ClientPackets::ServerStates& m); +json ToJson(const ClientPackets::CreateCharacter& m); +json ToJson(const ClientPackets::ChatModerationString& m); +json ToJson(const ClientPackets::MakeGMResponse& m); +json ToJson(const ClientPackets::HTTPMonitorInfoResponse& m); +json ToJson(const ClientPackets::DebugOutput& m); +json ToJson(const ClientPackets::BlueprintSaveResponse& m); +json ToJson(const ClientPackets::BlueprintSaveResponse::Model& m); +json ToJson(const ClientPackets::BlueprintLoadItemResponse& m); +json ToJson(const ClientPackets::UgcManifestResponse& m); +json ToJson(const ClientPackets::MinimumChatModeResponse& m); +json ToJson(const ClientPackets::MinimumChatModeResponsePrivate& m); +json ToJson(const ClientPackets::SendCannedText& m); +json ToJson(const ClientPackets::GetFriendsListResponse& m); +json ToJson(const ClientPackets::AddFriendRequest& m); +json ToJson(const ClientPackets::AddFriendResponse& m); +json ToJson(const ClientPackets::RemoveFriendResponse& m); +json ToJson(const ClientPackets::UpdateFriendNotify& m); +json ToJson(const ClientPackets::WhoResponse& m); +json ToJson(const ClientPackets::ShowAllResponse& m); +json ToJson(const ClientPackets::ShowAllResponse::Player& m); +json ToJson(const ClientPackets::GetIgnoreListResponse& m); +json ToJson(const ClientPackets::GetIgnoreListResponse::Ignored& m); +json ToJson(const ClientPackets::AddIgnoreResponse& m); +json ToJson(const ClientPackets::RemoveIgnoreResponse& m); +json ToJson(const ClientPackets::TeamInvite& m); +json ToJson(const ClientPackets::TeamInviteInitialResponse& m); +json ToJson(const ClientPackets::GuildCreateResponse& m); +json ToJson(const ClientPackets::GuildInvite& m); +json ToJson(const ClientPackets::GuildInviteInitialResponse& m); +json ToJson(const ClientPackets::GuildInviteFinalResponse& m); +json ToJson(const ClientPackets::GuildInviteConfirm& m); +json ToJson(const ClientPackets::GuildAddPlayer& m); +json ToJson(const ClientPackets::GuildRemovePlayer& m); +json ToJson(const ClientPackets::GuildLoginLogout& m); +json ToJson(const ClientPackets::GuildData& m); +json ToJson(const ClientPackets::GuildData::Member& m); +json ToJson(const CommonPackets::ClientVersionConfirm& m); +json ToJson(const CommonPackets::ServerVersionConfirm& m); +json ToJson(const CommonPackets::DisconnectNotify& m); +json ToJson(const CommonPackets::GeneralNotify& m); +json ToJson(const MasterPackets::RequestZoneTransfer& m); +json ToJson(const MasterPackets::RequestZoneTransferResponse& m); +json ToJson(const MasterPackets::ServerInfo& m); +json ToJson(const MasterPackets::RequestSessionKey& m); +json ToJson(const MasterPackets::SetSessionKey& m); +json ToJson(const MasterPackets::SessionKeyResponse& m); +json ToJson(const MasterPackets::NewSessionAlert& m); +json ToJson(const MasterPackets::PlayerCountChange& m); +json ToJson(const MasterPackets::PlayerAdded& m); +json ToJson(const MasterPackets::PlayerRemoved& m); +json ToJson(const MasterPackets::CreatePrivateZone& m); +json ToJson(const MasterPackets::RequestPrivateZone& m); +json ToJson(const MasterPackets::WorldReady& m); +json ToJson(const MasterPackets::WorldReadyInfo& m); +json ToJson(const MasterPackets::PrepZone& m); +json ToJson(const MasterPackets::Shutdown& m); +json ToJson(const MasterPackets::ShutdownResponse& m); +json ToJson(const MasterPackets::WorldShutDown& m); +json ToJson(const MasterPackets::ShutdownUniverse& m); +json ToJson(const MasterPackets::AffirmTransfer& m); +json ToJson(const MasterPackets::AffirmTransferRequest& m); +json ToJson(const MasterPackets::AffirmTransferResponse& m); +json ToJson(const MasterPackets::RequestServerList& m); +json ToJson(const MasterPackets::ServerListResponse& m); +json ToJson(const MasterPackets::ServerListResponse::Instance& m); +json ToJson(const MasterPackets::ServerListResponse::Endpoint& m); +json ToJson(const MasterPackets::DashboardShutdown& m); +json ToJson(const MasterPackets::ConfigReload& m); +json ToJson(const MasterPackets::InstanceShutdown& m); +json ToJson(const WorldPackets::WorldLUBitStream& m); +json ToJson(const WorldPackets::Validation& m); +json ToJson(const WorldPackets::CharacterListRequest& m); +json ToJson(const WorldPackets::CharacterCreateRequest& m); +json ToJson(const WorldPackets::CharacterLoginRequest& m); +json ToJson(const WorldPackets::GameMessage& m); +json ToJson(const WorldPackets::CharacterDeleteRequest& m); +json ToJson(const WorldPackets::CharacterRenameRequest& m); +json ToJson(const WorldPackets::LevelLoadComplete& m); +json ToJson(const WorldPackets::PositionUpdate& m); +json ToJson(const WorldPackets::MailPacket& m); +json ToJson(const WorldPackets::RoutePacket& m); +json ToJson(const WorldPackets::StringCheck& m); +json ToJson(const WorldPackets::GeneralChatMessage& m); +json ToJson(const WorldPackets::TmpGuildCreate& m); +json ToJson(const WorldPackets::HandleFunness& m); +json ToJson(const WorldPackets::RequestUgcManifestInfo& m); +json ToJson(const WorldPackets::UgcDownloadFailed& m); +json ToJson(const WorldPackets::UIHelpTop5& m); +json ToJson(const ChatPackets::WorldRoutePacket& m); +json ToJson(const CDClientReload& m); +json ToJson(const PlayerPositions& m); +json ToJson(const PlayerPositions::Player& m); +json ToJson(const Announcement& m); +json ToJson(const DataChanged& m); +json ToJson(const DataChanged::Entry& m); +json ToJson(const InstanceMigrationRequest& m); +json ToJson(const MigratePlayersOrder& m); +json ToJson(const MigrationStatus& m); +json ToJson(const CarriedPlayerState& m); +json ToJson(const MigratePrepare& m); +json ToJson(const LiveUpdateRequest& m); +json ToJson(const LiveUpdateStatus& m); +json ToJson(const LiveUpdateStatus::Unit& m); +json ToJson(const LiveUpdateRetire& m); +json ToJson(const ChatServerReady& m); +json ToJson(const ChatHandoff& m); +json ToJson(const ChatHandoff::Team& m); +json ToJson(const MessageCaptureControl& m); +json ToJson(const MessageCaptureEntry& m); +json ToJson(const MessageCaptureData& m); +json ToJson(const PlayerActionRequest& m); +json ToJson(const PlayerActionResult& m); +json ToJson(const ProfileRequest& m); +json ToJson(const ProfileResult& m); +json ToJson(const ServerTraffic& m); +json ToJson(const UgcModelsMade& m); +json ToJson(const WorldFilesReport& m); +json ToJson(const WorldReloadRequest& m); +json ToJson(const WorldFilesStatus& m); +json ToJson(const WorldFilesStatus::File& m); +json ToJson(const WorldFilesStatus::Instance& m); +json ToJson(const WorldFilesStatus::Zone& m); +json ToJson(const RemoteInputInfo& m); +json ToJson(const LocalSpaceInfo& m); +json ToJson(const PositionUpdate& m); +json ToJson(const Profiler::Second& m); +json ToJson(const Profiler::MessageTime& m); +json ToJson(const Profiler::Node& m); +json ToJson(const Profiler::Frame& m); +json ToJson(const Profiler::Report& m); +json ToJson(const Profiler::Profile& m); +json ToJson(const TrafficStats::MessageKey& m); +json ToJson(const TrafficStats::PeerCounts& m); +json ToJson(const TrafficStats::MessageCount& m); +json ToJson(const TrafficStats::Second& m); +json ToJson(const TrafficStats::RouteStats& m); +json ToJson(const TrafficStats::Link& m); +json ToJson(const TrafficStats::Connection& m); +json ToJson(const TrafficStats::Report& m); +json ToJson(const ZoneFileLog::Entry& m); +json ToJson(const FriendData& m); + +json ToJson(const AuthPackets::LoginRequest& m) { + json j = json::object(); + j["username"] = ToJson(m.username); + j["localeID"] = ToJson(m.localeID); + j["clientOS"] = ToJson(m.clientOS); + j["memoryStats"] = ToJson(m.memoryStats); + j["videoCard"] = ToJson(m.videoCard); + j["numberOfProcessors"] = ToJson(m.numberOfProcessors); + j["processorType"] = ToJson(m.processorType); + j["processorLevel"] = ToJson(m.processorLevel); + j["processorRevision"] = ToJson(m.processorRevision); + j["osVersionInfoSize"] = ToJson(m.osVersionInfoSize); + j["majorVersion"] = ToJson(m.majorVersion); + j["minorVersion"] = ToJson(m.minorVersion); + j["buildNumber"] = ToJson(m.buildNumber); + j["platformID"] = ToJson(m.platformID); + return j; +} +json ToJson(const ChatPackets::LoginSessionNotify& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["playerName"] = ToJson(m.playerName); + j["zoneID"] = ToJson(m.zoneID); + j["muteExpire"] = ToJson(m.muteExpire); + j["gmLevel"] = ToJson(m.gmLevel); + j["resync"] = ToJson(m.resync); + return j; +} +json ToJson(const ChatPackets::UnexpectedDisconnect& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + return j; +} +json ToJson(const ChatPackets::GMLevelUpdate& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["gmLevel"] = ToJson(m.gmLevel); + return j; +} +json ToJson(const ChatPackets::GMMute& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["expire"] = ToJson(m.expire); + return j; +} +json ToJson(const ChatPackets::Announcement& m) { + json j = json::object(); + j["title"] = ToJson(m.title); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const ChatPackets::CreateTeam& m) { + json j = json::object(); + j["leaderID"] = ToJson(m.leaderID); + j["members"] = ToJson(m.members); + j["zoneID"] = ToJson(m.zoneID); + return j; +} +json ToJson(const ChatPackets::TeamUpdate& m) { + json j = json::object(); + j["teamID"] = ToJson(m.teamID); + j["deleteTeam"] = ToJson(m.deleteTeam); + j["lootFlag"] = ToJson(m.lootFlag); + j["members"] = ToJson(m.members); + return j; +} +json ToJson(const ChatPackets::AchievementNotify& m) { + json j = json::object(); + j["targetPlayerName"] = ToJson(m.targetPlayerName); + j["missionEmailID"] = ToJson(m.missionEmailID); + j["earningPlayerID"] = ToJson(m.earningPlayerID); + j["earnerName"] = ToJson(m.earnerName); + return j; +} +json ToJson(const ChatPackets::MailNotify& m) { + json j = json::object(); + j["receiverID"] = ToJson(m.receiverID); + return j; +} +json ToJson(const ChatPackets::ShowAllRequest& m) { + json j = json::object(); + j["requestor"] = ToJson(m.requestor); + j["displayZoneData"] = ToJson(m.displayZoneData); + j["displayIndividualPlayers"] = ToJson(m.displayIndividualPlayers); + return j; +} +json ToJson(const ChatPackets::FindPlayerRequest& m) { + json j = json::object(); + j["requestor"] = ToJson(m.requestor); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ChatPackets::GetFriendsList& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + return j; +} +json ToJson(const ChatPackets::AddFriendRequest& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["friendName"] = ToJson(m.friendName); + j["isBestFriendRequest"] = ToJson(m.isBestFriendRequest); + return j; +} +json ToJson(const ChatPackets::AddFriendResponse& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["responseCode"] = ToJson(m.responseCode); + j["friendName"] = ToJson(m.friendName); + return j; +} +json ToJson(const ChatPackets::RemoveFriend& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["friendName"] = ToJson(m.friendName); + return j; +} +json ToJson(const ChatPackets::GetIgnoreList& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + return j; +} +json ToJson(const ChatPackets::AddIgnore& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ChatPackets::RemoveIgnore& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ChatPackets::GeneralChatMessage& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["chatChannel"] = ToJson(m.chatChannel); + j["messageLength"] = ToJson(m.messageLength); + j["senderName"] = ToJson(m.senderName); + j["senderID"] = ToJson(m.senderID); + j["sourceID"] = ToJson(m.sourceID); + j["senderGMLevel"] = ToJson(m.senderGMLevel); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const ChatPackets::PrivateChatMessage& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["chatChannel"] = ToJson(m.chatChannel); + j["messageLength"] = ToJson(m.messageLength); + j["senderName"] = ToJson(m.senderName); + j["senderID"] = ToJson(m.senderID); + j["sourceID"] = ToJson(m.sourceID); + j["senderGMLevel"] = ToJson(m.senderGMLevel); + j["receiverName"] = ToJson(m.receiverName); + j["receiverGMLevel"] = ToJson(m.receiverGMLevel); + j["responseCode"] = ToJson(m.responseCode); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const ChatPackets::TeamInvite& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["invitedPlayer"] = ToJson(m.invitedPlayer); + return j; +} +json ToJson(const ChatPackets::TeamInviteResponse& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["declined"] = ToJson(m.declined); + j["leaderID"] = ToJson(m.leaderID); + return j; +} +json ToJson(const ChatPackets::TeamLeave& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + return j; +} +json ToJson(const ChatPackets::TeamKick& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["kickedPlayer"] = ToJson(m.kickedPlayer); + return j; +} +json ToJson(const ChatPackets::TeamSetLeader& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["promotedPlayer"] = ToJson(m.promotedPlayer); + return j; +} +json ToJson(const ChatPackets::TeamSetLoot& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["lootFlag"] = ToJson(m.lootFlag); + return j; +} +json ToJson(const ChatPackets::TeamGetStatus& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + return j; +} +json ToJson(const ChatPackets::RequestMinimumChatMode& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["chatChannel"] = ToJson(m.chatChannel); + return j; +} +json ToJson(const ChatPackets::RequestMinimumChatModePrivate& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["chatChannel"] = ToJson(m.chatChannel); + j["recipientName"] = ToJson(m.recipientName); + return j; +} +json ToJson(const ChatPackets::GuildInvite& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["invitedPlayer"] = ToJson(m.invitedPlayer); + return j; +} +json ToJson(const ChatPackets::GuildInviteResponse& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["declined"] = ToJson(m.declined); + return j; +} +json ToJson(const ChatPackets::GuildLeave& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + return j; +} +json ToJson(const ChatPackets::GuildGetAll& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + return j; +} +json ToJson(const ChatPackets::GuildCreate& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["guildName"] = ToJson(m.guildName); + return j; +} +json ToJson(const ChatPackets::GuildKick& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["unknown"] = ToJson(m.unknown); + j["kickedPlayer"] = ToJson(m.kickedPlayer); + return j; +} +json ToJson(const ChatPackets::GuildSetRank& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["targetPlayer"] = ToJson(m.targetPlayer); + j["rank"] = ToJson(m.rank); + return j; +} +json ToJson(const ChatPackets::GuildDisband& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + return j; +} +json ToJson(const ChatPackets::GuildStatus& m) { + json j = json::object(); + j["characterID"] = ToJson(m.characterID); + j["guildID"] = ToJson(m.guildID); + j["guildName"] = ToJson(m.guildName); + return j; +} +json ToJson(const ChatPackets::Client::GeneralChatMessage& m) { + json j = json::object(); + j["unknown"] = ToJson(m.unknown); + j["chatChannel"] = ToJson(m.chatChannel); + j["senderName"] = ToJson(m.senderName); + j["senderID"] = ToJson(m.senderID); + j["sourceID"] = ToJson(m.sourceID); + j["senderGMLevel"] = ToJson(m.senderGMLevel); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const ChatPackets::Client::PrivateChatMessage& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["chatChannel"] = ToJson(m.chatChannel); + j["messageLength"] = ToJson(m.messageLength); + j["senderName"] = ToJson(m.senderName); + j["senderID"] = ToJson(m.senderID); + j["sourceID"] = ToJson(m.sourceID); + j["senderGMLevel"] = ToJson(m.senderGMLevel); + j["receiverName"] = ToJson(m.receiverName); + j["receiverGMLevel"] = ToJson(m.receiverGMLevel); + j["responseCode"] = ToJson(m.responseCode); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const ClientPackets::LoginResponse& m) { + json j = json::object(); + j["responseCode"] = ToJson(m.responseCode); + j["versionMajor"] = ToJson(m.versionMajor); + j["versionCurrent"] = ToJson(m.versionCurrent); + j["versionMinor"] = ToJson(m.versionMinor); + j["worldServerIP"] = ToJson(m.worldServerIP); + j["chatServerIP"] = ToJson(m.chatServerIP); + j["worldServerPort"] = ToJson(m.worldServerPort); + j["chatServerPort"] = ToJson(m.chatServerPort); + j["cdnTicket"] = ToJson(m.cdnTicket); + j["language"] = ToJson(m.language); + j["localization"] = ToJson(m.localization); + j["justUpgradedFromF2P"] = ToJson(m.justUpgradedFromF2P); + j["isFreeToPlay"] = ToJson(m.isFreeToPlay); + j["freeToPlayTimeRemaining"] = ToJson(m.freeToPlayTimeRemaining); + j["errorMessage"] = ToJson(m.errorMessage); + j["stamps"] = ToJson(m.stamps); + return j; +} +json ToJson(const ClientPackets::LoadStaticZone& m) { + json j = json::object(); + j["mapID"] = ToJson(m.mapID); + j["instanceID"] = ToJson(m.instanceID); + j["cloneID"] = ToJson(m.cloneID); + j["mapChecksum"] = ToJson(m.mapChecksum); + j["editorEnabled"] = ToJson(m.editorEnabled); + j["editorLevel"] = ToJson(m.editorLevel); + j["playerPosition"] = ToJson(m.playerPosition); + j["instanceType"] = ToJson(m.instanceType); + return j; +} +json ToJson(const ClientPackets::CharacterListResponse& m) { + json j = json::object(); + j["selectedCharacterIndex"] = ToJson(m.selectedCharacterIndex); + j["characters"] = ToJson(m.characters); + return j; +} +json ToJson(const ClientPackets::CharacterListResponse::Character& m) { + json j = json::object(); + j["objectID"] = ToJson(m.objectID); + j["unknown1"] = ToJson(m.unknown1); + j["name"] = ToJson(m.name); + j["unapprovedName"] = ToJson(m.unapprovedName); + j["nameRejected"] = ToJson(m.nameRejected); + j["isFreeToPlay"] = ToJson(m.isFreeToPlay); + j["unknown2"] = ToJson(m.unknown2); + j["shirtColor"] = ToJson(m.shirtColor); + j["shirtStyle"] = ToJson(m.shirtStyle); + j["pantsColor"] = ToJson(m.pantsColor); + j["hairStyle"] = ToJson(m.hairStyle); + j["hairColor"] = ToJson(m.hairColor); + j["leftHand"] = ToJson(m.leftHand); + j["rightHand"] = ToJson(m.rightHand); + j["eyebrows"] = ToJson(m.eyebrows); + j["eyes"] = ToJson(m.eyes); + j["mouth"] = ToJson(m.mouth); + j["unknown3"] = ToJson(m.unknown3); + j["zoneID"] = ToJson(m.zoneID); + j["zoneInstance"] = ToJson(m.zoneInstance); + j["zoneClone"] = ToJson(m.zoneClone); + j["lastLogin"] = ToJson(m.lastLogin); + j["equippedItems"] = ToJson(m.equippedItems); + return j; +} +json ToJson(const ClientPackets::CharacterCreateResponse& m) { + json j = json::object(); + j["response"] = ToJson(m.response); + return j; +} +json ToJson(const ClientPackets::CharacterRenameResponse& m) { + json j = json::object(); + j["response"] = ToJson(m.response); + return j; +} +json ToJson(const ClientPackets::DeleteCharacterResponse& m) { + json j = json::object(); + j["success"] = ToJson(m.success); + return j; +} +json ToJson(const ClientPackets::TransferToWorld& m) { + json j = json::object(); + j["serverIP"] = ToJson(m.serverIP); + j["serverPort"] = ToJson(m.serverPort); + j["mythranShift"] = ToJson(m.mythranShift); + return j; +} +json ToJson(const ClientPackets::ServerStates& m) { + json j = json::object(); + j["serverState"] = ToJson(m.serverState); + return j; +} +json ToJson(const ClientPackets::CreateCharacter& m) { + json j = json::object(); + j["objectID"] = ToJson(m.objectID); + j["templateID"] = ToJson(m.templateID); + j["xmlData"] = ToJson(m.xmlData); + j["name"] = ToJson(m.name); + j["gmLevel"] = ToJson(m.gmLevel); + j["chatMode"] = ToJson(m.chatMode); + j["reputation"] = ToJson(m.reputation); + j["propertyCloneID"] = ToJson(m.propertyCloneID); + return j; +} +json ToJson(const ClientPackets::ChatModerationString& m) { + json j = json::object(); + j["requestAccepted"] = ToJson(m.requestAccepted); + j["sourceID"] = ToJson(m.sourceID); + j["requestID"] = ToJson(m.requestID); + j["chatMode"] = ToJson(m.chatMode); + j["receiver"] = ToJson(m.receiver); + j["rejectedSegments"] = ToJson(m.rejectedSegments); + return j; +} +json ToJson(const ClientPackets::MakeGMResponse& m) { + json j = json::object(); + j["success"] = ToJson(m.success); + j["highestLevel"] = ToJson(m.highestLevel); + j["previousLevel"] = ToJson(m.previousLevel); + j["newLevel"] = ToJson(m.newLevel); + return j; +} +json ToJson(const ClientPackets::HTTPMonitorInfoResponse& m) { + json j = json::object(); + j["port"] = ToJson(m.port); + j["openWeb"] = ToJson(m.openWeb); + j["supportsSum"] = ToJson(m.supportsSum); + j["supportsDetail"] = ToJson(m.supportsDetail); + j["supportsWho"] = ToJson(m.supportsWho); + j["supportsObjects"] = ToJson(m.supportsObjects); + return j; +} +json ToJson(const ClientPackets::DebugOutput& m) { + json j = json::object(); + j["data"] = ToJson(m.data); + return j; +} +json ToJson(const ClientPackets::BlueprintSaveResponse& m) { + json j = json::object(); + j["localId"] = ToJson(m.localId); + j["reasonCode"] = ToJson(m.reasonCode); + j["models"] = ToJson(m.models); + return j; +} +json ToJson(const ClientPackets::BlueprintSaveResponse::Model& m) { + json j = json::object(); + j["blueprintId"] = ToJson(m.blueprintId); + j["data"] = ToJson(m.data); + return j; +} +json ToJson(const ClientPackets::BlueprintLoadItemResponse& m) { + json j = json::object(); + j["success"] = ToJson(m.success); + j["itemId"] = ToJson(m.itemId); + j["destItemId"] = ToJson(m.destItemId); + return j; +} +json ToJson(const ClientPackets::UgcManifestResponse& m) { + json j = json::object(); + j["blueprintId"] = ToJson(m.blueprintId); + j["resourceType"] = ToJson(m.resourceType); + j["valid"] = ToJson(m.valid); + j["fileSize"] = ToJson(m.fileSize); + j["md5"] = ToJson(m.md5); + return j; +} +json ToJson(const ClientPackets::MinimumChatModeResponse& m) { + json j = json::object(); + j["chatMode"] = ToJson(m.chatMode); + j["chatChannel"] = ToJson(m.chatChannel); + return j; +} +json ToJson(const ClientPackets::MinimumChatModeResponsePrivate& m) { + json j = json::object(); + j["chatMode"] = ToJson(m.chatMode); + j["chatChannel"] = ToJson(m.chatChannel); + j["recipientName"] = ToJson(m.recipientName); + j["recipientGMLevel"] = ToJson(m.recipientGMLevel); + return j; +} +json ToJson(const ClientPackets::SendCannedText& m) { + json j = json::object(); + j["responseType"] = ToJson(m.responseType); + return j; +} +json ToJson(const ClientPackets::GetFriendsListResponse& m) { + json j = json::object(); + j["responseCode"] = ToJson(m.responseCode); + j["packetLength"] = ToJson(m.packetLength); + j["friends"] = ToJson(m.friends); + return j; +} +json ToJson(const ClientPackets::AddFriendRequest& m) { + json j = json::object(); + j["requestorName"] = ToJson(m.requestorName); + j["isBestFriendRequest"] = ToJson(m.isBestFriendRequest); + return j; +} +json ToJson(const ClientPackets::AddFriendResponse& m) { + json j = json::object(); + j["responseCode"] = ToJson(m.responseCode); + j["isOnlineOrBestFriend"] = ToJson(m.isOnlineOrBestFriend); + j["friendName"] = ToJson(m.friendName); + j["friendID"] = ToJson(m.friendID); + j["zoneID"] = ToJson(m.zoneID); + j["isBestFriend"] = ToJson(m.isBestFriend); + j["isFreeTrial"] = ToJson(m.isFreeTrial); + return j; +} +json ToJson(const ClientPackets::RemoveFriendResponse& m) { + json j = json::object(); + j["isSuccessful"] = ToJson(m.isSuccessful); + j["friendName"] = ToJson(m.friendName); + return j; +} +json ToJson(const ClientPackets::UpdateFriendNotify& m) { + json j = json::object(); + j["notifyType"] = ToJson(m.notifyType); + j["friendName"] = ToJson(m.friendName); + j["zoneID"] = ToJson(m.zoneID); + j["isBestFriend"] = ToJson(m.isBestFriend); + j["isFreeTrial"] = ToJson(m.isFreeTrial); + return j; +} +json ToJson(const ClientPackets::WhoResponse& m) { + json j = json::object(); + j["isOnline"] = ToJson(m.isOnline); + j["zoneID"] = ToJson(m.zoneID); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ClientPackets::ShowAllResponse& m) { + json j = json::object(); + j["playerCount"] = ToJson(m.playerCount); + j["simCount"] = ToJson(m.simCount); + j["displayIndividualPlayers"] = ToJson(m.displayIndividualPlayers); + j["displayZoneData"] = ToJson(m.displayZoneData); + j["players"] = ToJson(m.players); + return j; +} +json ToJson(const ClientPackets::ShowAllResponse::Player& m) { + json j = json::object(); + j["name"] = ToJson(m.name); + j["zoneID"] = ToJson(m.zoneID); + return j; +} +json ToJson(const ClientPackets::GetIgnoreListResponse& m) { + json j = json::object(); + j["isFreeTrial"] = ToJson(m.isFreeTrial); + j["padding"] = ToJson(m.padding); + j["ignored"] = ToJson(m.ignored); + return j; +} +json ToJson(const ClientPackets::GetIgnoreListResponse::Ignored& m) { + json j = json::object(); + j["playerID"] = ToJson(m.playerID); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ClientPackets::AddIgnoreResponse& m) { + json j = json::object(); + j["responseCode"] = ToJson(m.responseCode); + j["playerName"] = ToJson(m.playerName); + j["playerID"] = ToJson(m.playerID); + return j; +} +json ToJson(const ClientPackets::RemoveIgnoreResponse& m) { + json j = json::object(); + j["responseCode"] = ToJson(m.responseCode); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ClientPackets::TeamInvite& m) { + json j = json::object(); + j["senderName"] = ToJson(m.senderName); + j["senderID"] = ToJson(m.senderID); + return j; +} +json ToJson(const ClientPackets::TeamInviteInitialResponse& m) { + json j = json::object(); + j["inviteFailedToSend"] = ToJson(m.inviteFailedToSend); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ClientPackets::GuildCreateResponse& m) { + json j = json::object(); + j["result"] = ToJson(m.result); + j["guildID"] = ToJson(m.guildID); + j["guildName"] = ToJson(m.guildName); + return j; +} +json ToJson(const ClientPackets::GuildInvite& m) { + json j = json::object(); + j["inviterName"] = ToJson(m.inviterName); + j["guildName"] = ToJson(m.guildName); + return j; +} +json ToJson(const ClientPackets::GuildInviteInitialResponse& m) { + json j = json::object(); + j["response"] = ToJson(m.response); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ClientPackets::GuildInviteFinalResponse& m) { + json j = json::object(); + j["response"] = ToJson(m.response); + j["playerName"] = ToJson(m.playerName); + return j; +} +json ToJson(const ClientPackets::GuildInviteConfirm& m) { + json j = json::object(); + j["failed"] = ToJson(m.failed); + j["guildName"] = ToJson(m.guildName); + return j; +} +json ToJson(const ClientPackets::GuildAddPlayer& m) { + json j = json::object(); + j["playerName"] = ToJson(m.playerName); + j["playerID"] = ToJson(m.playerID); + j["rank"] = ToJson(m.rank); + j["zoneID"] = ToJson(m.zoneID); + j["online"] = ToJson(m.online); + return j; +} +json ToJson(const ClientPackets::GuildRemovePlayer& m) { + json j = json::object(); + j["reason"] = ToJson(m.reason); + j["playerName"] = ToJson(m.playerName); + j["playerID"] = ToJson(m.playerID); + j["newLeaderID"] = ToJson(m.newLeaderID); + return j; +} +json ToJson(const ClientPackets::GuildLoginLogout& m) { + json j = json::object(); + j["playerName"] = ToJson(m.playerName); + j["playerID"] = ToJson(m.playerID); + j["online"] = ToJson(m.online); + j["zoneID"] = ToJson(m.zoneID); + j["worldUpdateOnly"] = ToJson(m.worldUpdateOnly); + return j; +} +json ToJson(const ClientPackets::GuildData& m) { + json j = json::object(); + j["status"] = ToJson(m.status); + j["guildName"] = ToJson(m.guildName); + j["joinDate"] = ToJson(m.joinDate); + j["foundDate"] = ToJson(m.foundDate); + j["reputation"] = ToJson(m.reputation); + j["unknown1"] = ToJson(m.unknown1); + j["unknown2"] = ToJson(m.unknown2); + j["unknown3"] = ToJson(m.unknown3); + j["members"] = ToJson(m.members); + return j; +} +json ToJson(const ClientPackets::GuildData::Member& m) { + json j = json::object(); + j["rank"] = ToJson(m.rank); + j["online"] = ToJson(m.online); + j["zoneID"] = ToJson(m.zoneID); + j["playerID"] = ToJson(m.playerID); + j["name"] = ToJson(m.name); + return j; +} +json ToJson(const CommonPackets::ClientVersionConfirm& m) { + json j = json::object(); + j["netVersion"] = ToJson(m.netVersion); + j["unknown"] = ToJson(m.unknown); + j["serviceType"] = ToJson(m.serviceType); + j["padding"] = ToJson(m.padding); + j["processID"] = ToJson(m.processID); + j["port"] = ToJson(m.port); + j["unknown2"] = ToJson(m.unknown2); + return j; +} +json ToJson(const CommonPackets::ServerVersionConfirm& m) { + json j = json::object(); + j["netVersion"] = ToJson(m.netVersion); + j["unknown"] = ToJson(m.unknown); + j["serviceType"] = ToJson(m.serviceType); + j["versionMajor"] = ToJson(m.versionMajor); + j["versionMinor"] = ToJson(m.versionMinor); + j["versionPatch"] = ToJson(m.versionPatch); + j["buildString"] = ToJson(m.buildString); + return j; +} +json ToJson(const CommonPackets::DisconnectNotify& m) { + json j = json::object(); + j["disconnectID"] = ToJson(m.disconnectID); + return j; +} +json ToJson(const CommonPackets::GeneralNotify& m) { + json j = json::object(); + j["notifyType"] = ToJson(m.notifyType); + j["showMessageBox"] = ToJson(m.showMessageBox); + return j; +} +json ToJson(const MasterPackets::RequestZoneTransfer& m) { + json j = json::object(); + j["requestID"] = ToJson(m.requestID); + j["mythranShift"] = ToJson(m.mythranShift); + j["zoneID"] = ToJson(m.zoneID); + j["cloneID"] = ToJson(m.cloneID); + j["stamps"] = ToJson(m.stamps); + return j; +} +json ToJson(const MasterPackets::RequestZoneTransferResponse& m) { + json j = json::object(); + j["requestID"] = ToJson(m.requestID); + j["mythranShift"] = ToJson(m.mythranShift); + j["zoneID"] = ToJson(m.zoneID); + j["zoneInstance"] = ToJson(m.zoneInstance); + j["zoneClone"] = ToJson(m.zoneClone); + j["serverPort"] = ToJson(m.serverPort); + j["serverIP"] = ToJson(m.serverIP); + j["stamps"] = ToJson(m.stamps); + return j; +} +json ToJson(const MasterPackets::ServerInfo& m) { + json j = json::object(); + j["port"] = ToJson(m.port); + j["zoneID"] = ToJson(m.zoneID); + j["instanceID"] = ToJson(m.instanceID); + j["serverType"] = ToJson(m.serverType); + j["ip"] = ToJson(m.ip); + return j; +} +json ToJson(const MasterPackets::RequestSessionKey& m) { + json j = json::object(); + j["username"] = ToJson(m.username); + return j; +} +json ToJson(const MasterPackets::SetSessionKey& m) { + json j = json::object(); + j["username"] = ToJson(m.username); + return j; +} +json ToJson(const MasterPackets::SessionKeyResponse& m) { + json j = json::object(); + j["username"] = ToJson(m.username); + return j; +} +json ToJson(const MasterPackets::NewSessionAlert& m) { + json j = json::object(); + j["username"] = ToJson(m.username); + return j; +} +json ToJson(const MasterPackets::PlayerCountChange& m) { + json j = json::object(); + j["zoneID"] = ToJson(m.zoneID); + j["instanceID"] = ToJson(m.instanceID); + return j; +} +json ToJson(const MasterPackets::PlayerAdded& m) { + json j = ToJson(static_cast(m)); + return j; +} +json ToJson(const MasterPackets::PlayerRemoved& m) { + json j = ToJson(static_cast(m)); + return j; +} +json ToJson(const MasterPackets::CreatePrivateZone& m) { + json j = json::object(); + j["zoneID"] = ToJson(m.zoneID); + j["cloneID"] = ToJson(m.cloneID); + return j; +} +json ToJson(const MasterPackets::RequestPrivateZone& m) { + json j = json::object(); + j["requestID"] = ToJson(m.requestID); + j["mythranShift"] = ToJson(m.mythranShift); + return j; +} +json ToJson(const MasterPackets::WorldReady& m) { + json j = json::object(); + j["zoneID"] = ToJson(m.zoneID); + j["instanceID"] = ToJson(m.instanceID); + return j; +} +json ToJson(const MasterPackets::WorldReadyInfo& m) { + json j = json::object(); + j["zoneID"] = ToJson(m.zoneID); + j["instanceID"] = ToJson(m.instanceID); + j["cloneID"] = ToJson(m.cloneID); + j["ip"] = ToJson(m.ip); + j["port"] = ToJson(m.port); + j["isPrivate"] = ToJson(m.isPrivate); + return j; +} +json ToJson(const MasterPackets::PrepZone& m) { + json j = json::object(); + j["zoneID"] = ToJson(m.zoneID); + j["cloneID"] = ToJson(m.cloneID); + return j; +} +json ToJson(const MasterPackets::Shutdown& m) { + json j = json::object(); + return j; +} +json ToJson(const MasterPackets::ShutdownResponse& m) { + json j = json::object(); + return j; +} +json ToJson(const MasterPackets::WorldShutDown& m) { + json j = json::object(); + j["zoneID"] = ToJson(m.zoneID); + j["instanceID"] = ToJson(m.instanceID); + return j; +} +json ToJson(const MasterPackets::ShutdownUniverse& m) { + json j = json::object(); + return j; +} +json ToJson(const MasterPackets::AffirmTransfer& m) { + json j = json::object(); + j["requestID"] = ToJson(m.requestID); + return j; +} +json ToJson(const MasterPackets::AffirmTransferRequest& m) { + json j = ToJson(static_cast(m)); + return j; +} +json ToJson(const MasterPackets::AffirmTransferResponse& m) { + json j = ToJson(static_cast(m)); + return j; +} +json ToJson(const MasterPackets::RequestServerList& m) { + json j = json::object(); + return j; +} +json ToJson(const MasterPackets::ServerListResponse& m) { + json j = json::object(); + j["authOnline"] = ToJson(m.authOnline); + j["chatOnline"] = ToJson(m.chatOnline); + j["instances"] = ToJson(m.instances); + j["ugcEnabled"] = ToJson(m.ugcEnabled); + j["ugcOnline"] = ToJson(m.ugcOnline); + j["ugcPid"] = ToJson(m.ugcPid); + j["endpoints"] = ToJson(m.endpoints); + return j; +} +json ToJson(const MasterPackets::ServerListResponse::Instance& m) { + json j = json::object(); + j["mapID"] = ToJson(m.mapID); + j["instanceID"] = ToJson(m.instanceID); + j["cloneID"] = ToJson(m.cloneID); + j["players"] = ToJson(m.players); + j["ip"] = ToJson(m.ip); + j["port"] = ToJson(m.port); + j["isPrivate"] = ToJson(m.isPrivate); + j["state"] = ToJson(m.state); + return j; +} +json ToJson(const MasterPackets::ServerListResponse::Endpoint& m) { + json j = json::object(); + j["type"] = ToJson(m.type); + j["zoneID"] = ToJson(m.zoneID); + j["instanceID"] = ToJson(m.instanceID); + j["ip"] = ToJson(m.ip); + j["port"] = ToJson(m.port); + j["host"] = ToJson(m.host); + return j; +} +json ToJson(const MasterPackets::DashboardShutdown& m) { + json j = json::object(); + return j; +} +json ToJson(const MasterPackets::ConfigReload& m) { + json j = json::object(); + return j; +} +json ToJson(const MasterPackets::InstanceShutdown& m) { + json j = json::object(); + j["zoneID"] = ToJson(m.zoneID); + j["instanceID"] = ToJson(m.instanceID); + return j; +} +json ToJson(const WorldPackets::WorldLUBitStream& m) { + json j = json::object(); + return j; +} +json ToJson(const WorldPackets::Validation& m) { + json j = ToJson(static_cast(m)); + j["username"] = ToJson(m.username); + j["fdbChecksum"] = ToJson(m.fdbChecksum); + return j; +} +json ToJson(const WorldPackets::CharacterListRequest& m) { + json j = ToJson(static_cast(m)); + return j; +} +json ToJson(const WorldPackets::CharacterCreateRequest& m) { + json j = ToJson(static_cast(m)); + j["name"] = ToJson(m.name); + j["firstNameIndex"] = ToJson(m.firstNameIndex); + j["middleNameIndex"] = ToJson(m.middleNameIndex); + j["lastNameIndex"] = ToJson(m.lastNameIndex); + j["unknown"] = ToJson(m.unknown); + j["shirtColor"] = ToJson(m.shirtColor); + j["shirtStyle"] = ToJson(m.shirtStyle); + j["pantsColor"] = ToJson(m.pantsColor); + j["hairStyle"] = ToJson(m.hairStyle); + j["hairColor"] = ToJson(m.hairColor); + j["leftHand"] = ToJson(m.leftHand); + j["rightHand"] = ToJson(m.rightHand); + j["eyebrows"] = ToJson(m.eyebrows); + j["eyes"] = ToJson(m.eyes); + j["mouth"] = ToJson(m.mouth); + return j; +} +json ToJson(const WorldPackets::CharacterLoginRequest& m) { + json j = ToJson(static_cast(m)); + j["playerID"] = ToJson(m.playerID); + return j; +} +json ToJson(const WorldPackets::GameMessage& m) { + json j = ToJson(static_cast(m)); + j["objectID"] = ToJson(m.objectID); + j["messageID"] = ToJson(m.messageID); + j["data"] = ToJson(m.data); + return j; +} +json ToJson(const WorldPackets::CharacterDeleteRequest& m) { + json j = ToJson(static_cast(m)); + j["objectID"] = ToJson(m.objectID); + return j; +} +json ToJson(const WorldPackets::CharacterRenameRequest& m) { + json j = ToJson(static_cast(m)); + j["objectID"] = ToJson(m.objectID); + j["name"] = ToJson(m.name); + return j; +} +json ToJson(const WorldPackets::LevelLoadComplete& m) { + json j = ToJson(static_cast(m)); + j["mapID"] = ToJson(m.mapID); + j["instanceID"] = ToJson(m.instanceID); + j["cloneID"] = ToJson(m.cloneID); + return j; +} +json ToJson(const WorldPackets::PositionUpdate& m) { + json j = ToJson(static_cast(m)); + j["update"] = ToJson(m.update); + j["hasVelocity"] = ToJson(m.hasVelocity); + j["hasAngularVelocity"] = ToJson(m.hasAngularVelocity); + j["hasLocalSpaceInfo"] = ToJson(m.hasLocalSpaceInfo); + j["hasLinearVelocity"] = ToJson(m.hasLinearVelocity); + j["hasRemoteInputInfo"] = ToJson(m.hasRemoteInputInfo); + return j; +} +json ToJson(const WorldPackets::MailPacket& m) { + json j = ToJson(static_cast(m)); + j["data"] = ToJson(m.data); + return j; +} +json ToJson(const WorldPackets::RoutePacket& m) { + json j = ToJson(static_cast(m)); + j["size"] = ToJson(m.size); + j["routedService"] = ToJson(m.routedService); + j["routedMessageID"] = ToJson(m.routedMessageID); + j["padding"] = ToJson(m.padding); + j["routedData"] = ToJson(m.routedData); + return j; +} +json ToJson(const WorldPackets::StringCheck& m) { + json j = ToJson(static_cast(m)); + j["chatLevel"] = ToJson(m.chatLevel); + j["requestID"] = ToJson(m.requestID); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const WorldPackets::GeneralChatMessage& m) { + json j = ToJson(static_cast(m)); + j["chatChannel"] = ToJson(m.chatChannel); + j["unknown"] = ToJson(m.unknown); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const WorldPackets::TmpGuildCreate& m) { + json j = ToJson(static_cast(m)); + j["guildName"] = ToJson(m.guildName); + return j; +} +json ToJson(const WorldPackets::HandleFunness& m) { + json j = ToJson(static_cast(m)); + j["cheatInfo"] = ToJson(m.cheatInfo); + j["cheatType"] = ToJson(m.cheatType); + return j; +} +json ToJson(const WorldPackets::RequestUgcManifestInfo& m) { + json j = ToJson(static_cast(m)); + j["blueprintId"] = ToJson(m.blueprintId); + j["resourceType"] = ToJson(m.resourceType); + return j; +} +json ToJson(const WorldPackets::UgcDownloadFailed& m) { + json j = ToJson(static_cast(m)); + j["resType"] = ToJson(m.resType); + j["blueprintId"] = ToJson(m.blueprintId); + j["statusCode"] = ToJson(m.statusCode); + j["charId"] = ToJson(m.charId); + return j; +} +json ToJson(const WorldPackets::UIHelpTop5& m) { + json j = ToJson(static_cast(m)); + j["language"] = ToJson(m.language); + return j; +} +json ToJson(const ChatPackets::WorldRoutePacket& m) { + json j = json::object(); + j["targetID"] = ToJson(m.targetID); + j["routedData"] = ToJson(m.routedData); + return j; +} +json ToJson(const CDClientReload& m) { + json j = json::object(); + j["requesterId"] = ToJson(m.requesterId); + j["fdb"] = ToJson(m.fdb); + j["sqlite"] = ToJson(m.sqlite); + return j; +} +json ToJson(const PlayerPositions& m) { + json j = json::object(); + j["zoneId"] = ToJson(m.zoneId); + j["instanceId"] = ToJson(m.instanceId); + j["cloneId"] = ToJson(m.cloneId); + j["players"] = ToJson(m.players); + return j; +} +json ToJson(const PlayerPositions::Player& m) { + json j = json::object(); + j["characterId"] = ToJson(m.characterId); + j["x"] = ToJson(m.x); + return j; +} +json ToJson(const Announcement& m) { + json j = json::object(); + j["title"] = ToJson(m.title); + j["message"] = ToJson(m.message); + j["zones"] = ToJson(m.zones); + return j; +} +json ToJson(const DataChanged& m) { + json j = json::object(); + j["entries"] = ToJson(m.entries); + return j; +} +json ToJson(const DataChanged::Entry& m) { + json j = json::object(); + j["table"] = ToJson(m.table); + j["id"] = ToJson(m.id); + return j; +} +json ToJson(const InstanceMigrationRequest& m) { + json j = json::object(); + j["requestId"] = ToJson(m.requestId); + j["kind"] = ToJson(m.kind); + j["zoneId"] = ToJson(m.zoneId); + j["sourceInstance"] = ToJson(m.sourceInstance); + j["targetInstance"] = ToJson(m.targetInstance); + j["warnSeconds"] = ToJson(m.warnSeconds); + j["shutdownSource"] = ToJson(m.shutdownSource); + j["seamless"] = ToJson(m.seamless); + j["requesterId"] = ToJson(m.requesterId); + j["requestedBy"] = ToJson(m.requestedBy); + return j; +} +json ToJson(const MigratePlayersOrder& m) { + json j = json::object(); + j["migrationId"] = ToJson(m.migrationId); + j["targetZone"] = ToJson(m.targetZone); + j["targetInstance"] = ToJson(m.targetInstance); + j["targetClone"] = ToJson(m.targetClone); + j["targetIp"] = ToJson(m.targetIp); + j["targetPort"] = ToJson(m.targetPort); + j["warnSeconds"] = ToJson(m.warnSeconds); + j["playersPerSecond"] = ToJson(m.playersPerSecond); + j["mythranShift"] = ToJson(m.mythranShift); + j["seamless"] = ToJson(m.seamless); + j["maxWaitSeconds"] = ToJson(m.maxWaitSeconds); + return j; +} +json ToJson(const MigrationStatus& m) { + json j = json::object(); + j["migrationId"] = ToJson(m.migrationId); + j["state"] = ToJson(m.state); + j["kind"] = ToJson(m.kind); + j["zoneId"] = ToJson(m.zoneId); + j["sourceInstance"] = ToJson(m.sourceInstance); + j["targetInstance"] = ToJson(m.targetInstance); + j["moved"] = ToJson(m.moved); + j["remaining"] = ToJson(m.remaining); + j["failed"] = ToJson(m.failed); + j["requesterId"] = ToJson(m.requesterId); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const CarriedPlayerState& m) { + json j = json::object(); + j["targetZone"] = ToJson(m.targetZone); + j["targetInstance"] = ToJson(m.targetInstance); + j["characterId"] = ToJson(m.characterId); + j["petItemId"] = ToJson(m.petItemId); + j["seamless"] = ToJson(m.seamless); + j["hasPosition"] = ToJson(m.hasPosition); + j["x"] = ToJson(m.x); + j["rotW"] = ToJson(m.rotW); + return j; +} +json ToJson(const MigratePrepare& m) { + json j = json::object(); + j["migrationId"] = ToJson(m.migrationId); + j["maxWaitSeconds"] = ToJson(m.maxWaitSeconds); + return j; +} +json ToJson(const LiveUpdateRequest& m) { + json j = json::object(); + j["action"] = ToJson(m.action); + j["warnSeconds"] = ToJson(m.warnSeconds); + j["requesterId"] = ToJson(m.requesterId); + j["requestedBy"] = ToJson(m.requestedBy); + return j; +} +json ToJson(const LiveUpdateStatus& m) { + json j = json::object(); + j["updateId"] = ToJson(m.updateId); + j["phase"] = ToJson(m.phase); + j["startedAt"] = ToJson(m.startedAt); + j["finishedAt"] = ToJson(m.finishedAt); + j["requesterId"] = ToJson(m.requesterId); + j["by"] = ToJson(m.by); + j["message"] = ToJson(m.message); + j["units"] = ToJson(m.units); + return j; +} +json ToJson(const LiveUpdateStatus::Unit& m) { + json j = json::object(); + j["kind"] = ToJson(m.kind); + j["state"] = ToJson(m.state); + j["zoneId"] = ToJson(m.zoneId); + j["instanceId"] = ToJson(m.instanceId); + j["cloneId"] = ToJson(m.cloneId); + j["replacement"] = ToJson(m.replacement); + j["players"] = ToJson(m.players); + j["moved"] = ToJson(m.moved); + j["isPrivate"] = ToJson(m.isPrivate); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const LiveUpdateRetire& m) { + json j = json::object(); + return j; +} +json ToJson(const ChatServerReady& m) { + json j = json::object(); + return j; +} +json ToJson(const ChatHandoff& m) { + json j = json::object(); + j["teams"] = ToJson(m.teams); + return j; +} +json ToJson(const ChatHandoff::Team& m) { + json j = json::object(); + j["teamId"] = ToJson(m.teamId); + j["leaderId"] = ToJson(m.leaderId); + j["members"] = ToJson(m.members); + j["lootFlag"] = ToJson(m.lootFlag); + j["local"] = ToJson(m.local); + j["zoneId"] = ToJson(m.zoneId); + j["instanceId"] = ToJson(m.instanceId); + j["cloneId"] = ToJson(m.cloneId); + return j; +} +json ToJson(const MessageCaptureControl& m) { + json j = json::object(); + j["captureId"] = ToJson(m.captureId); + j["action"] = ToJson(m.action); + j["characterId"] = ToJson(m.characterId); + j["seconds"] = ToJson(m.seconds); + j["toServer"] = ToJson(m.toServer); + j["toClient"] = ToJson(m.toClient); + j["only"] = ToJson(m.only); + j["skip"] = ToJson(m.skip); + j["slot"] = ToJson(m.slot); + j["target"] = ToJson(m.target); + j["accountId"] = ToJson(m.accountId); + j["accountName"] = ToJson(m.accountName); + j["characterIds"] = ToJson(m.characterIds); + return j; +} +json ToJson(const MessageCaptureEntry& m) { + json j = json::object(); + j["sequence"] = ToJson(m.sequence); + j["timeMs"] = ToJson(m.timeMs); + j["direction"] = ToJson(m.direction); + j["messageId"] = ToJson(m.messageId); + j["objectId"] = ToJson(m.objectId); + j["bits"] = ToJson(m.bits); + j["payload"] = ToJson(m.payload); + j["decoded"] = ToJson(m.decoded); + return j; +} +json ToJson(const MessageCaptureData& m) { + json j = json::object(); + j["captureId"] = ToJson(m.captureId); + j["status"] = ToJson(m.status); + j["characterId"] = ToJson(m.characterId); + j["zoneId"] = ToJson(m.zoneId); + j["instanceId"] = ToJson(m.instanceId); + j["reason"] = ToJson(m.reason); + j["dropped"] = ToJson(m.dropped); + j["entries"] = ToJson(m.entries); + j["cloneId"] = ToJson(m.cloneId); + j["source"] = ToJson(m.source); + j["slots"] = ToJson(m.slots); + j["packetCount"] = ToJson(m.packetCount); + j["packetsDropped"] = ToJson(m.packetsDropped); + j["packets"] = ToJson(m.packets); + return j; +} +json ToJson(const PlayerActionRequest& m) { + json j = json::object(); + j["requestId"] = ToJson(m.requestId); + j["action"] = ToJson(m.action); + j["accountId"] = ToJson(m.accountId); + j["characterId"] = ToJson(m.characterId); + j["zoneId"] = ToJson(m.zoneId); + j["disconnectReason"] = ToJson(m.disconnectReason); + j["targetId"] = ToJson(m.targetId); + j["approved"] = ToJson(m.approved); + j["text"] = ToJson(m.text); + j["name"] = ToJson(m.name); + j["instanceId"] = ToJson(m.instanceId); + return j; +} +json ToJson(const PlayerActionResult& m) { + json j = json::object(); + j["requestId"] = ToJson(m.requestId); + j["action"] = ToJson(m.action); + j["affected"] = ToJson(m.affected); + j["timedOut"] = ToJson(m.timedOut); + return j; +} +json ToJson(const ProfileRequest& m) { + json j = json::object(); + j["sessionId"] = ToJson(m.sessionId); + j["serverType"] = ToJson(m.serverType); + j["zoneId"] = ToJson(m.zoneId); + j["instanceId"] = ToJson(m.instanceId); + j["durationMs"] = ToJson(m.durationMs); + j["stop"] = ToJson(m.stop); + return j; +} +json ToJson(const ProfileResult& m) { + json j = json::object(); + j["sessionId"] = ToJson(m.sessionId); + j["serverType"] = ToJson(m.serverType); + j["zoneId"] = ToJson(m.zoneId); + j["instanceId"] = ToJson(m.instanceId); + j["status"] = ToJson(m.status); + j["error"] = ToJson(m.error); + j["profile"] = ToJson(m.profile); + return j; +} +json ToJson(const ServerTraffic& m) { + json j = json::object(); + j["serverType"] = ToJson(m.serverType); + j["zoneId"] = ToJson(m.zoneId); + j["instanceId"] = ToJson(m.instanceId); + j["report"] = ToJson(m.report); + j["frames"] = ToJson(m.frames); + return j; +} +json ToJson(const UgcModelsMade& m) { + json j = json::object(); + j["blueprintIds"] = ToJson(m.blueprintIds); + return j; +} +json ToJson(const WorldFilesReport& m) { + json j = json::object(); + j["zoneId"] = ToJson(m.zoneId); + j["instanceId"] = ToJson(m.instanceId); + j["cloneId"] = ToJson(m.cloneId); + j["files"] = ToJson(m.files); + return j; +} +json ToJson(const WorldReloadRequest& m) { + json j = json::object(); + j["zoneId"] = ToJson(m.zoneId); + j["warnSeconds"] = ToJson(m.warnSeconds); + j["requesterId"] = ToJson(m.requesterId); + j["requestedBy"] = ToJson(m.requestedBy); + return j; +} +json ToJson(const WorldFilesStatus& m) { + json j = json::object(); + j["watching"] = ToJson(m.watching); + j["watchSeconds"] = ToJson(m.watchSeconds); + j["seamless"] = ToJson(m.seamless); + j["zones"] = ToJson(m.zones); + return j; +} +json ToJson(const WorldFilesStatus::File& m) { + json j = json::object(); + j["disk"] = ToJson(m.disk); + j["hashed"] = ToJson(m.hashed); + j["missing"] = ToJson(m.missing); + j["changed"] = ToJson(m.changed); + return j; +} +json ToJson(const WorldFilesStatus::Instance& m) { + json j = json::object(); + j["instanceId"] = ToJson(m.instanceId); + j["cloneId"] = ToJson(m.cloneId); + j["players"] = ToJson(m.players); + j["stale"] = ToJson(m.stale); + j["reloading"] = ToJson(m.reloading); + return j; +} +json ToJson(const WorldFilesStatus::Zone& m) { + json j = json::object(); + j["zoneId"] = ToJson(m.zoneId); + j["files"] = ToJson(m.files); + j["instances"] = ToJson(m.instances); + j["message"] = ToJson(m.message); + return j; +} +json ToJson(const RemoteInputInfo& m) { + json j = json::object(); + j["m_RemoteInputX"] = ToJson(m.m_RemoteInputX); + j["m_RemoteInputY"] = ToJson(m.m_RemoteInputY); + j["m_IsPowersliding"] = ToJson(m.m_IsPowersliding); + j["m_IsModified"] = ToJson(m.m_IsModified); + return j; +} +json ToJson(const LocalSpaceInfo& m) { + json j = json::object(); + j["objectId"] = ToJson(m.objectId); + j["position"] = ToJson(m.position); + j["linearVelocity"] = ToJson(m.linearVelocity); + return j; +} +json ToJson(const PositionUpdate& m) { + json j = json::object(); + j["position"] = ToJson(m.position); + j["rotation"] = ToJson(m.rotation); + j["onGround"] = ToJson(m.onGround); + j["onRail"] = ToJson(m.onRail); + j["velocity"] = ToJson(m.velocity); + j["angularVelocity"] = ToJson(m.angularVelocity); + j["localSpaceInfo"] = ToJson(m.localSpaceInfo); + j["remoteInputInfo"] = ToJson(m.remoteInputInfo); + return j; +} +json ToJson(const Profiler::Second& m) { + json j = json::object(); + j["time"] = ToJson(m.time); + j["ticks"] = ToJson(m.ticks); + j["totalUs"] = ToJson(m.totalUs); + j["maxUs"] = ToJson(m.maxUs); + j["frames"] = ToJson(m.frames); + j["phaseUs"] = ToJson(m.phaseUs); + return j; +} +json ToJson(const Profiler::MessageTime& m) { + json j = json::object(); + j["key"] = ToJson(m.key); + j["count"] = ToJson(m.count); + j["totalUs"] = ToJson(m.totalUs); + j["maxUs"] = ToJson(m.maxUs); + return j; +} +json ToJson(const Profiler::Node& m) { + json j = json::object(); + j["name"] = ToJson(m.name); + j["arg"] = ToJson(m.arg); + j["depth"] = ToJson(m.depth); + j["count"] = ToJson(m.count); + j["totalUs"] = ToJson(m.totalUs); + j["startUs"] = ToJson(m.startUs); + return j; +} +json ToJson(const Profiler::Frame& m) { + json j = json::object(); + j["timeMs"] = ToJson(m.timeMs); + j["durationUs"] = ToJson(m.durationUs); + j["implicit"] = ToJson(m.implicit); + j["phaseUs"] = ToJson(m.phaseUs); + j["scopes"] = ToJson(m.scopes); + return j; +} +json ToJson(const Profiler::Report& m) { + json j = json::object(); + j["present"] = ToJson(m.present); + j["slowThresholdMs"] = ToJson(m.slowThresholdMs); + j["seconds"] = ToJson(m.seconds); + j["messages"] = ToJson(m.messages); + j["worst"] = ToJson(m.worst); + j["slow"] = ToJson(m.slow); + return j; +} +json ToJson(const Profiler::Profile& m) { + json j = json::object(); + j["id"] = ToJson(m.id); + j["durationMs"] = ToJson(m.durationMs); + j["frames"] = ToJson(m.frames); + j["totalUs"] = ToJson(m.totalUs); + j["truncated"] = ToJson(m.truncated); + j["nodes"] = ToJson(m.nodes); + return j; +} +json ToJson(const TrafficStats::MessageKey& m) { + json j = json::object(); + j["outbound"] = ToJson(m.outbound); + j["service"] = ToJson(m.service); + j["packet"] = ToJson(m.packet); + j["gameMessage"] = ToJson(m.gameMessage); + return j; +} +json ToJson(const TrafficStats::PeerCounts& m) { + json j = json::object(); + j["packetsIn"] = ToJson(m.packetsIn); + j["packetsOut"] = ToJson(m.packetsOut); + j["bytesIn"] = ToJson(m.bytesIn); + j["bytesOut"] = ToJson(m.bytesOut); + return j; +} +json ToJson(const TrafficStats::MessageCount& m) { + json j = json::object(); + j["key"] = ToJson(m.key); + j["count"] = ToJson(m.count); + j["bytes"] = ToJson(m.bytes); + return j; +} +json ToJson(const TrafficStats::Second& m) { + json j = json::object(); + j["time"] = ToJson(m.time); + j["packetsIn"] = ToJson(m.packetsIn); + j["packetsOut"] = ToJson(m.packetsOut); + j["bytesIn"] = ToJson(m.bytesIn); + j["bytesOut"] = ToJson(m.bytesOut); + j["httpRequests"] = ToJson(m.httpRequests); + j["httpStatus"] = ToJson(m.httpStatus); + j["httpBytesOut"] = ToJson(m.httpBytesOut); + j["httpLatency"] = ToJson(m.httpLatency); + j["peers"] = ToJson(m.peers); + j["httpFromServers"] = ToJson(m.httpFromServers); + j["httpFromServersBytesOut"] = ToJson(m.httpFromServersBytesOut); + j["httpOutRequests"] = ToJson(m.httpOutRequests); + j["httpOutBytesIn"] = ToJson(m.httpOutBytesIn); + return j; +} +json ToJson(const TrafficStats::RouteStats& m) { + json j = json::object(); + j["route"] = ToJson(m.route); + j["count"] = ToJson(m.count); + j["status"] = ToJson(m.status); + j["bytesOut"] = ToJson(m.bytesOut); + j["latency"] = ToJson(m.latency); + return j; +} +json ToJson(const TrafficStats::Link& m) { + json j = json::object(); + j["connections"] = ToJson(m.connections); + j["datagramsSent"] = ToJson(m.datagramsSent); + j["datagramsReceived"] = ToJson(m.datagramsReceived); + j["bytesSent"] = ToJson(m.bytesSent); + j["bytesReceived"] = ToJson(m.bytesReceived); + j["resends"] = ToJson(m.resends); + j["resendQueue"] = ToJson(m.resendQueue); + j["averagePingMs"] = ToJson(m.averagePingMs); + return j; +} +json ToJson(const TrafficStats::Connection& m) { + json j = json::object(); + j["address"] = ToJson(m.address); + j["port"] = ToJson(m.port); + j["peer"] = ToJson(m.peer); + j["http"] = ToJson(m.http); + j["packetsIn"] = ToJson(m.packetsIn); + j["bytesIn"] = ToJson(m.bytesIn); + j["resends"] = ToJson(m.resends); + j["pingMs"] = ToJson(m.pingMs); + j["accountId"] = ToJson(m.accountId); + j["characterId"] = ToJson(m.characterId); + return j; +} +json ToJson(const TrafficStats::Report& m) { + json j = json::object(); + j["seconds"] = ToJson(m.seconds); + j["messages"] = ToJson(m.messages); + j["routes"] = ToJson(m.routes); + j["link"] = ToJson(m.link); + j["gauges"] = ToJson(m.gauges); + j["peerSplit"] = ToJson(m.peerSplit); + j["connections"] = ToJson(m.connections); + j["otherConnections"] = ToJson(m.otherConnections); + j["otherConnectionCount"] = ToJson(m.otherConnectionCount); + j["hasConnections"] = ToJson(m.hasConnections); + return j; +} +json ToJson(const ZoneFileLog::Entry& m) { + json j = json::object(); + j["kind"] = ToJson(m.kind); + j["packed"] = ToJson(m.packed); + j["size"] = ToJson(m.size); + j["hash"] = ToJson(m.hash); + j["path"] = ToJson(m.path); + return j; +} +json ToJson(const FriendData& m) { + json j = json::object(); + j["isOnline"] = ToJson(m.isOnline); + j["isBestFriend"] = ToJson(m.isBestFriend); + j["isFTP"] = ToJson(m.isFTP); + j["zoneID"] = ToJson(m.zoneID); + j["friendID"] = ToJson(m.friendID); + j["friendName"] = ToJson(m.friendName); + return j; +} + +// Wire packets: (service, ID, struct, reader). An ID can have more than one struct (one per direction): +// the first that reads the whole packet is shown. +const std::vector& Entries() { + static const std::vector entries{ + { ServiceType::AUTH, static_cast(MessageType::Auth::LOGIN_REQUEST), "LoginRequest", true, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::LOGIN_SESSION_NOTIFY), "LoginSessionNotify", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::UNEXPECTED_DISCONNECT), "UnexpectedDisconnect", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GMLEVEL_UPDATE), "GMLevelUpdate", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GM_MUTE), "GMMute", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GM_ANNOUNCE), "Announcement", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::CREATE_TEAM), "CreateTeam", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::TEAM_GET_STATUS), "TeamUpdate", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::ACHIEVEMENT_NOTIFY), "AchievementNotify", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::MAIL), "MailNotify", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::SHOW_ALL), "ShowAllRequest", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::WHO), "FindPlayerRequest", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GET_FRIENDS_LIST), "GetFriendsList", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::ADD_FRIEND_REQUEST), "AddFriendRequest", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::ADD_FRIEND_RESPONSE), "AddFriendResponse", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::REMOVE_FRIEND), "RemoveFriend", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GET_IGNORE_LIST), "GetIgnoreList", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::ADD_IGNORE), "AddIgnore", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::REMOVE_IGNORE), "RemoveIgnore", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GENERAL_CHAT_MESSAGE), "GeneralChatMessage", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::PRIVATE_CHAT_MESSAGE), "PrivateChatMessage", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::TEAM_INVITE), "TeamInvite", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::TEAM_INVITE_RESPONSE), "TeamInviteResponse", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::TEAM_LEAVE), "TeamLeave", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::TEAM_KICK), "TeamKick", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::TEAM_SET_LEADER), "TeamSetLeader", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::TEAM_SET_LOOT), "TeamSetLoot", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::TEAM_GET_STATUS), "TeamGetStatus", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE), "RequestMinimumChatMode", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE_PRIVATE), "RequestMinimumChatModePrivate", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_INVITE), "GuildInvite", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_INVITE_RESPONSE), "GuildInviteResponse", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_LEAVE), "GuildLeave", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_GET_ALL), "GuildGetAll", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_CREATE), "GuildCreate", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_KICK), "GuildKick", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_SET_RANK), "GuildSetRank", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_DISBAND), "GuildDisband", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GUILD_GET_STATUS), "GuildStatus", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::GENERAL_CHAT_MESSAGE), "GeneralChatMessage", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::PRIVATE_CHAT_MESSAGE), "PrivateChatMessage", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::LOGIN_RESPONSE), "LoginResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::LOAD_STATIC_ZONE), "LoadStaticZone", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::CHARACTER_LIST_RESPONSE), "CharacterListResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::CHARACTER_CREATE_RESPONSE), "CharacterCreateResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::CHARACTER_RENAME_RESPONSE), "CharacterRenameResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::DELETE_CHARACTER_RESPONSE), "DeleteCharacterResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::TRANSFER_TO_WORLD), "TransferToWorld", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::SERVER_STATES), "ServerStates", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::CREATE_CHARACTER), "CreateCharacter", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::CHAT_MODERATION_STRING), "ChatModerationString", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::MAKE_GM_RESPONSE), "MakeGMResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::HTTP_MONITOR_INFO_RESPONSE), "HTTPMonitorInfoResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::DEBUG_OUTPUT), "DebugOutput", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::BLUEPRINT_SAVE_RESPONSE), "BlueprintSaveResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::BLUEPRINT_LOAD_RESPONSE_ITEMID), "BlueprintLoadItemResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::UGC_MANIFEST_RESPONSE), "UgcManifestResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::MINIMUM_CHAT_MODE_RESPONSE), "MinimumChatModeResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::MINIMUM_CHAT_MODE_RESPONSE_PRIVATE), "MinimumChatModeResponsePrivate", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::SEND_CANNED_TEXT), "SendCannedText", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GET_FRIENDS_LIST_RESPONSE), "GetFriendsListResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::ADD_FRIEND_REQUEST), "AddFriendRequest", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::ADD_FRIEND_RESPONSE), "AddFriendResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::REMOVE_FRIEND_RESPONSE), "RemoveFriendResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::UPDATE_FRIEND_NOTIFY), "UpdateFriendNotify", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::WHO_RESPONSE), "WhoResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::SHOW_ALL_RESPONSE), "ShowAllResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GET_IGNORE_LIST_RESPONSE), "GetIgnoreListResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::ADD_IGNORE_RESPONSE), "AddIgnoreResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::REMOVE_IGNORE_RESPONSE), "RemoveIgnoreResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::TEAM_INVITE), "TeamInvite", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::TEAM_INVITE_INITIAL_RESPONSE), "TeamInviteInitialResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_CREATE_RESPONSE), "GuildCreateResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_INVITE), "GuildInvite", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_INVITE_INITIAL_RESPONSE), "GuildInviteInitialResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_INVITE_FINAL_RESPONSE), "GuildInviteFinalResponse", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_INVITE_CONFIRM), "GuildInviteConfirm", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_ADD_PLAYER), "GuildAddPlayer", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_REMOVE_PLAYER), "GuildRemovePlayer", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_LOGIN_LOGOUT), "GuildLoginLogout", false, &ReadWith }, + { ServiceType::CLIENT, static_cast(MessageType::Client::GUILD_DATA), "GuildData", false, &ReadWith }, + { ServiceType::COMMON, static_cast(MessageType::Server::VERSION_CONFIRM), "ClientVersionConfirm", true, &ReadWith }, + { ServiceType::COMMON, static_cast(MessageType::Server::VERSION_CONFIRM), "ServerVersionConfirm", false, &ReadWith }, + { ServiceType::COMMON, static_cast(MessageType::Server::DISCONNECT_NOTIFY), "DisconnectNotify", false, &ReadWith }, + { ServiceType::COMMON, static_cast(MessageType::Server::GENERAL_NOTIFY), "GeneralNotify", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::REQUEST_ZONE_TRANSFER), "RequestZoneTransfer", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE), "RequestZoneTransferResponse", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SERVER_INFO), "ServerInfo", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::REQUEST_SESSION_KEY), "RequestSessionKey", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SET_SESSION_KEY), "SetSessionKey", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SESSION_KEY_RESPONSE), "SessionKeyResponse", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::NEW_SESSION_ALERT), "NewSessionAlert", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::PLAYER_ADDED), "PlayerAdded", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::PLAYER_REMOVED), "PlayerRemoved", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::CREATE_PRIVATE_ZONE), "CreatePrivateZone", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::REQUEST_PRIVATE_ZONE), "RequestPrivateZone", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::WORLD_READY), "WorldReady", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::WORLD_READY), "WorldReadyInfo", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::PREP_ZONE), "PrepZone", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SHUTDOWN), "Shutdown", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SHUTDOWN_RESPONSE), "ShutdownResponse", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SHUTDOWN_RESPONSE), "WorldShutDown", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SHUTDOWN_UNIVERSE), "ShutdownUniverse", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::AFFIRM_TRANSFER_REQUEST), "AffirmTransferRequest", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::AFFIRM_TRANSFER_RESPONSE), "AffirmTransferResponse", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::REQUEST_SERVER_LIST), "RequestServerList", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SERVER_LIST_RESPONSE), "ServerListResponse", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::DASHBOARD_SHUTDOWN), "DashboardShutdown", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::CONFIG_RELOAD), "ConfigReload", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::INSTANCE_SHUTDOWN), "InstanceShutdown", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::VALIDATION), "Validation", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::CHARACTER_LIST_REQUEST), "CharacterListRequest", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::CHARACTER_CREATE_REQUEST), "CharacterCreateRequest", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::LOGIN_REQUEST), "CharacterLoginRequest", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::GAME_MSG), "GameMessage", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::CHARACTER_DELETE_REQUEST), "CharacterDeleteRequest", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::CHARACTER_RENAME_REQUEST), "CharacterRenameRequest", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::LEVEL_LOAD_COMPLETE), "LevelLoadComplete", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::POSITION_UPDATE), "PositionUpdate", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::MAIL), "MailPacket", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::ROUTE_PACKET), "RoutePacket", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::STRING_CHECK), "StringCheck", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::GENERAL_CHAT_MESSAGE), "GeneralChatMessage", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::TMP_GUILD_CREATE), "TmpGuildCreate", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::HANDLE_FUNNESS), "HandleFunness", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::REQUEST_UGC_MANIFEST_INFO), "RequestUgcManifestInfo", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::UGC_DOWNLOAD_FAILED), "UgcDownloadFailed", false, &ReadWith }, + { ServiceType::WORLD, static_cast(MessageType::World::UI_HELP_TOP_5), "UIHelpTop5", false, &ReadWith }, + { ServiceType::CHAT, static_cast(MessageType::Chat::WORLD_ROUTE_PACKET), "WorldRoutePacket", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::CDCLIENT_RELOAD), "CDClientReload", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::PLAYER_POSITIONS), "PlayerPositions", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::ANNOUNCE), "Announcement", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::DATA_CHANGED), "DataChanged", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::INSTANCE_MIGRATE), "InstanceMigrationRequest", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::MIGRATE_PLAYERS), "MigratePlayersOrder", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::MIGRATE_STATUS), "MigrationStatus", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::MIGRATE_PLAYER_STATE), "CarriedPlayerState", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::MIGRATE_PREPARE), "MigratePrepare", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::LIVE_UPDATE_REQUEST), "LiveUpdateRequest", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::LIVE_UPDATE_STATUS), "LiveUpdateStatus", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::LIVE_UPDATE_RETIRE), "LiveUpdateRetire", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::CHAT_SERVER_READY), "ChatServerReady", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::CHAT_HANDOFF), "ChatHandoff", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::MESSAGE_CAPTURE_CONTROL), "MessageCaptureControl", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::MESSAGE_CAPTURE_DATA), "MessageCaptureData", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::PLAYER_ACTION), "PlayerActionRequest", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::PLAYER_ACTION_RESULT), "PlayerActionResult", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::PROFILE_REQUEST), "ProfileRequest", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::PROFILE_RESULT), "ProfileResult", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::SERVER_TRAFFIC), "ServerTraffic", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::UGC_MODELS_MADE), "UgcModelsMade", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::WORLD_FILES), "WorldFilesReport", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::WORLD_RELOAD), "WorldReloadRequest", false, &ReadWith }, + { ServiceType::MASTER, static_cast(MessageType::Master::WORLD_FILES_STATUS), "WorldFilesStatus", false, &ReadWith }, + }; + return entries; +} diff --git a/dNet/PacketJson.h b/dNet/PacketJson.h new file mode 100644 index 000000000..f1d59c118 --- /dev/null +++ b/dNet/PacketJson.h @@ -0,0 +1,118 @@ +#ifndef __PACKETJSON__H__ +#define __PACKETJSON__H__ + +#include +#include +#include +#include + +#include "BitStream.h" +#include "BitStreamUtils.h" +#include "dCommonVars.h" +#include "Stamps.h" +#include "GeneralUtils.h" +#include "json.hpp" +#include "LDFFormat.h" +#include "magic_enum.hpp" +#include "NiPoint3.h" +#include "NiQuaternion.h" + +/** + * How a packet struct's members are shown in the capture viewer: one ToJson overload per member type. The member + * lists are generated from the structs (tools/gen_game_message_fields.py); a member type without an overload here (or + * in the file that includes the generated list) fails the build. Bring them in with `using PacketJson::ToJson;`. + */ +namespace PacketJson { + using json = nlohmann::json; + + inline std::string Hex(std::string_view bytes) { + static constexpr char digits[] = "0123456789abcdef"; + std::string out; + out.reserve(bytes.size() * 2); + for (const unsigned char c : bytes) { + out += digits[c >> 4]; + out += digits[c & 15]; + } + return out; + } + + inline json ToJson(bool value) { return value; } + inline json ToJson(float value) { return value; } + inline json ToJson(double value) { return value; } + // 64-bit values (object IDs) as strings: they don't fit in a JavaScript number + inline json ToJson(int64_t value) { return std::to_string(value); } + inline json ToJson(uint64_t value) { return std::to_string(value); } + inline json ToJson(int32_t value) { return value; } + inline json ToJson(uint32_t value) { return value; } + inline json ToJson(int16_t value) { return value; } + inline json ToJson(uint16_t value) { return value; } + inline json ToJson(uint8_t value) { return value; } + inline json ToJson(int8_t value) { return value; } + inline json ToJson(char value) { return static_cast(value); } + inline json ToJson(const NiPoint3& point) { return json::array({ point.x, point.y, point.z }); } + inline json ToJson(const NiQuaternion& rotation) { return json::array({ rotation.x, rotation.y, rotation.z, rotation.w }); } + inline json ToJson(const std::u16string& text) { return GeneralUtils::UTF16ToWTF8(text); } + + // Text, or its bytes in hex when it isn't text (skill behavior streams are std::string too) + inline json ToJson(const std::string& text) { + for (const unsigned char c : text) { + if (c < 0x20 && c != '\n' && c != '\r' && c != '\t') return json{ {"hex", Hex(text)} }; + } + try { + (void)json(text).dump(); // throws when it isn't UTF-8 + } catch (const json::exception&) { + return json{ {"hex", Hex(text)} }; + } + return text; + } + + // Fixed-size strings: the text up to the first null + // (Templates, so nothing converts to them: they have implicit constructors from a size) + template requires std::is_same_v + json ToJson(const T& text) { return ToJson(text.string.substr(0, text.string.find('\0'))); } + template requires std::is_same_v + json ToJson(const T& text) { return ToJson(text.string.substr(0, text.string.find(u'\0'))); } + + inline json ToJson(const RakNet::BitStream& stream) { + return json{ {"hex", Hex(std::string_view(reinterpret_cast(stream.GetData()), stream.GetNumberOfBytesUsed()))} }; + } + + inline json ToJson(const LwoNameValue& config) { + json out = json::array(); + for (const auto& [key, value] : config.values) { + if (value) out.push_back(value->GetString()); + } + return out; + } + + inline json ToJson(const json& value) { return value; } + + inline json ToJson(const LWOZONEID& zone) { + return json{ {"mapID", zone.GetMapID()}, {"instanceID", zone.GetInstanceID()}, {"cloneID", zone.GetCloneID()} }; + } + + inline json ToJson(const Stamps& stamps) { + json out = json::array(); + for (const auto& stamp : stamps.list) { + const auto name = magic_enum::enum_name(stamp.type); + out.push_back({ {"type", name.empty() ? std::to_string(static_cast(stamp.type)) : std::string(name)}, {"value", stamp.value}, {"timestamp", std::to_string(stamp.timestamp)} }); + } + return out; + } + + template + json ToJson(const std::chrono::time_point& time) { + return std::to_string(std::chrono::duration_cast(time.time_since_epoch()).count()); + } + + // Enums by name, with the number when it has none + template requires std::is_enum_v + json ToJson(E value) { + const auto name = magic_enum::enum_name(value); + const auto number = static_cast>(value); + if (name.empty()) return number; + return std::string(name) + " (" + std::to_string(number) + ")"; + } +} + +#endif //!__PACKETJSON__H__ diff --git a/tools/gen_game_message_fields.py b/tools/gen_game_message_fields.py new file mode 100644 index 000000000..cf99bd12a --- /dev/null +++ b/tools/gen_game_message_fields.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +""" +Writes the member lists the capture viewer shows packets with, from the packet structs themselves: + + dGame/dGameMessages/GameMessageFields.inc from dGame/dGameMessages/*Messages.h (game messages, NetGameMsg) + dNet/PacketFields.inc from dNet/*Packets.h (LU packets, LUBitStream) + +For every struct: its members by name, in declaration order (what is shown after the struct's own Deserialize read +the packet); for every wire struct: its message ID, direction and whether it can be read. The lists come from the +struct definitions, so a member added to a struct shows up in the viewer, and the build fails if a member's type has +no ToJson overload (GameMessageDecoder.cpp, PacketDecoder.cpp). Members that hold secrets are never listed. + + gen_game_message_fields.py rewrite the files + gen_game_message_fields.py --check exit 1 if a file is out of date (the PacketFieldsUpToDate test) +""" +import pathlib +import re +import sys + +ROOT = pathlib.Path(__file__).resolve().parent.parent + +NAMESPACE = re.compile(r"^\s*namespace\s+(\w+)\s*\{") +STRUCT = re.compile(r"^(\s*)struct\s+(\w+)\s*(?:final\s*)?(?::\s*public\s+(\w+))?\s*\{") +MEMBER = re.compile(r"^\s*((?:const\s+)?[\w:]+(?:<.*>)?\s*\*?)\s+(\w+)\s*(?:\{.*\}|=.*)?;\s*$") +SERVICES = {"Auth": "AUTH", "Chat": "CHAT", "Client": "CLIENT", "Master": "MASTER", "Server": "COMMON", "World": "WORLD"} +SKIP_TYPES = ("static", "using", "friend", "return", "virtual", "typedef", "enum", "constexpr", "inline") +# Never shown, even blanked +SECRETS = {"password", "sessionKey", "userKey", "cdnKey", "passwordHash"} +# Set by the server, not read from the wire +NOT_WIRE = {"sysAddr"} +# Have their own ToJson (private members) +SKIP_STRUCTS = {"LWOZONEID"} + + +def strip_comment(line): + line = re.sub(r"/\*.*?\*/", "", line) + return line.split("//", 1)[0] + + +def parse(path): + structs = [] + stack = [] # (kind, value, depth): kind "ns" or "struct" + depth = 0 + in_block_comment = False + for raw in path.read_text().splitlines(): + line = raw + if in_block_comment: + if "*/" not in line: + continue + line = line.split("*/", 1)[1] + in_block_comment = False + line = strip_comment(line) + if "/*" in line: + line = line.split("/*", 1)[0] + in_block_comment = True + namespaces = [v for k, v, _ in stack if k == "ns"] + parents = [v for k, v, _ in stack if k == "struct"] + if (n := NAMESPACE.match(line)) and not parents: + depth += line.count("{") - line.count("}") + stack.append(("ns", n.group(1), depth)) + continue + m = STRUCT.match(line) + if m and not line.rstrip().endswith(";"): + name = m.group(2) + qualified = "::".join(namespaces + [p["short"] for p in parents] + [name]) + s = {"name": qualified, "short": "::".join([p["short"] for p in parents] + [name]), "base": m.group(3), + "members": [], "types": [], "id": None, "enum": None, "handle": False, "deserialize": False, "file": path.name} + structs.append(s) + depth += line.count("{") - line.count("}") + stack.append(("struct", s, depth)) + continue + current = parents[-1] if parents else None + if current is not None and stack[-1][0] == "struct" and depth == stack[-1][2]: + own = current["short"].split("::")[-1] + if (mid := re.search(rf"\b{own}\s*\(\s*\)\s*:.*MessageType::(\w+)::(\w+)", line)): + current["enum"], current["id"] = mid.group(1), mid.group(2) + if re.search(r"\bvoid\s+Handle\s*\(", line): + current["handle"] = True + if re.search(r"\bbool\s+Deserialize\s*\(", line): + current["deserialize"] = True + mm = MEMBER.match(line) + if mm and "(" not in line.split("{")[0].split("=")[0] and ")" not in line: + typ, name = mm.group(1).strip(), mm.group(2) + if typ.split()[0] not in SKIP_TYPES and not typ.endswith("*"): + current["members"].append(name) + current["types"].append(typ) + depth += line.count("{") - line.count("}") + while stack and depth < stack[-1][2]: + stack.pop() + return structs + + +def reachable(structs, wire): + """The wire structs, their bases and the structs their members hold: the only ones shown""" + by_short = {} + for s in structs: + by_short.setdefault(s["name"].split("::")[-1], []).append(s) + keep, todo = [], list(wire) + while todo: + s = todo.pop() + if s in keep: + continue + keep.append(s) + words = re.findall(r"\w+", " ".join(s["types"])) + [s["base"] or ""] + for word in words: + todo += by_short.get(word, []) + return [s for s in structs if s in keep] + + +def member_lines(structs): + known = {s["name"].split("::")[-1]: s["name"] for s in structs} + lines = ["// Every struct's members, in declaration order (a base struct's first)"] + for s in structs: + lines.append(f"json ToJson(const {s['name']}& m);") + lines.append("") + for s in structs: + lines.append(f"json ToJson(const {s['name']}& m) {{") + base = known.get(s["base"] or "") + lines.append(f"\tjson j = ToJson(static_cast(m));" if base else "\tjson j = json::object();") + for member in s["members"]: + if member in SECRETS or member in NOT_WIRE: + continue + lines.append(f"\tj[\"{member}\"] = ToJson(m.{member});") + lines.append("\treturn j;") + lines.append("}") + lines.append("") + return lines + + +def game_messages(): + directory = ROOT / "dGame" / "dGameMessages" + structs = [] + for path in sorted(directory.glob("*Messages.h")): + if path.name == "GameMessages.h": + continue + structs += [s for s in parse(path) if s["base"] != "GameMsg"] + wire = [s for s in structs if s["enum"] == "Game" and s["id"]] + lines = member_lines(reachable(structs, wire)) + lines.append("// Wire messages: (ID, direction, reader). A struct with Handle is what the server reads from a client.") + lines.append("// Structs with members but no Deserialize can't be read.") + lines.append("const std::vector& Entries() {") + lines.append("\tstatic const std::vector entries{") + for s in wire: + direction = "eDirection::TO_SERVER" if s["handle"] else "eDirection::TO_CLIENT" + readable = s["deserialize"] or not s["members"] + reader = f"&ReadWith<{s['name']}>" if readable else "nullptr" + lines.append(f"\t\t{{ MessageType::Game::{s['id']}, {direction}, \"{s['short']}\", {reader} }},") + lines.append("\t};") + lines.append("\treturn entries;") + lines.append("}") + unreadable = [s["name"] for s in wire if s["members"] and not s["deserialize"]] + return directory / "GameMessageFields.inc", lines, f"{len(wire)} game messages, unreadable: {unreadable}" + + +def packets(): + directory = ROOT / "dNet" + structs = [] + for path in sorted(directory.glob("*Packets.h")) + [directory / "WorldRoutePacket.h"] + sorted((directory / "master").glob("*.h")) + [ROOT / "dCommon" / name for name in ("PositionUpdate.h", "Profiler.h", "TrafficStats.h", "ZoneFileLog.h", "dEnums/dCommonVars.h")]: + structs += [s for s in parse(path) if s["name"] not in SKIP_STRUCTS] + wire = [s for s in structs if s["enum"] in SERVICES and s["id"]] + lines = member_lines(reachable(structs, wire)) + lines.append("// Wire packets: (service, ID, struct, reader). An ID can have more than one struct (one per direction):") + lines.append("// the first that reads the whole packet is shown.") + lines.append("const std::vector& Entries() {") + lines.append("\tstatic const std::vector entries{") + for s in wire: + enum, service = s["enum"], SERVICES[s["enum"]] + readable = s["deserialize"] or not s["members"] + reader = f"&ReadWith<{s['name']}>" if readable else "nullptr" + lines.append(f"\t\t{{ ServiceType::{service}, static_cast(MessageType::{enum}::{s['id']}), \"{s['short']}\", {s['handle'] and 'true' or 'false'}, {reader} }},") + lines.append("\t};") + lines.append("\treturn entries;") + lines.append("}") + unreadable = [s["name"] for s in wire if s["members"] and not s["deserialize"]] + return directory / "PacketFields.inc", lines, f"{len(wire)} packets, unreadable: {unreadable}" + + +def main(): + status = 0 + for out, lines, summary in (game_messages(), packets()): + header = [ + "// Generated by tools/gen_game_message_fields.py from the packet structs. Do not edit: run the script.", + "", + ] + text = "\n".join(header + lines) + "\n" + if "--check" in sys.argv: + if not out.exists() or out.read_text() != text: + print(f"{out.relative_to(ROOT)} is out of date: run tools/gen_game_message_fields.py", file=sys.stderr) + status = 1 + continue + out.write_text(text) + print(f"{out.relative_to(ROOT)}: {summary}") + return status + + +if __name__ == "__main__": + sys.exit(main())