Kelsidavis-WoWee/include/game/world_packets.hpp

2808 lines
78 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include "network/packet.hpp"
#include "game/opcodes.hpp"
#include "game/character.hpp"
#include "game/entity.hpp"
#include "game/spell_defines.hpp"
#include "game/group_defines.hpp"
#include <vector>
#include <cstdint>
#include <string>
#include <map>
#include <unordered_map>
#include <chrono>
namespace wowee {
namespace game {
// Normalize WoW in-text tokens (e.g. "$B", "|n") into plain text suitable for UI.
std::string normalizeWowTextTokens(std::string text);
/**
* SMSG_AUTH_CHALLENGE data (from server)
*
* Sent by world server immediately after TCP connect
* Contains server seed/salt for authentication hash
*/
struct AuthChallengeData {
uint32_t unknown1; // Always seems to be 0x00000001
uint32_t serverSeed; // Random seed from server
// Note: 3.3.5a has additional data after this
bool isValid() const { return true; }
};
/**
* CMSG_AUTH_SESSION packet builder
*
* Client authentication to world server using session key from auth server
*/
class AuthSessionPacket {
public:
/**
* Build CMSG_AUTH_SESSION packet
*
* @param build Client build number (12340 for 3.3.5a)
* @param accountName Account name (uppercase)
* @param clientSeed Random 4-byte seed generated by client
* @param sessionKey 40-byte session key from auth server
* @param serverSeed 4-byte seed from SMSG_AUTH_CHALLENGE
* @return Packet ready to send
*/
static network::Packet build(uint32_t build,
const std::string& accountName,
uint32_t clientSeed,
const std::vector<uint8_t>& sessionKey,
uint32_t serverSeed,
uint32_t realmId = 1);
private:
/**
* Compute authentication hash
*
* SHA1(account + [0,0,0,0] + clientSeed + serverSeed + sessionKey)
*
* @param accountName Account name
* @param clientSeed Client seed
* @param serverSeed Server seed
* @param sessionKey 40-byte session key
* @return 20-byte SHA1 hash
*/
static std::vector<uint8_t> computeAuthHash(
const std::string& accountName,
uint32_t clientSeed,
uint32_t serverSeed,
const std::vector<uint8_t>& sessionKey);
};
/**
* SMSG_AUTH_CHALLENGE response parser
*/
class AuthChallengeParser {
public:
static bool parse(network::Packet& packet, AuthChallengeData& data);
};
/**
* SMSG_AUTH_RESPONSE result codes
*/
enum class AuthResult : uint8_t {
// TrinityCore/AzerothCore auth response codes (3.3.5a)
OK = 0x0C, // Success, proceed to character screen
FAILED = 0x0D, // Generic failure
REJECT = 0x0E, // Reject
BAD_SERVER_PROOF = 0x0F, // Bad server proof
UNAVAILABLE = 0x10, // Unavailable
SYSTEM_ERROR = 0x11, // System error
BILLING_ERROR = 0x12, // Billing error
BILLING_EXPIRED = 0x13, // Billing expired
VERSION_MISMATCH = 0x14, // Version mismatch
UNKNOWN_ACCOUNT = 0x15, // Unknown account
INCORRECT_PASSWORD = 0x16, // Incorrect password
SESSION_EXPIRED = 0x17, // Session expired
SERVER_SHUTTING_DOWN = 0x18, // Server shutting down
ALREADY_LOGGING_IN = 0x19, // Already logging in
LOGIN_SERVER_NOT_FOUND = 0x1A, // Login server not found
WAIT_QUEUE = 0x1B, // Wait queue
BANNED = 0x1C, // Banned
ALREADY_ONLINE = 0x1D, // Already online
NO_TIME = 0x1E, // No game time
DB_BUSY = 0x1F, // DB busy
SUSPENDED = 0x20, // Suspended
PARENTAL_CONTROL = 0x21, // Parental control
LOCKED_ENFORCED = 0x22 // Account locked
};
/**
* SMSG_AUTH_RESPONSE data (from server)
*/
struct AuthResponseData {
AuthResult result;
bool isSuccess() const { return result == AuthResult::OK; }
};
/**
* SMSG_AUTH_RESPONSE parser
*/
class AuthResponseParser {
public:
static bool parse(network::Packet& packet, AuthResponseData& data);
};
/**
* Get human-readable string for auth result
*/
const char* getAuthResultString(AuthResult result);
// Forward declare Character
struct Character;
/**
* CMSG_CHAR_ENUM packet builder
*
* Request list of characters on account
*/
class CharEnumPacket {
public:
/**
* Build CMSG_CHAR_ENUM packet
*
* This packet has no body - just the opcode
*/
static network::Packet build();
};
/**
* SMSG_CHAR_ENUM response data (from server)
*
* Contains list of all characters on the account
*/
struct CharEnumResponse {
std::vector<Character> characters;
bool isEmpty() const { return characters.empty(); }
size_t count() const { return characters.size(); }
};
/**
* SMSG_CHAR_ENUM response parser
*/
class CharEnumParser {
public:
static bool parse(network::Packet& packet, CharEnumResponse& response);
};
// ============================================================
// Character Creation
// ============================================================
// WoW 3.3.5a ResponseCodes for character creation (from ResponseCodes enum)
// Windows wingdi.h defines ERROR as 0; undefine it for this enum scope.
#ifdef _WIN32
#pragma push_macro("ERROR")
#undef ERROR
#endif
enum class CharCreateResult : uint8_t {
// Success codes
SUCCESS = 0x2F, // CHAR_CREATE_SUCCESS
// CHAR_CREATE error codes
IN_PROGRESS = 0x2E, // CHAR_CREATE_IN_PROGRESS
CHAR_ERROR = 0x30, // CHAR_CREATE_ERROR
FAILED = 0x31, // CHAR_CREATE_FAILED
NAME_IN_USE = 0x32, // CHAR_CREATE_NAME_IN_USE
DISABLED = 0x33, // CHAR_CREATE_DISABLED
PVP_TEAMS_VIOLATION = 0x34, // CHAR_CREATE_PVP_TEAMS_VIOLATION
SERVER_LIMIT = 0x35, // CHAR_CREATE_SERVER_LIMIT
ACCOUNT_LIMIT = 0x36, // CHAR_CREATE_ACCOUNT_LIMIT
SERVER_QUEUE = 0x37, // CHAR_CREATE_SERVER_QUEUE
ONLY_EXISTING = 0x38, // CHAR_CREATE_ONLY_EXISTING
EXPANSION = 0x39, // CHAR_CREATE_EXPANSION
EXPANSION_CLASS = 0x3A, // CHAR_CREATE_EXPANSION_CLASS
LEVEL_REQUIREMENT = 0x3B, // CHAR_CREATE_LEVEL_REQUIREMENT
UNIQUE_CLASS_LIMIT = 0x3C, // CHAR_CREATE_UNIQUE_CLASS_LIMIT
CHARACTER_IN_GUILD = 0x3D, // CHAR_CREATE_CHARACTER_IN_GUILD
RESTRICTED_RACECLASS = 0x3E, // CHAR_CREATE_RESTRICTED_RACECLASS
CHARACTER_CHOOSE_RACE= 0x3F, // CHAR_CREATE_CHARACTER_CHOOSE_RACE
CHARACTER_ARENA_LEADER=0x40, // CHAR_CREATE_CHARACTER_ARENA_LEADER
CHARACTER_DELETE_MAIL= 0x41, // CHAR_CREATE_CHARACTER_DELETE_MAIL
CHARACTER_SWAP_FACTION=0x42, // CHAR_CREATE_CHARACTER_SWAP_FACTION
CHARACTER_RACE_ONLY = 0x43, // CHAR_CREATE_CHARACTER_RACE_ONLY
CHARACTER_GOLD_LIMIT = 0x44, // CHAR_CREATE_CHARACTER_GOLD_LIMIT
FORCE_LOGIN = 0x45, // CHAR_CREATE_FORCE_LOGIN
// CHAR_NAME error codes (name validation failures)
NAME_SUCCESS = 0x57, // CHAR_NAME_SUCCESS
NAME_FAILURE = 0x58, // CHAR_NAME_FAILURE
NAME_NO_NAME = 0x59, // CHAR_NAME_NO_NAME
NAME_TOO_SHORT = 0x5A, // CHAR_NAME_TOO_SHORT
NAME_TOO_LONG = 0x5B, // CHAR_NAME_TOO_LONG
NAME_INVALID_CHARACTER = 0x5C, // CHAR_NAME_INVALID_CHARACTER
NAME_MIXED_LANGUAGES = 0x5D, // CHAR_NAME_MIXED_LANGUAGES
NAME_PROFANE = 0x5E, // CHAR_NAME_PROFANE
NAME_RESERVED = 0x5F, // CHAR_NAME_RESERVED
NAME_INVALID_APOSTROPHE = 0x60, // CHAR_NAME_INVALID_APOSTROPHE
NAME_MULTIPLE_APOSTROPHES = 0x61, // CHAR_NAME_MULTIPLE_APOSTROPHES
NAME_THREE_CONSECUTIVE = 0x62, // CHAR_NAME_THREE_CONSECUTIVE (98 decimal)
NAME_INVALID_SPACE = 0x63, // CHAR_NAME_INVALID_SPACE
NAME_CONSECUTIVE_SPACES = 0x64, // CHAR_NAME_CONSECUTIVE_SPACES
NAME_RUSSIAN_CONSECUTIVE_SILENT = 0x65, // CHAR_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS
NAME_RUSSIAN_SILENT_AT_BEGIN_OR_END = 0x66, // CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END
NAME_DECLENSION_DOESNT_MATCH = 0x67, // CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME
};
#ifdef _WIN32
#pragma pop_macro("ERROR")
#endif
struct CharCreateData {
std::string name;
Race race;
Class characterClass;
Gender gender;
uint8_t skin = 0;
uint8_t face = 0;
uint8_t hairStyle = 0;
uint8_t hairColor = 0;
uint8_t facialHair = 0;
bool useFemaleModel = false; // For nonbinary: choose body type
};
class CharCreatePacket {
public:
static network::Packet build(const CharCreateData& data);
};
struct CharCreateResponseData {
CharCreateResult result;
};
class CharCreateResponseParser {
public:
static bool parse(network::Packet& packet, CharCreateResponseData& data);
};
/**
* CMSG_PLAYER_LOGIN packet builder
*
* Select character and enter world
*/
class PlayerLoginPacket {
public:
/**
* Build CMSG_PLAYER_LOGIN packet
*
* @param characterGuid GUID of character to log in with
*/
static network::Packet build(uint64_t characterGuid);
};
/**
* SMSG_LOGIN_VERIFY_WORLD data (from server)
*
* Confirms successful world entry with initial position and map info
*/
struct LoginVerifyWorldData {
uint32_t mapId; // Map ID where character spawned
float x, y, z; // Initial position coordinates
float orientation; // Initial orientation (facing direction)
bool isValid() const { return mapId != 0xFFFFFFFF; }
};
/**
* SMSG_LOGIN_VERIFY_WORLD parser
*/
class LoginVerifyWorldParser {
public:
static bool parse(network::Packet& packet, LoginVerifyWorldData& data);
};
/**
* SMSG_ACCOUNT_DATA_TIMES data (from server)
*
* Contains timestamps for account data (macros, keybindings, etc.)
*/
struct AccountDataTimesData {
uint32_t serverTime; // Current server time (Unix timestamp)
uint8_t unknown; // Unknown (always 1?)
uint32_t accountDataTimes[8]; // Timestamps for 8 account data slots
bool isValid() const { return true; }
};
/**
* SMSG_ACCOUNT_DATA_TIMES parser
*/
class AccountDataTimesParser {
public:
static bool parse(network::Packet& packet, AccountDataTimesData& data);
};
/**
* SMSG_MOTD data (from server)
*
* Message of the Day from server
*/
struct MotdData {
std::vector<std::string> lines; // MOTD text lines
bool isEmpty() const { return lines.empty(); }
size_t lineCount() const { return lines.size(); }
};
/**
* SMSG_MOTD parser
*/
class MotdParser {
public:
static bool parse(network::Packet& packet, MotdData& data);
};
/**
* CMSG_PING packet builder
*
* Heartbeat packet sent periodically to keep connection alive
*/
class PingPacket {
public:
/**
* Build CMSG_PING packet
*
* @param sequence Sequence number (increments with each ping)
* @param latency Client-side latency estimate in milliseconds
* @return Packet ready to send
*/
static network::Packet build(uint32_t sequence, uint32_t latency);
};
/**
* SMSG_PONG data (from server)
*
* Response to CMSG_PING, echoes back the sequence number
*/
struct PongData {
uint32_t sequence; // Sequence number from CMSG_PING
bool isValid() const { return true; }
};
/**
* SMSG_PONG parser
*/
class PongParser {
public:
static bool parse(network::Packet& packet, PongData& data);
};
/**
* Movement flags for player movement
*/
enum class MovementFlags : uint32_t {
NONE = 0x00000000,
FORWARD = 0x00000001,
BACKWARD = 0x00000002,
STRAFE_LEFT = 0x00000004,
STRAFE_RIGHT = 0x00000008,
TURN_LEFT = 0x00000010,
TURN_RIGHT = 0x00000020,
PITCH_UP = 0x00000040,
PITCH_DOWN = 0x00000080,
WALKING = 0x00000100,
ONTRANSPORT = 0x00000200,
LEVITATING = 0x00000400,
ROOT = 0x00000800,
FALLING = 0x00001000,
FALLINGFAR = 0x00002000,
FEATHER_FALL = 0x00004000, // Slow fall / Parachute
WATER_WALK = 0x00008000, // Walk on water surface
SWIMMING = 0x00200000,
ASCENDING = 0x00400000,
DESCENDING = 0x00800000,
CAN_FLY = 0x01000000,
FLYING = 0x02000000,
HOVER = 0x40000000,
};
/**
* Movement info structure
*
* Contains all movement-related data sent in movement packets
*/
struct MovementInfo {
uint32_t flags = 0; // Movement flags
uint16_t flags2 = 0; // Extra movement flags
uint32_t time = 0; // Movement timestamp (milliseconds)
float x = 0.0f; // Position X
float y = 0.0f; // Position Y
float z = 0.0f; // Position Z
float orientation = 0.0f; // Facing direction (radians)
// Optional fields (based on flags)
float pitch = 0.0f; // Pitch angle (swimming/flying)
uint32_t fallTime = 0; // Time falling (milliseconds)
float jumpVelocity = 0.0f; // Jump vertical velocity
float jumpSinAngle = 0.0f; // Jump horizontal sin
float jumpCosAngle = 0.0f; // Jump horizontal cos
float jumpXYSpeed = 0.0f; // Jump horizontal speed
// Transport fields (when ONTRANSPORT flag is set)
uint64_t transportGuid = 0; // GUID of transport (boat/zeppelin/etc)
float transportX = 0.0f; // Local position on transport
float transportY = 0.0f;
float transportZ = 0.0f;
float transportO = 0.0f; // Local orientation on transport
uint32_t transportTime = 0; // Transport movement timestamp
int8_t transportSeat = -1; // Transport seat (-1 when unknown/not seated)
uint32_t transportTime2 = 0; // Secondary transport time (when interpolated movement flag is set)
bool hasFlag(MovementFlags flag) const {
return (flags & static_cast<uint32_t>(flag)) != 0;
}
};
/**
* Movement packet builder
*
* Builds CMSG_MOVE_* packets with movement info
*/
class MovementPacket {
public:
static void writePackedGuid(network::Packet& packet, uint64_t guid);
static void writeMovementPayload(network::Packet& packet, const MovementInfo& info);
/**
* Build a movement packet
*
* @param opcode Movement opcode (MSG_MOVE_START_FORWARD, etc.)
* @param info Movement info
* @return Packet ready to send
*/
static network::Packet build(Opcode opcode, const MovementInfo& info, uint64_t playerGuid = 0);
};
// Forward declare Entity types
class Entity;
class EntityManager;
enum class ObjectType : uint8_t;
enum class UpdateType : uint8_t;
/**
* Update block for a single object in SMSG_UPDATE_OBJECT
*/
struct UpdateBlock {
UpdateType updateType;
uint64_t guid = 0;
ObjectType objectType;
// Movement data (for MOVEMENT updates)
bool hasMovement = false;
float x = 0.0f, y = 0.0f, z = 0.0f, orientation = 0.0f;
float runSpeed = 0.0f;
// Update flags from movement block (for detecting transports, etc.)
uint16_t updateFlags = 0;
// Raw movement flags from LIVING block (SWIMMING=0x200000, WALKING=0x100, CAN_FLY=0x800000, FLYING=0x1000000)
// Used to initialise swim/walk/fly state on entity spawn (cold-join).
uint32_t moveFlags = 0;
// Transport data from LIVING movement block (MOVEMENTFLAG_ONTRANSPORT)
bool onTransport = false;
uint64_t transportGuid = 0;
float transportX = 0.0f, transportY = 0.0f, transportZ = 0.0f, transportO = 0.0f;
// Field data (for VALUES and CREATE updates)
std::map<uint16_t, uint32_t> fields;
};
/**
* SMSG_UPDATE_OBJECT data
*
* Contains all update blocks in the packet
*/
struct UpdateObjectData {
uint32_t blockCount = 0;
std::vector<UpdateBlock> blocks;
// Out-of-range GUIDs (for OUT_OF_RANGE_OBJECTS)
std::vector<uint64_t> outOfRangeGuids;
};
/**
* SMSG_UPDATE_OBJECT parser
*
* Parses object updates from server
*/
class UpdateObjectParser {
public:
/**
* Parse SMSG_UPDATE_OBJECT packet
*
* @param packet Packet to parse
* @param data Output data
* @return true if successful
*/
static bool parse(network::Packet& packet, UpdateObjectData& data);
/**
* Read packed GUID from packet
*
* @param packet Packet to read from
* @return GUID value
*/
static uint64_t readPackedGuid(network::Packet& packet);
/**
* Parse a single update block
*
* @param packet Packet to read from
* @param block Output block
* @return true if successful
*/
static bool parseUpdateBlock(network::Packet& packet, UpdateBlock& block);
/**
* Parse movement block
*
* @param packet Packet to read from
* @param block Output block
* @return true if successful
*/
static bool parseMovementBlock(network::Packet& packet, UpdateBlock& block);
/**
* Parse update fields (mask + values)
*
* @param packet Packet to read from
* @param block Output block
* @return true if successful
*/
static bool parseUpdateFields(network::Packet& packet, UpdateBlock& block);
};
/**
* SMSG_DESTROY_OBJECT data
*/
struct DestroyObjectData {
uint64_t guid = 0;
bool isDeath = false; // true if unit died, false if despawned
};
/**
* SMSG_DESTROY_OBJECT parser
*/
class DestroyObjectParser {
public:
static bool parse(network::Packet& packet, DestroyObjectData& data);
};
/**
* Chat message types
*/
enum class ChatType : uint8_t {
SAY = 0,
PARTY = 1,
RAID = 2,
GUILD = 3,
OFFICER = 4,
YELL = 5,
WHISPER = 6,
WHISPER_INFORM = 7,
EMOTE = 8,
TEXT_EMOTE = 9,
SYSTEM = 10,
MONSTER_SAY = 11,
MONSTER_YELL = 12,
MONSTER_EMOTE = 13,
CHANNEL = 14,
CHANNEL_JOIN = 15,
CHANNEL_LEAVE = 16,
CHANNEL_LIST = 17,
CHANNEL_NOTICE = 18,
CHANNEL_NOTICE_USER = 19,
AFK = 20,
DND = 21,
IGNORED = 22,
SKILL = 23,
LOOT = 24,
BATTLEGROUND = 25,
BATTLEGROUND_LEADER = 26,
RAID_LEADER = 27,
RAID_WARNING = 28,
ACHIEVEMENT = 29,
GUILD_ACHIEVEMENT = 30,
MONSTER_WHISPER = 42,
RAID_BOSS_WHISPER = 43,
RAID_BOSS_EMOTE = 44,
MONSTER_PARTY = 50,
// BG/Arena system messages (WoW 3.3.5a — no sender, treated as SYSTEM in display)
BG_SYSTEM_NEUTRAL = 82,
BG_SYSTEM_ALLIANCE = 83,
BG_SYSTEM_HORDE = 84
};
/**
* Chat language IDs
*/
enum class ChatLanguage : uint32_t {
UNIVERSAL = 0,
ORCISH = 1,
DARNASSIAN = 2,
TAURAHE = 3,
DWARVISH = 6,
COMMON = 7,
DEMONIC = 8,
TITAN = 9,
THALASSIAN = 10,
DRACONIC = 11,
KALIMAG = 12,
GNOMISH = 13,
TROLL = 14,
GUTTERSPEAK = 33,
DRAENEI = 35,
ZOMBIE = 36,
GNOMISH_BINARY = 37,
GOBLIN_BINARY = 38,
ADDON = 0xFFFFFFFF // Used for addon communication
};
/**
* CMSG_MESSAGECHAT packet builder
*/
class MessageChatPacket {
public:
/**
* Build CMSG_MESSAGECHAT packet
*
* @param type Chat type (SAY, YELL, etc.)
* @param language Language ID
* @param message Message text
* @param target Target name (for whispers, empty otherwise)
* @return Packet ready to send
*/
static network::Packet build(ChatType type,
ChatLanguage language,
const std::string& message,
const std::string& target = "");
};
/**
* SMSG_MESSAGECHAT data
*/
struct MessageChatData {
ChatType type;
ChatLanguage language;
uint64_t senderGuid = 0;
std::string senderName;
uint64_t receiverGuid = 0;
std::string receiverName;
std::string message;
std::string channelName; // For channel messages
uint8_t chatTag = 0; // Player flags (AFK, DND, GM, etc.)
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
bool isValid() const { return !message.empty(); }
};
/**
* SMSG_MESSAGECHAT parser
*/
class MessageChatParser {
public:
static bool parse(network::Packet& packet, MessageChatData& data);
};
/**
* Get human-readable string for chat type
*/
const char* getChatTypeString(ChatType type);
// ============================================================
// Text Emotes
// ============================================================
/**
* CMSG_TEXT_EMOTE packet builder
*/
class TextEmotePacket {
public:
static network::Packet build(uint32_t textEmoteId, uint64_t targetGuid = 0);
};
/**
* SMSG_TEXT_EMOTE data
*/
struct TextEmoteData {
uint64_t senderGuid = 0;
uint32_t textEmoteId = 0;
uint32_t emoteNum = 0;
std::string targetName;
bool isValid() const { return senderGuid != 0; }
};
/**
* SMSG_TEXT_EMOTE parser
*/
class TextEmoteParser {
public:
// legacyFormat: Classic 1.12 and TBC 2.4.3 send textEmoteId+emoteNum first, then senderGuid.
// WotLK 3.3.5a reverses this: senderGuid first, then textEmoteId+emoteNum.
static bool parse(network::Packet& packet, TextEmoteData& data, bool legacyFormat = false);
};
// ============================================================
// Channel System
// ============================================================
/**
* CMSG_JOIN_CHANNEL packet builder
*/
class JoinChannelPacket {
public:
static network::Packet build(const std::string& channelName, const std::string& password = "");
};
/**
* CMSG_LEAVE_CHANNEL packet builder
*/
class LeaveChannelPacket {
public:
static network::Packet build(const std::string& channelName);
};
/**
* Channel notification types
*/
enum class ChannelNotifyType : uint8_t {
PLAYER_JOINED = 0x00,
PLAYER_LEFT = 0x01,
YOU_JOINED = 0x02,
YOU_LEFT = 0x03,
WRONG_PASSWORD = 0x04,
NOT_MEMBER = 0x05,
NOT_MODERATOR = 0x06,
PASSWORD_CHANGED = 0x07,
OWNER_CHANGED = 0x08,
PLAYER_NOT_FOUND = 0x09,
NOT_OWNER = 0x0A,
CHANNEL_OWNER = 0x0B,
MODE_CHANGE = 0x0C,
ANNOUNCEMENTS_ON = 0x0D,
ANNOUNCEMENTS_OFF = 0x0E,
MODERATION_ON = 0x0F,
MODERATION_OFF = 0x10,
MUTED = 0x11,
PLAYER_KICKED = 0x12,
BANNED = 0x13,
PLAYER_BANNED = 0x14,
PLAYER_UNBANNED = 0x15,
PLAYER_NOT_BANNED = 0x16,
PLAYER_ALREADY_MEMBER = 0x17,
INVITE = 0x18,
INVITE_WRONG_FACTION = 0x19,
WRONG_FACTION = 0x1A,
INVALID_NAME = 0x1B,
NOT_MODERATED = 0x1C,
PLAYER_INVITED = 0x1D,
PLAYER_INVITE_BANNED = 0x1E,
THROTTLED = 0x1F,
NOT_IN_AREA = 0x20,
NOT_IN_LFG = 0x21,
};
/**
* SMSG_CHANNEL_NOTIFY data
*/
struct ChannelNotifyData {
ChannelNotifyType notifyType = ChannelNotifyType::YOU_JOINED;
std::string channelName;
uint64_t senderGuid = 0;
bool isValid() const { return !channelName.empty(); }
};
/**
* SMSG_CHANNEL_NOTIFY parser
*/
class ChannelNotifyParser {
public:
static bool parse(network::Packet& packet, ChannelNotifyData& data);
};
// ============================================================
// Server Info Commands
// ============================================================
/** CMSG_QUERY_TIME packet builder */
class QueryTimePacket {
public:
static network::Packet build();
};
/** SMSG_QUERY_TIME_RESPONSE data */
struct QueryTimeResponseData {
uint32_t serverTime = 0; // Unix timestamp
uint32_t timeOffset = 0; // Time until next daily reset
};
/** SMSG_QUERY_TIME_RESPONSE parser */
class QueryTimeResponseParser {
public:
static bool parse(network::Packet& packet, QueryTimeResponseData& data);
};
/** CMSG_PLAYED_TIME packet builder */
class RequestPlayedTimePacket {
public:
static network::Packet build(bool sendToChat = true);
};
/** SMSG_PLAYED_TIME data */
struct PlayedTimeData {
uint32_t totalTimePlayed = 0; // Total seconds played
uint32_t levelTimePlayed = 0; // Seconds played at current level
bool triggerMessage = false; // Whether to show in chat
};
/** SMSG_PLAYED_TIME parser */
class PlayedTimeParser {
public:
static bool parse(network::Packet& packet, PlayedTimeData& data);
};
/** CMSG_WHO packet builder */
class WhoPacket {
public:
static network::Packet build(uint32_t minLevel = 0, uint32_t maxLevel = 100,
const std::string& playerName = "",
const std::string& guildName = "",
uint32_t raceMask = 0xFFFFFFFF,
uint32_t classMask = 0xFFFFFFFF,
uint32_t zones = 0);
};
// ============================================================
// Social Commands
// ============================================================
/** CMSG_ADD_FRIEND packet builder */
class AddFriendPacket {
public:
static network::Packet build(const std::string& playerName, const std::string& note = "");
};
/** CMSG_DEL_FRIEND packet builder */
class DelFriendPacket {
public:
static network::Packet build(uint64_t friendGuid);
};
/** CMSG_SET_CONTACT_NOTES packet builder */
class SetContactNotesPacket {
public:
static network::Packet build(uint64_t friendGuid, const std::string& note);
};
/** SMSG_FRIEND_STATUS data */
struct FriendStatusData {
uint8_t status = 0; // 0 = offline, 1 = online, etc.
uint64_t guid = 0;
std::string note;
uint8_t chatFlag = 0;
};
/** SMSG_FRIEND_STATUS parser */
class FriendStatusParser {
public:
static bool parse(network::Packet& packet, FriendStatusData& data);
};
/** CMSG_ADD_IGNORE packet builder */
class AddIgnorePacket {
public:
static network::Packet build(const std::string& playerName);
};
/** CMSG_DEL_IGNORE packet builder */
class DelIgnorePacket {
public:
static network::Packet build(uint64_t ignoreGuid);
};
// ============================================================
// Logout Commands
// ============================================================
/** CMSG_LOGOUT_REQUEST packet builder */
class LogoutRequestPacket {
public:
static network::Packet build();
};
/** CMSG_LOGOUT_CANCEL packet builder */
class LogoutCancelPacket {
public:
static network::Packet build();
};
/** SMSG_LOGOUT_RESPONSE data */
struct LogoutResponseData {
uint32_t result = 0; // 0 = success, 1 = failure
uint8_t instant = 0; // 1 = instant logout
};
/** SMSG_LOGOUT_RESPONSE parser */
class LogoutResponseParser {
public:
static bool parse(network::Packet& packet, LogoutResponseData& data);
};
// ============================================================
// Stand State
// ============================================================
/** CMSG_STANDSTATECHANGE packet builder */
class StandStateChangePacket {
public:
static network::Packet build(uint8_t state);
};
// ============================================================
// Action Bar
// ============================================================
/** CMSG_SET_ACTION_BUTTON packet builder */
class SetActionButtonPacket {
public:
// button: 0-based slot index
// type: ActionBarSlot::Type (SPELL=0, ITEM=1, MACRO=2, EMPTY=0)
// id: spellId, itemId, or macroId (0 to clear)
// isClassic: true for Vanilla/Turtle format (5-byte payload),
// false for TBC/WotLK (5-byte packed uint32)
static network::Packet build(uint8_t button, uint8_t type, uint32_t id, bool isClassic);
};
// ============================================================
// Display Toggles
// ============================================================
/** CMSG_SHOWING_HELM packet builder */
class ShowingHelmPacket {
public:
static network::Packet build(bool show);
};
/** CMSG_SHOWING_CLOAK packet builder */
class ShowingCloakPacket {
public:
static network::Packet build(bool show);
};
// ============================================================
// PvP
// ============================================================
/** CMSG_TOGGLE_PVP packet builder */
class TogglePvpPacket {
public:
static network::Packet build();
};
// ============================================================
// Guild Commands
// ============================================================
/** CMSG_GUILD_INFO packet builder */
class GuildInfoPacket {
public:
static network::Packet build();
};
/** CMSG_GUILD_ROSTER packet builder */
class GuildRosterPacket {
public:
static network::Packet build();
};
/** CMSG_GUILD_MOTD packet builder */
class GuildMotdPacket {
public:
static network::Packet build(const std::string& motd);
};
/** CMSG_GUILD_PROMOTE packet builder */
class GuildPromotePacket {
public:
static network::Packet build(const std::string& playerName);
};
/** CMSG_GUILD_DEMOTE packet builder */
class GuildDemotePacket {
public:
static network::Packet build(const std::string& playerName);
};
/** CMSG_GUILD_LEAVE packet builder */
class GuildLeavePacket {
public:
static network::Packet build();
};
/** CMSG_GUILD_INVITE packet builder */
class GuildInvitePacket {
public:
static network::Packet build(const std::string& playerName);
};
/** CMSG_GUILD_QUERY packet builder */
class GuildQueryPacket {
public:
static network::Packet build(uint32_t guildId);
};
/** CMSG_GUILD_REMOVE packet builder */
class GuildRemovePacket {
public:
static network::Packet build(const std::string& playerName);
};
/** CMSG_GUILD_DISBAND packet builder (empty body) */
class GuildDisbandPacket {
public:
static network::Packet build();
};
/** CMSG_GUILD_LEADER packet builder */
class GuildLeaderPacket {
public:
static network::Packet build(const std::string& playerName);
};
/** CMSG_GUILD_SET_PUBLIC_NOTE packet builder */
class GuildSetPublicNotePacket {
public:
static network::Packet build(const std::string& playerName, const std::string& note);
};
/** CMSG_GUILD_SET_OFFICER_NOTE packet builder */
class GuildSetOfficerNotePacket {
public:
static network::Packet build(const std::string& playerName, const std::string& note);
};
/** CMSG_GUILD_ACCEPT packet builder (empty body) */
class GuildAcceptPacket {
public:
static network::Packet build();
};
/** CMSG_GUILD_DECLINE packet builder (empty body) */
class GuildDeclineInvitationPacket {
public:
static network::Packet build();
};
/** CMSG_GUILD_CREATE packet builder */
class GuildCreatePacket {
public:
static network::Packet build(const std::string& guildName);
};
/** CMSG_GUILD_ADD_RANK packet builder */
class GuildAddRankPacket {
public:
static network::Packet build(const std::string& rankName);
};
/** CMSG_GUILD_DEL_RANK packet builder (empty body) */
class GuildDelRankPacket {
public:
static network::Packet build();
};
/** CMSG_PETITION_SHOWLIST packet builder */
class PetitionShowlistPacket {
public:
static network::Packet build(uint64_t npcGuid);
};
/** CMSG_PETITION_BUY packet builder */
class PetitionBuyPacket {
public:
static network::Packet build(uint64_t npcGuid, const std::string& guildName);
};
/** SMSG_PETITION_SHOWLIST data */
struct PetitionShowlistData {
uint64_t npcGuid = 0;
uint32_t itemId = 0;
uint32_t displayId = 0;
uint32_t cost = 0;
uint32_t charterType = 0;
uint32_t requiredSigs = 0;
bool isValid() const { return npcGuid != 0; }
};
/** SMSG_PETITION_SHOWLIST parser */
class PetitionShowlistParser {
public:
static bool parse(network::Packet& packet, PetitionShowlistData& data);
};
/** SMSG_TURN_IN_PETITION_RESULTS parser */
class TurnInPetitionResultsParser {
public:
static bool parse(network::Packet& packet, uint32_t& result);
};
// Guild event type constants
namespace GuildEvent {
constexpr uint8_t PROMOTION = 0;
constexpr uint8_t DEMOTION = 1;
constexpr uint8_t MOTD = 2;
constexpr uint8_t JOINED = 3;
constexpr uint8_t LEFT = 4;
constexpr uint8_t REMOVED = 5;
constexpr uint8_t LEADER_IS = 6;
constexpr uint8_t LEADER_CHANGED = 7;
constexpr uint8_t DISBANDED = 8;
constexpr uint8_t SIGNED_ON = 14;
constexpr uint8_t SIGNED_OFF = 15;
}
/** SMSG_GUILD_QUERY_RESPONSE data */
struct GuildQueryResponseData {
uint32_t guildId = 0;
std::string guildName;
std::string rankNames[10];
uint32_t emblemStyle = 0;
uint32_t emblemColor = 0;
uint32_t borderStyle = 0;
uint32_t borderColor = 0;
uint32_t backgroundColor = 0;
uint32_t rankCount = 0;
bool isValid() const { return guildId != 0 && !guildName.empty(); }
};
/** SMSG_GUILD_QUERY_RESPONSE parser */
class GuildQueryResponseParser {
public:
static bool parse(network::Packet& packet, GuildQueryResponseData& data);
};
/** SMSG_GUILD_INFO data */
struct GuildInfoData {
std::string guildName;
uint32_t creationDay = 0;
uint32_t creationMonth = 0;
uint32_t creationYear = 0;
uint32_t numMembers = 0;
uint32_t numAccounts = 0;
bool isValid() const { return !guildName.empty(); }
};
/** SMSG_GUILD_INFO parser */
class GuildInfoParser {
public:
static bool parse(network::Packet& packet, GuildInfoData& data);
};
/** Guild roster member entry */
struct GuildRosterMember {
uint64_t guid = 0;
bool online = false;
std::string name;
uint32_t rankIndex = 0;
uint8_t level = 0;
uint8_t classId = 0;
uint8_t gender = 0;
uint32_t zoneId = 0;
float lastOnline = 0.0f;
std::string publicNote;
std::string officerNote;
};
/** Guild rank info */
struct GuildRankInfo {
uint32_t rights = 0;
uint32_t goldLimit = 0;
};
/** SMSG_GUILD_ROSTER data */
struct GuildRosterData {
std::string motd;
std::string guildInfo;
std::vector<GuildRankInfo> ranks;
std::vector<GuildRosterMember> members;
bool isEmpty() const { return members.empty(); }
};
/** SMSG_GUILD_ROSTER parser */
class GuildRosterParser {
public:
static bool parse(network::Packet& packet, GuildRosterData& data);
};
/** SMSG_GUILD_EVENT data */
struct GuildEventData {
uint8_t eventType = 0;
uint8_t numStrings = 0;
std::string strings[3];
uint64_t guid = 0;
bool isValid() const { return true; }
};
/** SMSG_GUILD_EVENT parser */
class GuildEventParser {
public:
static bool parse(network::Packet& packet, GuildEventData& data);
};
/** SMSG_GUILD_INVITE data */
struct GuildInviteResponseData {
std::string inviterName;
std::string guildName;
bool isValid() const { return !inviterName.empty(); }
};
/** SMSG_GUILD_INVITE parser */
class GuildInviteResponseParser {
public:
static bool parse(network::Packet& packet, GuildInviteResponseData& data);
};
/** SMSG_GUILD_COMMAND_RESULT data */
struct GuildCommandResultData {
uint32_t command = 0;
std::string name;
uint32_t errorCode = 0;
bool isValid() const { return true; }
};
/** SMSG_GUILD_COMMAND_RESULT parser */
class GuildCommandResultParser {
public:
static bool parse(network::Packet& packet, GuildCommandResultData& data);
};
// ============================================================
// Ready Check
// ============================================================
/** MSG_RAID_READY_CHECK packet builder */
class ReadyCheckPacket {
public:
static network::Packet build();
};
/** MSG_RAID_READY_CHECK_CONFIRM packet builder */
class ReadyCheckConfirmPacket {
public:
static network::Packet build(bool ready);
};
// ============================================================
// Duel
// ============================================================
/** CMSG_DUEL_ACCEPTED packet builder (no payload) */
class DuelAcceptPacket {
public:
static network::Packet build();
};
/** CMSG_DUEL_CANCELLED packet builder */
class DuelCancelPacket {
public:
static network::Packet build();
};
// ============================================================
// Party/Raid Management
// ============================================================
/** CMSG_GROUP_UNINVITE_GUID packet builder */
class GroupUninvitePacket {
public:
static network::Packet build(const std::string& playerName);
};
/** CMSG_GROUP_DISBAND packet builder */
class GroupDisbandPacket {
public:
static network::Packet build();
};
/** MSG_RAID_TARGET_UPDATE packet builder */
class RaidTargetUpdatePacket {
public:
/**
* Build raid target marker update packet
* @param targetIndex 0-7 for raid icons, 0 = MainTank, 1 = MainAssist
* @param targetGuid GUID to mark, or 0 to clear
*/
static network::Packet build(uint8_t targetIndex, uint64_t targetGuid);
};
/** CMSG_REQUEST_RAID_INFO packet builder */
class RequestRaidInfoPacket {
public:
static network::Packet build();
};
// ============================================================
// Combat and Trade
// ============================================================
/** Duel request packet builder (implemented via CMSG_CAST_SPELL, spell 7266) */
class DuelProposedPacket {
public:
static network::Packet build(uint64_t targetGuid);
};
/** CMSG_INITIATE_TRADE packet builder */
class InitiateTradePacket {
public:
static network::Packet build(uint64_t targetGuid);
};
/** CMSG_BEGIN_TRADE packet builder (no payload — accepts incoming trade request) */
class BeginTradePacket {
public:
static network::Packet build();
};
/** CMSG_CANCEL_TRADE packet builder (no payload) */
class CancelTradePacket {
public:
static network::Packet build();
};
/** CMSG_ACCEPT_TRADE packet builder (no payload — lock in current offer) */
class AcceptTradePacket {
public:
static network::Packet build();
};
/** CMSG_SET_TRADE_ITEM packet builder (tradeSlot, bag, bagSlot) */
class SetTradeItemPacket {
public:
// tradeSlot: 0-5 (normal) or 6 (backpack money-only slot)
// bag: 255 = main backpack, 19-22 = bag slots
// bagSlot: slot within bag
static network::Packet build(uint8_t tradeSlot, uint8_t bag, uint8_t bagSlot);
};
/** CMSG_CLEAR_TRADE_ITEM packet builder (remove item from trade slot) */
class ClearTradeItemPacket {
public:
static network::Packet build(uint8_t tradeSlot);
};
/** CMSG_SET_TRADE_GOLD packet builder (gold offered, in copper) */
class SetTradeGoldPacket {
public:
static network::Packet build(uint64_t copper);
};
/** CMSG_UNACCEPT_TRADE packet builder (unaccept without cancelling) */
class UnacceptTradePacket {
public:
static network::Packet build();
};
/** CMSG_ATTACKSWING packet builder */
class AttackSwingPacket {
public:
static network::Packet build(uint64_t targetGuid);
};
/** CMSG_ATTACKSTOP packet builder */
class AttackStopPacket {
public:
static network::Packet build();
};
/** CMSG_CANCEL_CAST packet builder */
class CancelCastPacket {
public:
static network::Packet build(uint32_t spellId);
};
// ============================================================
// Random Roll
// ============================================================
/** CMSG_RANDOM_ROLL packet builder */
class RandomRollPacket {
public:
static network::Packet build(uint32_t minRoll, uint32_t maxRoll);
};
/** SMSG_RANDOM_ROLL data */
struct RandomRollData {
uint64_t rollerGuid = 0;
uint64_t targetGuid = 0; // 0 for party roll
uint32_t minRoll = 0;
uint32_t maxRoll = 0;
uint32_t result = 0;
};
/** SMSG_RANDOM_ROLL parser */
class RandomRollParser {
public:
static bool parse(network::Packet& packet, RandomRollData& data);
};
// ============================================================
// Phase 1: Foundation — Targeting, Name Queries
// ============================================================
/** CMSG_SET_SELECTION packet builder */
class SetSelectionPacket {
public:
static network::Packet build(uint64_t targetGuid);
};
/** CMSG_SET_ACTIVE_MOVER packet builder */
class SetActiveMoverPacket {
public:
static network::Packet build(uint64_t guid);
};
/** CMSG_INSPECT packet builder */
class InspectPacket {
public:
static network::Packet build(uint64_t targetGuid);
};
/** CMSG_QUERY_INSPECT_ACHIEVEMENTS packet builder (WotLK 3.3.5a) */
class QueryInspectAchievementsPacket {
public:
static network::Packet build(uint64_t targetGuid);
};
/** CMSG_NAME_QUERY packet builder */
class NameQueryPacket {
public:
static network::Packet build(uint64_t playerGuid);
};
/** SMSG_NAME_QUERY_RESPONSE data */
struct NameQueryResponseData {
uint64_t guid = 0;
uint8_t found = 1; // 0 = found, 1 = not found
std::string name;
std::string realmName;
uint8_t race = 0;
uint8_t gender = 0;
uint8_t classId = 0;
bool isValid() const { return found == 0 && !name.empty(); }
};
/** SMSG_NAME_QUERY_RESPONSE parser */
class NameQueryResponseParser {
public:
static bool parse(network::Packet& packet, NameQueryResponseData& data);
};
/** CMSG_CREATURE_QUERY packet builder */
class CreatureQueryPacket {
public:
static network::Packet build(uint32_t entry, uint64_t guid);
};
/** SMSG_CREATURE_QUERY_RESPONSE data */
struct CreatureQueryResponseData {
uint32_t entry = 0;
std::string name;
std::string subName;
std::string iconName;
uint32_t typeFlags = 0;
uint32_t creatureType = 0;
uint32_t family = 0;
uint32_t rank = 0; // 0=Normal, 1=Elite, 2=Rare Elite, 3=Boss, 4=Rare
bool isValid() const { return entry != 0 && !name.empty(); }
};
/** SMSG_CREATURE_QUERY_RESPONSE parser */
class CreatureQueryResponseParser {
public:
static bool parse(network::Packet& packet, CreatureQueryResponseData& data);
};
// ============================================================
// GameObject Query
// ============================================================
/** CMSG_GAMEOBJECT_QUERY packet builder */
class GameObjectQueryPacket {
public:
static network::Packet build(uint32_t entry, uint64_t guid);
};
/** SMSG_GAMEOBJECT_QUERY_RESPONSE data */
struct GameObjectQueryResponseData {
uint32_t entry = 0;
std::string name;
uint32_t type = 0; // GameObjectType (e.g. 3=chest, 2=questgiver, 15=MO_TRANSPORT)
uint32_t displayId = 0;
uint32_t data[24] = {}; // Type-specific data fields (e.g. data[0]=taxiPathId for MO_TRANSPORT)
bool hasData = false; // Whether data[] was parsed
bool isValid() const { return entry != 0 && !name.empty(); }
};
/** SMSG_GAMEOBJECT_QUERY_RESPONSE parser */
class GameObjectQueryResponseParser {
public:
static bool parse(network::Packet& packet, GameObjectQueryResponseData& data);
};
/** CMSG_PAGE_TEXT_QUERY packet builder */
class PageTextQueryPacket {
public:
static network::Packet build(uint32_t pageId, uint64_t guid);
};
/** SMSG_PAGE_TEXT_QUERY_RESPONSE data */
struct PageTextQueryResponseData {
uint32_t pageId = 0;
std::string text;
uint32_t nextPageId = 0;
bool isValid() const { return pageId != 0; }
};
/** SMSG_PAGE_TEXT_QUERY_RESPONSE parser */
class PageTextQueryResponseParser {
public:
static bool parse(network::Packet& packet, PageTextQueryResponseData& data);
};
// ============================================================
// Item Query
// ============================================================
/** CMSG_ITEM_QUERY_SINGLE packet builder */
class ItemQueryPacket {
public:
static network::Packet build(uint32_t entry, uint64_t guid);
};
/** SMSG_ITEM_QUERY_SINGLE_RESPONSE data */
struct ItemQueryResponseData {
uint32_t entry = 0;
std::string name;
uint32_t itemClass = 0;
uint32_t subClass = 0;
uint32_t displayInfoId = 0;
uint32_t quality = 0;
uint32_t itemFlags = 0; // Item flag bitmask (Heroic=0x8, Unique-Equipped=0x1000000)
uint32_t inventoryType = 0;
int32_t maxCount = 0; // Max that can be carried (1 = Unique, 0 = unlimited)
int32_t maxStack = 1;
uint32_t containerSlots = 0;
float damageMin = 0.0f;
float damageMax = 0.0f;
uint32_t delayMs = 0;
int32_t armor = 0;
int32_t holyRes = 0;
int32_t fireRes = 0;
int32_t natureRes = 0;
int32_t frostRes = 0;
int32_t shadowRes = 0;
int32_t arcaneRes = 0;
int32_t stamina = 0;
int32_t strength = 0;
int32_t agility = 0;
int32_t intellect = 0;
int32_t spirit = 0;
uint32_t sellPrice = 0;
uint32_t itemLevel = 0;
uint32_t requiredLevel = 0;
std::string subclassName;
// Item spells (up to 5)
struct ItemSpell {
uint32_t spellId = 0;
uint32_t spellTrigger = 0; // 0=Use, 1=Equip, 2=ChanceOnHit, 5=Learn
};
std::array<ItemSpell, 5> spells{};
uint32_t bindType = 0; // 0=none, 1=BoP, 2=BoE, 3=BoU, 4=BoQ
std::string description; // Flavor/lore text
// Generic stat pairs for non-primary stats (hit, crit, haste, AP, SP, etc.)
struct ExtraStat { uint32_t statType = 0; int32_t statValue = 0; };
std::vector<ExtraStat> extraStats;
uint32_t startQuestId = 0; // Non-zero: item begins a quest
// Gem socket slots (WotLK/TBC): 0=no socket; color mask: 1=Meta,2=Red,4=Yellow,8=Blue
std::array<uint32_t, 3> socketColor{};
uint32_t socketBonus = 0; // enchantmentId of socket bonus; 0=none
uint32_t itemSetId = 0; // ItemSet.dbc entry; 0=not part of a set
// Requirement fields
uint32_t requiredSkill = 0; // SkillLine.dbc ID (0 = no skill required)
uint32_t requiredSkillRank = 0; // Minimum skill value
uint32_t allowableClass = 0; // Class bitmask (0 = all classes)
uint32_t allowableRace = 0; // Race bitmask (0 = all races)
uint32_t requiredReputationFaction = 0; // Faction.dbc ID (0 = none)
uint32_t requiredReputationRank = 0; // 0=Hated..8=Exalted
bool valid = false;
};
/** SMSG_ITEM_QUERY_SINGLE_RESPONSE parser */
class ItemQueryResponseParser {
public:
static bool parse(network::Packet& packet, ItemQueryResponseData& data);
};
// ============================================================
// Phase 2: Combat Core
// ============================================================
/** SMSG_MONSTER_MOVE data */
struct MonsterMoveData {
uint64_t guid = 0;
float x = 0, y = 0, z = 0; // Current position (server coords)
uint8_t moveType = 0; // 0=Normal, 1=Stop, 2=FacingSpot, 3=FacingTarget, 4=FacingAngle
float facingAngle = 0;
uint64_t facingTarget = 0;
uint32_t splineFlags = 0;
uint32_t duration = 0;
// Destination (final point of the spline, server coords)
float destX = 0, destY = 0, destZ = 0;
bool hasDest = false;
};
class MonsterMoveParser {
public:
// WotLK 3.3.5a format: PackedGUID + uint8 unk + float[3] + uint32 splineId + uint8 moveType + ...
static bool parse(network::Packet& packet, MonsterMoveData& data);
// Vanilla 1.12 format: PackedGUID + float[3] + uint32 timeInMs + uint8 moveType + ...
// Used for Classic/TBC/Turtle WoW servers (no splineId, timeInMs before moveType)
static bool parseVanilla(network::Packet& packet, MonsterMoveData& data);
};
/** SMSG_ATTACKSTART data */
struct AttackStartData {
uint64_t attackerGuid = 0;
uint64_t victimGuid = 0;
bool isValid() const { return attackerGuid != 0 && victimGuid != 0; }
};
class AttackStartParser {
public:
static bool parse(network::Packet& packet, AttackStartData& data);
};
/** SMSG_ATTACKSTOP data */
struct AttackStopData {
uint64_t attackerGuid = 0;
uint64_t victimGuid = 0;
uint32_t unknown = 0;
bool isValid() const { return true; }
};
class AttackStopParser {
public:
static bool parse(network::Packet& packet, AttackStopData& data);
};
/** Sub-damage entry for melee hits */
struct SubDamage {
uint32_t schoolMask = 0;
float damage = 0.0f;
uint32_t intDamage = 0;
uint32_t absorbed = 0;
uint32_t resisted = 0;
};
/** SMSG_ATTACKERSTATEUPDATE data */
struct AttackerStateUpdateData {
uint32_t hitInfo = 0;
uint64_t attackerGuid = 0;
uint64_t targetGuid = 0;
int32_t totalDamage = 0;
uint8_t subDamageCount = 0;
std::vector<SubDamage> subDamages;
uint32_t victimState = 0; // 0=hit, 1=dodge, 2=parry, 3=interrupt, 4=block, etc.
int32_t overkill = -1;
uint32_t blocked = 0;
bool isValid() const { return attackerGuid != 0; }
bool isCrit() const { return (hitInfo & 0x0200) != 0; }
bool isMiss() const { return (hitInfo & 0x0010) != 0; }
bool isGlancing() const { return (hitInfo & 0x0800) != 0; }
bool isCrushing() const { return (hitInfo & 0x1000) != 0; }
};
class AttackerStateUpdateParser {
public:
static bool parse(network::Packet& packet, AttackerStateUpdateData& data);
};
/** SMSG_SPELLNONMELEEDAMAGELOG data (simplified) */
struct SpellDamageLogData {
uint64_t targetGuid = 0;
uint64_t attackerGuid = 0;
uint32_t spellId = 0;
uint32_t damage = 0;
uint32_t overkill = 0;
uint8_t schoolMask = 0;
uint32_t absorbed = 0;
uint32_t resisted = 0;
bool isCrit = false;
bool isValid() const { return spellId != 0; }
};
class SpellDamageLogParser {
public:
static bool parse(network::Packet& packet, SpellDamageLogData& data);
};
/** SMSG_SPELLHEALLOG data (simplified) */
struct SpellHealLogData {
uint64_t targetGuid = 0;
uint64_t casterGuid = 0;
uint32_t spellId = 0;
uint32_t heal = 0;
uint32_t overheal = 0;
uint32_t absorbed = 0;
bool isCrit = false;
bool isValid() const { return spellId != 0; }
};
class SpellHealLogParser {
public:
static bool parse(network::Packet& packet, SpellHealLogData& data);
};
// ============================================================
// XP Gain
// ============================================================
/** SMSG_LOG_XPGAIN data */
struct XpGainData {
uint64_t victimGuid = 0; // 0 for non-kill XP (quest, exploration)
uint32_t totalXp = 0;
uint8_t type = 0; // 0 = kill, 1 = non-kill
uint32_t groupBonus = 0;
bool isValid() const { return totalXp > 0; }
};
class XpGainParser {
public:
static bool parse(network::Packet& packet, XpGainData& data);
};
// ============================================================
// Phase 3: Spells, Action Bar, Auras
// ============================================================
/** SMSG_INITIAL_SPELLS data */
struct InitialSpellsData {
uint8_t talentSpec = 0;
std::vector<uint32_t> spellIds;
std::vector<SpellCooldownEntry> cooldowns;
bool isValid() const { return true; }
};
class InitialSpellsParser {
public:
// vanillaFormat=true: Classic 1.12 uint16 spellId + uint16 slot (4 bytes/spell)
// vanillaFormat=false: TBC/WotLK uint32 spellId + uint16 unk (6 bytes/spell)
static bool parse(network::Packet& packet, InitialSpellsData& data,
bool vanillaFormat = false);
};
/** CMSG_CAST_SPELL packet builder */
class CastSpellPacket {
public:
static network::Packet build(uint32_t spellId, uint64_t targetGuid, uint8_t castCount);
};
/** CMSG_CANCEL_AURA packet builder */
class CancelAuraPacket {
public:
static network::Packet build(uint32_t spellId);
};
/** CMSG_PET_ACTION packet builder */
class PetActionPacket {
public:
/** CMSG_PET_ACTION: petGuid + action + targetGuid (0 = no target) */
static network::Packet build(uint64_t petGuid, uint32_t action, uint64_t targetGuid = 0);
};
/** SMSG_CAST_FAILED data */
struct CastFailedData {
uint8_t castCount = 0;
uint32_t spellId = 0;
uint8_t result = 0;
bool isValid() const { return spellId != 0; }
};
class CastFailedParser {
public:
static bool parse(network::Packet& packet, CastFailedData& data);
};
/** SMSG_SPELL_START data (simplified) */
struct SpellStartData {
uint64_t casterGuid = 0;
uint64_t casterUnit = 0;
uint8_t castCount = 0;
uint32_t spellId = 0;
uint32_t castFlags = 0;
uint32_t castTime = 0;
uint64_t targetGuid = 0;
bool isValid() const { return spellId != 0; }
};
class SpellStartParser {
public:
static bool parse(network::Packet& packet, SpellStartData& data);
};
/** SMSG_SPELL_GO data (simplified) */
struct SpellGoMissEntry {
uint64_t targetGuid = 0;
uint8_t missType = 0; // 0=MISS 1=DODGE 2=PARRY 3=BLOCK 4=EVADE 5=IMMUNE 6=DEFLECT 7=ABSORB 8=RESIST 11=REFLECT
};
struct SpellGoData {
uint64_t casterGuid = 0;
uint64_t casterUnit = 0;
uint8_t castCount = 0;
uint32_t spellId = 0;
uint32_t castFlags = 0;
uint8_t hitCount = 0;
std::vector<uint64_t> hitTargets;
uint8_t missCount = 0;
std::vector<SpellGoMissEntry> missTargets;
uint64_t targetGuid = 0; ///< Primary target GUID from SpellCastTargets (0 = none/AoE)
bool isValid() const { return spellId != 0; }
};
class SpellGoParser {
public:
static bool parse(network::Packet& packet, SpellGoData& data);
};
/** SMSG_AURA_UPDATE / SMSG_AURA_UPDATE_ALL data */
struct AuraUpdateData {
uint64_t guid = 0;
std::vector<std::pair<uint8_t, AuraSlot>> updates; // slot index + aura data
bool isValid() const { return true; }
};
class AuraUpdateParser {
public:
static bool parse(network::Packet& packet, AuraUpdateData& data, bool isAll);
};
/** SMSG_SPELL_COOLDOWN data */
struct SpellCooldownData {
uint64_t guid = 0;
uint8_t flags = 0;
std::vector<std::pair<uint32_t, uint32_t>> cooldowns; // spellId, cooldownMs
bool isValid() const { return true; }
};
class SpellCooldownParser {
public:
static bool parse(network::Packet& packet, SpellCooldownData& data);
};
// ============================================================
// Phase 4: Group/Party System
// ============================================================
/** CMSG_GROUP_INVITE packet builder */
class GroupInvitePacket {
public:
static network::Packet build(const std::string& playerName);
};
/** SMSG_GROUP_INVITE data */
struct GroupInviteResponseData {
uint8_t canAccept = 0;
std::string inviterName;
bool isValid() const { return !inviterName.empty(); }
};
class GroupInviteResponseParser {
public:
static bool parse(network::Packet& packet, GroupInviteResponseData& data);
};
/** CMSG_GROUP_ACCEPT packet builder */
class GroupAcceptPacket {
public:
static network::Packet build();
};
/** CMSG_GROUP_DECLINE packet builder */
class GroupDeclinePacket {
public:
static network::Packet build();
};
/** SMSG_GROUP_LIST parser */
class GroupListParser {
public:
// hasRoles: WotLK 3.3.5a added a roles byte at group level and per-member for LFD.
// Classic 1.12 and TBC 2.4.3 do not send this byte.
static bool parse(network::Packet& packet, GroupListData& data, bool hasRoles = true);
};
/** SMSG_PARTY_COMMAND_RESULT data */
struct PartyCommandResultData {
PartyCommand command;
std::string name;
PartyResult result;
bool isValid() const { return true; }
};
class PartyCommandResultParser {
public:
static bool parse(network::Packet& packet, PartyCommandResultData& data);
};
/** SMSG_GROUP_DECLINE data */
struct GroupDeclineData {
std::string playerName;
bool isValid() const { return !playerName.empty(); }
};
class GroupDeclineResponseParser {
public:
static bool parse(network::Packet& packet, GroupDeclineData& data);
};
// ============================================================
// Phase 5: Loot System
// ============================================================
/** Loot item entry */
struct LootItem {
uint8_t slotIndex = 0;
uint32_t itemId = 0;
uint32_t count = 0;
uint32_t displayInfoId = 0;
uint32_t randomSuffix = 0;
uint32_t randomPropertyId = 0;
uint8_t lootSlotType = 0;
bool isQuestItem = false;
};
/** SMSG_LOOT_RESPONSE data */
struct LootResponseData {
uint64_t lootGuid = 0;
uint8_t lootType = 0;
uint32_t gold = 0; // In copper
std::vector<LootItem> items;
bool isValid() const { return true; }
uint32_t getGold() const { return gold / 10000; }
uint32_t getSilver() const { return (gold / 100) % 100; }
uint32_t getCopper() const { return gold % 100; }
};
/** CMSG_LOOT packet builder */
class LootPacket {
public:
static network::Packet build(uint64_t targetGuid);
};
/** CMSG_AUTOSTORE_LOOT_ITEM packet builder */
class AutostoreLootItemPacket {
public:
static network::Packet build(uint8_t slotIndex);
};
/** CMSG_USE_ITEM packet builder */
class UseItemPacket {
public:
static network::Packet build(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid, uint32_t spellId = 0);
};
/** CMSG_OPEN_ITEM packet builder (for locked containers / lockboxes) */
class OpenItemPacket {
public:
static network::Packet build(uint8_t bagIndex, uint8_t slotIndex);
};
/** CMSG_AUTOEQUIP_ITEM packet builder */
class AutoEquipItemPacket {
public:
static network::Packet build(uint8_t srcBag, uint8_t srcSlot);
};
/** CMSG_SWAP_ITEM packet builder */
class SwapItemPacket {
public:
// Order matches AzerothCore handler: destBag, destSlot, srcBag, srcSlot.
static network::Packet build(uint8_t dstBag, uint8_t dstSlot, uint8_t srcBag, uint8_t srcSlot);
};
/** CMSG_SPLIT_ITEM packet builder */
class SplitItemPacket {
public:
static network::Packet build(uint8_t srcBag, uint8_t srcSlot,
uint8_t dstBag, uint8_t dstSlot, uint8_t count);
};
/** CMSG_SWAP_INV_ITEM packet builder */
class SwapInvItemPacket {
public:
// WoW inventory: slots are in the "inventory" range (equipment 0-18, bags 19-22, backpack 23-38).
// This swaps two inventory slots directly.
static network::Packet build(uint8_t srcSlot, uint8_t dstSlot);
};
/** CMSG_LOOT_MONEY packet builder (empty body) */
class LootMoneyPacket {
public:
static network::Packet build();
};
/** CMSG_LOOT_RELEASE packet builder */
class LootReleasePacket {
public:
static network::Packet build(uint64_t lootGuid);
};
/** SMSG_LOOT_RESPONSE parser */
class LootResponseParser {
public:
// isWotlkFormat: true for WotLK (has trailing quest item section),
// false for Classic/TBC (no quest item section).
// Per-item size is 22 bytes across all expansions.
static bool parse(network::Packet& packet, LootResponseData& data, bool isWotlkFormat = true);
};
// ============================================================
// Phase 5: NPC Gossip
// ============================================================
/** Gossip menu option */
struct GossipOption {
uint32_t id = 0;
uint8_t icon = 0; // 0=chat, 1=vendor, 2=taxi, 3=trainer, etc.
bool isCoded = false;
uint32_t boxMoney = 0;
std::string text;
std::string boxText;
};
/** Gossip quest item */
struct GossipQuestItem {
uint32_t questId = 0;
uint32_t questIcon = 0;
int32_t questLevel = 0;
uint32_t questFlags = 0;
uint8_t isRepeatable = 0;
std::string title;
};
/** SMSG_GOSSIP_MESSAGE data */
struct GossipMessageData {
uint64_t npcGuid = 0;
uint32_t menuId = 0;
uint32_t titleTextId = 0;
std::vector<GossipOption> options;
std::vector<GossipQuestItem> quests;
bool isValid() const { return true; }
};
/** CMSG_GOSSIP_HELLO packet builder */
class GossipHelloPacket {
public:
static network::Packet build(uint64_t npcGuid);
};
/** CMSG_QUESTGIVER_HELLO packet builder */
class QuestgiverHelloPacket {
public:
static network::Packet build(uint64_t npcGuid);
};
/** CMSG_GOSSIP_SELECT_OPTION packet builder */
class GossipSelectOptionPacket {
public:
static network::Packet build(uint64_t npcGuid, uint32_t menuId, uint32_t optionId, const std::string& code = "");
};
/** SMSG_GOSSIP_MESSAGE parser */
class GossipMessageParser {
public:
static bool parse(network::Packet& packet, GossipMessageData& data);
};
// ============================================================
// Bind Point (Hearthstone)
// ============================================================
struct BindPointUpdateData {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
uint32_t mapId = 0;
uint32_t zoneId = 0;
};
/** CMSG_BINDER_ACTIVATE packet builder */
class BinderActivatePacket {
public:
static network::Packet build(uint64_t npcGuid);
};
/** SMSG_BINDPOINTUPDATE parser */
class BindPointUpdateParser {
public:
static bool parse(network::Packet& packet, BindPointUpdateData& data);
};
/** CMSG_QUESTGIVER_QUERY_QUEST packet builder */
class QuestgiverQueryQuestPacket {
public:
static network::Packet build(uint64_t npcGuid, uint32_t questId);
};
/** CMSG_QUESTGIVER_ACCEPT_QUEST packet builder */
class QuestgiverAcceptQuestPacket {
public:
static network::Packet build(uint64_t npcGuid, uint32_t questId);
};
/** Reward item entry (shared by quest detail/offer windows) */
struct QuestRewardItem {
uint32_t itemId = 0;
uint32_t count = 0;
uint32_t displayInfoId = 0;
uint32_t choiceSlot = 0; // Original reward slot index from server payload
};
/** SMSG_QUESTGIVER_QUEST_DETAILS data (simplified) */
struct QuestDetailsData {
uint64_t npcGuid = 0;
uint32_t questId = 0;
std::string title;
std::string details; // Quest description text
std::string objectives; // Objectives text
uint32_t suggestedPlayers = 0;
uint32_t rewardMoney = 0;
uint32_t rewardXp = 0;
std::vector<QuestRewardItem> rewardChoiceItems; // Player picks one of these
std::vector<QuestRewardItem> rewardItems; // These are always given
};
/** SMSG_QUESTGIVER_QUEST_DETAILS parser */
class QuestDetailsParser {
public:
static bool parse(network::Packet& packet, QuestDetailsData& data);
};
/** SMSG_QUESTGIVER_REQUEST_ITEMS data (turn-in progress check) */
struct QuestRequestItemsData {
uint64_t npcGuid = 0;
uint32_t questId = 0;
std::string title;
std::string completionText;
uint32_t requiredMoney = 0;
uint32_t completableFlags = 0; // 0x03 = completable
std::vector<QuestRewardItem> requiredItems;
bool isCompletable() const { return (completableFlags & 0x03) != 0; }
};
/** SMSG_QUESTGIVER_REQUEST_ITEMS parser */
class QuestRequestItemsParser {
public:
static bool parse(network::Packet& packet, QuestRequestItemsData& data);
};
/** SMSG_QUESTGIVER_OFFER_REWARD data (choose reward) */
struct QuestOfferRewardData {
uint64_t npcGuid = 0;
uint32_t questId = 0;
std::string title;
std::string rewardText;
uint32_t rewardMoney = 0;
uint32_t rewardXp = 0;
std::vector<QuestRewardItem> choiceRewards; // Pick one
std::vector<QuestRewardItem> fixedRewards; // Always given
};
/** SMSG_QUESTGIVER_OFFER_REWARD parser */
class QuestOfferRewardParser {
public:
static bool parse(network::Packet& packet, QuestOfferRewardData& data);
};
/** CMSG_QUESTGIVER_COMPLETE_QUEST packet builder */
class QuestgiverCompleteQuestPacket {
public:
static network::Packet build(uint64_t npcGuid, uint32_t questId);
};
/** CMSG_QUESTGIVER_REQUEST_REWARD packet builder */
class QuestgiverRequestRewardPacket {
public:
static network::Packet build(uint64_t npcGuid, uint32_t questId);
};
/** CMSG_QUESTGIVER_CHOOSE_REWARD packet builder */
class QuestgiverChooseRewardPacket {
public:
static network::Packet build(uint64_t npcGuid, uint32_t questId, uint32_t rewardIndex);
};
// ============================================================
// Phase 5: Vendor
// ============================================================
/** Vendor item entry */
struct VendorItem {
uint32_t slot = 0;
uint32_t itemId = 0;
uint32_t displayInfoId = 0;
int32_t maxCount = -1; // -1 = unlimited
uint32_t buyPrice = 0; // In copper
uint32_t durability = 0;
uint32_t stackCount = 0;
uint32_t extendedCost = 0;
};
/** SMSG_LIST_INVENTORY data */
struct ListInventoryData {
uint64_t vendorGuid = 0;
std::vector<VendorItem> items;
bool canRepair = false; // Set when vendor was opened via GOSSIP_OPTION_ARMORER
bool isValid() const { return true; }
};
/** CMSG_LIST_INVENTORY packet builder */
class ListInventoryPacket {
public:
static network::Packet build(uint64_t npcGuid);
};
/** CMSG_BUY_ITEM packet builder */
class BuyItemPacket {
public:
static network::Packet build(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint32_t count);
};
/** CMSG_SELL_ITEM packet builder */
class SellItemPacket {
public:
static network::Packet build(uint64_t vendorGuid, uint64_t itemGuid, uint32_t count);
};
/** CMSG_BUYBACK_ITEM packet builder */
class BuybackItemPacket {
public:
static network::Packet build(uint64_t vendorGuid, uint32_t slot);
};
/** SMSG_LIST_INVENTORY parser */
class ListInventoryParser {
public:
static bool parse(network::Packet& packet, ListInventoryData& data);
};
// ============================================================
// Trainer
// ============================================================
struct TrainerSpell {
uint32_t spellId = 0;
uint8_t state = 0; // 0=unavailable(grey), 1=available(green), 2=known(green)
uint32_t spellCost = 0; // copper
uint32_t profDialog = 0;
uint32_t profButton = 0;
uint8_t reqLevel = 0;
uint32_t reqSkill = 0;
uint32_t reqSkillValue = 0;
uint32_t chainNode1 = 0;
uint32_t chainNode2 = 0;
uint32_t chainNode3 = 0;
};
struct TrainerListData {
uint64_t trainerGuid = 0;
uint32_t trainerType = 0; // 0=class, 1=mounts, 2=tradeskills, 3=pets
std::vector<TrainerSpell> spells;
std::string greeting;
bool isValid() const { return true; }
};
class TrainerListParser {
public:
// isClassic: Classic 1.12 per-spell layout has no profDialog/profButton fields
// (reqLevel immediately follows cost), plus a trailing unk uint32 per entry.
static bool parse(network::Packet& packet, TrainerListData& data, bool isClassic = false);
};
class TrainerBuySpellPacket {
public:
static network::Packet build(uint64_t trainerGuid, uint32_t spellId);
};
// ============================================================
// Talents
// ============================================================
/** Talent info for a single talent */
struct TalentInfo {
uint32_t talentId = 0; // Talent.dbc ID
uint8_t currentRank = 0; // 0-5 (0 = not learned)
};
/** SMSG_TALENTS_INFO data */
struct TalentsInfoData {
uint8_t talentSpec = 0; // Active spec (0 or 1 for dual-spec)
uint8_t unspentPoints = 0; // Talent points available
std::vector<TalentInfo> talents; // Learned talents
std::vector<uint32_t> glyphs; // Glyph spell IDs
bool isValid() const { return true; }
};
/** SMSG_TALENTS_INFO parser */
class TalentsInfoParser {
public:
static bool parse(network::Packet& packet, TalentsInfoData& data);
};
/** CMSG_LEARN_TALENT packet builder */
class LearnTalentPacket {
public:
static network::Packet build(uint32_t talentId, uint32_t requestedRank);
};
/** MSG_TALENT_WIPE_CONFIRM packet builder */
class TalentWipeConfirmPacket {
public:
static network::Packet build(bool accept);
};
/** CMSG_SET_ACTIVE_TALENT_GROUP_OBSOLETE (0x4C3) — switch dual-spec talent group */
class ActivateTalentGroupPacket {
public:
/** @param group 0 = primary spec, 1 = secondary spec */
static network::Packet build(uint32_t group);
};
// ============================================================
// Taxi / Flight Paths
// ============================================================
static constexpr uint32_t TLK_TAXI_MASK_SIZE = 12;
/** SMSG_SHOWTAXINODES data */
struct ShowTaxiNodesData {
uint32_t windowInfo = 0; // 1 = show window
uint64_t npcGuid = 0;
uint32_t nearestNode = 0; // Taxi node player is at
uint32_t nodeMask[TLK_TAXI_MASK_SIZE] = {};
bool isNodeKnown(uint32_t nodeId) const {
if (nodeId == 0) return false;
uint32_t bitIndex = nodeId - 1;
uint32_t idx = bitIndex / 32;
uint32_t bit = bitIndex % 32;
return idx < TLK_TAXI_MASK_SIZE && (nodeMask[idx] & (1u << bit));
}
};
/** SMSG_SHOWTAXINODES parser */
class ShowTaxiNodesParser {
public:
static bool parse(network::Packet& packet, ShowTaxiNodesData& data);
};
/** SMSG_ACTIVATETAXIREPLY data */
struct ActivateTaxiReplyData {
uint32_t result = 0; // 0 = OK
};
/** SMSG_ACTIVATETAXIREPLY parser */
class ActivateTaxiReplyParser {
public:
static bool parse(network::Packet& packet, ActivateTaxiReplyData& data);
};
/** CMSG_ACTIVATETAXIEXPRESS packet builder */
class ActivateTaxiExpressPacket {
public:
static network::Packet build(uint64_t npcGuid, uint32_t totalCost, const std::vector<uint32_t>& pathNodes);
};
/** CMSG_ACTIVATETAXI packet builder */
class ActivateTaxiPacket {
public:
static network::Packet build(uint64_t npcGuid, uint32_t srcNode, uint32_t destNode);
};
/** CMSG_GAMEOBJ_USE packet builder */
class GameObjectUsePacket {
public:
static network::Packet build(uint64_t guid);
};
/** CMSG_REPOP_REQUEST packet builder */
class RepopRequestPacket {
public:
static network::Packet build();
};
/** CMSG_RECLAIM_CORPSE packet builder */
class ReclaimCorpsePacket {
public:
static network::Packet build(uint64_t guid);
};
/** CMSG_SPIRIT_HEALER_ACTIVATE packet builder */
class SpiritHealerActivatePacket {
public:
static network::Packet build(uint64_t npcGuid);
};
/** CMSG_RESURRECT_RESPONSE packet builder */
class ResurrectResponsePacket {
public:
static network::Packet build(uint64_t casterGuid, bool accept);
};
// ============================================================
// Mail System
// ============================================================
struct MailAttachment {
uint8_t slot = 0;
uint32_t itemGuidLow = 0;
uint32_t itemId = 0;
uint32_t enchantId = 0;
uint32_t randomPropertyId = 0;
uint32_t randomSuffix = 0;
uint32_t stackCount = 1;
uint32_t chargesOrDurability = 0;
uint32_t maxDurability = 0;
};
struct MailMessage {
uint32_t messageId = 0;
uint8_t messageType = 0; // 0=normal, 2=auction, 3=creature, 4=gameobject
uint64_t senderGuid = 0;
uint32_t senderEntry = 0; // For non-player mail
std::string senderName;
std::string subject;
std::string body;
uint32_t stationeryId = 0;
uint32_t money = 0;
uint32_t cod = 0; // Cash on delivery
uint32_t flags = 0;
float expirationTime = 0.0f;
uint32_t mailTemplateId = 0;
bool read = false;
std::vector<MailAttachment> attachments;
};
/** CMSG_GET_MAIL_LIST packet builder */
class GetMailListPacket {
public:
static network::Packet build(uint64_t mailboxGuid);
};
/** CMSG_SEND_MAIL packet builder */
class SendMailPacket {
public:
static network::Packet build(uint64_t mailboxGuid, const std::string& recipient,
const std::string& subject, const std::string& body,
uint32_t money, uint32_t cod,
const std::vector<uint64_t>& itemGuids = {});
};
/** CMSG_MAIL_TAKE_MONEY packet builder */
class MailTakeMoneyPacket {
public:
static network::Packet build(uint64_t mailboxGuid, uint32_t mailId);
};
/** CMSG_MAIL_TAKE_ITEM packet builder */
class MailTakeItemPacket {
public:
static network::Packet build(uint64_t mailboxGuid, uint32_t mailId, uint32_t itemGuidLow);
};
/** CMSG_MAIL_DELETE packet builder */
class MailDeletePacket {
public:
static network::Packet build(uint64_t mailboxGuid, uint32_t mailId, uint32_t mailTemplateId);
};
/** CMSG_MAIL_MARK_AS_READ packet builder */
class MailMarkAsReadPacket {
public:
static network::Packet build(uint64_t mailboxGuid, uint32_t mailId);
};
// ============================================================
// Bank System
// ============================================================
/** CMSG_BANKER_ACTIVATE packet builder */
class BankerActivatePacket {
public:
static network::Packet build(uint64_t guid);
};
/** CMSG_BUY_BANK_SLOT packet builder */
class BuyBankSlotPacket {
public:
static network::Packet build(uint64_t guid);
};
/** CMSG_AUTOBANK_ITEM packet builder (deposit item to bank) */
class AutoBankItemPacket {
public:
static network::Packet build(uint8_t srcBag, uint8_t srcSlot);
};
/** CMSG_AUTOSTORE_BANK_ITEM packet builder (withdraw item from bank) */
class AutoStoreBankItemPacket {
public:
static network::Packet build(uint8_t srcBag, uint8_t srcSlot);
};
// ============================================================
// Guild Bank System
// ============================================================
struct GuildBankItemSlot {
uint8_t slotId = 0;
uint32_t itemEntry = 0;
uint32_t stackCount = 1;
uint32_t enchantId = 0;
uint32_t randomPropertyId = 0;
};
struct GuildBankTab {
std::string tabName;
std::string tabIcon;
std::vector<GuildBankItemSlot> items;
};
struct GuildBankData {
uint64_t money = 0;
uint8_t tabId = 0;
int32_t withdrawAmount = -1; // -1 = unlimited
std::vector<GuildBankTab> tabs; // Only populated on fullUpdate
std::vector<GuildBankItemSlot> tabItems; // Current tab items
};
/** CMSG_GUILD_BANKER_ACTIVATE packet builder */
class GuildBankerActivatePacket {
public:
static network::Packet build(uint64_t guid);
};
/** CMSG_GUILD_BANK_QUERY_TAB packet builder */
class GuildBankQueryTabPacket {
public:
static network::Packet build(uint64_t guid, uint8_t tabId, bool fullUpdate);
};
/** CMSG_GUILD_BANK_BUY_TAB packet builder */
class GuildBankBuyTabPacket {
public:
static network::Packet build(uint64_t guid, uint8_t tabId);
};
/** CMSG_GUILD_BANK_DEPOSIT_MONEY packet builder */
class GuildBankDepositMoneyPacket {
public:
static network::Packet build(uint64_t guid, uint32_t amount);
};
/** CMSG_GUILD_BANK_WITHDRAW_MONEY packet builder */
class GuildBankWithdrawMoneyPacket {
public:
static network::Packet build(uint64_t guid, uint32_t amount);
};
/** CMSG_GUILD_BANK_SWAP_ITEMS packet builder */
class GuildBankSwapItemsPacket {
public:
// Bank to inventory
static network::Packet buildBankToInventory(uint64_t guid, uint8_t tabId, uint8_t bankSlot,
uint8_t destBag, uint8_t destSlot, uint32_t splitCount = 0);
// Inventory to bank
static network::Packet buildInventoryToBank(uint64_t guid, uint8_t tabId, uint8_t bankSlot,
uint8_t srcBag, uint8_t srcSlot, uint32_t splitCount = 0);
};
/** SMSG_GUILD_BANK_LIST parser */
class GuildBankListParser {
public:
static bool parse(network::Packet& packet, GuildBankData& data);
};
// ============================================================
// Auction House System
// ============================================================
struct AuctionEntry {
uint32_t auctionId = 0;
uint32_t itemEntry = 0;
uint32_t stackCount = 1;
uint32_t enchantId = 0;
uint32_t randomPropertyId = 0;
uint32_t suffixFactor = 0;
uint64_t ownerGuid = 0;
uint32_t startBid = 0;
uint32_t minBidIncrement = 0;
uint32_t buyoutPrice = 0;
uint32_t timeLeftMs = 0;
uint64_t bidderGuid = 0;
uint32_t currentBid = 0;
};
struct AuctionListResult {
std::vector<AuctionEntry> auctions;
uint32_t totalCount = 0;
uint32_t searchDelay = 0;
};
struct AuctionCommandResult {
uint32_t auctionId = 0;
uint32_t action = 0; // 0=create, 1=cancel, 2=bid, 3=buyout
uint32_t errorCode = 0; // 0=success
uint32_t bidError = 0; // secondary error for bid actions
};
struct AuctionHelloData {
uint64_t auctioneerGuid = 0;
uint32_t auctionHouseId = 0;
uint8_t enabled = 1;
};
/** MSG_AUCTION_HELLO packet builder */
class AuctionHelloPacket {
public:
static network::Packet build(uint64_t guid);
};
/** MSG_AUCTION_HELLO parser (server response) */
class AuctionHelloParser {
public:
static bool parse(network::Packet& packet, AuctionHelloData& data);
};
/** CMSG_AUCTION_LIST_ITEMS packet builder */
class AuctionListItemsPacket {
public:
static network::Packet build(uint64_t guid, uint32_t offset,
const std::string& searchName,
uint8_t levelMin, uint8_t levelMax,
uint32_t invTypeMask, uint32_t itemClass,
uint32_t itemSubClass, uint32_t quality,
uint8_t usableOnly, uint8_t exactMatch);
};
/** CMSG_AUCTION_SELL_ITEM packet builder */
class AuctionSellItemPacket {
public:
static network::Packet build(uint64_t auctioneerGuid, uint64_t itemGuid,
uint32_t stackCount, uint32_t bid,
uint32_t buyout, uint32_t duration);
};
/** CMSG_AUCTION_PLACE_BID packet builder */
class AuctionPlaceBidPacket {
public:
static network::Packet build(uint64_t auctioneerGuid, uint32_t auctionId, uint32_t amount);
};
/** CMSG_AUCTION_REMOVE_ITEM packet builder */
class AuctionRemoveItemPacket {
public:
static network::Packet build(uint64_t auctioneerGuid, uint32_t auctionId);
};
/** CMSG_AUCTION_LIST_OWNER_ITEMS packet builder */
class AuctionListOwnerItemsPacket {
public:
static network::Packet build(uint64_t auctioneerGuid, uint32_t offset);
};
/** CMSG_AUCTION_LIST_BIDDER_ITEMS packet builder */
class AuctionListBidderItemsPacket {
public:
static network::Packet build(uint64_t auctioneerGuid, uint32_t offset,
const std::vector<uint32_t>& outbiddedIds = {});
};
/** SMSG_AUCTION_LIST_RESULT parser (shared for browse/owner/bidder) */
class AuctionListResultParser {
public:
// numEnchantSlots: Classic 1.12 = 1, TBC/WotLK = 3 (extra enchant slots per entry)
static bool parse(network::Packet& packet, AuctionListResult& data, int numEnchantSlots = 3);
};
/** SMSG_AUCTION_COMMAND_RESULT parser */
class AuctionCommandResultParser {
public:
static bool parse(network::Packet& packet, AuctionCommandResult& data);
};
/** Pet Stable packet builders */
class ListStabledPetsPacket {
public:
/** MSG_LIST_STABLED_PETS (CMSG): request list from stable master */
static network::Packet build(uint64_t stableMasterGuid);
};
class StablePetPacket {
public:
/** CMSG_STABLE_PET: store active pet in the given stable slot (1-based) */
static network::Packet build(uint64_t stableMasterGuid, uint8_t slot);
};
class UnstablePetPacket {
public:
/** CMSG_UNSTABLE_PET: retrieve a stabled pet by its server-side petNumber */
static network::Packet build(uint64_t stableMasterGuid, uint32_t petNumber);
};
class PetRenamePacket {
public:
/** CMSG_PET_RENAME: rename the player's active pet.
* petGuid: the pet's object GUID (from GameHandler::getPetGuid())
* name: new name (max 12 chars; server validates and may reject)
* isDeclined: 0 for non-Cyrillic locales (no declined name forms) */
static network::Packet build(uint64_t petGuid, const std::string& name, uint8_t isDeclined = 0);
};
/** CMSG_SET_TITLE packet builder.
* titleBit >= 0: activate the title with that bit index.
* titleBit == -1: clear the current title (show no title). */
class SetTitlePacket {
public:
static network::Packet build(int32_t titleBit);
};
/** CMSG_ALTER_APPEARANCE barber shop: change hair style, color, facial hair.
* Payload: uint32 hairStyle, uint32 hairColor, uint32 facialHair. */
class AlterAppearancePacket {
public:
static network::Packet build(uint32_t hairStyle, uint32_t hairColor, uint32_t facialHair);
};
} // namespace game
} // namespace wowee