Kelsidavis-WoWee/include/game/world_packets.hpp

1365 lines
36 KiB
C++
Raw Normal View History

#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>
namespace wowee {
namespace game {
/**
* 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 unknown1 != 0; }
};
/**
* 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)
enum class CharCreateResult : uint8_t {
// Success codes
SUCCESS = 0x2F, // CHAR_CREATE_SUCCESS
// CHAR_CREATE error codes
IN_PROGRESS = 0x2E, // CHAR_CREATE_IN_PROGRESS
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
};
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;
};
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,
SWIMMING = 0x00200000,
ASCENDING = 0x00400000,
CAN_FLY = 0x00800000,
FLYING = 0x01000000,
};
/**
* 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
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:
/**
* Build a movement packet
*
* @param opcode Movement opcode (CMSG_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;
// 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);
private:
/**
* 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
};
/**
* 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.)
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);
// ============================================================
// 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_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);
};
// ============================================================
// 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 displayInfoId = 0;
uint32_t quality = 0;
uint32_t inventoryType = 0;
int32_t maxStack = 1;
uint32_t containerSlots = 0;
int32_t armor = 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;
std::string subclassName;
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:
static bool parse(network::Packet& packet, MonsterMoveData& data);
};
/** 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();
};
/** 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 & 0x200) != 0; }
bool isMiss() const { return (hitInfo & 0x10) != 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:
static bool parse(network::Packet& packet, InitialSpellsData& data);
};
/** CMSG_CAST_SPELL packet builder */
class CastSpellPacket {
public:
static network::Packet build(uint32_t spellId, uint64_t targetGuid, uint8_t castCount);
};
/** CMSG_CANCEL_CAST packet builder */
class CancelCastPacket {
public:
static network::Packet build(uint32_t spellId);
};
/** CMSG_CANCEL_AURA packet builder */
class CancelAuraPacket {
public:
static network::Packet build(uint32_t spellId);
};
/** 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 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;
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();
};
/** CMSG_GROUP_DISBAND (leave party) packet builder */
class GroupDisbandPacket {
public:
static network::Packet build();
};
/** SMSG_GROUP_LIST parser */
class GroupListParser {
public:
static bool parse(network::Packet& packet, GroupListData& data);
};
/** 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;
};
/** 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);
};
/** CMSG_AUTOEQUIP_ITEM packet builder */
class AutoEquipItemPacket {
public:
static network::Packet build(uint8_t srcBag, uint8_t srcSlot);
};
/** 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:
static bool parse(network::Packet& packet, LootResponseData& data);
};
// ============================================================
// 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_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);
};
/** 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);
};
/** 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;
};
/** SMSG_QUESTGIVER_QUEST_DETAILS parser */
class QuestDetailsParser {
public:
static bool parse(network::Packet& packet, QuestDetailsData& data);
};
/** Reward item entry (shared by quest detail/offer windows) */
struct QuestRewardItem {
uint32_t itemId = 0;
uint32_t count = 0;
uint32_t displayInfoId = 0;
};
/** 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_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 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, uint8_t count);
};
/** CMSG_SELL_ITEM packet builder */
class SellItemPacket {
public:
static network::Packet build(uint64_t vendorGuid, uint64_t itemGuid, uint32_t count);
};
/** SMSG_LIST_INVENTORY parser */
class ListInventoryParser {
public:
static bool parse(network::Packet& packet, ListInventoryData& data);
};
/** CMSG_REPOP_REQUEST packet builder */
class RepopRequestPacket {
public:
static network::Packet build();
};
/** CMSG_SPIRIT_HEALER_ACTIVATE packet builder */
class SpiritHealerActivatePacket {
public:
static network::Packet build(uint64_t npcGuid);
};
} // namespace game
} // namespace wowee