mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Add multi-expansion support with data-driven protocol layer
Replace hardcoded WotLK protocol constants with a data-driven architecture supporting Classic 1.12.1, TBC 2.4.3, and WotLK 3.3.5a. Each expansion has JSON profiles for opcodes, update fields, and DBC layouts, plus C++ polymorphic packet parsers for binary format differences (movement flags, speed fields, transport data, spline format, char enum layout). Key components: - ExpansionRegistry: scans Data/expansions/*/expansion.json at startup - OpcodeTable: logical enum <-> wire values loaded from JSON - UpdateFieldTable: field indices loaded from JSON per expansion - DBCLayout: schema-driven DBC field lookups replacing magic numbers - PacketParsers: WotLK/TBC/Classic parsers with correct flag positions - Multi-manifest AssetManager: layered manifests with priority ordering - HDPackManager: overlay texture packs with expansion compatibility - Auth screen expansion picker replacing hardcoded version dropdown
This commit is contained in:
parent
aa16a687c2
commit
7092844b5e
51 changed files with 5258 additions and 887 deletions
|
|
@ -45,6 +45,10 @@ public:
|
|||
void authenticate(const std::string& username, const std::string& password);
|
||||
void authenticateWithHash(const std::string& username, const std::vector<uint8_t>& authHash);
|
||||
|
||||
// Set client version info (call before authenticate)
|
||||
void setClientInfo(const ClientInfo& info) { clientInfo = info; }
|
||||
const ClientInfo& getClientInfo() const { return clientInfo; }
|
||||
|
||||
// Realm list
|
||||
void requestRealmList();
|
||||
const std::vector<Realm>& getRealms() const { return realms; }
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ struct ClientInfo {
|
|||
uint8_t minorVersion = 3;
|
||||
uint8_t patchVersion = 5;
|
||||
uint16_t build = 12340; // 3.3.5a
|
||||
uint8_t protocolVersion = 8; // SRP auth protocol version
|
||||
std::string game = "WoW";
|
||||
std::string platform = "x86";
|
||||
std::string os = "Win";
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ namespace wowee {
|
|||
namespace rendering { class Renderer; }
|
||||
namespace ui { class UIManager; }
|
||||
namespace auth { class AuthHandler; }
|
||||
namespace game { class GameHandler; class World; }
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace game { class GameHandler; class World; class ExpansionRegistry; }
|
||||
namespace pipeline { class AssetManager; class DBCLayout; class HDPackManager; }
|
||||
namespace audio { enum class VoiceType; }
|
||||
|
||||
namespace core {
|
||||
|
|
@ -54,6 +54,9 @@ public:
|
|||
game::GameHandler* getGameHandler() { return gameHandler.get(); }
|
||||
game::World* getWorld() { return world.get(); }
|
||||
pipeline::AssetManager* getAssetManager() { return assetManager.get(); }
|
||||
game::ExpansionRegistry* getExpansionRegistry() { return expansionRegistry_.get(); }
|
||||
pipeline::DBCLayout* getDBCLayout() { return dbcLayout_.get(); }
|
||||
pipeline::HDPackManager* getHDPackManager() { return hdPackManager_.get(); }
|
||||
|
||||
// Singleton access
|
||||
static Application& getInstance() { return *instance; }
|
||||
|
|
@ -104,6 +107,9 @@ private:
|
|||
std::unique_ptr<game::GameHandler> gameHandler;
|
||||
std::unique_ptr<game::World> world;
|
||||
std::unique_ptr<pipeline::AssetManager> assetManager;
|
||||
std::unique_ptr<game::ExpansionRegistry> expansionRegistry_;
|
||||
std::unique_ptr<pipeline::DBCLayout> dbcLayout_;
|
||||
std::unique_ptr<pipeline::HDPackManager> hdPackManager_;
|
||||
|
||||
AppState state = AppState::AUTHENTICATION;
|
||||
bool running = false;
|
||||
|
|
|
|||
67
include/game/expansion_profile.hpp
Normal file
67
include/game/expansion_profile.hpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* Identifies a WoW expansion for protocol/asset selection.
|
||||
*/
|
||||
struct ExpansionProfile {
|
||||
std::string id; // "classic", "tbc", "wotlk", "cata"
|
||||
std::string name; // "Wrath of the Lich King"
|
||||
std::string shortName; // "WotLK"
|
||||
uint8_t majorVersion = 0;
|
||||
uint8_t minorVersion = 0;
|
||||
uint8_t patchVersion = 0;
|
||||
uint16_t build = 0;
|
||||
uint8_t protocolVersion = 0; // SRP auth protocol version byte
|
||||
std::string dataPath; // Absolute path to expansion data dir
|
||||
uint32_t maxLevel = 60;
|
||||
std::vector<uint32_t> races;
|
||||
std::vector<uint32_t> classes;
|
||||
|
||||
std::string versionString() const; // e.g. "3.3.5a"
|
||||
};
|
||||
|
||||
/**
|
||||
* Scans Data/expansions/ for available expansion profiles and manages the active selection.
|
||||
*/
|
||||
class ExpansionRegistry {
|
||||
public:
|
||||
/**
|
||||
* Scan dataRoot/expansions/ for expansion.json files.
|
||||
* @param dataRoot Path to Data/ directory (e.g. "./Data")
|
||||
* @return Number of profiles discovered
|
||||
*/
|
||||
size_t initialize(const std::string& dataRoot);
|
||||
|
||||
/** All discovered profiles. */
|
||||
const std::vector<ExpansionProfile>& getAllProfiles() const { return profiles_; }
|
||||
|
||||
/** Lookup by id (e.g. "wotlk"). Returns nullptr if not found. */
|
||||
const ExpansionProfile* getProfile(const std::string& id) const;
|
||||
|
||||
/** Set the active expansion. Returns false if id not found. */
|
||||
bool setActive(const std::string& id);
|
||||
|
||||
/** Get the active expansion profile. Never null after successful initialize(). */
|
||||
const ExpansionProfile* getActive() const;
|
||||
|
||||
/** Convenience: active expansion id. Empty if none. */
|
||||
const std::string& getActiveId() const { return activeId_; }
|
||||
|
||||
private:
|
||||
std::vector<ExpansionProfile> profiles_;
|
||||
std::string activeId_;
|
||||
|
||||
bool loadProfile(const std::string& jsonPath, const std::string& dirPath);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "game/world_packets.hpp"
|
||||
#include "game/character.hpp"
|
||||
#include "game/opcode_table.hpp"
|
||||
#include "game/update_field_table.hpp"
|
||||
#include "game/inventory.hpp"
|
||||
#include "game/spell_defines.hpp"
|
||||
#include "game/group_defines.hpp"
|
||||
|
|
@ -23,6 +25,7 @@ namespace wowee::game {
|
|||
class TransportManager;
|
||||
class WardenCrypto;
|
||||
class WardenModuleManager;
|
||||
class PacketParsers;
|
||||
}
|
||||
|
||||
namespace wowee {
|
||||
|
|
@ -109,6 +112,14 @@ public:
|
|||
GameHandler();
|
||||
~GameHandler();
|
||||
|
||||
/** Access the active opcode table (wire ↔ logical mapping). */
|
||||
const OpcodeTable& getOpcodeTable() const { return opcodeTable_; }
|
||||
OpcodeTable& getOpcodeTable() { return opcodeTable_; }
|
||||
const UpdateFieldTable& getUpdateFieldTable() const { return updateFieldTable_; }
|
||||
UpdateFieldTable& getUpdateFieldTable() { return updateFieldTable_; }
|
||||
PacketParsers* getPacketParsers() { return packetParsers_.get(); }
|
||||
void setPacketParsers(std::unique_ptr<PacketParsers> parsers);
|
||||
|
||||
/**
|
||||
* Connect to world server
|
||||
*
|
||||
|
|
@ -927,6 +938,15 @@ private:
|
|||
float localOrientation);
|
||||
void clearTransportAttachment(uint64_t childGuid);
|
||||
|
||||
// Opcode translation table (expansion-specific wire ↔ logical mapping)
|
||||
OpcodeTable opcodeTable_;
|
||||
|
||||
// Update field table (expansion-specific field index mapping)
|
||||
UpdateFieldTable updateFieldTable_;
|
||||
|
||||
// Packet parsers (expansion-specific binary format handling)
|
||||
std::unique_ptr<PacketParsers> packetParsers_;
|
||||
|
||||
// Network
|
||||
std::unique_ptr<network::WorldSocket> socket;
|
||||
|
||||
|
|
@ -1210,7 +1230,6 @@ private:
|
|||
std::unordered_map<uint32_t, uint32_t> spellToSkillLine_; // spellID -> skillLineID
|
||||
bool skillLineDbcLoaded_ = false;
|
||||
bool skillLineAbilityLoaded_ = false;
|
||||
static constexpr uint16_t PLAYER_EXPLORED_ZONES_START = 1041; // 3.3.5a UpdateFields
|
||||
static constexpr size_t PLAYER_EXPLORED_ZONES_COUNT = 128;
|
||||
std::vector<uint32_t> playerExploredZones_ =
|
||||
std::vector<uint32_t>(PLAYER_EXPLORED_ZONES_COUNT, 0u);
|
||||
|
|
|
|||
407
include/game/opcode_table.hpp
Normal file
407
include/game/opcode_table.hpp
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* Logical opcode identifiers (expansion-agnostic).
|
||||
*
|
||||
* These are compile-time enum values used in switch statements.
|
||||
* The actual wire values depend on the active expansion and are
|
||||
* loaded from JSON at runtime via OpcodeTable.
|
||||
*/
|
||||
enum class LogicalOpcode : uint16_t {
|
||||
// ---- Client to Server (Core) ----
|
||||
CMSG_PING,
|
||||
CMSG_AUTH_SESSION,
|
||||
CMSG_CHAR_CREATE,
|
||||
CMSG_CHAR_ENUM,
|
||||
CMSG_CHAR_DELETE,
|
||||
CMSG_PLAYER_LOGIN,
|
||||
|
||||
// ---- Movement ----
|
||||
CMSG_MOVE_START_FORWARD,
|
||||
CMSG_MOVE_START_BACKWARD,
|
||||
CMSG_MOVE_STOP,
|
||||
CMSG_MOVE_START_STRAFE_LEFT,
|
||||
CMSG_MOVE_START_STRAFE_RIGHT,
|
||||
CMSG_MOVE_STOP_STRAFE,
|
||||
CMSG_MOVE_JUMP,
|
||||
CMSG_MOVE_START_TURN_LEFT,
|
||||
CMSG_MOVE_START_TURN_RIGHT,
|
||||
CMSG_MOVE_STOP_TURN,
|
||||
CMSG_MOVE_SET_FACING,
|
||||
CMSG_MOVE_FALL_LAND,
|
||||
CMSG_MOVE_START_SWIM,
|
||||
CMSG_MOVE_STOP_SWIM,
|
||||
CMSG_MOVE_HEARTBEAT,
|
||||
|
||||
// ---- Server to Client (Core) ----
|
||||
SMSG_AUTH_CHALLENGE,
|
||||
SMSG_AUTH_RESPONSE,
|
||||
SMSG_CHAR_CREATE,
|
||||
SMSG_CHAR_ENUM,
|
||||
SMSG_CHAR_DELETE,
|
||||
SMSG_PONG,
|
||||
SMSG_LOGIN_VERIFY_WORLD,
|
||||
SMSG_LOGIN_SETTIMESPEED,
|
||||
SMSG_TUTORIAL_FLAGS,
|
||||
SMSG_WARDEN_DATA,
|
||||
CMSG_WARDEN_DATA,
|
||||
SMSG_ACCOUNT_DATA_TIMES,
|
||||
SMSG_CLIENTCACHE_VERSION,
|
||||
SMSG_FEATURE_SYSTEM_STATUS,
|
||||
SMSG_MOTD,
|
||||
|
||||
// ---- Entity/Object updates ----
|
||||
SMSG_UPDATE_OBJECT,
|
||||
SMSG_COMPRESSED_UPDATE_OBJECT,
|
||||
SMSG_MONSTER_MOVE_TRANSPORT,
|
||||
SMSG_DESTROY_OBJECT,
|
||||
|
||||
// ---- Chat ----
|
||||
CMSG_MESSAGECHAT,
|
||||
SMSG_MESSAGECHAT,
|
||||
|
||||
// ---- Server Info Commands ----
|
||||
CMSG_WHO,
|
||||
SMSG_WHO,
|
||||
CMSG_REQUEST_PLAYED_TIME,
|
||||
SMSG_PLAYED_TIME,
|
||||
CMSG_QUERY_TIME,
|
||||
SMSG_QUERY_TIME_RESPONSE,
|
||||
|
||||
// ---- Social Commands ----
|
||||
SMSG_FRIEND_STATUS,
|
||||
CMSG_ADD_FRIEND,
|
||||
CMSG_DEL_FRIEND,
|
||||
CMSG_SET_CONTACT_NOTES,
|
||||
CMSG_ADD_IGNORE,
|
||||
CMSG_DEL_IGNORE,
|
||||
|
||||
// ---- Logout Commands ----
|
||||
CMSG_PLAYER_LOGOUT,
|
||||
CMSG_LOGOUT_REQUEST,
|
||||
CMSG_LOGOUT_CANCEL,
|
||||
SMSG_LOGOUT_RESPONSE,
|
||||
SMSG_LOGOUT_COMPLETE,
|
||||
|
||||
// ---- Stand State ----
|
||||
CMSG_STAND_STATE_CHANGE,
|
||||
|
||||
// ---- Display Toggles ----
|
||||
CMSG_SHOWING_HELM,
|
||||
CMSG_SHOWING_CLOAK,
|
||||
|
||||
// ---- PvP ----
|
||||
CMSG_TOGGLE_PVP,
|
||||
|
||||
// ---- Guild ----
|
||||
CMSG_GUILD_INVITE,
|
||||
CMSG_GUILD_ACCEPT,
|
||||
CMSG_GUILD_DECLINE_INVITATION,
|
||||
CMSG_GUILD_INFO,
|
||||
CMSG_GUILD_GET_ROSTER,
|
||||
CMSG_GUILD_PROMOTE_MEMBER,
|
||||
CMSG_GUILD_DEMOTE_MEMBER,
|
||||
CMSG_GUILD_LEAVE,
|
||||
CMSG_GUILD_MOTD,
|
||||
SMSG_GUILD_INFO,
|
||||
SMSG_GUILD_ROSTER,
|
||||
|
||||
// ---- Ready Check ----
|
||||
MSG_RAID_READY_CHECK,
|
||||
MSG_RAID_READY_CHECK_CONFIRM,
|
||||
|
||||
// ---- Duel ----
|
||||
CMSG_DUEL_PROPOSED,
|
||||
CMSG_DUEL_ACCEPTED,
|
||||
CMSG_DUEL_CANCELLED,
|
||||
SMSG_DUEL_REQUESTED,
|
||||
|
||||
// ---- Trade ----
|
||||
CMSG_INITIATE_TRADE,
|
||||
|
||||
// ---- Random Roll ----
|
||||
MSG_RANDOM_ROLL,
|
||||
|
||||
// ---- Phase 1: Foundation (Targeting, Queries) ----
|
||||
CMSG_SET_SELECTION,
|
||||
CMSG_NAME_QUERY,
|
||||
SMSG_NAME_QUERY_RESPONSE,
|
||||
CMSG_CREATURE_QUERY,
|
||||
SMSG_CREATURE_QUERY_RESPONSE,
|
||||
CMSG_GAMEOBJECT_QUERY,
|
||||
SMSG_GAMEOBJECT_QUERY_RESPONSE,
|
||||
CMSG_SET_ACTIVE_MOVER,
|
||||
CMSG_BINDER_ACTIVATE,
|
||||
|
||||
// ---- XP ----
|
||||
SMSG_LOG_XPGAIN,
|
||||
|
||||
// ---- Creature Movement ----
|
||||
SMSG_MONSTER_MOVE,
|
||||
|
||||
// ---- Phase 2: Combat Core ----
|
||||
CMSG_ATTACKSWING,
|
||||
CMSG_ATTACKSTOP,
|
||||
SMSG_ATTACKSTART,
|
||||
SMSG_ATTACKSTOP,
|
||||
SMSG_ATTACKERSTATEUPDATE,
|
||||
SMSG_SPELLNONMELEEDAMAGELOG,
|
||||
SMSG_SPELLHEALLOG,
|
||||
SMSG_SPELLENERGIZELOG,
|
||||
SMSG_PERIODICAURALOG,
|
||||
SMSG_ENVIRONMENTALDAMAGELOG,
|
||||
|
||||
// ---- Phase 3: Spells, Action Bar, Auras ----
|
||||
CMSG_CAST_SPELL,
|
||||
CMSG_CANCEL_CAST,
|
||||
CMSG_CANCEL_AURA,
|
||||
SMSG_CAST_FAILED,
|
||||
SMSG_SPELL_START,
|
||||
SMSG_SPELL_GO,
|
||||
SMSG_SPELL_FAILURE,
|
||||
SMSG_SPELL_COOLDOWN,
|
||||
SMSG_COOLDOWN_EVENT,
|
||||
SMSG_UPDATE_AURA_DURATION,
|
||||
SMSG_INITIAL_SPELLS,
|
||||
SMSG_LEARNED_SPELL,
|
||||
SMSG_SUPERCEDED_SPELL,
|
||||
SMSG_REMOVED_SPELL,
|
||||
SMSG_SEND_UNLEARN_SPELLS,
|
||||
SMSG_SPELL_DELAYED,
|
||||
SMSG_AURA_UPDATE,
|
||||
SMSG_AURA_UPDATE_ALL,
|
||||
SMSG_SET_FLAT_SPELL_MODIFIER,
|
||||
SMSG_SET_PCT_SPELL_MODIFIER,
|
||||
|
||||
// ---- Talents ----
|
||||
SMSG_TALENTS_INFO,
|
||||
CMSG_LEARN_TALENT,
|
||||
MSG_TALENT_WIPE_CONFIRM,
|
||||
|
||||
// ---- Phase 4: Group/Party ----
|
||||
CMSG_GROUP_INVITE,
|
||||
SMSG_GROUP_INVITE,
|
||||
CMSG_GROUP_ACCEPT,
|
||||
CMSG_GROUP_DECLINE,
|
||||
SMSG_GROUP_DECLINE,
|
||||
CMSG_GROUP_UNINVITE_GUID,
|
||||
SMSG_GROUP_UNINVITE,
|
||||
CMSG_GROUP_SET_LEADER,
|
||||
SMSG_GROUP_SET_LEADER,
|
||||
CMSG_GROUP_DISBAND,
|
||||
SMSG_GROUP_LIST,
|
||||
SMSG_PARTY_COMMAND_RESULT,
|
||||
MSG_RAID_TARGET_UPDATE,
|
||||
CMSG_REQUEST_RAID_INFO,
|
||||
SMSG_RAID_INSTANCE_INFO,
|
||||
|
||||
// ---- Phase 5: Loot ----
|
||||
CMSG_AUTOSTORE_LOOT_ITEM,
|
||||
CMSG_LOOT,
|
||||
CMSG_LOOT_MONEY,
|
||||
CMSG_LOOT_RELEASE,
|
||||
SMSG_LOOT_RESPONSE,
|
||||
SMSG_LOOT_RELEASE_RESPONSE,
|
||||
SMSG_LOOT_REMOVED,
|
||||
SMSG_LOOT_MONEY_NOTIFY,
|
||||
SMSG_LOOT_CLEAR_MONEY,
|
||||
|
||||
// ---- Phase 5: Taxi / Flight Paths ----
|
||||
CMSG_ACTIVATETAXI,
|
||||
|
||||
// ---- Phase 5: NPC Gossip ----
|
||||
CMSG_GOSSIP_HELLO,
|
||||
CMSG_GOSSIP_SELECT_OPTION,
|
||||
SMSG_GOSSIP_MESSAGE,
|
||||
SMSG_GOSSIP_COMPLETE,
|
||||
SMSG_NPC_TEXT_UPDATE,
|
||||
|
||||
// ---- Phase 5: GameObject ----
|
||||
CMSG_GAMEOBJECT_USE,
|
||||
|
||||
// ---- Phase 5: Quests ----
|
||||
CMSG_QUESTGIVER_STATUS_QUERY,
|
||||
SMSG_QUESTGIVER_STATUS,
|
||||
SMSG_QUESTGIVER_STATUS_MULTIPLE,
|
||||
CMSG_QUESTGIVER_HELLO,
|
||||
CMSG_QUESTGIVER_QUERY_QUEST,
|
||||
SMSG_QUESTGIVER_QUEST_DETAILS,
|
||||
CMSG_QUESTGIVER_ACCEPT_QUEST,
|
||||
CMSG_QUESTGIVER_COMPLETE_QUEST,
|
||||
SMSG_QUESTGIVER_REQUEST_ITEMS,
|
||||
CMSG_QUESTGIVER_REQUEST_REWARD,
|
||||
SMSG_QUESTGIVER_OFFER_REWARD,
|
||||
CMSG_QUESTGIVER_CHOOSE_REWARD,
|
||||
SMSG_QUESTGIVER_QUEST_INVALID,
|
||||
SMSG_QUESTGIVER_QUEST_COMPLETE,
|
||||
CMSG_QUESTLOG_REMOVE_QUEST,
|
||||
SMSG_QUESTUPDATE_ADD_KILL,
|
||||
SMSG_QUESTUPDATE_COMPLETE,
|
||||
CMSG_QUEST_QUERY,
|
||||
SMSG_QUEST_QUERY_RESPONSE,
|
||||
SMSG_QUESTLOG_FULL,
|
||||
|
||||
// ---- Phase 5: Vendor ----
|
||||
CMSG_LIST_INVENTORY,
|
||||
SMSG_LIST_INVENTORY,
|
||||
CMSG_SELL_ITEM,
|
||||
SMSG_SELL_ITEM,
|
||||
CMSG_BUY_ITEM,
|
||||
SMSG_BUY_FAILED,
|
||||
|
||||
// ---- Trainer ----
|
||||
CMSG_TRAINER_LIST,
|
||||
SMSG_TRAINER_LIST,
|
||||
CMSG_TRAINER_BUY_SPELL,
|
||||
SMSG_TRAINER_BUY_FAILED,
|
||||
|
||||
// ---- Phase 5: Item/Equip ----
|
||||
CMSG_ITEM_QUERY_SINGLE,
|
||||
SMSG_ITEM_QUERY_SINGLE_RESPONSE,
|
||||
CMSG_USE_ITEM,
|
||||
CMSG_AUTOEQUIP_ITEM,
|
||||
CMSG_SWAP_ITEM,
|
||||
CMSG_SWAP_INV_ITEM,
|
||||
SMSG_INVENTORY_CHANGE_FAILURE,
|
||||
CMSG_INSPECT,
|
||||
SMSG_INSPECT_RESULTS,
|
||||
|
||||
// ---- Death/Respawn ----
|
||||
CMSG_REPOP_REQUEST,
|
||||
SMSG_RESURRECT_REQUEST,
|
||||
CMSG_RESURRECT_RESPONSE,
|
||||
CMSG_SPIRIT_HEALER_ACTIVATE,
|
||||
SMSG_SPIRIT_HEALER_CONFIRM,
|
||||
SMSG_RESURRECT_CANCEL,
|
||||
|
||||
// ---- Teleport / Transfer ----
|
||||
MSG_MOVE_TELEPORT_ACK,
|
||||
SMSG_TRANSFER_PENDING,
|
||||
SMSG_NEW_WORLD,
|
||||
MSG_MOVE_WORLDPORT_ACK,
|
||||
SMSG_TRANSFER_ABORTED,
|
||||
|
||||
// ---- Speed Changes ----
|
||||
SMSG_FORCE_RUN_SPEED_CHANGE,
|
||||
CMSG_FORCE_RUN_SPEED_CHANGE_ACK,
|
||||
|
||||
// ---- Mount ----
|
||||
CMSG_CANCEL_MOUNT_AURA,
|
||||
|
||||
// ---- Taxi / Flight Paths ----
|
||||
SMSG_SHOWTAXINODES,
|
||||
SMSG_ACTIVATETAXIREPLY,
|
||||
SMSG_ACTIVATETAXIREPLY_ALT,
|
||||
SMSG_NEW_TAXI_PATH,
|
||||
CMSG_ACTIVATETAXIEXPRESS,
|
||||
|
||||
// ---- Battleground ----
|
||||
SMSG_BATTLEFIELD_PORT_DENIED,
|
||||
SMSG_REMOVED_FROM_PVP_QUEUE,
|
||||
SMSG_TRAINER_BUY_SUCCEEDED,
|
||||
SMSG_BINDPOINTUPDATE,
|
||||
CMSG_BATTLEFIELD_LIST,
|
||||
SMSG_BATTLEFIELD_LIST,
|
||||
CMSG_BATTLEFIELD_JOIN,
|
||||
CMSG_BATTLEFIELD_STATUS,
|
||||
SMSG_BATTLEFIELD_STATUS,
|
||||
CMSG_BATTLEFIELD_PORT,
|
||||
CMSG_BATTLEMASTER_HELLO,
|
||||
MSG_PVP_LOG_DATA,
|
||||
CMSG_LEAVE_BATTLEFIELD,
|
||||
SMSG_GROUP_JOINED_BATTLEGROUND,
|
||||
MSG_BATTLEGROUND_PLAYER_POSITIONS,
|
||||
SMSG_BATTLEGROUND_PLAYER_JOINED,
|
||||
SMSG_BATTLEGROUND_PLAYER_LEFT,
|
||||
CMSG_BATTLEMASTER_JOIN,
|
||||
SMSG_JOINED_BATTLEGROUND_QUEUE,
|
||||
|
||||
// ---- Arena Team ----
|
||||
CMSG_ARENA_TEAM_CREATE,
|
||||
SMSG_ARENA_TEAM_COMMAND_RESULT,
|
||||
CMSG_ARENA_TEAM_QUERY,
|
||||
SMSG_ARENA_TEAM_QUERY_RESPONSE,
|
||||
CMSG_ARENA_TEAM_ROSTER,
|
||||
SMSG_ARENA_TEAM_ROSTER,
|
||||
CMSG_ARENA_TEAM_INVITE,
|
||||
SMSG_ARENA_TEAM_INVITE,
|
||||
CMSG_ARENA_TEAM_ACCEPT,
|
||||
CMSG_ARENA_TEAM_DECLINE,
|
||||
CMSG_ARENA_TEAM_LEAVE,
|
||||
CMSG_ARENA_TEAM_REMOVE,
|
||||
CMSG_ARENA_TEAM_DISBAND,
|
||||
CMSG_ARENA_TEAM_LEADER,
|
||||
SMSG_ARENA_TEAM_EVENT,
|
||||
CMSG_BATTLEMASTER_JOIN_ARENA,
|
||||
SMSG_ARENA_TEAM_STATS,
|
||||
SMSG_ARENA_ERROR,
|
||||
MSG_INSPECT_ARENA_TEAMS,
|
||||
|
||||
// Sentinel
|
||||
COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps LogicalOpcode ↔ expansion-specific wire values.
|
||||
*
|
||||
* Loaded from JSON (e.g. Data/expansions/wotlk/opcodes.json).
|
||||
* Used for sending packets (toWire) and receiving them (fromWire).
|
||||
*/
|
||||
class OpcodeTable {
|
||||
public:
|
||||
/**
|
||||
* Load opcode mappings from a JSON file.
|
||||
* Format: { "CMSG_PING": "0x1DC", "SMSG_AUTH_CHALLENGE": "0x1EC", ... }
|
||||
*/
|
||||
bool loadFromJson(const std::string& path);
|
||||
|
||||
/** Load built-in WotLK defaults (hardcoded fallback). */
|
||||
void loadWotlkDefaults();
|
||||
|
||||
/** LogicalOpcode → wire value for sending packets. Returns 0xFFFF if unknown. */
|
||||
uint16_t toWire(LogicalOpcode op) const;
|
||||
|
||||
/** Wire value → LogicalOpcode for receiving packets. Returns nullopt if unknown. */
|
||||
std::optional<LogicalOpcode> fromWire(uint16_t wireValue) const;
|
||||
|
||||
/** Check if a logical opcode has a wire mapping. */
|
||||
bool hasOpcode(LogicalOpcode op) const;
|
||||
|
||||
/** Number of mapped opcodes. */
|
||||
size_t size() const { return logicalToWire_.size(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<uint16_t, uint16_t> logicalToWire_; // LogicalOpcode → wire
|
||||
std::unordered_map<uint16_t, uint16_t> wireToLogical_; // wire → LogicalOpcode
|
||||
|
||||
static std::optional<LogicalOpcode> nameToLogical(const std::string& name);
|
||||
static const char* logicalToName(LogicalOpcode op);
|
||||
};
|
||||
|
||||
/**
|
||||
* Global active opcode table pointer (set by GameHandler at startup).
|
||||
* Used by world_packets.cpp and other code that needs to send packets
|
||||
* without direct access to a GameHandler instance.
|
||||
*/
|
||||
void setActiveOpcodeTable(const OpcodeTable* table);
|
||||
const OpcodeTable* getActiveOpcodeTable();
|
||||
|
||||
/**
|
||||
* Get the wire value for a logical opcode using the active table.
|
||||
* Convenience helper for packet construction code.
|
||||
*/
|
||||
inline uint16_t wireOpcode(LogicalOpcode op) {
|
||||
const auto* table = getActiveOpcodeTable();
|
||||
return table ? table->toWire(op) : 0xFFFF;
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
@ -1,344 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "game/opcode_table.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
// World of Warcraft 3.3.5a opcodes
|
||||
// Values derived from community reverse-engineering efforts
|
||||
// Reference: https://wowdev.wiki/World_Packet
|
||||
enum class Opcode : uint16_t {
|
||||
// ---- Client to Server (Core) ----
|
||||
CMSG_PING = 0x1DC,
|
||||
CMSG_AUTH_SESSION = 0x1ED,
|
||||
CMSG_CHAR_CREATE = 0x036,
|
||||
CMSG_CHAR_ENUM = 0x037,
|
||||
CMSG_CHAR_DELETE = 0x038,
|
||||
CMSG_PLAYER_LOGIN = 0x03D,
|
||||
|
||||
// ---- Movement ----
|
||||
CMSG_MOVE_START_FORWARD = 0x0B5,
|
||||
CMSG_MOVE_START_BACKWARD = 0x0B6,
|
||||
CMSG_MOVE_STOP = 0x0B7,
|
||||
CMSG_MOVE_START_STRAFE_LEFT = 0x0B8,
|
||||
CMSG_MOVE_START_STRAFE_RIGHT = 0x0B9,
|
||||
CMSG_MOVE_STOP_STRAFE = 0x0BA,
|
||||
CMSG_MOVE_JUMP = 0x0BB,
|
||||
CMSG_MOVE_START_TURN_LEFT = 0x0BC,
|
||||
CMSG_MOVE_START_TURN_RIGHT = 0x0BD,
|
||||
CMSG_MOVE_STOP_TURN = 0x0BE,
|
||||
CMSG_MOVE_SET_FACING = 0x0DA,
|
||||
CMSG_MOVE_FALL_LAND = 0x0C9,
|
||||
CMSG_MOVE_START_SWIM = 0x0CA,
|
||||
CMSG_MOVE_STOP_SWIM = 0x0CB,
|
||||
CMSG_MOVE_HEARTBEAT = 0x0EE,
|
||||
|
||||
// ---- Server to Client (Core) ----
|
||||
SMSG_AUTH_CHALLENGE = 0x1EC,
|
||||
SMSG_AUTH_RESPONSE = 0x1EE,
|
||||
SMSG_CHAR_CREATE = 0x03A,
|
||||
SMSG_CHAR_ENUM = 0x03B,
|
||||
SMSG_CHAR_DELETE = 0x03C,
|
||||
SMSG_PONG = 0x1DD,
|
||||
SMSG_LOGIN_VERIFY_WORLD = 0x236,
|
||||
SMSG_LOGIN_SETTIMESPEED = 0x042,
|
||||
SMSG_TUTORIAL_FLAGS = 0x0FD,
|
||||
SMSG_WARDEN_DATA = 0x2E6,
|
||||
CMSG_WARDEN_DATA = 0x2E7,
|
||||
SMSG_ACCOUNT_DATA_TIMES = 0x209,
|
||||
SMSG_CLIENTCACHE_VERSION = 0x4AB,
|
||||
SMSG_FEATURE_SYSTEM_STATUS = 0x3ED,
|
||||
SMSG_MOTD = 0x33D,
|
||||
|
||||
// ---- Entity/Object updates ----
|
||||
SMSG_UPDATE_OBJECT = 0x0A9,
|
||||
SMSG_COMPRESSED_UPDATE_OBJECT = 0x1F6,
|
||||
SMSG_MONSTER_MOVE_TRANSPORT = 0x2AE,
|
||||
SMSG_DESTROY_OBJECT = 0x0AA,
|
||||
|
||||
// ---- Chat ----
|
||||
CMSG_MESSAGECHAT = 0x095,
|
||||
SMSG_MESSAGECHAT = 0x096,
|
||||
|
||||
// ---- Server Info Commands ----
|
||||
CMSG_WHO = 0x062,
|
||||
SMSG_WHO = 0x063,
|
||||
CMSG_REQUEST_PLAYED_TIME = 0x1CC,
|
||||
SMSG_PLAYED_TIME = 0x1CD,
|
||||
CMSG_QUERY_TIME = 0x1CE,
|
||||
SMSG_QUERY_TIME_RESPONSE = 0x1CF,
|
||||
|
||||
// ---- Social Commands ----
|
||||
SMSG_FRIEND_STATUS = 0x068,
|
||||
CMSG_ADD_FRIEND = 0x069,
|
||||
CMSG_DEL_FRIEND = 0x06A,
|
||||
CMSG_SET_CONTACT_NOTES = 0x06B,
|
||||
CMSG_ADD_IGNORE = 0x06C,
|
||||
CMSG_DEL_IGNORE = 0x06D,
|
||||
|
||||
// ---- Logout Commands ----
|
||||
CMSG_PLAYER_LOGOUT = 0x04A,
|
||||
CMSG_LOGOUT_REQUEST = 0x04B,
|
||||
CMSG_LOGOUT_CANCEL = 0x04E,
|
||||
SMSG_LOGOUT_RESPONSE = 0x04C,
|
||||
SMSG_LOGOUT_COMPLETE = 0x04D,
|
||||
|
||||
// ---- Stand State ----
|
||||
CMSG_STAND_STATE_CHANGE = 0x101,
|
||||
|
||||
// ---- Display Toggles ----
|
||||
CMSG_SHOWING_HELM = 0x2B9,
|
||||
CMSG_SHOWING_CLOAK = 0x2BA,
|
||||
|
||||
// ---- PvP ----
|
||||
CMSG_TOGGLE_PVP = 0x253,
|
||||
|
||||
// ---- Guild ----
|
||||
CMSG_GUILD_INVITE = 0x082,
|
||||
CMSG_GUILD_ACCEPT = 0x084,
|
||||
CMSG_GUILD_DECLINE_INVITATION = 0x085,
|
||||
CMSG_GUILD_INFO = 0x087,
|
||||
CMSG_GUILD_GET_ROSTER = 0x089,
|
||||
CMSG_GUILD_PROMOTE_MEMBER = 0x08B,
|
||||
CMSG_GUILD_DEMOTE_MEMBER = 0x08C,
|
||||
CMSG_GUILD_LEAVE = 0x08D,
|
||||
CMSG_GUILD_MOTD = 0x091,
|
||||
SMSG_GUILD_INFO = 0x088,
|
||||
SMSG_GUILD_ROSTER = 0x08A,
|
||||
|
||||
// ---- Ready Check ----
|
||||
MSG_RAID_READY_CHECK = 0x322,
|
||||
MSG_RAID_READY_CHECK_CONFIRM = 0x3AE,
|
||||
|
||||
// ---- Duel ----
|
||||
CMSG_DUEL_PROPOSED = 0x166,
|
||||
CMSG_DUEL_ACCEPTED = 0x16C,
|
||||
CMSG_DUEL_CANCELLED = 0x16D,
|
||||
SMSG_DUEL_REQUESTED = 0x167,
|
||||
|
||||
// ---- Trade ----
|
||||
CMSG_INITIATE_TRADE = 0x116,
|
||||
|
||||
// ---- Random Roll ----
|
||||
MSG_RANDOM_ROLL = 0x1FB,
|
||||
|
||||
// ---- Phase 1: Foundation (Targeting, Queries) ----
|
||||
CMSG_SET_SELECTION = 0x13D,
|
||||
CMSG_NAME_QUERY = 0x050,
|
||||
SMSG_NAME_QUERY_RESPONSE = 0x051,
|
||||
CMSG_CREATURE_QUERY = 0x060,
|
||||
SMSG_CREATURE_QUERY_RESPONSE = 0x061,
|
||||
CMSG_GAMEOBJECT_QUERY = 0x05E,
|
||||
SMSG_GAMEOBJECT_QUERY_RESPONSE = 0x05F,
|
||||
CMSG_SET_ACTIVE_MOVER = 0x26A,
|
||||
CMSG_BINDER_ACTIVATE = 0x1B5,
|
||||
|
||||
// ---- XP ----
|
||||
SMSG_LOG_XPGAIN = 0x1D0,
|
||||
|
||||
// ---- Creature Movement ----
|
||||
SMSG_MONSTER_MOVE = 0x0DD,
|
||||
|
||||
// ---- Phase 2: Combat Core ----
|
||||
CMSG_ATTACKSWING = 0x141,
|
||||
CMSG_ATTACKSTOP = 0x142,
|
||||
SMSG_ATTACKSTART = 0x143,
|
||||
SMSG_ATTACKSTOP = 0x144,
|
||||
SMSG_ATTACKERSTATEUPDATE = 0x14A,
|
||||
SMSG_SPELLNONMELEEDAMAGELOG = 0x250,
|
||||
SMSG_SPELLHEALLOG = 0x150,
|
||||
SMSG_SPELLENERGIZELOG = 0x25B,
|
||||
SMSG_PERIODICAURALOG = 0x24E,
|
||||
SMSG_ENVIRONMENTALDAMAGELOG = 0x1FC,
|
||||
|
||||
// ---- Phase 3: Spells, Action Bar, Auras ----
|
||||
CMSG_CAST_SPELL = 0x12E,
|
||||
CMSG_CANCEL_CAST = 0x12F,
|
||||
CMSG_CANCEL_AURA = 0x033,
|
||||
SMSG_CAST_FAILED = 0x130,
|
||||
SMSG_SPELL_START = 0x131,
|
||||
SMSG_SPELL_GO = 0x132,
|
||||
SMSG_SPELL_FAILURE = 0x133,
|
||||
SMSG_SPELL_COOLDOWN = 0x134,
|
||||
SMSG_COOLDOWN_EVENT = 0x135,
|
||||
SMSG_UPDATE_AURA_DURATION = 0x137,
|
||||
SMSG_INITIAL_SPELLS = 0x12A,
|
||||
SMSG_LEARNED_SPELL = 0x12B,
|
||||
SMSG_SUPERCEDED_SPELL = 0x12C,
|
||||
SMSG_REMOVED_SPELL = 0x203,
|
||||
SMSG_SEND_UNLEARN_SPELLS = 0x41F,
|
||||
SMSG_SPELL_DELAYED = 0x1E2,
|
||||
SMSG_AURA_UPDATE = 0x3FA,
|
||||
SMSG_AURA_UPDATE_ALL = 0x495,
|
||||
SMSG_SET_FLAT_SPELL_MODIFIER = 0x266,
|
||||
SMSG_SET_PCT_SPELL_MODIFIER = 0x267,
|
||||
|
||||
// ---- Talents ----
|
||||
SMSG_TALENTS_INFO = 0x4C0,
|
||||
CMSG_LEARN_TALENT = 0x251,
|
||||
MSG_TALENT_WIPE_CONFIRM = 0x2AB,
|
||||
|
||||
// ---- Phase 4: Group/Party ----
|
||||
CMSG_GROUP_INVITE = 0x06E,
|
||||
SMSG_GROUP_INVITE = 0x06F,
|
||||
CMSG_GROUP_ACCEPT = 0x072,
|
||||
CMSG_GROUP_DECLINE = 0x073,
|
||||
SMSG_GROUP_DECLINE = 0x074,
|
||||
CMSG_GROUP_UNINVITE_GUID = 0x076,
|
||||
SMSG_GROUP_UNINVITE = 0x077,
|
||||
CMSG_GROUP_SET_LEADER = 0x078,
|
||||
SMSG_GROUP_SET_LEADER = 0x079,
|
||||
CMSG_GROUP_DISBAND = 0x07B,
|
||||
SMSG_GROUP_LIST = 0x07D,
|
||||
SMSG_PARTY_COMMAND_RESULT = 0x07E,
|
||||
MSG_RAID_TARGET_UPDATE = 0x321,
|
||||
CMSG_REQUEST_RAID_INFO = 0x2CD,
|
||||
SMSG_RAID_INSTANCE_INFO = 0x2CC,
|
||||
|
||||
// ---- Phase 5: Loot ----
|
||||
CMSG_AUTOSTORE_LOOT_ITEM = 0x108,
|
||||
CMSG_LOOT = 0x15D,
|
||||
CMSG_LOOT_MONEY = 0x15E,
|
||||
CMSG_LOOT_RELEASE = 0x15F,
|
||||
SMSG_LOOT_RESPONSE = 0x160,
|
||||
SMSG_LOOT_RELEASE_RESPONSE = 0x161,
|
||||
SMSG_LOOT_REMOVED = 0x162,
|
||||
SMSG_LOOT_MONEY_NOTIFY = 0x163,
|
||||
SMSG_LOOT_CLEAR_MONEY = 0x165,
|
||||
|
||||
// ---- Phase 5: Taxi / Flight Paths ----
|
||||
CMSG_ACTIVATETAXI = 0x19D,
|
||||
|
||||
// ---- Phase 5: NPC Gossip ----
|
||||
CMSG_GOSSIP_HELLO = 0x17B,
|
||||
CMSG_GOSSIP_SELECT_OPTION = 0x17C,
|
||||
SMSG_GOSSIP_MESSAGE = 0x17D,
|
||||
SMSG_GOSSIP_COMPLETE = 0x17E,
|
||||
SMSG_NPC_TEXT_UPDATE = 0x180,
|
||||
|
||||
// ---- Phase 5: GameObject ----
|
||||
CMSG_GAMEOBJECT_USE = 0x01B,
|
||||
|
||||
// ---- Phase 5: Quests ----
|
||||
CMSG_QUESTGIVER_STATUS_QUERY = 0x182,
|
||||
SMSG_QUESTGIVER_STATUS = 0x183,
|
||||
SMSG_QUESTGIVER_STATUS_MULTIPLE = 0x198,
|
||||
CMSG_QUESTGIVER_HELLO = 0x184,
|
||||
CMSG_QUESTGIVER_QUERY_QUEST = 0x186,
|
||||
SMSG_QUESTGIVER_QUEST_DETAILS = 0x188,
|
||||
CMSG_QUESTGIVER_ACCEPT_QUEST = 0x189,
|
||||
CMSG_QUESTGIVER_COMPLETE_QUEST = 0x18A,
|
||||
SMSG_QUESTGIVER_REQUEST_ITEMS = 0x18B,
|
||||
CMSG_QUESTGIVER_REQUEST_REWARD = 0x18C,
|
||||
SMSG_QUESTGIVER_OFFER_REWARD = 0x18D,
|
||||
CMSG_QUESTGIVER_CHOOSE_REWARD = 0x18E,
|
||||
SMSG_QUESTGIVER_QUEST_INVALID = 0x18F,
|
||||
SMSG_QUESTGIVER_QUEST_COMPLETE = 0x191,
|
||||
CMSG_QUESTLOG_REMOVE_QUEST = 0x194,
|
||||
SMSG_QUESTUPDATE_ADD_KILL = 0x196, // Quest kill count update
|
||||
SMSG_QUESTUPDATE_COMPLETE = 0x195, // Quest objectives completed
|
||||
CMSG_QUEST_QUERY = 0x05C, // Client requests quest data
|
||||
SMSG_QUEST_QUERY_RESPONSE = 0x05D, // Server sends quest data
|
||||
SMSG_QUESTLOG_FULL = 0x1A3, // Full quest log on login
|
||||
|
||||
// ---- Phase 5: Vendor ----
|
||||
CMSG_LIST_INVENTORY = 0x19E,
|
||||
SMSG_LIST_INVENTORY = 0x19F,
|
||||
CMSG_SELL_ITEM = 0x1A0,
|
||||
SMSG_SELL_ITEM = 0x1A1,
|
||||
CMSG_BUY_ITEM = 0x1A2,
|
||||
SMSG_BUY_FAILED = 0x1A5,
|
||||
|
||||
// ---- Trainer ----
|
||||
CMSG_TRAINER_LIST = 0x01B0,
|
||||
SMSG_TRAINER_LIST = 0x01B1,
|
||||
CMSG_TRAINER_BUY_SPELL = 0x01B2,
|
||||
SMSG_TRAINER_BUY_FAILED = 0x01B4,
|
||||
|
||||
// ---- Phase 5: Item/Equip ----
|
||||
CMSG_ITEM_QUERY_SINGLE = 0x056,
|
||||
SMSG_ITEM_QUERY_SINGLE_RESPONSE = 0x058,
|
||||
CMSG_USE_ITEM = 0x00AB,
|
||||
CMSG_AUTOEQUIP_ITEM = 0x10A,
|
||||
CMSG_SWAP_ITEM = 0x10C,
|
||||
CMSG_SWAP_INV_ITEM = 0x10D,
|
||||
SMSG_INVENTORY_CHANGE_FAILURE = 0x112,
|
||||
CMSG_INSPECT = 0x114,
|
||||
SMSG_INSPECT_RESULTS = 0x115,
|
||||
|
||||
// ---- Death/Respawn ----
|
||||
CMSG_REPOP_REQUEST = 0x015A,
|
||||
SMSG_RESURRECT_REQUEST = 0x015B,
|
||||
CMSG_RESURRECT_RESPONSE = 0x015C,
|
||||
CMSG_SPIRIT_HEALER_ACTIVATE = 0x021C,
|
||||
SMSG_SPIRIT_HEALER_CONFIRM = 0x0222,
|
||||
SMSG_RESURRECT_CANCEL = 0x0390,
|
||||
|
||||
// ---- Teleport / Transfer ----
|
||||
MSG_MOVE_TELEPORT_ACK = 0x0C7,
|
||||
SMSG_TRANSFER_PENDING = 0x003F,
|
||||
SMSG_NEW_WORLD = 0x003E,
|
||||
MSG_MOVE_WORLDPORT_ACK = 0x00DC,
|
||||
SMSG_TRANSFER_ABORTED = 0x0040,
|
||||
|
||||
// ---- Speed Changes ----
|
||||
SMSG_FORCE_RUN_SPEED_CHANGE = 0x00E2,
|
||||
CMSG_FORCE_RUN_SPEED_CHANGE_ACK = 0x00E3,
|
||||
|
||||
// ---- Mount ----
|
||||
CMSG_CANCEL_MOUNT_AURA = 0x0375,
|
||||
|
||||
// ---- Taxi / Flight Paths ----
|
||||
SMSG_SHOWTAXINODES = 0x01A9,
|
||||
SMSG_ACTIVATETAXIREPLY = 0x01AE,
|
||||
// Some cores send activate taxi reply on 0x029D (observed in logs)
|
||||
SMSG_ACTIVATETAXIREPLY_ALT = 0x029D,
|
||||
SMSG_NEW_TAXI_PATH = 0x01AF,
|
||||
CMSG_ACTIVATETAXIEXPRESS = 0x0312,
|
||||
|
||||
// ---- Battleground ----
|
||||
SMSG_BATTLEFIELD_PORT_DENIED = 0x014B,
|
||||
SMSG_REMOVED_FROM_PVP_QUEUE = 0x0170,
|
||||
SMSG_TRAINER_BUY_SUCCEEDED = 0x01B3,
|
||||
SMSG_BINDPOINTUPDATE = 0x0155,
|
||||
CMSG_BATTLEFIELD_LIST = 0x023C,
|
||||
SMSG_BATTLEFIELD_LIST = 0x023D,
|
||||
CMSG_BATTLEFIELD_JOIN = 0x023E,
|
||||
CMSG_BATTLEFIELD_STATUS = 0x02D3,
|
||||
SMSG_BATTLEFIELD_STATUS = 0x02D4,
|
||||
CMSG_BATTLEFIELD_PORT = 0x02D5,
|
||||
CMSG_BATTLEMASTER_HELLO = 0x02D7,
|
||||
MSG_PVP_LOG_DATA = 0x02E0,
|
||||
CMSG_LEAVE_BATTLEFIELD = 0x02E1,
|
||||
SMSG_GROUP_JOINED_BATTLEGROUND = 0x02E8,
|
||||
MSG_BATTLEGROUND_PLAYER_POSITIONS = 0x02E9,
|
||||
SMSG_BATTLEGROUND_PLAYER_JOINED = 0x02EC,
|
||||
SMSG_BATTLEGROUND_PLAYER_LEFT = 0x02ED,
|
||||
CMSG_BATTLEMASTER_JOIN = 0x02EE,
|
||||
SMSG_JOINED_BATTLEGROUND_QUEUE = 0x038A,
|
||||
|
||||
// ---- Arena Team ----
|
||||
CMSG_ARENA_TEAM_CREATE = 0x0348,
|
||||
SMSG_ARENA_TEAM_COMMAND_RESULT = 0x0349,
|
||||
CMSG_ARENA_TEAM_QUERY = 0x034B,
|
||||
SMSG_ARENA_TEAM_QUERY_RESPONSE = 0x034C,
|
||||
CMSG_ARENA_TEAM_ROSTER = 0x034D,
|
||||
SMSG_ARENA_TEAM_ROSTER = 0x034E,
|
||||
CMSG_ARENA_TEAM_INVITE = 0x034F,
|
||||
SMSG_ARENA_TEAM_INVITE = 0x0350,
|
||||
CMSG_ARENA_TEAM_ACCEPT = 0x0351,
|
||||
CMSG_ARENA_TEAM_DECLINE = 0x0352,
|
||||
CMSG_ARENA_TEAM_LEAVE = 0x0353,
|
||||
CMSG_ARENA_TEAM_REMOVE = 0x0354,
|
||||
CMSG_ARENA_TEAM_DISBAND = 0x0355,
|
||||
CMSG_ARENA_TEAM_LEADER = 0x0356,
|
||||
SMSG_ARENA_TEAM_EVENT = 0x0357,
|
||||
CMSG_BATTLEMASTER_JOIN_ARENA = 0x0358,
|
||||
SMSG_ARENA_TEAM_STATS = 0x035B,
|
||||
SMSG_ARENA_ERROR = 0x0376,
|
||||
MSG_INSPECT_ARENA_TEAMS = 0x0377,
|
||||
};
|
||||
// Backwards-compatibility alias: existing code uses Opcode::X which now maps
|
||||
// to LogicalOpcode::X (the expansion-agnostic logical enum).
|
||||
// Wire values are resolved at runtime via OpcodeTable.
|
||||
using Opcode = LogicalOpcode;
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
|
|||
178
include/game/packet_parsers.hpp
Normal file
178
include/game/packet_parsers.hpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#pragma once
|
||||
|
||||
#include "game/world_packets.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* PacketParsers - Polymorphic interface for expansion-specific packet parsing.
|
||||
*
|
||||
* Binary packet formats differ significantly between WoW expansions
|
||||
* (movement flags, update fields, character enum layout, etc.).
|
||||
* Each expansion implements this interface with its specific parsing logic.
|
||||
*
|
||||
* The base PacketParsers delegates to the existing static parser classes
|
||||
* in world_packets.hpp. Expansion subclasses override the methods that
|
||||
* differ from WotLK.
|
||||
*/
|
||||
class PacketParsers {
|
||||
public:
|
||||
virtual ~PacketParsers() = default;
|
||||
|
||||
// --- Movement ---
|
||||
|
||||
/** Parse movement block from SMSG_UPDATE_OBJECT */
|
||||
virtual bool parseMovementBlock(network::Packet& packet, UpdateBlock& block) {
|
||||
return UpdateObjectParser::parseMovementBlock(packet, block);
|
||||
}
|
||||
|
||||
/** Write movement payload for CMSG_MOVE_* packets */
|
||||
virtual void writeMovementPayload(network::Packet& packet, const MovementInfo& info) {
|
||||
MovementPacket::writeMovementPayload(packet, info);
|
||||
}
|
||||
|
||||
/** Build a complete movement packet with packed GUID + payload */
|
||||
virtual network::Packet buildMovementPacket(LogicalOpcode opcode,
|
||||
const MovementInfo& info,
|
||||
uint64_t playerGuid = 0) {
|
||||
return MovementPacket::build(opcode, info, playerGuid);
|
||||
}
|
||||
|
||||
// --- Character Enumeration ---
|
||||
|
||||
/** Parse SMSG_CHAR_ENUM */
|
||||
virtual bool parseCharEnum(network::Packet& packet, CharEnumResponse& response) {
|
||||
return CharEnumParser::parse(packet, response);
|
||||
}
|
||||
|
||||
// --- Update Object ---
|
||||
|
||||
/** Parse a full SMSG_UPDATE_OBJECT packet */
|
||||
virtual bool parseUpdateObject(network::Packet& packet, UpdateObjectData& data) {
|
||||
return UpdateObjectParser::parse(packet, data);
|
||||
}
|
||||
|
||||
/** Parse update fields block (value mask + field values) */
|
||||
virtual bool parseUpdateFields(network::Packet& packet, UpdateBlock& block) {
|
||||
return UpdateObjectParser::parseUpdateFields(packet, block);
|
||||
}
|
||||
|
||||
// --- Monster Movement ---
|
||||
|
||||
/** Parse SMSG_MONSTER_MOVE */
|
||||
virtual bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) {
|
||||
return MonsterMoveParser::parse(packet, data);
|
||||
}
|
||||
|
||||
// --- Combat ---
|
||||
|
||||
/** Parse SMSG_ATTACKERSTATEUPDATE */
|
||||
virtual bool parseAttackerStateUpdate(network::Packet& packet, AttackerStateUpdateData& data) {
|
||||
return AttackerStateUpdateParser::parse(packet, data);
|
||||
}
|
||||
|
||||
/** Parse SMSG_SPELLNONMELEEDAMAGELOG */
|
||||
virtual bool parseSpellDamageLog(network::Packet& packet, SpellDamageLogData& data) {
|
||||
return SpellDamageLogParser::parse(packet, data);
|
||||
}
|
||||
|
||||
// --- Spells ---
|
||||
|
||||
/** Parse SMSG_INITIAL_SPELLS */
|
||||
virtual bool parseInitialSpells(network::Packet& packet, InitialSpellsData& data) {
|
||||
return InitialSpellsParser::parse(packet, data);
|
||||
}
|
||||
|
||||
/** Parse SMSG_AURA_UPDATE / SMSG_AURA_UPDATE_ALL */
|
||||
virtual bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) {
|
||||
return AuraUpdateParser::parse(packet, data, isAll);
|
||||
}
|
||||
|
||||
// --- Utility ---
|
||||
|
||||
/** Read a packed GUID from the packet */
|
||||
virtual uint64_t readPackedGuid(network::Packet& packet) {
|
||||
return UpdateObjectParser::readPackedGuid(packet);
|
||||
}
|
||||
|
||||
/** Write a packed GUID to the packet */
|
||||
virtual void writePackedGuid(network::Packet& packet, uint64_t guid) {
|
||||
MovementPacket::writePackedGuid(packet, guid);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* WotLK 3.3.5a packet parsers.
|
||||
*
|
||||
* Uses the default implementations which delegate to the existing
|
||||
* static parser classes. All current parsing code is WotLK-specific,
|
||||
* so no overrides are needed.
|
||||
*/
|
||||
class WotlkPacketParsers : public PacketParsers {
|
||||
// All methods use the defaults from PacketParsers base class,
|
||||
// which delegate to the existing WotLK static parsers.
|
||||
};
|
||||
|
||||
/**
|
||||
* TBC 2.4.3 packet parsers.
|
||||
*
|
||||
* Overrides methods where TBC binary format differs from WotLK:
|
||||
* - SMSG_UPDATE_OBJECT: u8 has_transport after blockCount (WotLK removed it)
|
||||
* - UpdateFlags is u8 (not u16), no VEHICLE/ROTATION/POSITION flags
|
||||
* - Movement flags2 is u8 (not u16), no transport seat byte
|
||||
* - Movement flags: JUMPING=0x2000 gates jump data (WotLK: FALLING=0x1000)
|
||||
* - SPLINE_ENABLED=0x08000000, SPLINE_ELEVATION=0x04000000 (same as WotLK)
|
||||
* - Pitch: SWIMMING or else ONTRANSPORT(0x02000000)
|
||||
* - CharEnum: uint8 firstLogin (not uint32+uint8), 20 equipment items (not 23)
|
||||
* - Aura updates use inline update fields, not SMSG_AURA_UPDATE
|
||||
*/
|
||||
class TbcPacketParsers : public PacketParsers {
|
||||
public:
|
||||
bool parseMovementBlock(network::Packet& packet, UpdateBlock& block) override;
|
||||
void writeMovementPayload(network::Packet& packet, const MovementInfo& info) override;
|
||||
network::Packet buildMovementPacket(LogicalOpcode opcode,
|
||||
const MovementInfo& info,
|
||||
uint64_t playerGuid = 0) override;
|
||||
bool parseUpdateObject(network::Packet& packet, UpdateObjectData& data) override;
|
||||
bool parseCharEnum(network::Packet& packet, CharEnumResponse& response) override;
|
||||
bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Classic 1.12.1 packet parsers.
|
||||
*
|
||||
* Inherits from TBC (shared: u8 UpdateFlags, has_transport byte).
|
||||
*
|
||||
* Differences from TBC:
|
||||
* - No moveFlags2 byte (TBC has u8, Classic has none)
|
||||
* - Only 6 speed fields (no flight speeds — flying added in TBC)
|
||||
* - SPLINE_ENABLED at 0x00400000 (TBC/WotLK: 0x08000000)
|
||||
* - Transport data has no timestamp (TBC adds u32 timestamp)
|
||||
* - Pitch: only SWIMMING (no ONTRANSPORT secondary pitch)
|
||||
* - CharEnum: no enchantment field per equipment slot
|
||||
* - No SMSG_AURA_UPDATE (uses update fields, same as TBC)
|
||||
*/
|
||||
class ClassicPacketParsers : public TbcPacketParsers {
|
||||
public:
|
||||
bool parseCharEnum(network::Packet& packet, CharEnumResponse& response) override;
|
||||
bool parseMovementBlock(network::Packet& packet, UpdateBlock& block) override;
|
||||
void writeMovementPayload(network::Packet& packet, const MovementInfo& info) override;
|
||||
network::Packet buildMovementPacket(LogicalOpcode opcode,
|
||||
const MovementInfo& info,
|
||||
uint64_t playerGuid = 0) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Factory function to create the right parser set for an expansion.
|
||||
*/
|
||||
inline std::unique_ptr<PacketParsers> createPacketParsers(const std::string& expansionId) {
|
||||
if (expansionId == "classic") return std::make_unique<ClassicPacketParsers>();
|
||||
if (expansionId == "tbc") return std::make_unique<TbcPacketParsers>();
|
||||
return std::make_unique<WotlkPacketParsers>();
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
93
include/game/update_field_table.hpp
Normal file
93
include/game/update_field_table.hpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* Logical update field identifiers (expansion-agnostic).
|
||||
* Wire indices are loaded at runtime from JSON.
|
||||
*/
|
||||
enum class UF : uint16_t {
|
||||
// Object fields
|
||||
OBJECT_FIELD_ENTRY,
|
||||
|
||||
// Unit fields
|
||||
UNIT_FIELD_TARGET_LO,
|
||||
UNIT_FIELD_TARGET_HI,
|
||||
UNIT_FIELD_HEALTH,
|
||||
UNIT_FIELD_POWER1,
|
||||
UNIT_FIELD_MAXHEALTH,
|
||||
UNIT_FIELD_MAXPOWER1,
|
||||
UNIT_FIELD_LEVEL,
|
||||
UNIT_FIELD_FACTIONTEMPLATE,
|
||||
UNIT_FIELD_FLAGS,
|
||||
UNIT_FIELD_FLAGS_2,
|
||||
UNIT_FIELD_DISPLAYID,
|
||||
UNIT_FIELD_MOUNTDISPLAYID,
|
||||
UNIT_NPC_FLAGS,
|
||||
UNIT_DYNAMIC_FLAGS,
|
||||
UNIT_END,
|
||||
|
||||
// Player fields
|
||||
PLAYER_FLAGS,
|
||||
PLAYER_XP,
|
||||
PLAYER_NEXT_LEVEL_XP,
|
||||
PLAYER_FIELD_COINAGE,
|
||||
PLAYER_QUEST_LOG_START,
|
||||
PLAYER_FIELD_INV_SLOT_HEAD,
|
||||
PLAYER_FIELD_PACK_SLOT_1,
|
||||
PLAYER_SKILL_INFO_START,
|
||||
PLAYER_EXPLORED_ZONES_START,
|
||||
|
||||
// GameObject fields
|
||||
GAMEOBJECT_DISPLAYID,
|
||||
|
||||
// Item fields
|
||||
ITEM_FIELD_STACK_COUNT,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps logical update field names to expansion-specific wire indices.
|
||||
* Loaded from JSON (e.g. Data/expansions/wotlk/update_fields.json).
|
||||
*/
|
||||
class UpdateFieldTable {
|
||||
public:
|
||||
/** Load from JSON file. Returns true if successful. */
|
||||
bool loadFromJson(const std::string& path);
|
||||
|
||||
/** Load built-in WotLK 3.3.5a defaults. */
|
||||
void loadWotlkDefaults();
|
||||
|
||||
/** Get the wire index for a logical field. Returns 0xFFFF if unknown. */
|
||||
uint16_t index(UF field) const;
|
||||
|
||||
/** Check if a field is mapped. */
|
||||
bool hasField(UF field) const;
|
||||
|
||||
/** Number of mapped fields. */
|
||||
size_t size() const { return fieldMap_.size(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<uint16_t, uint16_t> fieldMap_; // UF enum → wire index
|
||||
};
|
||||
|
||||
/**
|
||||
* Global active update field table (set by Application at startup).
|
||||
*/
|
||||
void setActiveUpdateFieldTable(const UpdateFieldTable* table);
|
||||
const UpdateFieldTable* getActiveUpdateFieldTable();
|
||||
|
||||
/** Convenience: get wire index for a logical field. */
|
||||
inline uint16_t fieldIndex(UF field) {
|
||||
const auto* t = getActiveUpdateFieldTable();
|
||||
return t ? t->index(field) : 0xFFFF;
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
@ -514,7 +514,6 @@ public:
|
|||
*/
|
||||
static uint64_t readPackedGuid(network::Packet& packet);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parse a single update block
|
||||
*
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "pipeline/loose_file_reader.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
|
|
@ -16,6 +17,8 @@ namespace pipeline {
|
|||
* AssetManager - Unified interface for loading WoW assets
|
||||
*
|
||||
* Reads pre-extracted loose files indexed by manifest.json.
|
||||
* Supports layered manifests: overlay manifests (HD packs, mods)
|
||||
* are checked before the base manifest, with higher priority first.
|
||||
* Use the asset_extract tool to extract MPQ archives first.
|
||||
* All reads are fully parallel (no serialization mutex needed).
|
||||
*/
|
||||
|
|
@ -41,6 +44,26 @@ public:
|
|||
*/
|
||||
bool isInitialized() const { return initialized; }
|
||||
|
||||
/**
|
||||
* Add an overlay manifest (HD packs, mods) checked before the base manifest.
|
||||
* Higher priority overlays are checked first.
|
||||
* @param manifestPath Full path to the overlay's manifest.json
|
||||
* @param priority Priority level (higher = checked first)
|
||||
* @param id Unique identifier for this overlay (e.g. "hd_character")
|
||||
* @return true if overlay loaded successfully
|
||||
*/
|
||||
bool addOverlayManifest(const std::string& manifestPath, int priority, const std::string& id);
|
||||
|
||||
/**
|
||||
* Remove a previously added overlay manifest by id.
|
||||
*/
|
||||
void removeOverlay(const std::string& id);
|
||||
|
||||
/**
|
||||
* Get list of active overlay IDs.
|
||||
*/
|
||||
std::vector<std::string> getOverlayIds() const;
|
||||
|
||||
/**
|
||||
* Load a BLP texture
|
||||
* @param path Virtual path to BLP file (e.g., "Textures\\Minimap\\Background.blp")
|
||||
|
|
@ -105,10 +128,24 @@ private:
|
|||
bool initialized = false;
|
||||
std::string dataPath;
|
||||
|
||||
// Loose file backend
|
||||
// Base manifest (loaded from dataPath/manifest.json)
|
||||
AssetManifest manifest_;
|
||||
LooseFileReader looseReader_;
|
||||
|
||||
// Overlay manifests (HD packs, mods) - sorted by priority descending
|
||||
struct ManifestLayer {
|
||||
AssetManifest manifest;
|
||||
int priority;
|
||||
std::string id;
|
||||
};
|
||||
std::vector<ManifestLayer> overlayLayers_; // Sorted by priority desc
|
||||
|
||||
/**
|
||||
* Resolve filesystem path checking overlays first, then base manifest.
|
||||
* Returns empty string if not found in any layer.
|
||||
*/
|
||||
std::string resolveLayeredPath(const std::string& normalizedPath) const;
|
||||
|
||||
mutable std::mutex cacheMutex;
|
||||
std::map<std::string, std::shared_ptr<DBCFile>> dbcCache;
|
||||
|
||||
|
|
|
|||
64
include/pipeline/dbc_layout.hpp
Normal file
64
include/pipeline/dbc_layout.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* Maps DBC field names to column indices for a single DBC file.
|
||||
* Column indices vary between WoW expansions.
|
||||
*/
|
||||
struct DBCFieldMap {
|
||||
std::unordered_map<std::string, uint32_t> fields;
|
||||
|
||||
/** Get column index by field name. Returns 0xFFFFFFFF if unknown. */
|
||||
uint32_t field(const std::string& name) const {
|
||||
auto it = fields.find(name);
|
||||
return (it != fields.end()) ? it->second : 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
/** Convenience operator for shorter syntax: layout["Name"] */
|
||||
uint32_t operator[](const std::string& name) const { return field(name); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps DBC file names to their field layouts.
|
||||
* Loaded from JSON (e.g. Data/expansions/wotlk/dbc_layouts.json).
|
||||
*/
|
||||
class DBCLayout {
|
||||
public:
|
||||
/** Load from JSON file. Returns true if successful. */
|
||||
bool loadFromJson(const std::string& path);
|
||||
|
||||
/** Load built-in WotLK 3.3.5a defaults. */
|
||||
void loadWotlkDefaults();
|
||||
|
||||
/** Get the field map for a DBC file. Returns nullptr if unknown. */
|
||||
const DBCFieldMap* getLayout(const std::string& dbcName) const;
|
||||
|
||||
/** Number of DBC layouts loaded. */
|
||||
size_t size() const { return layouts_.size(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, DBCFieldMap> layouts_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Global active DBC layout (set by Application at startup).
|
||||
*/
|
||||
void setActiveDBCLayout(const DBCLayout* layout);
|
||||
const DBCLayout* getActiveDBCLayout();
|
||||
|
||||
/** Convenience: get field index for a DBC field. */
|
||||
inline uint32_t dbcField(const std::string& dbcName, const std::string& fieldName) {
|
||||
const auto* l = getActiveDBCLayout();
|
||||
if (!l) return 0xFFFFFFFF;
|
||||
const auto* fm = l->getLayout(dbcName);
|
||||
return fm ? fm->field(fieldName) : 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
97
include/pipeline/hd_pack_manager.hpp
Normal file
97
include/pipeline/hd_pack_manager.hpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
class AssetManager;
|
||||
|
||||
/**
|
||||
* Metadata for a single HD texture pack on disk.
|
||||
*
|
||||
* Each pack lives in Data/hd/<packDir>/ and contains:
|
||||
* pack.json - metadata (id, name, group, compatible expansions, size)
|
||||
* manifest.json - standard asset manifest with HD override textures
|
||||
* assets/ - the actual HD files
|
||||
*/
|
||||
struct HDPack {
|
||||
std::string id; // Unique identifier (e.g. "character_hd")
|
||||
std::string name; // Human-readable name
|
||||
std::string group; // Grouping label (e.g. "Character", "Terrain")
|
||||
std::vector<std::string> expansions; // Compatible expansion IDs
|
||||
uint32_t totalSizeMB = 0; // Approximate total size on disk
|
||||
std::string manifestPath; // Full path to manifest.json
|
||||
std::string packDir; // Full path to pack directory
|
||||
bool enabled = false; // User-toggled enable state
|
||||
};
|
||||
|
||||
/**
|
||||
* HDPackManager - discovers, manages, and wires HD texture packs.
|
||||
*
|
||||
* Scans Data/hd/ subdirectories for pack.json files. Each pack can be
|
||||
* enabled/disabled via setPackEnabled(). Enabled packs are wired into
|
||||
* AssetManager as high-priority overlay manifests so that HD textures
|
||||
* override the base expansion assets transparently.
|
||||
*/
|
||||
class HDPackManager {
|
||||
public:
|
||||
HDPackManager() = default;
|
||||
|
||||
/**
|
||||
* Scan the HD root directory for available packs.
|
||||
* @param hdRootPath Path to Data/hd/ directory
|
||||
*/
|
||||
void initialize(const std::string& hdRootPath);
|
||||
|
||||
/**
|
||||
* Get all discovered packs.
|
||||
*/
|
||||
const std::vector<HDPack>& getAllPacks() const { return packs_; }
|
||||
|
||||
/**
|
||||
* Get packs compatible with a specific expansion.
|
||||
*/
|
||||
std::vector<const HDPack*> getPacksForExpansion(const std::string& expansionId) const;
|
||||
|
||||
/**
|
||||
* Enable or disable a pack. Persists state in enabledPacks_ map.
|
||||
*/
|
||||
void setPackEnabled(const std::string& packId, bool enabled);
|
||||
|
||||
/**
|
||||
* Check if a pack is enabled.
|
||||
*/
|
||||
bool isPackEnabled(const std::string& packId) const;
|
||||
|
||||
/**
|
||||
* Apply enabled packs as overlays to the asset manager.
|
||||
* Removes previously applied overlays and re-adds enabled ones.
|
||||
*/
|
||||
void applyToAssetManager(AssetManager* assetManager, const std::string& expansionId);
|
||||
|
||||
/**
|
||||
* Save enabled pack state to a settings file.
|
||||
*/
|
||||
void saveSettings(const std::string& settingsPath) const;
|
||||
|
||||
/**
|
||||
* Load enabled pack state from a settings file.
|
||||
*/
|
||||
void loadSettings(const std::string& settingsPath);
|
||||
|
||||
private:
|
||||
std::vector<HDPack> packs_;
|
||||
std::unordered_map<std::string, bool> enabledState_; // packId → enabled
|
||||
|
||||
// Overlay IDs currently applied to AssetManager (for removal on re-apply)
|
||||
std::vector<std::string> appliedOverlayIds_;
|
||||
|
||||
static constexpr int HD_OVERLAY_PRIORITY_BASE = 100; // High priority, above expansion base
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
#include "auth/auth_handler.hpp"
|
||||
#include "rendering/video_player.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace wowee { namespace ui {
|
||||
|
|
@ -46,7 +47,7 @@ private:
|
|||
char username[256] = "";
|
||||
char password[256] = "";
|
||||
int port = 3724;
|
||||
int compatibilityMode = 0; // 0 = 3.3.5a
|
||||
int expansionIndex = 0; // Index into expansion registry profiles
|
||||
bool authenticating = false;
|
||||
bool showPassword = false;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue