2026-02-02 12:24:50 -08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "network/packet.hpp"
|
|
|
|
|
#include "game/opcodes.hpp"
|
2026-02-05 14:13:48 -08:00
|
|
|
#include "game/character.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
#include "game/entity.hpp"
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
#include "game/spell_defines.hpp"
|
|
|
|
|
#include "game/group_defines.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <vector>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <map>
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
#include <unordered_map>
|
2026-02-14 18:27:59 -08:00
|
|
|
#include <chrono>
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace game {
|
|
|
|
|
|
2026-02-20 23:20:02 -08:00
|
|
|
// Normalize WoW in-text tokens (e.g. "$B", "|n") into plain text suitable for UI.
|
|
|
|
|
std::string normalizeWowTextTokens(std::string text);
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
|
2026-02-13 16:53:28 -08:00
|
|
|
bool isValid() const { return true; }
|
2026-02-02 12:24:50 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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,
|
2026-02-05 21:03:11 -08:00
|
|
|
uint32_t serverSeed,
|
|
|
|
|
uint32_t realmId = 1);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
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 {
|
2026-02-05 21:03:11 -08:00
|
|
|
// 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
|
2026-02-02 12:24:50 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-05 14:13:48 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// Character Creation
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
2026-02-05 21:03:11 -08:00
|
|
|
// WoW 3.3.5a ResponseCodes for character creation (from ResponseCodes enum)
|
2026-02-18 17:52:28 -08:00
|
|
|
// Windows wingdi.h defines ERROR as 0; undefine it for this enum scope.
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#pragma push_macro("ERROR")
|
|
|
|
|
#undef ERROR
|
|
|
|
|
#endif
|
2026-02-05 14:13:48 -08:00
|
|
|
enum class CharCreateResult : uint8_t {
|
2026-02-05 21:03:11 -08:00
|
|
|
// Success codes
|
|
|
|
|
SUCCESS = 0x2F, // CHAR_CREATE_SUCCESS
|
|
|
|
|
|
|
|
|
|
// CHAR_CREATE error codes
|
|
|
|
|
IN_PROGRESS = 0x2E, // CHAR_CREATE_IN_PROGRESS
|
2026-02-18 18:39:07 -08:00
|
|
|
CHAR_ERROR = 0x30, // CHAR_CREATE_ERROR
|
2026-02-05 21:03:11 -08:00
|
|
|
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
|
2026-02-05 14:13:48 -08:00
|
|
|
};
|
2026-02-18 17:52:28 -08:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
#pragma pop_macro("ERROR")
|
|
|
|
|
#endif
|
2026-02-05 14:13:48 -08:00
|
|
|
|
|
|
|
|
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;
|
2026-02-09 17:56:04 -08:00
|
|
|
bool useFemaleModel = false; // For nonbinary: choose body type
|
2026-02-05 14:13:48 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
/**
|
|
|
|
|
* 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,
|
Add missing movement ACK responses to avoid server stalls
Implement generic handlers for force speed changes (walk, run back,
swim, swim back, flight, flight back, turn rate, pitch rate),
movement flag toggles (CAN_FLY, HOVER, feather fall, water walk),
and knockback ACKs. Fix SMSG_TIME_SYNC_REQ to respond with
CMSG_TIME_SYNC_RESP instead of silently dropping.
2026-02-26 03:02:51 -08:00
|
|
|
HOVER = 0x02000000,
|
2026-02-02 12:24:50 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
|
2026-02-11 02:23:37 -08:00
|
|
|
// 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
|
2026-02-11 15:24:05 -08:00
|
|
|
int8_t transportSeat = -1; // Transport seat (-1 when unknown/not seated)
|
|
|
|
|
uint32_t transportTime2 = 0; // Secondary transport time (when interpolated movement flag is set)
|
2026-02-11 02:23:37 -08:00
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
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:
|
2026-02-11 21:14:35 -08:00
|
|
|
static void writePackedGuid(network::Packet& packet, uint64_t guid);
|
|
|
|
|
static void writeMovementPayload(network::Packet& packet, const MovementInfo& info);
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
/**
|
|
|
|
|
* Build a movement packet
|
|
|
|
|
*
|
2026-02-20 02:50:59 -08:00
|
|
|
* @param opcode Movement opcode (MSG_MOVE_START_FORWARD, etc.)
|
2026-02-02 12:24:50 -08:00
|
|
|
* @param info Movement info
|
|
|
|
|
* @return Packet ready to send
|
|
|
|
|
*/
|
2026-02-06 03:24:46 -08:00
|
|
|
static network::Packet build(Opcode opcode, const MovementInfo& info, uint64_t playerGuid = 0);
|
2026-02-02 12:24:50 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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;
|
2026-02-07 20:24:25 -08:00
|
|
|
float runSpeed = 0.0f;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-08 00:59:40 -08:00
|
|
|
// Update flags from movement block (for detecting transports, etc.)
|
|
|
|
|
uint16_t updateFlags = 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;
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
// 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);
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
/**
|
|
|
|
|
* Read packed GUID from packet
|
|
|
|
|
*
|
|
|
|
|
* @param packet Packet to read from
|
|
|
|
|
* @return GUID value
|
|
|
|
|
*/
|
|
|
|
|
static uint64_t readPackedGuid(network::Packet& packet);
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
/**
|
|
|
|
|
* 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.)
|
2026-02-14 18:27:59 -08:00
|
|
|
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2026-02-14 14:30:09 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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:
|
|
|
|
|
static bool parse(network::Packet& packet, TextEmoteData& data);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// 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 {
|
|
|
|
|
YOU_JOINED = 0x00,
|
|
|
|
|
YOU_LEFT = 0x01,
|
|
|
|
|
WRONG_PASSWORD = 0x02,
|
|
|
|
|
NOT_MEMBER = 0x03,
|
|
|
|
|
NOT_MODERATOR = 0x04,
|
|
|
|
|
PASSWORD_CHANGED = 0x05,
|
|
|
|
|
OWNER_CHANGED = 0x06,
|
|
|
|
|
PLAYER_NOT_FOUND = 0x07,
|
|
|
|
|
NOT_OWNER = 0x08,
|
|
|
|
|
CHANNEL_OWNER = 0x09,
|
|
|
|
|
MODE_CHANGE = 0x0A,
|
|
|
|
|
ANNOUNCEMENTS_ON = 0x0B,
|
|
|
|
|
ANNOUNCEMENTS_OFF = 0x0C,
|
|
|
|
|
MODERATION_ON = 0x0D,
|
|
|
|
|
MODERATION_OFF = 0x0E,
|
|
|
|
|
MUTED = 0x0F,
|
|
|
|
|
PLAYER_KICKED = 0x10,
|
|
|
|
|
BANNED = 0x11,
|
|
|
|
|
PLAYER_BANNED = 0x12,
|
|
|
|
|
PLAYER_UNBANNED = 0x13,
|
|
|
|
|
PLAYER_NOT_BANNED = 0x14,
|
|
|
|
|
PLAYER_ALREADY_MEMBER = 0x15,
|
|
|
|
|
INVITE = 0x16,
|
|
|
|
|
INVITE_WRONG_FACTION = 0x17,
|
|
|
|
|
WRONG_FACTION = 0x18,
|
|
|
|
|
INVALID_NAME = 0x19,
|
|
|
|
|
NOT_MODERATED = 0x1A,
|
|
|
|
|
PLAYER_INVITED = 0x1B,
|
|
|
|
|
PLAYER_INVITE_BANNED = 0x1C,
|
|
|
|
|
THROTTLED = 0x1D,
|
|
|
|
|
NOT_IN_AREA = 0x1E,
|
|
|
|
|
NOT_IN_LFG = 0x1F,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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);
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
// ============================================================
|
2026-02-07 12:43:32 -08:00
|
|
|
// 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-20 02:50:59 -08:00
|
|
|
/** CMSG_PLAYED_TIME packet builder */
|
2026-02-07 12:43:32 -08:00
|
|
|
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:
|
2026-02-14 15:05:18 -08:00
|
|
|
static network::Packet build(uint32_t minLevel = 0, uint32_t maxLevel = 100,
|
2026-02-07 12:43:32 -08:00
|
|
|
const std::string& playerName = "",
|
|
|
|
|
const std::string& guildName = "",
|
|
|
|
|
uint32_t raceMask = 0xFFFFFFFF,
|
|
|
|
|
uint32_t classMask = 0xFFFFFFFF,
|
|
|
|
|
uint32_t zones = 0);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
Add /roll and friend management commands
Roll Command:
- Add /roll, /random, /rnd commands for random number generation
- Support multiple formats: /roll, /roll 100, /roll 1-100, /roll 10 50
- Broadcasts rolls to party/raid with "[Name] rolls X (min-max)" format
- Cap max roll at 10,000 to prevent abuse
- Use MSG_RANDOM_ROLL (0x1FB) bidirectional opcode
Friend Commands:
- Add /friend add <name>, /addfriend <name> to add friends
- Add /friend remove <name>, /removefriend <name> to remove friends
- Support aliases: /delfriend, /remfriend
- Maintain local friends cache mapping names to GUIDs for lookups
- Display status messages for all friend actions:
- Friend added/removed confirmations
- Friend online/offline notifications
- Error messages (not found, already friends, list full, ignoring)
Social Opcodes:
- Add CMSG_ADD_FRIEND (0x69) and SMSG_FRIEND_STATUS (0x68)
- Add CMSG_DEL_FRIEND (0x6A) for friend removal
- Add CMSG_SET_CONTACT_NOTES (0x6B) for friend notes (future use)
- Add CMSG_ADD_IGNORE (0x6C) and CMSG_DEL_IGNORE (0x6D) (future use)
Implementation:
- Add RandomRollPacket builder and RandomRollParser for roll data
- Add AddFriendPacket and DelFriendPacket builders
- Add FriendStatusParser to handle server friend status updates
- Add friendsCache map to store friend name-to-GUID mappings
- Add handleRandomRoll() and handleFriendStatus() packet handlers
- Comprehensive slash command parsing with multiple formats and aliases
2026-02-07 12:51:30 -08:00
|
|
|
// 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-07 12:58:11 -08:00
|
|
|
/** 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
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
2026-02-20 02:50:59 -08:00
|
|
|
/** CMSG_STANDSTATECHANGE packet builder */
|
2026-02-07 12:58:11 -08:00
|
|
|
class StandStateChangePacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint8_t state);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-07 13:03:21 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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);
|
|
|
|
|
};
|
|
|
|
|
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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();
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-20 02:50:59 -08:00
|
|
|
/** CMSG_GUILD_ROSTER packet builder */
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
class GuildRosterPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** CMSG_GUILD_MOTD packet builder */
|
|
|
|
|
class GuildMotdPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(const std::string& motd);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-20 02:50:59 -08:00
|
|
|
/** CMSG_GUILD_PROMOTE packet builder */
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
class GuildPromotePacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(const std::string& playerName);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-20 02:50:59 -08:00
|
|
|
/** CMSG_GUILD_DEMOTE packet builder */
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-13 21:39:48 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-16 20:16:14 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-13 21:39:48 -08:00
|
|
|
/** CMSG_GUILD_ACCEPT packet builder (empty body) */
|
|
|
|
|
class GuildAcceptPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build();
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-20 02:50:59 -08:00
|
|
|
/** CMSG_GUILD_DECLINE packet builder (empty body) */
|
2026-02-13 21:39:48 -08:00
|
|
|
class GuildDeclineInvitationPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build();
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-25 14:44:44 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-13 21:39:48 -08:00
|
|
|
// 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);
|
|
|
|
|
};
|
|
|
|
|
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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_CANCELLED packet builder */
|
|
|
|
|
class DuelCancelPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build();
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-07 13:28:46 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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();
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-07 13:36:50 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// Combat and Trade
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
2026-02-20 01:13:01 -08:00
|
|
|
/** Duel request packet builder (implemented via CMSG_CAST_SPELL, spell 7266) */
|
2026-02-07 13:36:50 -08:00
|
|
|
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_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);
|
|
|
|
|
};
|
|
|
|
|
|
Add /roll and friend management commands
Roll Command:
- Add /roll, /random, /rnd commands for random number generation
- Support multiple formats: /roll, /roll 100, /roll 1-100, /roll 10 50
- Broadcasts rolls to party/raid with "[Name] rolls X (min-max)" format
- Cap max roll at 10,000 to prevent abuse
- Use MSG_RANDOM_ROLL (0x1FB) bidirectional opcode
Friend Commands:
- Add /friend add <name>, /addfriend <name> to add friends
- Add /friend remove <name>, /removefriend <name> to remove friends
- Support aliases: /delfriend, /remfriend
- Maintain local friends cache mapping names to GUIDs for lookups
- Display status messages for all friend actions:
- Friend added/removed confirmations
- Friend online/offline notifications
- Error messages (not found, already friends, list full, ignoring)
Social Opcodes:
- Add CMSG_ADD_FRIEND (0x69) and SMSG_FRIEND_STATUS (0x68)
- Add CMSG_DEL_FRIEND (0x6A) for friend removal
- Add CMSG_SET_CONTACT_NOTES (0x6B) for friend notes (future use)
- Add CMSG_ADD_IGNORE (0x6C) and CMSG_DEL_IGNORE (0x6D) (future use)
Implementation:
- Add RandomRollPacket builder and RandomRollParser for roll data
- Add AddFriendPacket and DelFriendPacket builders
- Add FriendStatusParser to handle server friend status updates
- Add friendsCache map to store friend name-to-GUID mappings
- Add handleRandomRoll() and handleFriendStatus() packet handlers
- Comprehensive slash command parsing with multiple formats and aliases
2026-02-07 12:51:30 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
// 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-07 12:37:13 -08:00
|
|
|
/** CMSG_INSPECT packet builder */
|
|
|
|
|
class InspectPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t targetGuid);
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 00:59:40 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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;
|
2026-02-14 20:20:43 -08:00
|
|
|
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
|
2026-02-08 00:59:40 -08:00
|
|
|
|
|
|
|
|
bool isValid() const { return entry != 0 && !name.empty(); }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** SMSG_GAMEOBJECT_QUERY_RESPONSE parser */
|
|
|
|
|
class GameObjectQueryResponseParser {
|
|
|
|
|
public:
|
|
|
|
|
static bool parse(network::Packet& packet, GameObjectQueryResponseData& data);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-20 23:31:30 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 03:11:43 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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;
|
2026-02-18 03:46:03 -08:00
|
|
|
uint32_t itemClass = 0;
|
|
|
|
|
uint32_t subClass = 0;
|
2026-02-06 03:11:43 -08:00
|
|
|
uint32_t displayInfoId = 0;
|
|
|
|
|
uint32_t quality = 0;
|
|
|
|
|
uint32_t inventoryType = 0;
|
|
|
|
|
int32_t maxStack = 1;
|
|
|
|
|
uint32_t containerSlots = 0;
|
2026-02-18 03:46:03 -08:00
|
|
|
float damageMin = 0.0f;
|
|
|
|
|
float damageMax = 0.0f;
|
|
|
|
|
uint32_t delayMs = 0;
|
2026-02-06 03:11:43 -08:00
|
|
|
int32_t armor = 0;
|
|
|
|
|
int32_t stamina = 0;
|
|
|
|
|
int32_t strength = 0;
|
|
|
|
|
int32_t agility = 0;
|
|
|
|
|
int32_t intellect = 0;
|
|
|
|
|
int32_t spirit = 0;
|
2026-02-06 13:47:03 -08:00
|
|
|
uint32_t sellPrice = 0;
|
2026-02-06 03:11:43 -08:00
|
|
|
std::string subclassName;
|
2026-02-26 00:59:07 -08:00
|
|
|
// 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{};
|
2026-02-06 03:11:43 -08:00
|
|
|
bool valid = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** SMSG_ITEM_QUERY_SINGLE_RESPONSE parser */
|
|
|
|
|
class ItemQueryResponseParser {
|
|
|
|
|
public:
|
|
|
|
|
static bool parse(network::Packet& packet, ItemQueryResponseData& data);
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// Phase 2: Combat Core
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
2026-02-06 13:47:03 -08:00
|
|
|
/** 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:
|
2026-02-18 03:15:25 -08:00
|
|
|
// WotLK 3.3.5a format: PackedGUID + uint8 unk + float[3] + uint32 splineId + uint8 moveType + ...
|
2026-02-06 13:47:03 -08:00
|
|
|
static bool parse(network::Packet& packet, MonsterMoveData& data);
|
2026-02-18 03:15:25 -08:00
|
|
|
// 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);
|
2026-02-06 13:47:03 -08:00
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-05 12:07:58 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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);
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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_AURA packet builder */
|
|
|
|
|
class CancelAuraPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint32_t spellId);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-26 10:41:29 -08:00
|
|
|
/** CMSG_PET_ACTION packet builder */
|
|
|
|
|
class PetActionPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t petGuid, uint32_t action);
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
/** 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();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 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;
|
2026-02-18 04:06:14 -08:00
|
|
|
bool isQuestItem = false;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 18:34:45 -08:00
|
|
|
/** CMSG_USE_ITEM packet builder */
|
|
|
|
|
class UseItemPacket {
|
|
|
|
|
public:
|
2026-02-26 00:59:07 -08:00
|
|
|
static network::Packet build(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid, uint32_t spellId = 0);
|
2026-02-06 18:34:45 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** CMSG_AUTOEQUIP_ITEM packet builder */
|
|
|
|
|
class AutoEquipItemPacket {
|
|
|
|
|
public:
|
2026-02-06 19:13:38 -08:00
|
|
|
static network::Packet build(uint8_t srcBag, uint8_t srcSlot);
|
2026-02-06 18:34:45 -08:00
|
|
|
};
|
|
|
|
|
|
2026-02-12 14:55:27 -08:00
|
|
|
/** 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_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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 19:24:44 -08:00
|
|
|
/** CMSG_LOOT_MONEY packet builder (empty body) */
|
|
|
|
|
class LootMoneyPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build();
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-07 21:12:54 -08:00
|
|
|
/** CMSG_QUESTGIVER_HELLO packet builder */
|
|
|
|
|
class QuestgiverHelloPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t npcGuid);
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
/** CMSG_GOSSIP_SELECT_OPTION packet builder */
|
|
|
|
|
class GossipSelectOptionPacket {
|
|
|
|
|
public:
|
2026-02-06 11:45:35 -08:00
|
|
|
static network::Packet build(uint64_t npcGuid, uint32_t menuId, uint32_t optionId, const std::string& code = "");
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** SMSG_GOSSIP_MESSAGE parser */
|
|
|
|
|
class GossipMessageParser {
|
|
|
|
|
public:
|
|
|
|
|
static bool parse(network::Packet& packet, GossipMessageData& data);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 03:32:00 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 11:45:35 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 11:59:51 -08:00
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 21:50:15 -08:00
|
|
|
/** Reward item entry (shared by quest detail/offer windows) */
|
|
|
|
|
struct QuestRewardItem {
|
|
|
|
|
uint32_t itemId = 0;
|
|
|
|
|
uint32_t count = 0;
|
|
|
|
|
uint32_t displayInfoId = 0;
|
2026-02-19 03:12:57 -08:00
|
|
|
uint32_t choiceSlot = 0; // Original reward slot index from server payload
|
2026-02-06 21:50:15 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-19 01:12:14 -08:00
|
|
|
/** CMSG_QUESTGIVER_REQUEST_REWARD packet builder */
|
|
|
|
|
class QuestgiverRequestRewardPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t npcGuid, uint32_t questId);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 21:50:15 -08:00
|
|
|
/** CMSG_QUESTGIVER_CHOOSE_REWARD packet builder */
|
|
|
|
|
class QuestgiverChooseRewardPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t npcGuid, uint32_t questId, uint32_t rewardIndex);
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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:
|
2026-02-19 16:17:06 -08:00
|
|
|
static network::Packet build(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint32_t count);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** CMSG_SELL_ITEM packet builder */
|
|
|
|
|
class SellItemPacket {
|
|
|
|
|
public:
|
2026-02-06 19:50:22 -08:00
|
|
|
static network::Packet build(uint64_t vendorGuid, uint64_t itemGuid, uint32_t count);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
};
|
|
|
|
|
|
2026-02-19 16:17:06 -08:00
|
|
|
/** CMSG_BUYBACK_ITEM packet builder */
|
|
|
|
|
class BuybackItemPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t vendorGuid, uint32_t slot);
|
|
|
|
|
};
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
/** SMSG_LIST_INVENTORY parser */
|
|
|
|
|
class ListInventoryParser {
|
|
|
|
|
public:
|
|
|
|
|
static bool parse(network::Packet& packet, ListInventoryData& data);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 14:33:39 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// Trainer
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
struct TrainerSpell {
|
|
|
|
|
uint32_t spellId = 0;
|
2026-02-09 22:19:13 -08:00
|
|
|
uint8_t state = 0; // 0=unavailable(grey), 1=available(green), 2=known(green)
|
2026-02-08 14:33:39 -08:00
|
|
|
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:
|
|
|
|
|
static bool parse(network::Packet& packet, TrainerListData& data);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TrainerBuySpellPacket {
|
|
|
|
|
public:
|
2026-02-10 01:24:37 -08:00
|
|
|
static network::Packet build(uint64_t trainerGuid, uint32_t spellId);
|
2026-02-08 14:33:39 -08:00
|
|
|
};
|
|
|
|
|
|
Implement complete talent system with dual spec support
Network Protocol:
- Add SMSG_TALENTS_INFO (0x4C0) packet parsing for talent data
- Add CMSG_LEARN_TALENT (0x251) to request learning talents
- Add MSG_TALENT_WIPE_CONFIRM (0x2AB) opcode for spec switching
- Parse talent spec, unspent points, and learned talent ranks
DBC Parsing:
- Load Talent.dbc: talent grid positions, ranks, prerequisites, spell IDs
- Load TalentTab.dbc: talent tree definitions with correct field indices
- Fix localized string field handling (17 fields per string)
- Load Spell.dbc and SpellIcon.dbc for talent icons and tooltips
- Class mask filtering using bitwise operations (1 << (class - 1))
UI Implementation:
- Complete talent tree UI with tabbed interface for specs
- Display talent icons from spell data with proper tinting/borders
- Enhanced tooltips: spell name, rank, current/next descriptions, prereqs
- Visual states: green (maxed), yellow (partial), white (available), gray (locked)
- Tier unlock system (5 points per tier requirement)
- Rank overlay on icons with shadow text
- Click to learn talents with validation
Dual Spec Support:
- Store unspent points and learned talents per spec (0 and 1)
- Track active spec and display its talents
- Spec switching UI with buttons for Spec 1/Spec 2
- Handle both SMSG_TALENTS_INFO packets from server at login
- Display unspent points for both specs in header
- Independent talent trees for each specialization
2026-02-10 02:00:13 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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
|
2026-02-10 13:16:38 -08:00
|
|
|
std::vector<uint32_t> glyphs; // Glyph spell IDs
|
Implement complete talent system with dual spec support
Network Protocol:
- Add SMSG_TALENTS_INFO (0x4C0) packet parsing for talent data
- Add CMSG_LEARN_TALENT (0x251) to request learning talents
- Add MSG_TALENT_WIPE_CONFIRM (0x2AB) opcode for spec switching
- Parse talent spec, unspent points, and learned talent ranks
DBC Parsing:
- Load Talent.dbc: talent grid positions, ranks, prerequisites, spell IDs
- Load TalentTab.dbc: talent tree definitions with correct field indices
- Fix localized string field handling (17 fields per string)
- Load Spell.dbc and SpellIcon.dbc for talent icons and tooltips
- Class mask filtering using bitwise operations (1 << (class - 1))
UI Implementation:
- Complete talent tree UI with tabbed interface for specs
- Display talent icons from spell data with proper tinting/borders
- Enhanced tooltips: spell name, rank, current/next descriptions, prereqs
- Visual states: green (maxed), yellow (partial), white (available), gray (locked)
- Tier unlock system (5 points per tier requirement)
- Rank overlay on icons with shadow text
- Click to learn talents with validation
Dual Spec Support:
- Store unspent points and learned talents per spec (0 and 1)
- Track active spec and display its talents
- Spec switching UI with buttons for Spec 1/Spec 2
- Handle both SMSG_TALENTS_INFO packets from server at login
- Display unspent points for both specs in header
- Independent talent trees for each specialization
2026-02-10 02:00:13 -08:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-07 16:59:20 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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 {
|
2026-02-07 19:44:03 -08:00
|
|
|
if (nodeId == 0) return false;
|
|
|
|
|
uint32_t bitIndex = nodeId - 1;
|
|
|
|
|
uint32_t idx = bitIndex / 32;
|
|
|
|
|
uint32_t bit = bitIndex % 32;
|
2026-02-07 16:59:20 -08:00
|
|
|
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:
|
2026-02-08 03:05:38 -08:00
|
|
|
static network::Packet build(uint64_t npcGuid, uint32_t totalCost, const std::vector<uint32_t>& pathNodes);
|
2026-02-07 16:59:20 -08:00
|
|
|
};
|
|
|
|
|
|
2026-02-07 19:44:03 -08:00
|
|
|
/** CMSG_ACTIVATETAXI packet builder */
|
|
|
|
|
class ActivateTaxiPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t npcGuid, uint32_t srcNode, uint32_t destNode);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-20 02:50:59 -08:00
|
|
|
/** CMSG_GAMEOBJ_USE packet builder */
|
2026-02-07 19:44:03 -08:00
|
|
|
class GameObjectUsePacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t guid);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 17:27:20 -08:00
|
|
|
/** CMSG_REPOP_REQUEST packet builder */
|
|
|
|
|
class RepopRequestPacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build();
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-06 18:34:45 -08:00
|
|
|
/** CMSG_SPIRIT_HEALER_ACTIVATE packet builder */
|
|
|
|
|
class SpiritHealerActivatePacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t npcGuid);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-07 21:47:14 -08:00
|
|
|
/** CMSG_RESURRECT_RESPONSE packet builder */
|
|
|
|
|
class ResurrectResponsePacket {
|
|
|
|
|
public:
|
|
|
|
|
static network::Packet build(uint64_t casterGuid, bool accept);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-15 14:00:41 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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,
|
2026-02-25 14:11:09 -08:00
|
|
|
uint32_t money, uint32_t cod,
|
|
|
|
|
const std::vector<uint64_t>& itemGuids = {});
|
2026-02-15 14:00:41 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 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 itemIndex);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 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);
|
|
|
|
|
};
|
|
|
|
|
|
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
2026-02-16 21:11:18 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// 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:
|
|
|
|
|
static bool parse(network::Packet& packet, AuctionListResult& data);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** SMSG_AUCTION_COMMAND_RESULT parser */
|
|
|
|
|
class AuctionCommandResultParser {
|
|
|
|
|
public:
|
|
|
|
|
static bool parse(network::Packet& packet, AuctionCommandResult& data);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
} // namespace game
|
|
|
|
|
} // namespace wowee
|