// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3 #pragma once #define EQEMU_PROTOCOL_VERSION "0.5.0" #include #include #include #include #include #include "types.hpp" #include "packet/packet_functions.hpp" // Server timing constants #define SERVER_TIMEOUT 45000 #define INTERSERVER_TIMER 10000 #define LoginServer_StatusUpdateInterval 15000 #define LoginServer_AuthStale 60000 #define AUTHCHANGE_TIMEOUT 900 // Server operation codes #define ServerOP_KeepAlive 0x0001 #define ServerOP_ChannelMessage 0x0002 #define ServerOP_SetZone 0x0003 #define ServerOP_ShutdownAll 0x0004 #define ServerOP_ZoneShutdown 0x0005 #define ServerOP_ZoneBootup 0x0006 #define ServerOP_ZoneStatus 0x0007 #define ServerOP_SetConnectInfo 0x0008 #define ServerOP_EmoteMessage 0x0009 #define ServerOP_ClientList 0x000A #define ServerOP_Who 0x000B #define ServerOP_ZonePlayer 0x000C #define ServerOP_KickPlayer 0x000D #define ServerOP_RefreshGuild 0x000E #define ServerOP_GuildKickAll 0x000F #define ServerOP_GuildInvite 0x0010 #define ServerOP_GuildRemove 0x0011 #define ServerOP_GuildPromote 0x0012 #define ServerOP_GuildDemote 0x0013 #define ServerOP_GuildLeader 0x0014 #define ServerOP_GuildGMSet 0x0015 #define ServerOP_GuildGMSetRank 0x0016 #define ServerOP_FlagUpdate 0x0018 #define ServerOP_GMGoto 0x0019 #define ServerOP_MultiLineMsg 0x001A #define ServerOP_Lock 0x001B #define ServerOP_Motd 0x001C #define ServerOP_Uptime 0x001D #define ServerOP_Petition 0x001E #define ServerOP_KillPlayer 0x001F #define ServerOP_UpdateGM 0x0020 #define ServerOP_RezzPlayer 0x0021 #define ServerOP_ZoneReboot 0x0022 #define ServerOP_ZoneToZoneRequest 0x0023 #define ServerOP_AcceptWorldEntrance 0x0024 #define ServerOP_ZAAuth 0x0025 #define ServerOP_ZAAuthFailed 0x0026 #define ServerOP_ZoneIncClient 0x0027 #define ServerOP_ClientListKA 0x0028 #define ServerOP_ChangeWID 0x0029 #define ServerOP_IPLookup 0x002A #define ServerOP_LockZone 0x002B #define ServerOP_ItemStatus 0x002C #define ServerOP_OOCMute 0x002D #define ServerOP_Revoke 0x002E #define ServerOP_GuildJoin 0x002F #define ServerOP_GroupIDReq 0x0030 #define ServerOP_GroupIDReply 0x0031 #define ServerOP_GroupLeave 0x0032 #define ServerOP_RezzPlayerAccept 0x0033 #define ServerOP_SpawnCondition 0x0034 #define ServerOP_SpawnEvent 0x0035 // Update server operation codes #define UpdateServerOP_Verified 0x5090 #define UpdateServerOP_DisplayMsg 0x5091 #define UpdateServerOP_Completed 0x5092 // Login server operation codes #define ServerOP_LSInfo 0x1000 #define ServerOP_LSStatus 0x1001 #define ServerOP_LSClientAuth 0x1002 #define ServerOP_LSFatalError 0x1003 #define ServerOP_SystemwideMessage 0x1005 #define ServerOP_ListWorlds 0x1006 #define ServerOP_PeerConnect 0x1007 // Login server zone operation codes #define ServerOP_LSZoneInfo 0x3001 #define ServerOP_LSZoneStart 0x3002 #define ServerOP_LSZoneBoot 0x3003 #define ServerOP_LSZoneShutdown 0x3004 #define ServerOP_LSZoneSleep 0x3005 #define ServerOP_LSPlayerLeftWorld 0x3006 #define ServerOP_LSPlayerJoinWorld 0x3007 #define ServerOP_LSPlayerZoneChange 0x3008 // User to world operation codes #define ServerOP_UsertoWorldReq 0xAB00 #define ServerOP_UsertoWorldResp 0xAB01 // Packet operation codes #define ServerOP_EncapPacket 0x2007 #define ServerOP_WorldListUpdate 0x2008 #define ServerOP_WorldListRemove 0x2009 #define ServerOP_TriggerWorldListRefresh 0x200A #define ServerOP_WhoAll 0x0210 // Time operation codes #define ServerOP_SetWorldTime 0x200B #define ServerOP_GetWorldTime 0x200C #define ServerOP_SyncWorldTime 0x200E // EQ2 specific operation codes #define ServerOP_CharTimeStamp 0x200F #define ServerOP_NameFilterCheck 0x2011 #define ServerOP_BasicCharUpdate 0x2012 #define ServerOP_CharacterCreate 0x2013 #define ServerOP_NameCharUpdate 0x2014 #define ServerOP_GetLatestTables 0x2015 #define ServerOP_GetTableQuery 0x2016 #define ServerOP_GetTableData 0x2017 #define ServerOP_RaceUpdate 0x2018 #define ServerOP_ZoneUpdate 0x2019 #define ServerOP_BugReport 0x201A #define ServerOP_ResetDatabase 0x201B #define ServerOP_ZoneUpdates 0x201C #define ServerOP_LoginEquipment 0x201D #define ServerOP_CharacterPicture 0x201E // Character update flags #define LEVEL_UPDATE_FLAG 1 #define RACE_UPDATE_FLAG 2 #define CLASS_UPDATE_FLAG 4 #define GENDER_UPDATE_FLAG 8 #define ZONE_UPDATE_FLAG 16 #define ARMOR_UPDATE_FLAG 32 #define NAME_UPDATE_FLAG 64 #define DELETE_UPDATE_FLAG 128 // Structure size limits #define CHARNAMEUPDATESTRUCT_MAXSIZE 74 #define CHARZONESTRUCT_MAXSIZE 78 #define CHARPICSTRUCT_MINSIZE 10 /** * Core server packet class for inter-server communication * Handles packet creation, compression, and memory management */ class ServerPacket { public: /** * Constructor - Creates a new server packet with specified opcode and size * @param in_opcode - The operation code for this packet * @param in_size - The size of the packet data buffer */ ServerPacket(int16 in_opcode = 0, int32 in_size = 0) { compressed = false; size = in_size; opcode = in_opcode; if (size == 0) { pBuffer = nullptr; } else { pBuffer = new uchar[size]; std::memset(pBuffer, 0, size); } destination = 0; InflatedSize = 0; } /** * Destructor - Cleans up packet buffer memory */ ~ServerPacket() { delete[] pBuffer; } /** * Creates a deep copy of this packet * @return New ServerPacket instance with copied data, or nullptr if this packet is null */ ServerPacket* Copy() { if (this == nullptr) { return nullptr; } ServerPacket* ret = new ServerPacket(this->opcode, this->size); if (this->size) { std::memcpy(ret->pBuffer, this->pBuffer, this->size); } ret->compressed = this->compressed; ret->InflatedSize = this->InflatedSize; return ret; } /** * Compresses the packet data using deflate algorithm * @return true if compression successful, false if already compressed or invalid data */ bool Deflate() { if (compressed) { return false; } if (!pBuffer || !size) { return false; } uchar* tmp = new uchar[size + 128]; int32 tmpsize = DeflatePacket(pBuffer, size, tmp, size + 128); if (!tmpsize) { delete[] tmp; return false; } compressed = true; InflatedSize = size; size = tmpsize; uchar* new_buffer = new uchar[size]; std::memcpy(new_buffer, tmp, size); delete[] tmp; delete[] pBuffer; pBuffer = new_buffer; return true; } /** * Decompresses the packet data using inflate algorithm * @return true if decompression successful, false if not compressed or invalid data */ bool Inflate() { if (!compressed) { return false; } if (!pBuffer || !size) { return false; } uchar* tmp = new uchar[InflatedSize]; int32 tmpsize = InflatePacket(pBuffer, size, tmp, InflatedSize); if (!tmpsize) { delete[] tmp; return false; } compressed = false; size = tmpsize; delete[] pBuffer; pBuffer = tmp; return true; } int32 size; // Current size of packet buffer int16 opcode; // Operation code identifying packet type uchar* pBuffer; // Raw packet data buffer bool compressed; // Flag indicating if packet is compressed int32 InflatedSize; // Original size before compression int32 destination; // Target server ID for routing }; #pragma pack(1) /** * Structure for requesting latest table versions */ struct GetLatestTables_Struct { float table_version; // Current table version float data_version; // Current data version }; /** * Login server information structure */ struct ServerLSInfo_Struct { char name[201]; // Server name identifier char address[250]; // DNS address of the server char account[31]; // Account name for authentication char password[256]; // Password for authentication char protocolversion[25]; // Protocol version string char serverversion[64]; // Server software version int8 servertype; // Server type: 0=world, 1=chat, 2=login, 3=MeshLogin, 4=World Debug int32 dbversion; // Database version for compatibility }; /** * Login server status information */ struct ServerLSStatus_Struct { sint32 status; // Current server status sint32 num_players; // Number of connected players sint32 num_zones; // Number of active zones int8 world_max_level; // Maximum character level allowed }; /** * System-wide message structure */ struct ServerSystemwideMessage { int32 lsaccount_id; // Login server account ID char key[30]; // Session ID key for verification int32 type; // Message type identifier char message[0]; // Variable length message data }; /** * World list synchronization structure */ struct ServerSyncWorldList_Struct { int32 RemoteID; // Remote server identifier int32 ip; // Server IP address sint32 status; // Server status code char name[201]; // Server name char address[250]; // Server address char account[31]; // Account name int32 accountid; // Account ID int8 authlevel; // Authorization level int8 servertype; // Server type classification int32 adminid; // Administrator ID int8 showdown; // Shutdown flag sint32 num_players; // Current player count sint32 num_zones; // Current zone count bool placeholder; // Placeholder server flag }; /** * User to world request structure */ struct UsertoWorldRequest_Struct { int32 lsaccountid; // Login server account ID int32 char_id; // Character ID int32 worldid; // Target world ID int32 FromID; // Source server ID int32 ToID; // Destination server ID char ip_address[21]; // Client IP address }; /** * User to world response structure */ struct UsertoWorldResponse_Struct { int32 lsaccountid; // Login server account ID int32 char_id; // Character ID int32 worldid; // World ID int32 access_key; // Access key for authentication int8 response; // Response code char ip_address[80]; // Server IP address int32 port; // Server port int32 FromID; // Source server ID int32 ToID; // Destination server ID }; /** * Encapsulated packet structure for packet forwarding */ struct ServerEncapPacket_Struct { int32 ToID; // Target server ID int16 opcode; // Encapsulated packet opcode int16 size; // Encapsulated packet size uchar data[0]; // Variable length packet data }; /** * Emote message structure */ struct ServerEmoteMessage_Struct { char to[64]; // Target recipient int32 guilddbid; // Guild database ID sint16 minstatus; // Minimum status required int32 type; // Emote type char message[0]; // Variable length message }; /** * Table version information structure */ typedef struct { char name[256]; // Table name unsigned int name_len; // Length of table name unsigned int version; // Table version number unsigned int data_version; // Data version number } TableVersion; /** * Helper template function to append data to string buffer */ template void AddPtrData(std::string* buffer, Type& data) { buffer->append(reinterpret_cast(&data), sizeof(Type)); } /** * Helper template function to append array data to string buffer */ template void AddPtrData(std::string* buffer, Type* data, int16 size) { buffer->append(reinterpret_cast(data), size); } /** * Class for managing latest table versions across servers * Handles serialization and deserialization of table version data */ class LatestTableVersions { public: /** * Constructor - Initializes empty table version list */ LatestTableVersions() { tables = nullptr; current_index = 0; total_tables = 0; data_version = 0; } /** * Destructor - Cleans up allocated table array */ ~LatestTableVersions() { delete[] tables; } /** * Allocates memory for specified number of tables * @param size - Number of tables to allocate space for */ void SetTableSize(int16 size) { total_tables = size; tables = new TableVersion[total_tables]; } /** * Adds a table version entry to the collection * @param name - Table name * @param version - Table version number * @param data_version - Data version number */ void AddTable(char* name, int32 version, int32 data_version) { std::strcpy(tables[current_index].name, name); tables[current_index].version = version; tables[current_index].data_version = data_version; current_index++; } /** * Calculates total serialized size of all table data * @return Total size in bytes */ int16 GetTotalSize() { return total_tables * sizeof(TableVersion) + sizeof(int16); } /** * Gets total number of tables in collection * @return Number of tables */ int16 GetTotalTables() { return total_tables; } /** * Gets pointer to table array * @return Pointer to TableVersion array */ TableVersion* GetTables() { return tables; } /** * Gets specific table by index * @param index - Index of table to retrieve * @return TableVersion structure */ TableVersion GetTable(int16 index) { return tables[index]; } /** * Serializes all table data into string buffer * @return Serialized data string */ std::string Serialize() { AddPtrData(&buffer, total_tables); for (int16 i = 0; i < total_tables; i++) { AddPtrData(&buffer, tables[i].name, sizeof(tables[i].name)); AddPtrData(&buffer, tables[i].version); AddPtrData(&buffer, tables[i].data_version); } return buffer; } /** * Deserializes table data from byte array * @param data - Raw byte data to deserialize */ void DeSerialize(uchar* data) { uchar* ptr = data; std::memcpy(&total_tables, ptr, sizeof(total_tables)); ptr += sizeof(total_tables); tables = new TableVersion[total_tables]; for (int16 i = 0; i < total_tables; i++) { std::memcpy(&tables[i].name, ptr, sizeof(tables[i].name)); ptr += sizeof(tables[i].name); std::memcpy(&tables[i].version, ptr, sizeof(tables[i].version)); ptr += sizeof(tables[i].version); std::memcpy(&tables[i].data_version, ptr, sizeof(tables[i].data_version)); ptr += sizeof(tables[i].data_version); } } int32 data_version; // Global data version private: int16 current_index; // Current insertion index int16 total_tables; // Total number of tables allocated TableVersion* tables; // Array of table versions std::string buffer; // Serialization buffer }; /** * Structure for table data queries */ struct TableData { int16 size; // Size of query string char* query; // SQL query string }; /** * Class for managing table query operations * Handles multiple SQL queries with version tracking */ class TableQuery { public: /** * Constructor - Initializes empty query collection */ TableQuery() { try_delete = true; num_queries = 0; data_version = 0; current_index = 0; latest_version = 0; your_version = 0; total_size = sizeof(num_queries) + sizeof(latest_version) + sizeof(your_version) + sizeof(tablename); } /** * Destructor - Cleans up query strings if owned */ ~TableQuery() { if (try_delete) { for (size_t i = 0; i < tmp_queries.size(); i++) { delete[] tmp_queries[i]; } } } /** * Concatenates all queries into single string * @return Combined query string with newline separators */ std::string GetQueriesString() { std::string query_string; for (size_t i = 0; i < tmp_queries.size(); i++) { query_string.append(tmp_queries[i]).append("\n"); } return query_string; } /** * Adds a query string to the collection * @param query - SQL query string to add */ void AddQuery(char* query) { num_queries++; total_size += std::strlen(query) + 1; tmp_queries.push_back(query); } /** * Gets total serialized size of query collection * @return Total size in bytes */ int16 GetTotalSize() { return total_size; } /** * Gets number of queries in collection * @return Query count */ int16 GetTotalQueries() { return num_queries; } /** * Gets specific query by index * @param index - Index of query to retrieve * @return Query string pointer */ char* GetQuery(int16 index) { return tmp_queries[index]; } /** * Serializes query collection to string buffer * @return Serialized data string */ std::string Serialize() { num_queries = tmp_queries.size(); AddPtrData(&buffer, num_queries); AddPtrData(&buffer, latest_version); AddPtrData(&buffer, your_version); AddPtrData(&buffer, data_version); AddPtrData(&buffer, tablename, sizeof(tablename)); for (int16 i = 0; i < GetTotalQueries(); i++) { AddPtrData(&buffer, tmp_queries[i], std::strlen(tmp_queries[i]) + 1); } return buffer; } /** * Deserializes query data from byte array * @param data - Raw byte data to deserialize */ void DeSerialize(uchar* data) { try_delete = false; uchar* ptr = data; std::memcpy(&num_queries, ptr, sizeof(num_queries)); ptr += sizeof(num_queries); std::memcpy(&latest_version, ptr, sizeof(latest_version)); ptr += sizeof(latest_version); std::memcpy(&your_version, ptr, sizeof(your_version)); ptr += sizeof(your_version); std::memcpy(&data_version, ptr, sizeof(data_version)); ptr += sizeof(data_version); std::memcpy(&tablename, ptr, sizeof(tablename)); ptr += sizeof(tablename); for (int16 i = 0; i < GetTotalQueries(); i++) { tmp_queries.push_back(reinterpret_cast(ptr)); ptr += std::strlen(reinterpret_cast(ptr)) + 1; } } int16 current_index; // Current processing index int16 num_queries; // Number of queries in collection int32 latest_version; // Latest available version int32 your_version; // Client's current version int32 data_version; // Data version number bool try_delete; // Flag to control memory cleanup char tablename[64]; // Associated table name int32 total_size; // Total serialized size std::string buffer; // Serialization buffer private: std::vector tmp_queries; // Collection of query strings }; /** * Class for managing table data queries with column information * Handles database query results with metadata */ class TableDataQuery { public: /** * Constructor - Initializes with table name * @param table_name - Name of the associated table */ TableDataQuery(char* table_name) { if (std::strlen(table_name) >= sizeof(tablename)) { return; } std::strcpy(tablename, table_name); num_queries = 0; columns_size = 0; columns = nullptr; version = 0; table_size = 0; } /** * Default constructor */ TableDataQuery() { num_queries = 0; columns_size = 0; columns = nullptr; version = 0; table_size = 0; } /** * Destructor - Cleans up column data and queries */ ~TableDataQuery() { delete[] columns; for (int32 i = 0; i < num_queries; i++) { delete[] queries[i]->query; delete queries[i]; } } /** * Gets total number of queries * @return Query count */ int32 GetTotalQueries() { return num_queries; } /** * Serializes query data to string buffer with query limit * @return Pointer to serialized data string, or nullptr if no queries */ std::string* Serialize() { buffer = ""; num_queries = queries.size(); if (GetTotalQueries() == 0) { return nullptr; } table_size = std::strlen(tablename); AddPtrData(&buffer, table_size); AddPtrData(&buffer, tablename, table_size + 1); AddPtrData(&buffer, version); if (num_queries > 200) { int32 max_queries = 200; AddPtrData(&buffer, max_queries); } else { AddPtrData(&buffer, num_queries); } AddPtrData(&buffer, columns_size); AddPtrData(&buffer, columns, columns_size); // Process queries in reverse order with limit of 200 int16 count = 0; for (int i = GetTotalQueries() - 1; i >= 0 && count < 200; i--) { AddPtrData(&buffer, queries[i]->size); AddPtrData(&buffer, queries[i]->query, queries[i]->size); delete[] queries[i]->query; delete queries[i]; queries.pop_back(); count++; } return &buffer; } /** * Deserializes query data from byte array * @param data - Raw byte data to deserialize */ void DeSerialize(uchar* data) { uchar* ptr = data; std::memcpy(&table_size, ptr, sizeof(table_size)); ptr += sizeof(table_size); std::memcpy(&tablename, ptr, table_size + 1); ptr += table_size + 1; std::memcpy(&version, ptr, sizeof(version)); ptr += sizeof(version); std::memcpy(&num_queries, ptr, sizeof(num_queries)); ptr += sizeof(num_queries); std::memcpy(&columns_size, ptr, sizeof(columns_size)); ptr += sizeof(columns_size); columns = new char[columns_size + 1]; std::memcpy(columns, ptr, columns_size + 1); ptr += columns_size; for (int32 i = 0; i < GetTotalQueries(); i++) { TableData* new_query = new TableData; try { std::memcpy(&new_query->size, ptr, sizeof(new_query->size)); ptr += sizeof(new_query->size); new_query->query = new char[new_query->size + 1]; std::memcpy(new_query->query, ptr, new_query->size); ptr += new_query->size; queries.push_back(new_query); } catch (const std::bad_alloc& ba) { std::cout << ba.what() << std::endl; delete new_query; } } } std::string buffer; // Serialization buffer int32 num_queries; // Number of queries in collection int32 version; // Table version number int16 table_size; // Size of table name string char tablename[64]; // Associated table name int16 columns_size; // Size of column definition string char* columns; // Column definitions std::vector queries; // Collection of table data queries }; /** * Equipment update request structure */ struct EquipmentUpdateRequest_Struct { int16 max_per_batch; // Maximum updates per batch }; /** * Login server equipment update structure */ struct LoginEquipmentUpdate { int32 world_char_id; // Character ID int16 equip_type; // Equipment type identifier int8 red; // Red color component int8 green; // Green color component int8 blue; // Blue color component int8 highlight_red; // Highlight red component int8 highlight_green; // Highlight green component int8 highlight_blue; // Highlight blue component int32 slot; // Equipment slot identifier }; /** * World server equipment update structure */ struct EquipmentUpdate_Struct { int32 id; // Unique record identifier int32 world_char_id; // Character ID int16 equip_type; // Equipment type identifier int8 red; // Red color component int8 green; // Green color component int8 blue; // Blue color component int8 highlight_red; // Highlight red component int8 highlight_green; // Highlight green component int8 highlight_blue; // Highlight blue component int32 slot; // Equipment slot identifier }; /** * Equipment update list structure */ struct EquipmentUpdateList_Struct { sint16 total_updates; // Total number of updates available }; /** * Zone update request structure */ struct ZoneUpdateRequest_Struct { int16 max_per_batch; // Maximum updates per batch }; /** * Login server zone update structure */ struct LoginZoneUpdate { std::string name; // Zone name std::string description; // Zone description }; /** * Zone update structure with variable data */ struct ZoneUpdate_Struct { int32 zone_id; // Zone identifier int8 zone_name_length; // Length of zone name int8 zone_desc_length; // Length of zone description char data[0]; // Variable length name and description data }; /** * Zone update list structure */ struct ZoneUpdateList_Struct { uint16 total_updates; // Total number of zone updates char data[0]; // Variable length update data }; /** * Character timestamp structure for EQ2 */ struct CharacterTimeStamp_Struct { int32 char_id; // Character identifier int32 account_id; // Account identifier int32 unix_timestamp; // Unix timestamp of last update }; /** * Character data update structure for basic changes */ struct CharDataUpdate_Struct { int32 account_id; // Account identifier int32 char_id; // Character identifier int8 update_field; // Bitfield of updated fields int32 update_data; // Updated data value }; /** * Bug report structure */ struct BugReport { char category[64]; // Bug category char subcategory[64]; // Bug subcategory char causes_crash[64]; // Crash information char reproducible[64]; // Reproducibility info char summary[128]; // Bug summary char description[2000]; // Detailed description char version[32]; // Game version char player[64]; // Player name int32 account_id; // Account identifier char spawn_name[64]; // Associated spawn name int32 spawn_id; // Spawn identifier int32 zone_id; // Zone identifier }; /** * Race update structure */ struct RaceUpdate_Struct { int32 account_id; // Account identifier int32 char_id; // Character identifier int16 model_type; // Character model type int8 race; // Race identifier }; /** * Character name update structure */ struct CharNameUpdate_Struct { int32 account_id; // Account identifier int32 char_id; // Character identifier int8 name_length; // Length of new name char new_name[0]; // Variable length new name }; /** * Character zone update structure */ struct CharZoneUpdate_Struct { int32 account_id; // Account identifier int32 char_id; // Character identifier int32 zone_id; // Zone identifier int8 zone_length; // Length of zone name char new_zone[0]; // Variable length zone name }; /** * World character creation structure */ struct WorldCharCreate_Struct { int32 account_id; // Account identifier int32 char_id; // Character identifier int16 model_type; // Character model type int16 char_size; // Size of character data uchar character[0]; // Variable length character data }; /** * Character name filter request structure */ struct WorldCharNameFilter_Struct { int32 account_id; // Account identifier int16 name_length; // Length of name to filter uchar name[0]; // Variable length name data }; /** * Character name filter response structure */ struct WorldCharNameFilterResponse_Struct { int32 account_id; // Account identifier int32 char_id; // Character identifier int8 response; // Filter result code }; /** * Character picture update structure */ struct CharPictureUpdate_Struct { int32 account_id; // Account identifier int32 char_id; // Character identifier int16 pic_size; // Size of picture data char pic[0]; // Variable length picture data }; #pragma pack()