2026-02-02 12:24:50 -08:00
|
|
|
#include "game/game_handler.hpp"
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
#include "game/game_utils.hpp"
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
#include "game/chat_handler.hpp"
|
|
|
|
|
#include "game/movement_handler.hpp"
|
|
|
|
|
#include "game/combat_handler.hpp"
|
|
|
|
|
#include "game/spell_handler.hpp"
|
|
|
|
|
#include "game/inventory_handler.hpp"
|
|
|
|
|
#include "game/social_handler.hpp"
|
|
|
|
|
#include "game/quest_handler.hpp"
|
|
|
|
|
#include "game/warden_handler.hpp"
|
2026-02-12 22:56:36 -08:00
|
|
|
#include "game/packet_parsers.hpp"
|
2026-02-10 21:29:10 -08:00
|
|
|
#include "game/transport_manager.hpp"
|
2026-02-12 02:09:15 -08:00
|
|
|
#include "game/warden_crypto.hpp"
|
2026-02-14 02:00:15 -08:00
|
|
|
#include "game/warden_memory.hpp"
|
2026-02-12 02:43:20 -08:00
|
|
|
#include "game/warden_module.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
#include "game/opcodes.hpp"
|
2026-02-12 22:56:36 -08:00
|
|
|
#include "game/update_field_table.hpp"
|
2026-02-18 04:18:51 -08:00
|
|
|
#include "game/expansion_profile.hpp"
|
2026-02-14 15:11:43 -08:00
|
|
|
#include "rendering/renderer.hpp"
|
2026-04-05 19:30:44 +03:00
|
|
|
#include "rendering/spell_visual_system.hpp"
|
chore(renderer): extract AnimationController and remove audio pass-throughs
Extract ~1,500 lines of character animation state from Renderer into a dedicated
AnimationController class, and complete the AudioCoordinator migration by removing
all 10 audio pass-through getters from Renderer.
AnimationController:
- New: include/rendering/animation_controller.hpp (182 lines)
- New: src/rendering/animation_controller.cpp (1,703 lines)
- Moves: locomotion state machine (50+ members), mount animation (40+ members),
emote system, footstep triggering, surface detection, melee combat animations
- Renderer holds std::unique_ptr<AnimationController> and delegates completely
- AnimationController accesses audio via renderer_->getAudioCoordinator()
Audio caller migration:
- Migrate ~60 external callers from renderer->getXManager() to AudioCoordinator
directly, grouped by access pattern:
- UIServices: settings_panel, game_screen, toast_manager, chat_panel,
combat_ui, window_manager
- GameServices: game_handler, spell_handler, inventory_handler, quest_handler,
social_handler, combat_handler
- Application singleton: application.cpp, auth_screen.cpp, lua_engine.cpp
- Remove 10 pass-through getter definitions from renderer.cpp
- Remove 10 pass-through getter declarations from renderer.hpp
- Remove individual audio manager forward declarations from renderer.hpp
- Redirect 69 internal renderer.cpp audio calls to audioCoordinator_ directly
- game_handler.cpp: withSoundManager template uses services_.audioCoordinator;
MFP changed from &Renderer::getUiSoundManager to &AudioCoordinator::getUiSoundManager
- GameServices struct: add AudioCoordinator* audioCoordinator member
- settings_panel: applyAudioVolumes(Renderer*) -> applyAudioVolumes(AudioCoordinator*)
2026-04-02 13:06:31 +03:00
|
|
|
#include "audio/audio_coordinator.hpp"
|
2026-02-19 21:31:37 -08:00
|
|
|
#include "audio/activity_sound_manager.hpp"
|
|
|
|
|
#include "audio/combat_sound_manager.hpp"
|
2026-02-17 03:50:36 -08:00
|
|
|
#include "audio/spell_sound_manager.hpp"
|
2026-02-18 03:37:03 -08:00
|
|
|
#include "audio/ui_sound_manager.hpp"
|
2026-02-12 22:56:36 -08:00
|
|
|
#include "pipeline/dbc_layout.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
#include "network/world_socket.hpp"
|
|
|
|
|
#include "network/packet.hpp"
|
2026-02-12 02:22:04 -08:00
|
|
|
#include "auth/crypto.hpp"
|
2026-02-04 18:27:52 -08:00
|
|
|
#include "core/coordinates.hpp"
|
2026-02-07 14:21:50 -08:00
|
|
|
#include "core/application.hpp"
|
|
|
|
|
#include "pipeline/asset_manager.hpp"
|
|
|
|
|
#include "pipeline/dbc_loader.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
#include "core/logger.hpp"
|
2026-04-06 22:43:13 +03:00
|
|
|
#include "game/protocol_constants.hpp"
|
2026-04-05 12:27:35 +03:00
|
|
|
#include "rendering/animation/animation_ids.hpp"
|
2026-02-12 00:04:53 -08:00
|
|
|
#include <glm/gtx/quaternion.hpp>
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <algorithm>
|
2026-02-05 14:01:26 -08:00
|
|
|
#include <cmath>
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <cctype>
|
2026-02-07 12:43:32 -08:00
|
|
|
#include <ctime>
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <random>
|
2026-02-13 18:59:09 -08:00
|
|
|
#include <zlib.h>
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <chrono>
|
2026-02-05 14:01:26 -08:00
|
|
|
#include <filesystem>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <unordered_map>
|
2026-02-11 22:27:02 -08:00
|
|
|
#include <unordered_set>
|
2026-02-05 14:01:26 -08:00
|
|
|
#include <functional>
|
2026-02-20 01:57:21 -08:00
|
|
|
#include <array>
|
2026-02-05 14:01:26 -08:00
|
|
|
#include <cstdlib>
|
2026-02-13 18:59:09 -08:00
|
|
|
#include <cstring>
|
2026-02-20 01:49:43 -08:00
|
|
|
#include <limits>
|
2026-02-12 03:50:28 -08:00
|
|
|
#include <openssl/sha.h>
|
2026-02-20 01:53:40 -08:00
|
|
|
#include <openssl/hmac.h>
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace game {
|
|
|
|
|
|
2026-02-12 02:27:59 -08:00
|
|
|
namespace {
|
|
|
|
|
const char* worldStateName(WorldState state) {
|
|
|
|
|
switch (state) {
|
|
|
|
|
case WorldState::DISCONNECTED: return "DISCONNECTED";
|
|
|
|
|
case WorldState::CONNECTING: return "CONNECTING";
|
|
|
|
|
case WorldState::CONNECTED: return "CONNECTED";
|
|
|
|
|
case WorldState::CHALLENGE_RECEIVED: return "CHALLENGE_RECEIVED";
|
|
|
|
|
case WorldState::AUTH_SENT: return "AUTH_SENT";
|
|
|
|
|
case WorldState::AUTHENTICATED: return "AUTHENTICATED";
|
|
|
|
|
case WorldState::READY: return "READY";
|
|
|
|
|
case WorldState::CHAR_LIST_REQUESTED: return "CHAR_LIST_REQUESTED";
|
|
|
|
|
case WorldState::CHAR_LIST_RECEIVED: return "CHAR_LIST_RECEIVED";
|
|
|
|
|
case WorldState::ENTERING_WORLD: return "ENTERING_WORLD";
|
|
|
|
|
case WorldState::IN_WORLD: return "IN_WORLD";
|
|
|
|
|
case WorldState::FAILED: return "FAILED";
|
|
|
|
|
}
|
|
|
|
|
return "UNKNOWN";
|
|
|
|
|
}
|
|
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
2026-02-18 03:37:03 -08:00
|
|
|
std::string formatCopperAmount(uint32_t amount) {
|
2026-04-06 22:43:13 +03:00
|
|
|
uint32_t gold = amount / game::COPPER_PER_GOLD;
|
|
|
|
|
uint32_t silver = (amount / game::COPPER_PER_SILVER) % 100;
|
|
|
|
|
uint32_t copper = amount % game::COPPER_PER_SILVER;
|
2026-02-18 03:37:03 -08:00
|
|
|
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
bool wrote = false;
|
|
|
|
|
if (gold > 0) {
|
|
|
|
|
oss << gold << "g";
|
|
|
|
|
wrote = true;
|
|
|
|
|
}
|
|
|
|
|
if (silver > 0) {
|
|
|
|
|
if (wrote) oss << " ";
|
|
|
|
|
oss << silver << "s";
|
|
|
|
|
wrote = true;
|
|
|
|
|
}
|
|
|
|
|
if (copper > 0 || !wrote) {
|
|
|
|
|
if (wrote) oss << " ";
|
|
|
|
|
oss << copper << "c";
|
|
|
|
|
}
|
|
|
|
|
return oss.str();
|
|
|
|
|
}
|
2026-02-19 00:30:21 -08:00
|
|
|
|
2026-03-25 14:50:18 -07:00
|
|
|
// Registration helpers for common dispatch table patterns
|
|
|
|
|
void GameHandler::registerSkipHandler(LogicalOpcode op) {
|
|
|
|
|
dispatchTable_[op] = [](network::Packet& packet) { packet.skipAll(); };
|
|
|
|
|
}
|
|
|
|
|
void GameHandler::registerErrorHandler(LogicalOpcode op, const char* msg) {
|
|
|
|
|
dispatchTable_[op] = [this, msg](network::Packet&) {
|
|
|
|
|
addUIError(msg);
|
|
|
|
|
addSystemChatMessage(msg);
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-03-25 15:08:22 -07:00
|
|
|
void GameHandler::registerHandler(LogicalOpcode op, void (GameHandler::*handler)(network::Packet&)) {
|
|
|
|
|
dispatchTable_[op] = [this, handler](network::Packet& packet) { (this->*handler)(packet); };
|
|
|
|
|
}
|
2026-03-25 15:11:15 -07:00
|
|
|
void GameHandler::registerWorldHandler(LogicalOpcode op, void (GameHandler::*handler)(network::Packet&)) {
|
|
|
|
|
dispatchTable_[op] = [this, handler](network::Packet& packet) {
|
|
|
|
|
if (state == WorldState::IN_WORLD) (this->*handler)(packet);
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-03-25 14:50:18 -07:00
|
|
|
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
GameHandler::GameHandler(GameServices& services)
|
|
|
|
|
: services_(services) {
|
2026-02-02 12:24:50 -08:00
|
|
|
LOG_DEBUG("GameHandler created");
|
2026-02-04 11:31:08 -08:00
|
|
|
|
2026-02-12 22:56:36 -08:00
|
|
|
setActiveOpcodeTable(&opcodeTable_);
|
|
|
|
|
setActiveUpdateFieldTable(&updateFieldTable_);
|
|
|
|
|
|
|
|
|
|
// Initialize packet parsers (WotLK default, may be replaced for other expansions)
|
|
|
|
|
packetParsers_ = std::make_unique<WotlkPacketParsers>();
|
|
|
|
|
|
2026-02-10 21:29:10 -08:00
|
|
|
// Initialize transport manager
|
|
|
|
|
transportManager_ = std::make_unique<TransportManager>();
|
|
|
|
|
|
2026-02-12 02:43:20 -08:00
|
|
|
// Initialize Warden module manager
|
|
|
|
|
wardenModuleManager_ = std::make_unique<WardenModuleManager>();
|
|
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
// Initialize domain handlers
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
entityController_ = std::make_unique<EntityController>(*this);
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
chatHandler_ = std::make_unique<ChatHandler>(*this);
|
|
|
|
|
movementHandler_ = std::make_unique<MovementHandler>(*this);
|
|
|
|
|
combatHandler_ = std::make_unique<CombatHandler>(*this);
|
|
|
|
|
spellHandler_ = std::make_unique<SpellHandler>(*this);
|
|
|
|
|
inventoryHandler_ = std::make_unique<InventoryHandler>(*this);
|
|
|
|
|
socialHandler_ = std::make_unique<SocialHandler>(*this);
|
|
|
|
|
questHandler_ = std::make_unique<QuestHandler>(*this);
|
|
|
|
|
wardenHandler_ = std::make_unique<WardenHandler>(*this);
|
|
|
|
|
wardenHandler_->initModuleManager();
|
|
|
|
|
|
2026-02-04 11:31:08 -08:00
|
|
|
// Default action bar layout
|
|
|
|
|
actionBar[0].type = ActionBarSlot::SPELL;
|
2026-04-06 22:43:13 +03:00
|
|
|
actionBar[0].id = game::SPELL_ID_ATTACK; // Attack in slot 1
|
2026-02-08 03:05:38 -08:00
|
|
|
actionBar[11].type = ActionBarSlot::SPELL;
|
2026-04-06 22:43:13 +03:00
|
|
|
actionBar[11].id = game::SPELL_ID_HEARTHSTONE; // Hearthstone in slot 12
|
2026-03-25 07:26:38 +03:00
|
|
|
|
|
|
|
|
// Build the opcode dispatch table (replaces switch(*logicalOp) in handlePacket)
|
|
|
|
|
registerOpcodeHandlers();
|
2026-02-02 12:24:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GameHandler::~GameHandler() {
|
|
|
|
|
disconnect();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 22:56:36 -08:00
|
|
|
void GameHandler::setPacketParsers(std::unique_ptr<PacketParsers> parsers) {
|
|
|
|
|
packetParsers_ = std::move(parsers);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
bool GameHandler::connect(const std::string& host,
|
|
|
|
|
uint16_t port,
|
|
|
|
|
const std::vector<uint8_t>& sessionKey,
|
|
|
|
|
const std::string& accountName,
|
2026-02-13 01:51:49 -08:00
|
|
|
uint32_t build,
|
|
|
|
|
uint32_t realmId) {
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
if (sessionKey.size() != 40) {
|
|
|
|
|
LOG_ERROR("Invalid session key size: ", sessionKey.size(), " (expected 40)");
|
|
|
|
|
fail("Invalid session key");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_INFO("========================================");
|
|
|
|
|
LOG_INFO(" CONNECTING TO WORLD SERVER");
|
|
|
|
|
LOG_INFO("========================================");
|
|
|
|
|
LOG_INFO("Host: ", host);
|
|
|
|
|
LOG_INFO("Port: ", port);
|
|
|
|
|
LOG_INFO("Account: ", accountName);
|
|
|
|
|
LOG_INFO("Build: ", build);
|
|
|
|
|
|
|
|
|
|
// Store authentication data
|
|
|
|
|
this->sessionKey = sessionKey;
|
|
|
|
|
this->accountName = accountName;
|
|
|
|
|
this->build = build;
|
2026-02-13 01:51:49 -08:00
|
|
|
this->realmId_ = realmId;
|
2026-02-13 16:53:28 -08:00
|
|
|
|
|
|
|
|
// Diagnostic: dump session key for AUTH_REJECT debugging
|
2026-03-25 12:12:03 -07:00
|
|
|
LOG_INFO("GameHandler session key (", sessionKey.size(), "): ",
|
|
|
|
|
core::toHexString(sessionKey.data(), sessionKey.size()));
|
2026-03-29 18:39:38 -07:00
|
|
|
resetWardenState();
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Generate random client seed
|
|
|
|
|
this->clientSeed = generateClientSeed();
|
|
|
|
|
LOG_DEBUG("Generated client seed: 0x", std::hex, clientSeed, std::dec);
|
|
|
|
|
|
|
|
|
|
// Create world socket
|
|
|
|
|
socket = std::make_unique<network::WorldSocket>();
|
|
|
|
|
|
|
|
|
|
// Set up packet callback
|
|
|
|
|
socket->setPacketCallback([this](const network::Packet& packet) {
|
2026-03-15 01:21:23 -07:00
|
|
|
enqueueIncomingPacket(packet);
|
2026-02-02 12:24:50 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Connect to world server
|
|
|
|
|
setState(WorldState::CONNECTING);
|
|
|
|
|
|
|
|
|
|
if (!socket->connect(host, port)) {
|
|
|
|
|
LOG_ERROR("Failed to connect to world server");
|
|
|
|
|
fail("Connection failed");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setState(WorldState::CONNECTED);
|
|
|
|
|
LOG_INFO("Connected to world server, waiting for SMSG_AUTH_CHALLENGE...");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 18:39:38 -07:00
|
|
|
void GameHandler::resetWardenState() {
|
|
|
|
|
requiresWarden_ = false;
|
|
|
|
|
wardenGateSeen_ = false;
|
|
|
|
|
wardenGateElapsed_ = 0.0f;
|
|
|
|
|
wardenGateNextStatusLog_ = 2.0f;
|
|
|
|
|
wardenPacketsAfterGate_ = 0;
|
|
|
|
|
wardenCharEnumBlockedLogged_ = false;
|
|
|
|
|
wardenCrypto_.reset();
|
|
|
|
|
wardenState_ = WardenState::WAIT_MODULE_USE;
|
|
|
|
|
wardenModuleHash_.clear();
|
|
|
|
|
wardenModuleKey_.clear();
|
|
|
|
|
wardenModuleSize_ = 0;
|
|
|
|
|
wardenModuleData_.clear();
|
|
|
|
|
wardenLoadedModule_.reset();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
void GameHandler::disconnect() {
|
2026-02-08 03:05:38 -08:00
|
|
|
if (onTaxiFlight_) {
|
|
|
|
|
taxiRecoverPending_ = true;
|
|
|
|
|
} else {
|
|
|
|
|
taxiRecoverPending_ = false;
|
|
|
|
|
}
|
2026-02-02 12:24:50 -08:00
|
|
|
if (socket) {
|
|
|
|
|
socket->disconnect();
|
|
|
|
|
socket.reset();
|
|
|
|
|
}
|
2026-02-06 20:49:17 -08:00
|
|
|
activeCharacterGuid_ = 0;
|
2026-03-18 09:44:43 -07:00
|
|
|
guildNameCache_.clear();
|
|
|
|
|
pendingGuildNameQueries_.clear();
|
2026-03-10 01:15:51 -07:00
|
|
|
friendGuids_.clear();
|
2026-03-10 05:46:03 -07:00
|
|
|
contacts_.clear();
|
2026-02-12 00:04:53 -08:00
|
|
|
transportAttachments_.clear();
|
2026-03-29 18:39:38 -07:00
|
|
|
resetWardenState();
|
2026-03-15 01:21:23 -07:00
|
|
|
pendingIncomingPackets_.clear();
|
2026-03-20 18:07:00 -07:00
|
|
|
// Fire despawn callbacks so the renderer releases M2/character model resources.
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
for (const auto& [guid, entity] : entityController_->getEntityManager().getEntities()) {
|
2026-03-20 18:07:00 -07:00
|
|
|
if (guid == playerGuid) continue;
|
|
|
|
|
if (entity->getType() == ObjectType::UNIT && creatureDespawnCallback_)
|
|
|
|
|
creatureDespawnCallback_(guid);
|
|
|
|
|
else if (entity->getType() == ObjectType::PLAYER && playerDespawnCallback_)
|
|
|
|
|
playerDespawnCallback_(guid);
|
|
|
|
|
else if (entity->getType() == ObjectType::GAMEOBJECT && gameObjectDespawnCallback_)
|
|
|
|
|
gameObjectDespawnCallback_(guid);
|
|
|
|
|
}
|
|
|
|
|
otherPlayerVisibleItemEntries_.clear();
|
|
|
|
|
otherPlayerVisibleDirty_.clear();
|
|
|
|
|
otherPlayerMoveTimeMs_.clear();
|
feat(game): introduce GameHandler domain interfaces and eliminate friend declarations
Add game_interfaces.hpp with five narrow domain contracts that GameHandler now
publishes to its domain handlers, replacing the previous friend-class anti-pattern.
Changes:
- include/game/game_interfaces.hpp (new): IConnectionState, ITargetingState,
IEntityAccess, ISocialState, IPvpState — each interface exposes only the state
its consumer legitimately needs
- include/game/game_handler.hpp: GameHandler inherits all five interfaces;
include of game_interfaces.hpp added
- include/game/movement_handler.hpp: remove `friend class GameHandler`; add
public named accessors for previously-private fields (monsterMovePacketsThisTickRef,
timeSinceLastMoveHeartbeatRef, resetMovementClock, setFalling, setFallStartMs)
- include/game/spell_handler.hpp: remove `friend class GameHandler/InventoryHandler/
CombatHandler/EntityController`; promote private packet handlers (handlePetSpells,
handleListStabledPets, pet stable commands, DBC loaders) to public; add accessor
methods for aura cache, known spells, and player aura slot mutation
- src/game/game_handler.cpp, game_handler_callbacks.cpp, game_handler_packets.cpp:
replace direct private field access with the new accessor API
(e.g. casting_ → isCasting(), monsterMovePacketsThisTick_ → ...ThisTickRef())
- src/game/inventory_handler.cpp, combat_handler.cpp, entity_controller.cpp:
replace friend-class private access with public accessor calls
No behaviour change. All 13 test suites pass. Zero build warnings.
2026-04-05 20:25:02 +03:00
|
|
|
if (spellHandler_) spellHandler_->clearUnitCastStates();
|
|
|
|
|
if (spellHandler_) spellHandler_->clearUnitAurasCache();
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (combatHandler_) combatHandler_->clearCombatText();
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
entityController_->clearAll();
|
2026-02-02 12:24:50 -08:00
|
|
|
setState(WorldState::DISCONNECTED);
|
|
|
|
|
LOG_INFO("Disconnected from world server");
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 19:27:35 -08:00
|
|
|
void GameHandler::resetDbcCaches() {
|
|
|
|
|
spellNameCacheLoaded_ = false;
|
|
|
|
|
spellNameCache_.clear();
|
|
|
|
|
skillLineDbcLoaded_ = false;
|
|
|
|
|
skillLineNames_.clear();
|
|
|
|
|
skillLineCategories_.clear();
|
|
|
|
|
skillLineAbilityLoaded_ = false;
|
|
|
|
|
spellToSkillLine_.clear();
|
|
|
|
|
taxiDbcLoaded_ = false;
|
|
|
|
|
taxiNodes_.clear();
|
|
|
|
|
taxiPathEdges_.clear();
|
|
|
|
|
taxiPathNodes_.clear();
|
2026-02-26 17:56:11 -08:00
|
|
|
areaTriggerDbcLoaded_ = false;
|
|
|
|
|
areaTriggers_.clear();
|
|
|
|
|
activeAreaTriggers_.clear();
|
2026-02-14 19:27:35 -08:00
|
|
|
talentDbcLoaded_ = false;
|
|
|
|
|
talentCache_.clear();
|
|
|
|
|
talentTabCache_.clear();
|
2026-03-10 03:27:30 -07:00
|
|
|
// Clear the AssetManager DBC file cache so that expansion-specific DBCs
|
|
|
|
|
// (CharSections, ItemDisplayInfo, etc.) are reloaded from the new expansion's
|
|
|
|
|
// MPQ files instead of returning stale data from a previous session/expansion.
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
auto* am = services_.assetManager;
|
2026-03-10 03:27:30 -07:00
|
|
|
if (am) {
|
|
|
|
|
am->clearDBCCache();
|
|
|
|
|
}
|
2026-02-14 19:27:35 -08:00
|
|
|
LOG_INFO("GameHandler: DBC caches cleared for expansion switch");
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
bool GameHandler::isConnected() const {
|
|
|
|
|
return socket && socket->isConnected();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 15:19:03 -07:00
|
|
|
void GameHandler::updateNetworking(float deltaTime) {
|
2026-03-14 22:18:28 -07:00
|
|
|
// Reset per-tick monster-move budget tracking (Classic/Turtle flood protection).
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (movementHandler_) {
|
feat(game): introduce GameHandler domain interfaces and eliminate friend declarations
Add game_interfaces.hpp with five narrow domain contracts that GameHandler now
publishes to its domain handlers, replacing the previous friend-class anti-pattern.
Changes:
- include/game/game_interfaces.hpp (new): IConnectionState, ITargetingState,
IEntityAccess, ISocialState, IPvpState — each interface exposes only the state
its consumer legitimately needs
- include/game/game_handler.hpp: GameHandler inherits all five interfaces;
include of game_interfaces.hpp added
- include/game/movement_handler.hpp: remove `friend class GameHandler`; add
public named accessors for previously-private fields (monsterMovePacketsThisTickRef,
timeSinceLastMoveHeartbeatRef, resetMovementClock, setFalling, setFallStartMs)
- include/game/spell_handler.hpp: remove `friend class GameHandler/InventoryHandler/
CombatHandler/EntityController`; promote private packet handlers (handlePetSpells,
handleListStabledPets, pet stable commands, DBC loaders) to public; add accessor
methods for aura cache, known spells, and player aura slot mutation
- src/game/game_handler.cpp, game_handler_callbacks.cpp, game_handler_packets.cpp:
replace direct private field access with the new accessor API
(e.g. casting_ → isCasting(), monsterMovePacketsThisTick_ → ...ThisTickRef())
- src/game/inventory_handler.cpp, combat_handler.cpp, entity_controller.cpp:
replace friend-class private access with public accessor calls
No behaviour change. All 13 test suites pass. Zero build warnings.
2026-04-05 20:25:02 +03:00
|
|
|
movementHandler_->monsterMovePacketsThisTickRef() = 0;
|
|
|
|
|
movementHandler_->monsterMovePacketsDroppedThisTickRef() = 0;
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
}
|
2026-03-14 22:18:28 -07:00
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
// Update socket (processes incoming data and triggers callbacks)
|
2026-02-05 12:01:03 -08:00
|
|
|
if (socket) {
|
2026-03-07 13:44:09 -08:00
|
|
|
auto socketStart = std::chrono::steady_clock::now();
|
2026-02-05 12:01:03 -08:00
|
|
|
socket->update();
|
2026-03-07 13:44:09 -08:00
|
|
|
float socketMs = std::chrono::duration<float, std::milli>(
|
|
|
|
|
std::chrono::steady_clock::now() - socketStart).count();
|
|
|
|
|
if (socketMs > 3.0f) {
|
|
|
|
|
LOG_WARNING("SLOW socket->update: ", socketMs, "ms");
|
|
|
|
|
}
|
2026-02-05 12:01:03 -08:00
|
|
|
}
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-03-15 01:21:23 -07:00
|
|
|
{
|
|
|
|
|
auto packetStart = std::chrono::steady_clock::now();
|
|
|
|
|
processQueuedIncomingPackets();
|
|
|
|
|
float packetMs = std::chrono::duration<float, std::milli>(
|
|
|
|
|
std::chrono::steady_clock::now() - packetStart).count();
|
|
|
|
|
if (packetMs > 3.0f) {
|
|
|
|
|
LOG_WARNING("SLOW queued packet handling: ", packetMs, "ms");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 16:46:29 -07:00
|
|
|
// Drain pending async Warden response (built on background thread to avoid 5s stalls)
|
|
|
|
|
if (wardenResponsePending_) {
|
|
|
|
|
auto status = wardenPendingEncrypted_.wait_for(std::chrono::milliseconds(0));
|
|
|
|
|
if (status == std::future_status::ready) {
|
|
|
|
|
auto plaintext = wardenPendingEncrypted_.get();
|
|
|
|
|
wardenResponsePending_ = false;
|
|
|
|
|
if (!plaintext.empty() && wardenCrypto_) {
|
|
|
|
|
std::vector<uint8_t> encrypted = wardenCrypto_->encrypt(plaintext);
|
|
|
|
|
network::Packet response(wireOpcode(Opcode::CMSG_WARDEN_DATA));
|
|
|
|
|
for (uint8_t byte : encrypted) {
|
|
|
|
|
response.writeUInt8(byte);
|
|
|
|
|
}
|
|
|
|
|
if (socket && socket->isConnected()) {
|
|
|
|
|
socket->send(response);
|
|
|
|
|
LOG_WARNING("Warden: Sent async CHEAT_CHECKS_RESULT (", plaintext.size(), " bytes plaintext)");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 17:38:25 -07:00
|
|
|
// Detect RX silence (server stopped sending packets but TCP still open)
|
2026-03-25 14:21:19 -07:00
|
|
|
if (isInWorld() && socket->isConnected() &&
|
2026-03-16 17:38:25 -07:00
|
|
|
lastRxTime_.time_since_epoch().count() > 0) {
|
|
|
|
|
auto silenceMs = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
|
std::chrono::steady_clock::now() - lastRxTime_).count();
|
2026-04-06 22:43:13 +03:00
|
|
|
if (silenceMs > game::RX_SILENCE_WARNING_MS && !rxSilenceLogged_) {
|
2026-03-16 17:38:25 -07:00
|
|
|
rxSilenceLogged_ = true;
|
|
|
|
|
LOG_WARNING("RX SILENCE: No packets from server for ", silenceMs, "ms — possible soft disconnect");
|
|
|
|
|
}
|
2026-04-06 22:43:13 +03:00
|
|
|
if (silenceMs > game::RX_SILENCE_CRITICAL_MS && !rxSilence15sLogged_) {
|
2026-03-29 18:46:25 -07:00
|
|
|
rxSilence15sLogged_ = true;
|
2026-03-16 17:38:25 -07:00
|
|
|
LOG_WARNING("RX SILENCE: 15s — server appears to have stopped sending");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 18:46:54 -08:00
|
|
|
// Detect server-side disconnect (socket closed during update)
|
|
|
|
|
if (socket && !socket->isConnected() && state != WorldState::DISCONNECTED) {
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
if (pendingIncomingPackets_.empty() && !entityController_->hasPendingUpdateObjectWork()) {
|
2026-03-15 01:21:23 -07:00
|
|
|
LOG_WARNING("Server closed connection in state: ", worldStateName(state));
|
|
|
|
|
disconnect();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
LOG_DEBUG("World socket closed with ", pendingIncomingPackets_.size(),
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
" queued packet(s) and update-object batch(es) pending dispatch");
|
2026-02-14 18:46:54 -08:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 01:53:21 -08:00
|
|
|
// Post-gate visibility: determine whether server goes silent or closes after Warden requirement.
|
2026-02-12 02:27:59 -08:00
|
|
|
if (wardenGateSeen_ && socket && socket->isConnected()) {
|
2026-02-12 01:53:21 -08:00
|
|
|
wardenGateElapsed_ += deltaTime;
|
|
|
|
|
if (wardenGateElapsed_ >= wardenGateNextStatusLog_) {
|
2026-02-16 00:45:47 -08:00
|
|
|
LOG_DEBUG("Warden gate status: elapsed=", wardenGateElapsed_,
|
2026-02-12 01:53:21 -08:00
|
|
|
"s connected=", socket->isConnected() ? "yes" : "no",
|
|
|
|
|
" packetsAfterGate=", wardenPacketsAfterGate_);
|
2026-04-06 22:43:13 +03:00
|
|
|
wardenGateNextStatusLog_ += game::WARDEN_GATE_LOG_INTERVAL_SEC;
|
2026-02-12 01:53:21 -08:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-25 15:19:03 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-25 15:32:51 -07:00
|
|
|
void GameHandler::updateTaxiAndMountState(float deltaTime) {
|
|
|
|
|
// Update taxi landing cooldown
|
|
|
|
|
if (taxiLandingCooldown_ > 0.0f) {
|
|
|
|
|
taxiLandingCooldown_ -= deltaTime;
|
|
|
|
|
}
|
|
|
|
|
if (taxiStartGrace_ > 0.0f) {
|
|
|
|
|
taxiStartGrace_ -= deltaTime;
|
|
|
|
|
}
|
|
|
|
|
if (playerTransportStickyTimer_ > 0.0f) {
|
|
|
|
|
playerTransportStickyTimer_ -= deltaTime;
|
|
|
|
|
if (playerTransportStickyTimer_ <= 0.0f) {
|
|
|
|
|
playerTransportStickyTimer_ = 0.0f;
|
|
|
|
|
playerTransportStickyGuid_ = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Detect taxi flight landing: UNIT_FLAG_TAXI_FLIGHT (0x00000100) cleared
|
|
|
|
|
if (onTaxiFlight_) {
|
|
|
|
|
updateClientTaxi(deltaTime);
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
auto playerEntity = entityController_->getEntityManager().getEntity(playerGuid);
|
2026-03-25 15:32:51 -07:00
|
|
|
auto unit = std::dynamic_pointer_cast<Unit>(playerEntity);
|
|
|
|
|
if (unit &&
|
2026-04-06 22:43:13 +03:00
|
|
|
(unit->getUnitFlags() & game::UNIT_FLAG_TAXI_FLIGHT) == 0 &&
|
2026-03-25 15:32:51 -07:00
|
|
|
!taxiClientActive_ &&
|
|
|
|
|
!taxiActivatePending_ &&
|
|
|
|
|
taxiStartGrace_ <= 0.0f) {
|
|
|
|
|
onTaxiFlight_ = false;
|
|
|
|
|
taxiLandingCooldown_ = 2.0f; // 2 second cooldown to prevent re-entering
|
|
|
|
|
if (taxiMountActive_ && mountCallback_) {
|
|
|
|
|
mountCallback_(0);
|
|
|
|
|
}
|
|
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiMountDisplayId_ = 0;
|
|
|
|
|
currentMountDisplayId_ = 0;
|
|
|
|
|
taxiClientActive_ = false;
|
|
|
|
|
taxiClientPath_.clear();
|
|
|
|
|
taxiRecoverPending_ = false;
|
|
|
|
|
movementInfo.flags = 0;
|
|
|
|
|
movementInfo.flags2 = 0;
|
|
|
|
|
if (socket) {
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_STOP);
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Taxi flight landed");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Safety: if taxi flight ended but mount is still active, force dismount.
|
|
|
|
|
// Guard against transient taxi-state flicker.
|
|
|
|
|
if (!onTaxiFlight_ && taxiMountActive_) {
|
|
|
|
|
bool serverStillTaxi = false;
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
auto playerEntity = entityController_->getEntityManager().getEntity(playerGuid);
|
2026-03-25 15:32:51 -07:00
|
|
|
auto playerUnit = std::dynamic_pointer_cast<Unit>(playerEntity);
|
|
|
|
|
if (playerUnit) {
|
2026-04-06 22:43:13 +03:00
|
|
|
serverStillTaxi = (playerUnit->getUnitFlags() & game::UNIT_FLAG_TAXI_FLIGHT) != 0;
|
2026-03-25 15:32:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (taxiStartGrace_ > 0.0f || serverStillTaxi || taxiClientActive_ || taxiActivatePending_) {
|
|
|
|
|
onTaxiFlight_ = true;
|
|
|
|
|
} else {
|
|
|
|
|
if (mountCallback_) mountCallback_(0);
|
|
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiMountDisplayId_ = 0;
|
|
|
|
|
currentMountDisplayId_ = 0;
|
|
|
|
|
movementInfo.flags = 0;
|
|
|
|
|
movementInfo.flags2 = 0;
|
|
|
|
|
if (socket) {
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_STOP);
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Taxi dismount cleanup");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keep non-taxi mount state server-authoritative.
|
|
|
|
|
// Some server paths don't emit explicit mount field updates in lockstep
|
|
|
|
|
// with local visual state changes, so reconcile continuously.
|
|
|
|
|
if (!onTaxiFlight_ && !taxiMountActive_) {
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
auto playerEntity = entityController_->getEntityManager().getEntity(playerGuid);
|
2026-03-25 15:32:51 -07:00
|
|
|
auto playerUnit = std::dynamic_pointer_cast<Unit>(playerEntity);
|
|
|
|
|
if (playerUnit) {
|
|
|
|
|
uint32_t serverMountDisplayId = playerUnit->getMountDisplayId();
|
|
|
|
|
if (serverMountDisplayId != currentMountDisplayId_) {
|
|
|
|
|
LOG_INFO("Mount reconcile: server=", serverMountDisplayId,
|
|
|
|
|
" local=", currentMountDisplayId_);
|
|
|
|
|
currentMountDisplayId_ = serverMountDisplayId;
|
|
|
|
|
if (mountCallback_) {
|
|
|
|
|
mountCallback_(serverMountDisplayId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (taxiRecoverPending_ && state == WorldState::IN_WORLD) {
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
auto playerEntity = entityController_->getEntityManager().getEntity(playerGuid);
|
2026-03-25 15:32:51 -07:00
|
|
|
if (playerEntity) {
|
|
|
|
|
playerEntity->setPosition(taxiRecoverPos_.x, taxiRecoverPos_.y,
|
|
|
|
|
taxiRecoverPos_.z, movementInfo.orientation);
|
|
|
|
|
movementInfo.x = taxiRecoverPos_.x;
|
|
|
|
|
movementInfo.y = taxiRecoverPos_.y;
|
|
|
|
|
movementInfo.z = taxiRecoverPos_.z;
|
|
|
|
|
if (socket) {
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
|
|
|
|
}
|
|
|
|
|
taxiRecoverPending_ = false;
|
|
|
|
|
LOG_INFO("Taxi recovery applied");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (taxiActivatePending_) {
|
|
|
|
|
taxiActivateTimer_ += deltaTime;
|
|
|
|
|
if (taxiActivateTimer_ > 5.0f) {
|
|
|
|
|
// If client taxi simulation is already active, server reply may be missing/late.
|
|
|
|
|
// Do not cancel the flight in that case; clear pending state and continue.
|
|
|
|
|
if (onTaxiFlight_ || taxiClientActive_ || taxiMountActive_) {
|
|
|
|
|
taxiActivatePending_ = false;
|
|
|
|
|
taxiActivateTimer_ = 0.0f;
|
|
|
|
|
} else {
|
|
|
|
|
taxiActivatePending_ = false;
|
|
|
|
|
taxiActivateTimer_ = 0.0f;
|
|
|
|
|
if (taxiMountActive_ && mountCallback_) {
|
|
|
|
|
mountCallback_(0);
|
|
|
|
|
}
|
|
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiMountDisplayId_ = 0;
|
|
|
|
|
taxiClientActive_ = false;
|
|
|
|
|
taxiClientPath_.clear();
|
|
|
|
|
onTaxiFlight_ = false;
|
|
|
|
|
LOG_WARNING("Taxi activation timed out");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 15:37:19 -07:00
|
|
|
void GameHandler::updateAutoAttack(float deltaTime) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (combatHandler_) combatHandler_->updateAutoAttack(deltaTime);
|
2026-03-25 15:37:19 -07:00
|
|
|
|
|
|
|
|
// Close NPC windows if player walks too far (15 units)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 15:27:31 -07:00
|
|
|
void GameHandler::updateEntityInterpolation(float deltaTime) {
|
|
|
|
|
// Update entity movement interpolation (keeps targeting in sync with visuals)
|
|
|
|
|
// Only update entities within reasonable distance for performance
|
2026-04-06 22:43:13 +03:00
|
|
|
const float updateRadiusSq = game::ENTITY_UPDATE_RADIUS * game::ENTITY_UPDATE_RADIUS; // 150 unit radius
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
auto playerEntity = entityController_->getEntityManager().getEntity(playerGuid);
|
2026-03-25 15:27:31 -07:00
|
|
|
glm::vec3 playerPos = playerEntity ? glm::vec3(playerEntity->getX(), playerEntity->getY(), playerEntity->getZ()) : glm::vec3(0.0f);
|
|
|
|
|
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
for (auto& [guid, entity] : entityController_->getEntityManager().getEntities()) {
|
2026-03-25 15:27:31 -07:00
|
|
|
// Always update player
|
|
|
|
|
if (guid == playerGuid) {
|
|
|
|
|
entity->updateMovement(deltaTime);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Keep selected/engaged target interpolation exact for UI targeting circle.
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (guid == targetGuid || (combatHandler_ && guid == combatHandler_->getAutoAttackTargetGuid())) {
|
2026-03-25 15:27:31 -07:00
|
|
|
entity->updateMovement(deltaTime);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Distance cull other entities (use latest position to avoid culling by stale origin)
|
|
|
|
|
glm::vec3 entityPos(entity->getLatestX(), entity->getLatestY(), entity->getLatestZ());
|
|
|
|
|
float distSq = glm::dot(entityPos - playerPos, entityPos - playerPos);
|
|
|
|
|
if (distSq < updateRadiusSq) {
|
|
|
|
|
entity->updateMovement(deltaTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 15:23:31 -07:00
|
|
|
void GameHandler::updateTimers(float deltaTime) {
|
2026-03-29 16:49:17 -07:00
|
|
|
if (spellHandler_) spellHandler_->updateTimers(deltaTime);
|
|
|
|
|
|
2026-03-29 17:44:46 -07:00
|
|
|
// Periodically clear stale pending item queries so they can be retried.
|
|
|
|
|
// Without this, a lost/malformed response leaves the entry stuck forever.
|
|
|
|
|
pendingItemQueryTimer_ += deltaTime;
|
|
|
|
|
if (pendingItemQueryTimer_ >= 5.0f) {
|
|
|
|
|
pendingItemQueryTimer_ = 0.0f;
|
|
|
|
|
if (!pendingItemQueries_.empty()) {
|
|
|
|
|
LOG_DEBUG("Clearing ", pendingItemQueries_.size(), " stale pending item queries");
|
|
|
|
|
pendingItemQueries_.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
if (auctionSearchDelayTimer_ > 0.0f) {
|
|
|
|
|
auctionSearchDelayTimer_ -= deltaTime;
|
|
|
|
|
if (auctionSearchDelayTimer_ < 0.0f) auctionSearchDelayTimer_ = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 17:14:13 -08:00
|
|
|
for (auto it = pendingQuestAcceptTimeouts_.begin(); it != pendingQuestAcceptTimeouts_.end();) {
|
|
|
|
|
it->second -= deltaTime;
|
|
|
|
|
if (it->second <= 0.0f) {
|
|
|
|
|
const uint32_t questId = it->first;
|
|
|
|
|
const uint64_t npcGuid = pendingQuestAcceptNpcGuids_.count(questId) != 0
|
|
|
|
|
? pendingQuestAcceptNpcGuids_[questId] : 0;
|
|
|
|
|
triggerQuestAcceptResync(questId, npcGuid, "timeout");
|
|
|
|
|
it = pendingQuestAcceptTimeouts_.erase(it);
|
|
|
|
|
pendingQuestAcceptNpcGuids_.erase(questId);
|
|
|
|
|
} else {
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 19:47:49 -08:00
|
|
|
if (pendingMoneyDeltaTimer_ > 0.0f) {
|
|
|
|
|
pendingMoneyDeltaTimer_ -= deltaTime;
|
|
|
|
|
if (pendingMoneyDeltaTimer_ <= 0.0f) {
|
|
|
|
|
pendingMoneyDeltaTimer_ = 0.0f;
|
|
|
|
|
pendingMoneyDelta_ = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
// autoAttackRangeWarnCooldown_ decrement moved into CombatHandler::updateAutoAttack()
|
2026-02-13 19:47:49 -08:00
|
|
|
|
2026-02-20 17:14:13 -08:00
|
|
|
if (pendingLoginQuestResync_) {
|
|
|
|
|
pendingLoginQuestResyncTimeout_ -= deltaTime;
|
|
|
|
|
if (resyncQuestLogFromServerSlots(true)) {
|
|
|
|
|
pendingLoginQuestResync_ = false;
|
|
|
|
|
pendingLoginQuestResyncTimeout_ = 0.0f;
|
|
|
|
|
} else if (pendingLoginQuestResyncTimeout_ <= 0.0f) {
|
|
|
|
|
pendingLoginQuestResync_ = false;
|
|
|
|
|
pendingLoginQuestResyncTimeout_ = 0.0f;
|
|
|
|
|
LOG_WARNING("Quest login resync timed out waiting for player quest slot fields");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-18 04:13:26 -08:00
|
|
|
for (auto it = pendingGameObjectLootRetries_.begin(); it != pendingGameObjectLootRetries_.end();) {
|
|
|
|
|
it->timer -= deltaTime;
|
|
|
|
|
if (it->timer <= 0.0f) {
|
2026-03-25 14:21:19 -07:00
|
|
|
if (it->remainingRetries > 0 && isInWorld()) {
|
2026-02-20 19:51:04 -08:00
|
|
|
// Keep server-side position/facing fresh before retrying GO use.
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
2026-02-18 04:17:11 -08:00
|
|
|
auto usePacket = GameObjectUsePacket::build(it->guid);
|
|
|
|
|
socket->send(usePacket);
|
2026-02-20 19:51:04 -08:00
|
|
|
if (it->sendLoot) {
|
|
|
|
|
auto lootPacket = LootPacket::build(it->guid);
|
|
|
|
|
socket->send(lootPacket);
|
|
|
|
|
}
|
2026-02-18 04:13:26 -08:00
|
|
|
--it->remainingRetries;
|
|
|
|
|
it->timer = 0.20f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (it->remainingRetries == 0) {
|
|
|
|
|
it = pendingGameObjectLootRetries_.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 19:51:04 -08:00
|
|
|
for (auto it = pendingGameObjectLootOpens_.begin(); it != pendingGameObjectLootOpens_.end();) {
|
|
|
|
|
it->timer -= deltaTime;
|
|
|
|
|
if (it->timer <= 0.0f) {
|
2026-03-25 14:21:19 -07:00
|
|
|
if (isInWorld()) {
|
2026-03-14 04:41:46 -07:00
|
|
|
// Avoid sending CMSG_LOOT while a timed cast is active (e.g. gathering).
|
|
|
|
|
// handleSpellGo will trigger loot after the cast completes.
|
feat(game): introduce GameHandler domain interfaces and eliminate friend declarations
Add game_interfaces.hpp with five narrow domain contracts that GameHandler now
publishes to its domain handlers, replacing the previous friend-class anti-pattern.
Changes:
- include/game/game_interfaces.hpp (new): IConnectionState, ITargetingState,
IEntityAccess, ISocialState, IPvpState — each interface exposes only the state
its consumer legitimately needs
- include/game/game_handler.hpp: GameHandler inherits all five interfaces;
include of game_interfaces.hpp added
- include/game/movement_handler.hpp: remove `friend class GameHandler`; add
public named accessors for previously-private fields (monsterMovePacketsThisTickRef,
timeSinceLastMoveHeartbeatRef, resetMovementClock, setFalling, setFallStartMs)
- include/game/spell_handler.hpp: remove `friend class GameHandler/InventoryHandler/
CombatHandler/EntityController`; promote private packet handlers (handlePetSpells,
handleListStabledPets, pet stable commands, DBC loaders) to public; add accessor
methods for aura cache, known spells, and player aura slot mutation
- src/game/game_handler.cpp, game_handler_callbacks.cpp, game_handler_packets.cpp:
replace direct private field access with the new accessor API
(e.g. casting_ → isCasting(), monsterMovePacketsThisTick_ → ...ThisTickRef())
- src/game/inventory_handler.cpp, combat_handler.cpp, entity_controller.cpp:
replace friend-class private access with public accessor calls
No behaviour change. All 13 test suites pass. Zero build warnings.
2026-04-05 20:25:02 +03:00
|
|
|
if (spellHandler_ && spellHandler_->isCasting() && spellHandler_->getCurrentCastSpellId() != 0) {
|
2026-03-14 04:41:46 -07:00
|
|
|
it->timer = 0.20f;
|
|
|
|
|
++it;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-02-20 19:51:04 -08:00
|
|
|
lootTarget(it->guid);
|
|
|
|
|
}
|
|
|
|
|
it = pendingGameObjectLootOpens_.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 07:00:43 -07:00
|
|
|
// Periodically re-query names for players whose initial CMSG_NAME_QUERY was
|
|
|
|
|
// lost (server didn't respond) or whose entity was recreated while the query
|
|
|
|
|
// was still pending. Runs every 5 seconds to keep overhead minimal.
|
2026-03-25 14:21:19 -07:00
|
|
|
if (isInWorld()) {
|
2026-03-10 07:00:43 -07:00
|
|
|
static float nameResyncTimer = 0.0f;
|
|
|
|
|
nameResyncTimer += deltaTime;
|
|
|
|
|
if (nameResyncTimer >= 5.0f) {
|
|
|
|
|
nameResyncTimer = 0.0f;
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
for (const auto& [guid, entity] : entityController_->getEntityManager().getEntities()) {
|
2026-03-10 07:00:43 -07:00
|
|
|
if (!entity || entity->getType() != ObjectType::PLAYER) continue;
|
|
|
|
|
if (guid == playerGuid) continue;
|
|
|
|
|
auto player = std::static_pointer_cast<Player>(entity);
|
|
|
|
|
if (!player->getName().empty()) continue;
|
|
|
|
|
// Player entity exists with empty name and no pending query — resend.
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
entityController_->queryPlayerName(guid);
|
2026-03-10 07:00:43 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-18 04:11:00 -08:00
|
|
|
if (pendingLootMoneyNotifyTimer_ > 0.0f) {
|
|
|
|
|
pendingLootMoneyNotifyTimer_ -= deltaTime;
|
|
|
|
|
if (pendingLootMoneyNotifyTimer_ <= 0.0f) {
|
|
|
|
|
pendingLootMoneyNotifyTimer_ = 0.0f;
|
|
|
|
|
bool alreadyAnnounced = false;
|
|
|
|
|
if (pendingLootMoneyGuid_ != 0) {
|
|
|
|
|
auto it = localLootState_.find(pendingLootMoneyGuid_);
|
|
|
|
|
if (it != localLootState_.end()) {
|
|
|
|
|
alreadyAnnounced = it->second.moneyTaken;
|
|
|
|
|
it->second.moneyTaken = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!alreadyAnnounced && pendingLootMoneyAmount_ > 0) {
|
|
|
|
|
addSystemChatMessage("Looted: " + formatCopperAmount(pendingLootMoneyAmount_));
|
chore(renderer): extract AnimationController and remove audio pass-throughs
Extract ~1,500 lines of character animation state from Renderer into a dedicated
AnimationController class, and complete the AudioCoordinator migration by removing
all 10 audio pass-through getters from Renderer.
AnimationController:
- New: include/rendering/animation_controller.hpp (182 lines)
- New: src/rendering/animation_controller.cpp (1,703 lines)
- Moves: locomotion state machine (50+ members), mount animation (40+ members),
emote system, footstep triggering, surface detection, melee combat animations
- Renderer holds std::unique_ptr<AnimationController> and delegates completely
- AnimationController accesses audio via renderer_->getAudioCoordinator()
Audio caller migration:
- Migrate ~60 external callers from renderer->getXManager() to AudioCoordinator
directly, grouped by access pattern:
- UIServices: settings_panel, game_screen, toast_manager, chat_panel,
combat_ui, window_manager
- GameServices: game_handler, spell_handler, inventory_handler, quest_handler,
social_handler, combat_handler
- Application singleton: application.cpp, auth_screen.cpp, lua_engine.cpp
- Remove 10 pass-through getter definitions from renderer.cpp
- Remove 10 pass-through getter declarations from renderer.hpp
- Remove individual audio manager forward declarations from renderer.hpp
- Redirect 69 internal renderer.cpp audio calls to audioCoordinator_ directly
- game_handler.cpp: withSoundManager template uses services_.audioCoordinator;
MFP changed from &Renderer::getUiSoundManager to &AudioCoordinator::getUiSoundManager
- GameServices struct: add AudioCoordinator* audioCoordinator member
- settings_panel: applyAudioVolumes(Renderer*) -> applyAudioVolumes(AudioCoordinator*)
2026-04-02 13:06:31 +03:00
|
|
|
auto* ac = services_.audioCoordinator;
|
|
|
|
|
if (ac) {
|
|
|
|
|
if (auto* sfx = ac->getUiSoundManager()) {
|
2026-02-18 04:11:00 -08:00
|
|
|
if (pendingLootMoneyAmount_ >= 10000) {
|
|
|
|
|
sfx->playLootCoinLarge();
|
|
|
|
|
} else {
|
|
|
|
|
sfx->playLootCoinSmall();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (pendingLootMoneyGuid_ != 0) {
|
|
|
|
|
recentLootMoneyAnnounceCooldowns_[pendingLootMoneyGuid_] = 1.5f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pendingLootMoneyGuid_ = 0;
|
|
|
|
|
pendingLootMoneyAmount_ = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto it = recentLootMoneyAnnounceCooldowns_.begin(); it != recentLootMoneyAnnounceCooldowns_.end();) {
|
|
|
|
|
it->second -= deltaTime;
|
|
|
|
|
if (it->second <= 0.0f) {
|
|
|
|
|
it = recentLootMoneyAnnounceCooldowns_.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 20:26:55 -08:00
|
|
|
// Auto-inspect throttling (fallback for player equipment visuals).
|
|
|
|
|
if (inspectRateLimit_ > 0.0f) {
|
|
|
|
|
inspectRateLimit_ = std::max(0.0f, inspectRateLimit_ - deltaTime);
|
|
|
|
|
}
|
2026-03-25 14:21:19 -07:00
|
|
|
if (isInWorld() && inspectRateLimit_ <= 0.0f && !pendingAutoInspect_.empty()) {
|
2026-02-13 20:26:55 -08:00
|
|
|
uint64_t guid = *pendingAutoInspect_.begin();
|
|
|
|
|
pendingAutoInspect_.erase(pendingAutoInspect_.begin());
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
if (guid != 0 && guid != playerGuid && entityController_->getEntityManager().hasEntity(guid)) {
|
2026-02-13 20:26:55 -08:00
|
|
|
auto pkt = InspectPacket::build(guid);
|
|
|
|
|
socket->send(pkt);
|
2026-02-16 00:51:59 -08:00
|
|
|
inspectRateLimit_ = 2.0f; // throttle to avoid compositing stutter
|
|
|
|
|
LOG_DEBUG("Sent CMSG_INSPECT for player 0x", std::hex, guid, std::dec);
|
2026-02-13 20:26:55 -08:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-25 15:23:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::update(float deltaTime) {
|
|
|
|
|
// Fire deferred char-create callback (outside ImGui render)
|
|
|
|
|
if (pendingCharCreateResult_) {
|
|
|
|
|
pendingCharCreateResult_ = false;
|
|
|
|
|
if (charCreateCallback_) {
|
|
|
|
|
charCreateCallback_(pendingCharCreateSuccess_, pendingCharCreateMsg_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!socket) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateNetworking(deltaTime);
|
|
|
|
|
if (!socket) return; // disconnect() may have been called
|
|
|
|
|
|
2026-04-10 22:22:14 +03:00
|
|
|
// Fallback for CMSG_CHAR_DELETE with no server response: if the server
|
|
|
|
|
// doesn't send SMSG_CHAR_DELETE within 3 seconds, re-request the character
|
|
|
|
|
// list. Some server cores silently process the delete without responding.
|
|
|
|
|
if (pendingCharDeleteResponse_) {
|
|
|
|
|
pendingDeleteTimer_ += deltaTime;
|
|
|
|
|
if (pendingDeleteTimer_ >= 3.0f) {
|
|
|
|
|
LOG_WARNING("No SMSG_CHAR_DELETE response after 3s — requesting character list to verify");
|
|
|
|
|
pendingCharDeleteResponse_ = false;
|
|
|
|
|
pendingDeleteFallbackEnum_ = true;
|
|
|
|
|
requestCharacterList();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// After the fallback SMSG_CHAR_ENUM has been processed, check if the
|
|
|
|
|
// character was actually removed and fire the delete callback.
|
|
|
|
|
if (pendingDeleteFallbackEnum_ && state == WorldState::CHAR_LIST_RECEIVED) {
|
|
|
|
|
pendingDeleteFallbackEnum_ = false;
|
|
|
|
|
uint64_t deletedGuid = pendingDeleteGuid_;
|
|
|
|
|
pendingDeleteGuid_ = 0;
|
|
|
|
|
bool found = false;
|
|
|
|
|
for (const auto& ch : characters) {
|
|
|
|
|
if (ch.guid == deletedGuid) { found = true; break; }
|
|
|
|
|
}
|
|
|
|
|
bool deleted = !found;
|
|
|
|
|
LOG_INFO("Char delete fallback: GUID 0x", std::hex, deletedGuid, std::dec,
|
|
|
|
|
deleted ? " was deleted" : " still exists");
|
|
|
|
|
std::string msg;
|
|
|
|
|
if (deleted) {
|
|
|
|
|
msg = "Character deleted.";
|
|
|
|
|
} else {
|
|
|
|
|
msg = "Delete failed: the server did not respond. "
|
|
|
|
|
"This usually happens if you recently logged out — "
|
|
|
|
|
"wait 20-30 seconds and try again.";
|
|
|
|
|
}
|
|
|
|
|
if (charDeleteCallback_) charDeleteCallback_(deleted, msg);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 15:23:31 -07:00
|
|
|
// Validate target still exists
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
if (targetGuid != 0 && !entityController_->getEntityManager().hasEntity(targetGuid)) {
|
2026-03-25 15:23:31 -07:00
|
|
|
clearTarget();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 17:54:56 -07:00
|
|
|
// Update auto-follow: refresh render position or cancel if entity disappeared
|
|
|
|
|
if (followTargetGuid_ != 0) {
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
auto followEnt = entityController_->getEntityManager().getEntity(followTargetGuid_);
|
2026-03-27 17:54:56 -07:00
|
|
|
if (followEnt) {
|
|
|
|
|
followRenderPos_ = core::coords::canonicalToRender(
|
|
|
|
|
glm::vec3(followEnt->getX(), followEnt->getY(), followEnt->getZ()));
|
|
|
|
|
} else {
|
|
|
|
|
cancelFollow();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 15:23:31 -07:00
|
|
|
// Detect combat state transitions → fire PLAYER_REGEN_DISABLED / PLAYER_REGEN_ENABLED
|
|
|
|
|
{
|
|
|
|
|
bool combatNow = isInCombat();
|
|
|
|
|
if (combatNow != wasCombat_) {
|
|
|
|
|
wasCombat_ = combatNow;
|
|
|
|
|
fireAddonEvent(combatNow ? "PLAYER_REGEN_DISABLED" : "PLAYER_REGEN_ENABLED", {});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateTimers(deltaTime);
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
// Send periodic heartbeat if in world
|
2026-02-06 23:52:16 -08:00
|
|
|
if (state == WorldState::IN_WORLD) {
|
2026-02-02 12:24:50 -08:00
|
|
|
timeSinceLastPing += deltaTime;
|
feat(game): introduce GameHandler domain interfaces and eliminate friend declarations
Add game_interfaces.hpp with five narrow domain contracts that GameHandler now
publishes to its domain handlers, replacing the previous friend-class anti-pattern.
Changes:
- include/game/game_interfaces.hpp (new): IConnectionState, ITargetingState,
IEntityAccess, ISocialState, IPvpState — each interface exposes only the state
its consumer legitimately needs
- include/game/game_handler.hpp: GameHandler inherits all five interfaces;
include of game_interfaces.hpp added
- include/game/movement_handler.hpp: remove `friend class GameHandler`; add
public named accessors for previously-private fields (monsterMovePacketsThisTickRef,
timeSinceLastMoveHeartbeatRef, resetMovementClock, setFalling, setFallStartMs)
- include/game/spell_handler.hpp: remove `friend class GameHandler/InventoryHandler/
CombatHandler/EntityController`; promote private packet handlers (handlePetSpells,
handleListStabledPets, pet stable commands, DBC loaders) to public; add accessor
methods for aura cache, known spells, and player aura slot mutation
- src/game/game_handler.cpp, game_handler_callbacks.cpp, game_handler_packets.cpp:
replace direct private field access with the new accessor API
(e.g. casting_ → isCasting(), monsterMovePacketsThisTick_ → ...ThisTickRef())
- src/game/inventory_handler.cpp, combat_handler.cpp, entity_controller.cpp:
replace friend-class private access with public accessor calls
No behaviour change. All 13 test suites pass. Zero build warnings.
2026-04-05 20:25:02 +03:00
|
|
|
if (movementHandler_) movementHandler_->timeSinceLastMoveHeartbeatRef() += deltaTime;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-03-15 01:21:23 -07:00
|
|
|
const float currentPingInterval =
|
2026-04-06 22:43:13 +03:00
|
|
|
(isPreWotlk()) ? game::CLASSIC_PING_INTERVAL_SEC : pingInterval;
|
2026-03-15 01:21:23 -07:00
|
|
|
if (timeSinceLastPing >= currentPingInterval) {
|
2026-02-05 12:01:03 -08:00
|
|
|
if (socket) {
|
|
|
|
|
sendPing();
|
|
|
|
|
}
|
2026-02-02 12:24:50 -08:00
|
|
|
timeSinceLastPing = 0.0f;
|
|
|
|
|
}
|
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-20 03:38:12 -08:00
|
|
|
const bool classicLikeCombatSync =
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
(combatHandler_ && combatHandler_->hasAutoAttackIntent()) && (isPreWotlk());
|
2026-03-29 18:28:58 -07:00
|
|
|
// Must match the locomotion bitmask in movement_handler.cpp so both
|
|
|
|
|
// sites agree on what constitutes "moving" for heartbeat throttling.
|
2026-03-15 06:13:36 -07:00
|
|
|
const uint32_t locomotionFlags =
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FORWARD) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::BACKWARD) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::STRAFE_LEFT) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::STRAFE_RIGHT) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::TURN_LEFT) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::TURN_RIGHT) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::ASCENDING) |
|
2026-03-29 18:28:58 -07:00
|
|
|
static_cast<uint32_t>(MovementFlags::DESCENDING) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::SWIMMING) |
|
2026-03-15 06:13:36 -07:00
|
|
|
static_cast<uint32_t>(MovementFlags::FALLING) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FALLINGFAR);
|
|
|
|
|
const bool classicLikeStationaryCombatSync =
|
|
|
|
|
classicLikeCombatSync &&
|
|
|
|
|
!onTaxiFlight_ &&
|
|
|
|
|
!taxiActivatePending_ &&
|
|
|
|
|
!taxiClientActive_ &&
|
|
|
|
|
(movementInfo.flags & locomotionFlags) == 0;
|
2026-02-20 03:38:12 -08:00
|
|
|
float heartbeatInterval = (onTaxiFlight_ || taxiActivatePending_ || taxiClientActive_)
|
2026-04-06 22:43:13 +03:00
|
|
|
? game::HEARTBEAT_INTERVAL_TAXI
|
|
|
|
|
: (classicLikeStationaryCombatSync ? game::HEARTBEAT_INTERVAL_STATIONARY_COMBAT
|
|
|
|
|
: (classicLikeCombatSync ? game::HEARTBEAT_INTERVAL_MOVING_COMBAT
|
2026-03-15 06:13:36 -07:00
|
|
|
: moveHeartbeatInterval_));
|
feat(game): introduce GameHandler domain interfaces and eliminate friend declarations
Add game_interfaces.hpp with five narrow domain contracts that GameHandler now
publishes to its domain handlers, replacing the previous friend-class anti-pattern.
Changes:
- include/game/game_interfaces.hpp (new): IConnectionState, ITargetingState,
IEntityAccess, ISocialState, IPvpState — each interface exposes only the state
its consumer legitimately needs
- include/game/game_handler.hpp: GameHandler inherits all five interfaces;
include of game_interfaces.hpp added
- include/game/movement_handler.hpp: remove `friend class GameHandler`; add
public named accessors for previously-private fields (monsterMovePacketsThisTickRef,
timeSinceLastMoveHeartbeatRef, resetMovementClock, setFalling, setFallStartMs)
- include/game/spell_handler.hpp: remove `friend class GameHandler/InventoryHandler/
CombatHandler/EntityController`; promote private packet handlers (handlePetSpells,
handleListStabledPets, pet stable commands, DBC loaders) to public; add accessor
methods for aura cache, known spells, and player aura slot mutation
- src/game/game_handler.cpp, game_handler_callbacks.cpp, game_handler_packets.cpp:
replace direct private field access with the new accessor API
(e.g. casting_ → isCasting(), monsterMovePacketsThisTick_ → ...ThisTickRef())
- src/game/inventory_handler.cpp, combat_handler.cpp, entity_controller.cpp:
replace friend-class private access with public accessor calls
No behaviour change. All 13 test suites pass. Zero build warnings.
2026-04-05 20:25:02 +03:00
|
|
|
if (movementHandler_ && movementHandler_->timeSinceLastMoveHeartbeatRef() >= heartbeatInterval) {
|
2026-02-20 02:50:59 -08:00
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
feat(game): introduce GameHandler domain interfaces and eliminate friend declarations
Add game_interfaces.hpp with five narrow domain contracts that GameHandler now
publishes to its domain handlers, replacing the previous friend-class anti-pattern.
Changes:
- include/game/game_interfaces.hpp (new): IConnectionState, ITargetingState,
IEntityAccess, ISocialState, IPvpState — each interface exposes only the state
its consumer legitimately needs
- include/game/game_handler.hpp: GameHandler inherits all five interfaces;
include of game_interfaces.hpp added
- include/game/movement_handler.hpp: remove `friend class GameHandler`; add
public named accessors for previously-private fields (monsterMovePacketsThisTickRef,
timeSinceLastMoveHeartbeatRef, resetMovementClock, setFalling, setFallStartMs)
- include/game/spell_handler.hpp: remove `friend class GameHandler/InventoryHandler/
CombatHandler/EntityController`; promote private packet handlers (handlePetSpells,
handleListStabledPets, pet stable commands, DBC loaders) to public; add accessor
methods for aura cache, known spells, and player aura slot mutation
- src/game/game_handler.cpp, game_handler_callbacks.cpp, game_handler_packets.cpp:
replace direct private field access with the new accessor API
(e.g. casting_ → isCasting(), monsterMovePacketsThisTick_ → ...ThisTickRef())
- src/game/inventory_handler.cpp, combat_handler.cpp, entity_controller.cpp:
replace friend-class private access with public accessor calls
No behaviour change. All 13 test suites pass. Zero build warnings.
2026-04-05 20:25:02 +03:00
|
|
|
movementHandler_->timeSinceLastMoveHeartbeatRef() = 0.0f;
|
2026-02-11 21:14:35 -08:00
|
|
|
}
|
|
|
|
|
|
2026-02-26 17:56:11 -08:00
|
|
|
// Check area triggers (instance portals, tavern rests, etc.)
|
|
|
|
|
areaTriggerCheckTimer_ += deltaTime;
|
2026-04-06 22:43:13 +03:00
|
|
|
if (areaTriggerCheckTimer_ >= game::AREA_TRIGGER_CHECK_INTERVAL) {
|
2026-02-26 17:56:11 -08:00
|
|
|
areaTriggerCheckTimer_ = 0.0f;
|
|
|
|
|
checkAreaTriggers();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 18:21:03 -07:00
|
|
|
// Cancel GO interaction cast if player enters combat (auto-attack).
|
2026-02-19 03:31:49 -08:00
|
|
|
if (pendingGameObjectInteractGuid_ != 0 &&
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
combatHandler_ && (combatHandler_->isAutoAttacking() || combatHandler_->hasAutoAttackIntent())) {
|
2026-02-19 03:31:49 -08:00
|
|
|
pendingGameObjectInteractGuid_ = 0;
|
2026-03-29 18:21:03 -07:00
|
|
|
if (spellHandler_) spellHandler_->resetCastState();
|
2026-03-12 01:22:42 -07:00
|
|
|
addUIError("Interrupted.");
|
2026-02-19 03:31:49 -08:00
|
|
|
addSystemChatMessage("Interrupted.");
|
|
|
|
|
}
|
2026-03-29 18:21:03 -07:00
|
|
|
// Check if client-side cast timer expired (tick-down is in SpellHandler::updateTimers).
|
2026-03-29 22:58:49 -07:00
|
|
|
// Two paths depending on whether this is a GO interaction cast:
|
feat(game): introduce GameHandler domain interfaces and eliminate friend declarations
Add game_interfaces.hpp with five narrow domain contracts that GameHandler now
publishes to its domain handlers, replacing the previous friend-class anti-pattern.
Changes:
- include/game/game_interfaces.hpp (new): IConnectionState, ITargetingState,
IEntityAccess, ISocialState, IPvpState — each interface exposes only the state
its consumer legitimately needs
- include/game/game_handler.hpp: GameHandler inherits all five interfaces;
include of game_interfaces.hpp added
- include/game/movement_handler.hpp: remove `friend class GameHandler`; add
public named accessors for previously-private fields (monsterMovePacketsThisTickRef,
timeSinceLastMoveHeartbeatRef, resetMovementClock, setFalling, setFallStartMs)
- include/game/spell_handler.hpp: remove `friend class GameHandler/InventoryHandler/
CombatHandler/EntityController`; promote private packet handlers (handlePetSpells,
handleListStabledPets, pet stable commands, DBC loaders) to public; add accessor
methods for aura cache, known spells, and player aura slot mutation
- src/game/game_handler.cpp, game_handler_callbacks.cpp, game_handler_packets.cpp:
replace direct private field access with the new accessor API
(e.g. casting_ → isCasting(), monsterMovePacketsThisTick_ → ...ThisTickRef())
- src/game/inventory_handler.cpp, combat_handler.cpp, entity_controller.cpp:
replace friend-class private access with public accessor calls
No behaviour change. All 13 test suites pass. Zero build warnings.
2026-04-05 20:25:02 +03:00
|
|
|
if (spellHandler_ && spellHandler_->isCasting() && spellHandler_->getCastTimeRemaining() <= 0.0f) {
|
2026-03-29 18:21:03 -07:00
|
|
|
if (pendingGameObjectInteractGuid_ != 0) {
|
2026-03-29 22:58:49 -07:00
|
|
|
// GO interaction cast: do NOT call resetCastState() here. The server
|
|
|
|
|
// sends SMSG_SPELL_GO when the cast completes server-side (~50-200ms
|
|
|
|
|
// after the client timer expires due to float precision/frame timing).
|
|
|
|
|
// handleSpellGo checks `wasInTimedCast = casting_ && spellId == currentCastSpellId_`
|
|
|
|
|
// — if we clear those fields now, wasInTimedCast is false and the loot
|
|
|
|
|
// path (CMSG_LOOT via lastInteractedGoGuid_) never fires.
|
|
|
|
|
// Let the cast bar sit at 100% until SMSG_SPELL_GO arrives to clean up.
|
2026-03-29 18:21:03 -07:00
|
|
|
pendingGameObjectInteractGuid_ = 0;
|
2026-03-29 22:58:49 -07:00
|
|
|
} else {
|
|
|
|
|
// Regular cast with no GO pending: clean up immediately.
|
|
|
|
|
spellHandler_->resetCastState();
|
2026-03-09 23:06:40 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 18:21:03 -07:00
|
|
|
// Unit cast states and spell cooldowns are ticked by SpellHandler::updateTimers()
|
|
|
|
|
// (called from GameHandler::updateTimers above). No duplicate tick-down here.
|
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
|
|
|
|
|
|
|
|
// Update action bar cooldowns
|
|
|
|
|
for (auto& slot : actionBar) {
|
|
|
|
|
if (slot.cooldownRemaining > 0.0f) {
|
|
|
|
|
slot.cooldownRemaining -= deltaTime;
|
|
|
|
|
if (slot.cooldownRemaining < 0.0f) slot.cooldownRemaining = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update combat text (Phase 2)
|
|
|
|
|
updateCombatText(deltaTime);
|
2026-03-09 20:36:20 -07:00
|
|
|
tickMinimapPings(deltaTime);
|
2026-02-05 12:01:03 -08:00
|
|
|
|
2026-03-13 10:13:54 -07:00
|
|
|
// Tick logout countdown
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (socialHandler_) socialHandler_->updateLogoutCountdown(deltaTime);
|
2026-03-13 10:13:54 -07:00
|
|
|
|
2026-03-25 15:32:51 -07:00
|
|
|
updateTaxiAndMountState(deltaTime);
|
2026-02-08 23:15:26 -08:00
|
|
|
|
2026-02-10 21:29:10 -08:00
|
|
|
// Update transport manager
|
|
|
|
|
if (transportManager_) {
|
|
|
|
|
transportManager_->update(deltaTime);
|
2026-02-12 00:04:53 -08:00
|
|
|
updateAttachedTransportChildren(deltaTime);
|
2026-02-10 21:29:10 -08:00
|
|
|
}
|
|
|
|
|
|
2026-03-25 15:37:19 -07:00
|
|
|
updateAutoAttack(deltaTime);
|
2026-03-25 14:53:16 -07:00
|
|
|
auto closeIfTooFar = [&](bool windowOpen, uint64_t npcGuid, auto closeFn, const char* label) {
|
|
|
|
|
if (!windowOpen || npcGuid == 0) return;
|
refactor(game): extract EntityController from GameHandler (step 1.3)
Moves entity lifecycle, name/creature/game-object caches, transport GUID
tracking, and the entire update-object pipeline out of GameHandler into a
new EntityController class (friend-class pattern, same as CombatHandler
et al.).
What moved:
- applyUpdateObjectBlock() — 1,520-line core of all entity creation,
field updates, and movement application
- processOutOfRangeObjects() / finalizeUpdateObjectBatch()
- handleUpdateObject() / handleCompressedUpdateObject() / handleDestroyObject()
- handleNameQueryResponse() / handleCreatureQueryResponse()
- handleGameObjectQueryResponse() / handleGameObjectPageText()
- handlePageTextQueryResponse()
- enqueueUpdateObjectWork() / processPendingUpdateObjectWork()
- playerNameCache, playerClassRaceCache_, pendingNameQueries
- creatureInfoCache, pendingCreatureQueries
- gameObjectInfoCache_, pendingGameObjectQueries_
- transportGuids_, serverUpdatedTransportGuids_
- EntityManager (accessed by other handlers via getEntityManager())
8 opcodes re-registered by EntityController::registerOpcodes():
SMSG_UPDATE_OBJECT, SMSG_COMPRESSED_UPDATE_OBJECT, SMSG_DESTROY_OBJECT,
SMSG_NAME_QUERY_RESPONSE, SMSG_CREATURE_QUERY_RESPONSE,
SMSG_GAMEOBJECT_QUERY_RESPONSE, SMSG_GAMEOBJECT_PAGETEXT,
SMSG_PAGE_TEXT_QUERY_RESPONSE
Other handler files (combat, movement, social, spell, inventory, quest,
chat) updated to access EntityManager via getEntityManager() and the
name cache via getPlayerNameCache() — no logic changes.
Also included:
- .clang-tidy: add modernize-use-nodiscard,
modernize-use-designated-initializers; set -std=c++20 in ExtraArgs
- test.sh: prepend clang's own resource include dir before GCC's to
silence xmmintrin.h / ia32intrin.h conflicts during clang-tidy runs
Line counts:
entity_controller.hpp 147 lines (new)
entity_controller.cpp 2172 lines (new)
game_handler.cpp 8095 lines (was 10143, −2048)
Build: 0 errors, 0 warnings.
2026-03-29 08:21:27 +03:00
|
|
|
auto npc = entityController_->getEntityManager().getEntity(npcGuid);
|
2026-03-25 14:53:16 -07:00
|
|
|
if (!npc) return;
|
|
|
|
|
float dx = movementInfo.x - npc->getX();
|
|
|
|
|
float dy = movementInfo.y - npc->getY();
|
2026-04-06 22:43:13 +03:00
|
|
|
if (std::sqrt(dx * dx + dy * dy) > game::NPC_INTERACT_MAX_DISTANCE) {
|
2026-03-25 14:53:16 -07:00
|
|
|
closeFn();
|
|
|
|
|
LOG_INFO(label, " closed: walked too far from NPC");
|
2026-02-07 19:44:03 -08:00
|
|
|
}
|
2026-03-25 14:53:16 -07:00
|
|
|
};
|
2026-03-28 12:43:44 -07:00
|
|
|
closeIfTooFar(isVendorWindowOpen(), getVendorItems().vendorGuid, [this]{ closeVendor(); }, "Vendor");
|
|
|
|
|
closeIfTooFar(isGossipWindowOpen(), getCurrentGossip().npcGuid, [this]{ closeGossip(); }, "Gossip");
|
|
|
|
|
closeIfTooFar(isTaxiWindowOpen(), taxiNpcGuid_, [this]{ closeTaxi(); }, "Taxi window");
|
|
|
|
|
closeIfTooFar(isTrainerWindowOpen(), getTrainerSpells().trainerGuid, [this]{ closeTrainer(); }, "Trainer");
|
2026-02-07 18:33:14 -08:00
|
|
|
|
2026-03-25 15:27:31 -07:00
|
|
|
updateEntityInterpolation(deltaTime);
|
2026-02-10 19:30:45 -08:00
|
|
|
|
2026-02-07 00:12:39 -08:00
|
|
|
}
|
2026-02-02 12:24:50 -08:00
|
|
|
}
|
|
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
// ============================================================
|
|
|
|
|
// Single-player local combat
|
|
|
|
|
// ============================================================
|
2026-02-08 21:32:38 -08:00
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
// ============================================================
|
|
|
|
|
// XP tracking
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
// WotLK 3.3.5a XP-to-next-level table (from player_xp_for_level)
|
2026-04-06 22:43:13 +03:00
|
|
|
static constexpr uint32_t XP_TABLE[] = {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
0, // level 0 (unused)
|
|
|
|
|
400, 900, 1400, 2100, 2800, 3600, 4500, 5400, 6500, 7600, // 1-10
|
|
|
|
|
8700, 9800, 11000, 12300, 13600, 15000, 16400, 17800, 19300, 20800, // 11-20
|
|
|
|
|
22400, 24000, 25500, 27200, 28900, 30500, 32200, 33900, 36300, 38800, // 21-30
|
|
|
|
|
41600, 44600, 48000, 51400, 55000, 58700, 62400, 66200, 70200, 74300, // 31-40
|
|
|
|
|
78500, 82800, 87100, 91600, 96300, 101000, 105800, 110700, 115700, 120900, // 41-50
|
|
|
|
|
126100, 131500, 137000, 142500, 148200, 154000, 159900, 165800, 172000, 290000, // 51-60
|
|
|
|
|
317000, 349000, 386000, 428000, 475000, 527000, 585000, 648000, 717000, 1523800, // 61-70
|
|
|
|
|
1539600, 1555700, 1571800, 1587900, 1604200, 1620700, 1637400, 1653900, 1670800 // 71-79
|
|
|
|
|
};
|
|
|
|
|
static constexpr uint32_t XP_TABLE_SIZE = sizeof(XP_TABLE) / sizeof(XP_TABLE[0]);
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::xpForLevel(uint32_t level) {
|
|
|
|
|
if (level == 0 || level >= XP_TABLE_SIZE) return 0;
|
|
|
|
|
return XP_TABLE[level];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::killXp(uint32_t playerLevel, uint32_t victimLevel) {
|
|
|
|
|
return CombatHandler::killXp(playerLevel, victimLevel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::handleXpGain(network::Packet& packet) {
|
|
|
|
|
if (combatHandler_) combatHandler_->handleXpGain(packet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::addMoneyCopper(uint32_t amount) {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->addMoneyCopper(amount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::addSystemChatMessage(const std::string& message) {
|
|
|
|
|
if (chatHandler_) chatHandler_->addSystemChatMessage(message);
|
|
|
|
|
}
|
2026-02-08 21:32:38 -08:00
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
// ============================================================
|
|
|
|
|
// Taxi / Flight Path Handlers
|
|
|
|
|
// ============================================================
|
2026-02-11 18:25:04 -08:00
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
void GameHandler::updateClientTaxi(float deltaTime) {
|
|
|
|
|
if (movementHandler_) movementHandler_->updateClientTaxi(deltaTime);
|
|
|
|
|
}
|
2026-03-13 08:05:16 -07:00
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
void GameHandler::closeTaxi() {
|
|
|
|
|
if (movementHandler_) movementHandler_->closeTaxi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getTaxiCostTo(uint32_t destNodeId) const {
|
|
|
|
|
if (movementHandler_) return movementHandler_->getTaxiCostTo(destNodeId);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::activateTaxi(uint32_t destNodeId) {
|
|
|
|
|
if (movementHandler_) movementHandler_->activateTaxi(destNodeId);
|
2026-02-07 16:59:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
2026-02-07 12:43:32 -08:00
|
|
|
// Server Info Command Handlers
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void GameHandler::handleQueryTimeResponse(network::Packet& packet) {
|
|
|
|
|
QueryTimeResponseData data;
|
|
|
|
|
if (!QueryTimeResponseParser::parse(packet, data)) {
|
|
|
|
|
LOG_WARNING("Failed to parse SMSG_QUERY_TIME_RESPONSE");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert Unix timestamp to readable format
|
|
|
|
|
time_t serverTime = static_cast<time_t>(data.serverTime);
|
|
|
|
|
struct tm* timeInfo = localtime(&serverTime);
|
|
|
|
|
char timeStr[64];
|
|
|
|
|
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", timeInfo);
|
|
|
|
|
|
|
|
|
|
std::string msg = "Server time: " + std::string(timeStr);
|
|
|
|
|
addSystemChatMessage(msg);
|
|
|
|
|
LOG_INFO("Server time: ", data.serverTime, " (", timeStr, ")");
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
uint32_t GameHandler::generateClientSeed() {
|
|
|
|
|
// Generate cryptographically random seed
|
|
|
|
|
std::random_device rd;
|
|
|
|
|
std::mt19937 gen(rd());
|
|
|
|
|
std::uniform_int_distribution<uint32_t> dis(1, 0xFFFFFFFF);
|
|
|
|
|
return dis(gen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::setState(WorldState newState) {
|
|
|
|
|
if (state != newState) {
|
2026-03-25 11:40:49 -07:00
|
|
|
LOG_DEBUG("World state: ", static_cast<int>(state), " -> ", static_cast<int>(newState));
|
2026-02-02 12:24:50 -08:00
|
|
|
state = newState;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::fail(const std::string& reason) {
|
|
|
|
|
LOG_ERROR("World connection failed: ", reason);
|
|
|
|
|
setState(WorldState::FAILED);
|
|
|
|
|
|
|
|
|
|
if (onFailure) {
|
|
|
|
|
onFailure(reason);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-07 14:21:50 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// Player Skills
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
static const std::string kEmptySkillName;
|
|
|
|
|
|
|
|
|
|
const std::string& GameHandler::getSkillName(uint32_t skillId) const {
|
|
|
|
|
auto it = skillLineNames_.find(skillId);
|
|
|
|
|
return (it != skillLineNames_.end()) ? it->second : kEmptySkillName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getSkillCategory(uint32_t skillId) const {
|
|
|
|
|
auto it = skillLineCategories_.find(skillId);
|
|
|
|
|
return (it != skillLineCategories_.end()) ? it->second : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 10:12:49 -07:00
|
|
|
bool GameHandler::isProfessionSpell(uint32_t spellId) const {
|
|
|
|
|
auto slIt = spellToSkillLine_.find(spellId);
|
|
|
|
|
if (slIt == spellToSkillLine_.end()) return false;
|
|
|
|
|
auto catIt = skillLineCategories_.find(slIt->second);
|
|
|
|
|
if (catIt == skillLineCategories_.end()) return false;
|
|
|
|
|
// Category 11 = profession (Blacksmithing, etc.), 9 = secondary (Cooking, First Aid, Fishing)
|
|
|
|
|
return catIt->second == 11 || catIt->second == 9;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-07 14:21:50 -08:00
|
|
|
void GameHandler::loadSkillLineDbc() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (spellHandler_) spellHandler_->loadSkillLineDbc();
|
2026-02-07 14:21:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::extractSkillFields(const std::map<uint16_t, uint32_t>& fields) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (spellHandler_) spellHandler_->extractSkillFields(fields);
|
2026-02-07 14:21:50 -08:00
|
|
|
}
|
|
|
|
|
|
2026-02-11 18:25:04 -08:00
|
|
|
void GameHandler::extractExploredZoneFields(const std::map<uint16_t, uint32_t>& fields) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (spellHandler_) spellHandler_->extractExploredZoneFields(fields);
|
2026-02-11 18:25:04 -08:00
|
|
|
}
|
|
|
|
|
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
std::string GameHandler::getCharacterConfigDir() {
|
|
|
|
|
std::string dir;
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
const char* appdata = std::getenv("APPDATA");
|
|
|
|
|
dir = appdata ? std::string(appdata) + "\\wowee\\characters" : "characters";
|
|
|
|
|
#else
|
|
|
|
|
const char* home = std::getenv("HOME");
|
|
|
|
|
dir = home ? std::string(home) + "/.wowee/characters" : "characters";
|
|
|
|
|
#endif
|
|
|
|
|
return dir;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 02:07:59 -07:00
|
|
|
static const std::string EMPTY_MACRO_TEXT;
|
|
|
|
|
|
|
|
|
|
const std::string& GameHandler::getMacroText(uint32_t macroId) const {
|
|
|
|
|
auto it = macros_.find(macroId);
|
|
|
|
|
return (it != macros_.end()) ? it->second : EMPTY_MACRO_TEXT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::setMacroText(uint32_t macroId, const std::string& text) {
|
|
|
|
|
if (text.empty())
|
|
|
|
|
macros_.erase(macroId);
|
|
|
|
|
else
|
|
|
|
|
macros_[macroId] = text;
|
|
|
|
|
saveCharacterConfig();
|
|
|
|
|
}
|
|
|
|
|
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
void GameHandler::saveCharacterConfig() {
|
|
|
|
|
const Character* ch = getActiveCharacter();
|
|
|
|
|
if (!ch || ch->name.empty()) return;
|
|
|
|
|
|
|
|
|
|
std::string dir = getCharacterConfigDir();
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
std::filesystem::create_directories(dir, ec);
|
|
|
|
|
|
|
|
|
|
std::string path = dir + "/" + ch->name + ".cfg";
|
|
|
|
|
std::ofstream out(path);
|
|
|
|
|
if (!out.is_open()) {
|
|
|
|
|
LOG_WARNING("Could not save character config to ", path);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out << "character_guid=" << playerGuid << "\n";
|
2026-02-09 17:39:21 -08:00
|
|
|
out << "gender=" << static_cast<int>(ch->gender) << "\n";
|
2026-02-18 22:36:34 -08:00
|
|
|
// For male/female, derive from gender; only nonbinary has a meaningful separate choice
|
|
|
|
|
bool saveUseFemaleModel = (ch->gender == Gender::NONBINARY) ? ch->useFemaleModel
|
|
|
|
|
: (ch->gender == Gender::FEMALE);
|
|
|
|
|
out << "use_female_model=" << (saveUseFemaleModel ? 1 : 0) << "\n";
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
for (int i = 0; i < ACTION_BAR_SLOTS; i++) {
|
|
|
|
|
out << "action_bar_" << i << "_type=" << static_cast<int>(actionBar[i].type) << "\n";
|
|
|
|
|
out << "action_bar_" << i << "_id=" << actionBar[i].id << "\n";
|
|
|
|
|
}
|
2026-02-10 01:24:37 -08:00
|
|
|
|
2026-03-18 02:44:28 -07:00
|
|
|
// Save client-side macro text (escape newlines as \n literal)
|
2026-03-18 02:07:59 -07:00
|
|
|
for (const auto& [id, text] : macros_) {
|
2026-03-18 02:44:28 -07:00
|
|
|
if (!text.empty()) {
|
|
|
|
|
std::string escaped;
|
|
|
|
|
escaped.reserve(text.size());
|
|
|
|
|
for (char c : text) {
|
|
|
|
|
if (c == '\n') { escaped += "\\n"; }
|
|
|
|
|
else if (c == '\r') { /* skip CR */ }
|
|
|
|
|
else if (c == '\\') { escaped += "\\\\"; }
|
|
|
|
|
else { escaped += c; }
|
|
|
|
|
}
|
|
|
|
|
out << "macro_" << id << "_text=" << escaped << "\n";
|
|
|
|
|
}
|
2026-03-18 02:07:59 -07:00
|
|
|
}
|
|
|
|
|
|
2026-02-10 01:24:37 -08:00
|
|
|
// Save quest log
|
|
|
|
|
out << "quest_log_count=" << questLog_.size() << "\n";
|
|
|
|
|
for (size_t i = 0; i < questLog_.size(); i++) {
|
|
|
|
|
const auto& quest = questLog_[i];
|
|
|
|
|
out << "quest_" << i << "_id=" << quest.questId << "\n";
|
|
|
|
|
out << "quest_" << i << "_title=" << quest.title << "\n";
|
|
|
|
|
out << "quest_" << i << "_complete=" << (quest.complete ? 1 : 0) << "\n";
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 09:44:41 -07:00
|
|
|
// Save tracked quest IDs so the quest tracker restores on login
|
|
|
|
|
if (!trackedQuestIds_.empty()) {
|
|
|
|
|
std::string ids;
|
|
|
|
|
for (uint32_t qid : trackedQuestIds_) {
|
|
|
|
|
if (!ids.empty()) ids += ',';
|
|
|
|
|
ids += std::to_string(qid);
|
|
|
|
|
}
|
|
|
|
|
out << "tracked_quests=" << ids << "\n";
|
|
|
|
|
}
|
|
|
|
|
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
LOG_INFO("Character config saved to ", path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::loadCharacterConfig() {
|
|
|
|
|
const Character* ch = getActiveCharacter();
|
|
|
|
|
if (!ch || ch->name.empty()) return;
|
|
|
|
|
|
|
|
|
|
std::string path = getCharacterConfigDir() + "/" + ch->name + ".cfg";
|
|
|
|
|
std::ifstream in(path);
|
|
|
|
|
if (!in.is_open()) return;
|
|
|
|
|
|
|
|
|
|
uint64_t savedGuid = 0;
|
|
|
|
|
std::array<int, ACTION_BAR_SLOTS> types{};
|
|
|
|
|
std::array<uint32_t, ACTION_BAR_SLOTS> ids{};
|
|
|
|
|
bool hasSlots = false;
|
2026-02-09 17:39:21 -08:00
|
|
|
int savedGender = -1;
|
2026-02-09 17:56:04 -08:00
|
|
|
int savedUseFemaleModel = -1;
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
while (std::getline(in, line)) {
|
|
|
|
|
size_t eq = line.find('=');
|
|
|
|
|
if (eq == std::string::npos) continue;
|
|
|
|
|
std::string key = line.substr(0, eq);
|
|
|
|
|
std::string val = line.substr(eq + 1);
|
|
|
|
|
|
|
|
|
|
if (key == "character_guid") {
|
|
|
|
|
try { savedGuid = std::stoull(val); } catch (...) {}
|
2026-02-09 17:39:21 -08:00
|
|
|
} else if (key == "gender") {
|
|
|
|
|
try { savedGender = std::stoi(val); } catch (...) {}
|
2026-02-09 17:56:04 -08:00
|
|
|
} else if (key == "use_female_model") {
|
|
|
|
|
try { savedUseFemaleModel = std::stoi(val); } catch (...) {}
|
2026-03-18 02:07:59 -07:00
|
|
|
} else if (key.rfind("macro_", 0) == 0) {
|
|
|
|
|
// Parse macro_N_text
|
|
|
|
|
size_t firstUnder = 6; // length of "macro_"
|
|
|
|
|
size_t secondUnder = key.find('_', firstUnder);
|
|
|
|
|
if (secondUnder == std::string::npos) continue;
|
|
|
|
|
uint32_t macroId = 0;
|
|
|
|
|
try { macroId = static_cast<uint32_t>(std::stoul(key.substr(firstUnder, secondUnder - firstUnder))); } catch (...) { continue; }
|
2026-03-18 02:44:28 -07:00
|
|
|
if (key.substr(secondUnder + 1) == "text" && !val.empty()) {
|
|
|
|
|
// Unescape \n and \\ sequences
|
|
|
|
|
std::string unescaped;
|
|
|
|
|
unescaped.reserve(val.size());
|
|
|
|
|
for (size_t i = 0; i < val.size(); ++i) {
|
|
|
|
|
if (val[i] == '\\' && i + 1 < val.size()) {
|
|
|
|
|
if (val[i+1] == 'n') { unescaped += '\n'; ++i; }
|
|
|
|
|
else if (val[i+1] == '\\') { unescaped += '\\'; ++i; }
|
|
|
|
|
else { unescaped += val[i]; }
|
|
|
|
|
} else {
|
|
|
|
|
unescaped += val[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
macros_[macroId] = std::move(unescaped);
|
|
|
|
|
}
|
2026-03-20 09:44:41 -07:00
|
|
|
} else if (key == "tracked_quests" && !val.empty()) {
|
|
|
|
|
// Parse comma-separated quest IDs
|
|
|
|
|
trackedQuestIds_.clear();
|
|
|
|
|
size_t tqPos = 0;
|
|
|
|
|
while (tqPos <= val.size()) {
|
|
|
|
|
size_t comma = val.find(',', tqPos);
|
|
|
|
|
std::string idStr = (comma != std::string::npos)
|
|
|
|
|
? val.substr(tqPos, comma - tqPos) : val.substr(tqPos);
|
|
|
|
|
try {
|
|
|
|
|
uint32_t qid = static_cast<uint32_t>(std::stoul(idStr));
|
|
|
|
|
if (qid != 0) trackedQuestIds_.insert(qid);
|
|
|
|
|
} catch (...) {}
|
|
|
|
|
if (comma == std::string::npos) break;
|
|
|
|
|
tqPos = comma + 1;
|
|
|
|
|
}
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
} else if (key.rfind("action_bar_", 0) == 0) {
|
|
|
|
|
// Parse action_bar_N_type or action_bar_N_id
|
|
|
|
|
size_t firstUnderscore = 11; // length of "action_bar_"
|
|
|
|
|
size_t secondUnderscore = key.find('_', firstUnderscore);
|
|
|
|
|
if (secondUnderscore == std::string::npos) continue;
|
|
|
|
|
int slot = -1;
|
|
|
|
|
try { slot = std::stoi(key.substr(firstUnderscore, secondUnderscore - firstUnderscore)); } catch (...) { continue; }
|
|
|
|
|
if (slot < 0 || slot >= ACTION_BAR_SLOTS) continue;
|
|
|
|
|
std::string suffix = key.substr(secondUnderscore + 1);
|
|
|
|
|
try {
|
|
|
|
|
if (suffix == "type") {
|
|
|
|
|
types[slot] = std::stoi(val);
|
|
|
|
|
hasSlots = true;
|
|
|
|
|
} else if (suffix == "id") {
|
|
|
|
|
ids[slot] = static_cast<uint32_t>(std::stoul(val));
|
|
|
|
|
hasSlots = true;
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate guid matches current character
|
|
|
|
|
if (savedGuid != 0 && savedGuid != playerGuid) {
|
|
|
|
|
LOG_WARNING("Character config guid mismatch for ", ch->name, ", using defaults");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 17:56:04 -08:00
|
|
|
// Apply saved gender and body type (allows nonbinary to persist even though server only stores male/female)
|
2026-02-09 17:39:21 -08:00
|
|
|
if (savedGender >= 0 && savedGender <= 2) {
|
|
|
|
|
for (auto& character : characters) {
|
|
|
|
|
if (character.guid == playerGuid) {
|
|
|
|
|
character.gender = static_cast<Gender>(savedGender);
|
2026-02-18 22:36:34 -08:00
|
|
|
if (character.gender == Gender::NONBINARY) {
|
|
|
|
|
// Only nonbinary characters have a meaningful body type choice
|
|
|
|
|
if (savedUseFemaleModel >= 0) {
|
|
|
|
|
character.useFemaleModel = (savedUseFemaleModel != 0);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Male/female always use the model matching their gender
|
|
|
|
|
character.useFemaleModel = (character.gender == Gender::FEMALE);
|
2026-02-09 17:56:04 -08:00
|
|
|
}
|
|
|
|
|
LOG_INFO("Applied saved gender: ", getGenderName(character.gender),
|
|
|
|
|
", body type: ", (character.useFemaleModel ? "feminine" : "masculine"));
|
2026-02-09 17:39:21 -08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
if (hasSlots) {
|
|
|
|
|
for (int i = 0; i < ACTION_BAR_SLOTS; i++) {
|
|
|
|
|
actionBar[i].type = static_cast<ActionBarSlot::Type>(types[i]);
|
|
|
|
|
actionBar[i].id = ids[i];
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Character config loaded from ", path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 00:04:53 -08:00
|
|
|
void GameHandler::setTransportAttachment(uint64_t childGuid, ObjectType type, uint64_t transportGuid,
|
|
|
|
|
const glm::vec3& localOffset, bool hasLocalOrientation,
|
|
|
|
|
float localOrientation) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (movementHandler_) movementHandler_->setTransportAttachment(childGuid, type, transportGuid, localOffset, hasLocalOrientation, localOrientation);
|
2026-02-12 00:04:53 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::clearTransportAttachment(uint64_t childGuid) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (movementHandler_) movementHandler_->clearTransportAttachment(childGuid);
|
2026-02-12 00:04:53 -08:00
|
|
|
}
|
|
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
void GameHandler::updateAttachedTransportChildren(float deltaTime) {
|
|
|
|
|
if (movementHandler_) movementHandler_->updateAttachedTransportChildren(deltaTime);
|
2026-02-12 00:04:53 -08:00
|
|
|
}
|
|
|
|
|
|
2026-02-15 14:00:41 -08:00
|
|
|
// ============================================================
|
|
|
|
|
// Mail System
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
2026-04-05 04:22:48 -07:00
|
|
|
void GameHandler::openMailbox(uint64_t guid) {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->openMailbox(guid);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 14:00:41 -08:00
|
|
|
void GameHandler::closeMailbox() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->closeMailbox();
|
2026-02-15 14:00:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::refreshMailList() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->refreshMailList();
|
2026-02-15 14:00:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::sendMail(const std::string& recipient, const std::string& subject,
|
2026-03-27 17:20:31 -07:00
|
|
|
const std::string& body, uint64_t money, uint64_t cod) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->sendMail(recipient, subject, body, money, cod);
|
2026-02-25 14:11:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::attachItemFromBackpack(int backpackIndex) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
return inventoryHandler_ && inventoryHandler_->attachItemFromBackpack(backpackIndex);
|
2026-02-25 14:11:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::attachItemFromBag(int bagIndex, int slotIndex) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
return inventoryHandler_ && inventoryHandler_->attachItemFromBag(bagIndex, slotIndex);
|
2026-02-25 14:11:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::detachMailAttachment(int attachIndex) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
return inventoryHandler_ && inventoryHandler_->detachMailAttachment(attachIndex);
|
2026-02-15 14:00:41 -08:00
|
|
|
}
|
|
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
void GameHandler::clearMailAttachments() {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->clearMailAttachments();
|
|
|
|
|
}
|
2026-02-15 14:00:41 -08:00
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
int GameHandler::getMailAttachmentCount() const {
|
|
|
|
|
if (inventoryHandler_) return inventoryHandler_->getMailAttachmentCount();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2026-02-15 14:00:41 -08:00
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
void GameHandler::mailTakeMoney(uint32_t mailId) {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->mailTakeMoney(mailId);
|
|
|
|
|
}
|
2026-02-15 14:00:41 -08:00
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
void GameHandler::mailTakeItem(uint32_t mailId, uint32_t itemGuidLow) {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->mailTakeItem(mailId, itemGuidLow);
|
2026-02-15 14:00:41 -08:00
|
|
|
}
|
|
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
void GameHandler::mailDelete(uint32_t mailId) {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->mailDelete(mailId);
|
2026-02-15 14:00:41 -08:00
|
|
|
}
|
|
|
|
|
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
void GameHandler::mailMarkAsRead(uint32_t mailId) {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->mailMarkAsRead(mailId);
|
2026-02-16 18:46:44 -08:00
|
|
|
}
|
|
|
|
|
|
2026-02-10 21:29:10 -08:00
|
|
|
glm::vec3 GameHandler::getComposedWorldPosition() {
|
|
|
|
|
if (playerTransportGuid_ != 0 && transportManager_) {
|
2026-04-05 16:01:14 -07:00
|
|
|
auto* tr = transportManager_->getTransport(playerTransportGuid_);
|
|
|
|
|
if (tr) {
|
|
|
|
|
return transportManager_->getPlayerWorldPosition(playerTransportGuid_, playerTransportOffset_);
|
|
|
|
|
}
|
|
|
|
|
// Transport not tracked — fall through to normal position
|
2026-02-10 21:29:10 -08:00
|
|
|
}
|
|
|
|
|
// Not on transport, return normal movement position
|
|
|
|
|
return glm::vec3(movementInfo.x, movementInfo.y, movementInfo.z);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void GameHandler::openBank(uint64_t guid) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->openBank(guid);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::closeBank() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->closeBank();
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::buyBankSlot() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->buyBankSlot();
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::depositItem(uint8_t srcBag, uint8_t srcSlot) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->depositItem(srcBag, srcSlot);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::withdrawItem(uint8_t srcBag, uint8_t srcSlot) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->withdrawItem(srcBag, srcSlot);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Guild Bank System
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void GameHandler::openGuildBank(uint64_t guid) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->openGuildBank(guid);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::closeGuildBank() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->closeGuildBank();
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::queryGuildBankTab(uint8_t tabId) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->queryGuildBankTab(tabId);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::buyGuildBankTab() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->buyGuildBankTab();
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::depositGuildBankMoney(uint32_t amount) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->depositGuildBankMoney(amount);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::withdrawGuildBankMoney(uint32_t amount) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->withdrawGuildBankMoney(amount);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::guildBankWithdrawItem(uint8_t tabId, uint8_t bankSlot, uint8_t destBag, uint8_t destSlot) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->guildBankWithdrawItem(tabId, bankSlot, destBag, destSlot);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::guildBankDepositItem(uint8_t tabId, uint8_t bankSlot, uint8_t srcBag, uint8_t srcSlot) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->guildBankDepositItem(tabId, bankSlot, srcBag, srcSlot);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Auction House System
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void GameHandler::openAuctionHouse(uint64_t guid) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->openAuctionHouse(guid);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::closeAuctionHouse() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->closeAuctionHouse();
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::auctionSearch(const std::string& name, uint8_t levelMin, uint8_t levelMax,
|
|
|
|
|
uint32_t quality, uint32_t itemClass, uint32_t itemSubClass,
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
uint32_t invTypeMask, uint8_t usableOnly, uint32_t offset) {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->auctionSearch(name, levelMin, levelMax, quality, itemClass, itemSubClass, invTypeMask, usableOnly, offset);
|
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
|
|
|
}
|
|
|
|
|
|
2026-04-03 18:46:49 -07:00
|
|
|
void GameHandler::auctionSellItem(int backpackIndex, uint32_t bid,
|
|
|
|
|
uint32_t buyout, uint32_t duration) {
|
|
|
|
|
if (inventoryHandler_) inventoryHandler_->auctionSellItem(backpackIndex, bid, buyout, duration);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::auctionPlaceBid(uint32_t auctionId, uint32_t amount) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->auctionPlaceBid(auctionId, amount);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::auctionBuyout(uint32_t auctionId, uint32_t buyoutPrice) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->auctionBuyout(auctionId, buyoutPrice);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::auctionCancelItem(uint32_t auctionId) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->auctionCancelItem(auctionId);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::auctionListOwnerItems(uint32_t offset) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->auctionListOwnerItems(offset);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::auctionListBidderItems(uint32_t offset) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->auctionListBidderItems(offset);
|
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
|
|
|
}
|
|
|
|
|
|
2026-03-09 14:15:59 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Item text (SMSG_ITEM_TEXT_QUERY_RESPONSE)
|
|
|
|
|
// uint64 itemGuid + uint8 isEmpty + string text (when !isEmpty)
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GameHandler::queryItemText(uint64_t itemGuid) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->queryItemText(itemGuid);
|
2026-03-09 14:15:59 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-09 14:14:15 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// SMSG_QUEST_CONFIRM_ACCEPT (shared quest from group member)
|
|
|
|
|
// uint32 questId + string questTitle + uint64 sharerGuid
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GameHandler::acceptSharedQuest() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (questHandler_) questHandler_->acceptSharedQuest();
|
2026-03-09 14:14:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::declineSharedQuest() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (questHandler_) questHandler_->declineSharedQuest();
|
2026-03-09 14:14:15 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-09 14:07:50 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// SMSG_SUMMON_REQUEST
|
|
|
|
|
// uint64 summonerGuid + uint32 zoneId + uint32 timeoutMs
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GameHandler::handleSummonRequest(network::Packet& packet) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (socialHandler_) socialHandler_->handleSummonRequest(packet);
|
2026-03-09 14:07:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::acceptSummon() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (socialHandler_) socialHandler_->acceptSummon();
|
2026-03-09 14:07:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::declineSummon() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (socialHandler_) socialHandler_->declineSummon();
|
2026-03-09 14:07:50 -07:00
|
|
|
}
|
|
|
|
|
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Trade (SMSG_TRADE_STATUS / SMSG_TRADE_STATUS_EXTENDED)
|
|
|
|
|
// WotLK 3.3.5a status values:
|
|
|
|
|
// 0=busy, 1=begin_trade(+guid), 2=open_window, 3=cancelled, 4=accepted,
|
|
|
|
|
// 5=busy2, 6=no_target, 7=back_to_trade, 8=complete, 9=rejected,
|
|
|
|
|
// 10=too_far, 11=wrong_faction, 12=close_window, 13=ignore,
|
|
|
|
|
// 14-19=stun/dead/logout, 20=trial, 21=conjured_only
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GameHandler::acceptTradeRequest() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->acceptTradeRequest();
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::declineTradeRequest() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->declineTradeRequest();
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::acceptTrade() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->acceptTrade();
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::cancelTrade() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->cancelTrade();
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-11 00:44:07 -07:00
|
|
|
void GameHandler::setTradeItem(uint8_t tradeSlot, uint8_t bag, uint8_t bagSlot) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->setTradeItem(tradeSlot, bag, bagSlot);
|
2026-03-11 00:44:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::clearTradeItem(uint8_t tradeSlot) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->clearTradeItem(tradeSlot);
|
2026-03-11 00:44:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::setTradeGold(uint64_t copper) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->setTradeGold(copper);
|
2026-03-11 00:44:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::resetTradeState() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->resetTradeState();
|
2026-03-11 00:44:07 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-09 13:53:42 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
2026-03-09 14:01:27 -07:00
|
|
|
// Group loot roll (SMSG_LOOT_ROLL / SMSG_LOOT_ROLL_WON / CMSG_LOOT_ROLL)
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GameHandler::sendLootRoll(uint64_t objectGuid, uint32_t slot, uint8_t rollType) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (inventoryHandler_) inventoryHandler_->sendLootRoll(objectGuid, slot, rollType);
|
2026-03-09 14:01:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
2026-03-09 13:53:42 -07:00
|
|
|
// SMSG_ACHIEVEMENT_EARNED (WotLK 3.3.5a wire 0x4AB)
|
|
|
|
|
// uint64 guid — player who earned it (may be another player)
|
|
|
|
|
// uint32 achievementId — Achievement.dbc ID
|
|
|
|
|
// PackedTime date — uint32 bitfield (seconds since epoch)
|
|
|
|
|
// uint32 realmFirst — how many on realm also got it (0 = realm first)
|
|
|
|
|
// ---------------------------------------------------------------------------
|
2026-03-25 13:21:02 -07:00
|
|
|
void GameHandler::loadTitleNameCache() const {
|
2026-03-12 19:05:54 -07:00
|
|
|
if (titleNameCacheLoaded_) return;
|
|
|
|
|
titleNameCacheLoaded_ = true;
|
|
|
|
|
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
auto* am = services_.assetManager;
|
2026-03-12 19:05:54 -07:00
|
|
|
if (!am || !am->isInitialized()) return;
|
|
|
|
|
|
|
|
|
|
auto dbc = am->loadDBC("CharTitles.dbc");
|
|
|
|
|
if (!dbc || !dbc->isLoaded() || dbc->getFieldCount() < 5) return;
|
|
|
|
|
|
|
|
|
|
const auto* layout = pipeline::getActiveDBCLayout()
|
|
|
|
|
? pipeline::getActiveDBCLayout()->getLayout("CharTitles") : nullptr;
|
|
|
|
|
|
|
|
|
|
uint32_t titleField = layout ? layout->field("Title") : 2;
|
|
|
|
|
uint32_t bitField = layout ? layout->field("TitleBit") : 36;
|
|
|
|
|
if (titleField == 0xFFFFFFFF) titleField = 2;
|
|
|
|
|
if (bitField == 0xFFFFFFFF) bitField = static_cast<uint32_t>(dbc->getFieldCount() - 1);
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) {
|
|
|
|
|
uint32_t bit = dbc->getUInt32(i, bitField);
|
|
|
|
|
if (bit == 0) continue;
|
|
|
|
|
std::string name = dbc->getString(i, titleField);
|
|
|
|
|
if (!name.empty()) titleNameCache_[bit] = std::move(name);
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("CharTitles: loaded ", titleNameCache_.size(), " title names from DBC");
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 20:23:36 -07:00
|
|
|
std::string GameHandler::getFormattedTitle(uint32_t bit) const {
|
2026-03-25 13:21:02 -07:00
|
|
|
loadTitleNameCache();
|
2026-03-12 20:23:36 -07:00
|
|
|
auto it = titleNameCache_.find(bit);
|
|
|
|
|
if (it == titleNameCache_.end() || it->second.empty()) return {};
|
|
|
|
|
|
2026-03-25 13:29:10 -07:00
|
|
|
const auto& ln2 = lookupName(playerGuid);
|
2026-03-12 20:52:58 -07:00
|
|
|
static const std::string kUnknown = "unknown";
|
2026-03-25 13:29:10 -07:00
|
|
|
const std::string& pName = ln2.empty() ? kUnknown : ln2;
|
2026-03-12 20:23:36 -07:00
|
|
|
|
|
|
|
|
const std::string& fmt = it->second;
|
|
|
|
|
size_t pos = fmt.find("%s");
|
|
|
|
|
if (pos != std::string::npos) {
|
|
|
|
|
return fmt.substr(0, pos) + pName + fmt.substr(pos + 2);
|
|
|
|
|
}
|
|
|
|
|
return fmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::sendSetTitle(int32_t bit) {
|
2026-03-25 14:21:19 -07:00
|
|
|
if (!isInWorld()) return;
|
2026-03-12 20:23:36 -07:00
|
|
|
auto packet = SetTitlePacket::build(bit);
|
|
|
|
|
socket->send(packet);
|
|
|
|
|
chosenTitleBit_ = bit;
|
|
|
|
|
LOG_INFO("sendSetTitle: bit=", bit);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 19:34:33 -07:00
|
|
|
void GameHandler::loadAchievementNameCache() {
|
|
|
|
|
if (achievementNameCacheLoaded_) return;
|
|
|
|
|
achievementNameCacheLoaded_ = true;
|
|
|
|
|
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
auto* am = services_.assetManager;
|
2026-03-09 19:34:33 -07:00
|
|
|
if (!am || !am->isInitialized()) return;
|
|
|
|
|
|
|
|
|
|
auto dbc = am->loadDBC("Achievement.dbc");
|
|
|
|
|
if (!dbc || !dbc->isLoaded() || dbc->getFieldCount() < 22) return;
|
|
|
|
|
|
|
|
|
|
const auto* achL = pipeline::getActiveDBCLayout()
|
|
|
|
|
? pipeline::getActiveDBCLayout()->getLayout("Achievement") : nullptr;
|
|
|
|
|
uint32_t titleField = achL ? achL->field("Title") : 4;
|
|
|
|
|
if (titleField == 0xFFFFFFFF) titleField = 4;
|
2026-03-12 12:49:38 -07:00
|
|
|
uint32_t descField = achL ? achL->field("Description") : 0xFFFFFFFF;
|
|
|
|
|
uint32_t ptsField = achL ? achL->field("Points") : 0xFFFFFFFF;
|
2026-03-09 19:34:33 -07:00
|
|
|
|
2026-03-12 12:49:38 -07:00
|
|
|
uint32_t fieldCount = dbc->getFieldCount();
|
2026-03-09 19:34:33 -07:00
|
|
|
for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) {
|
|
|
|
|
uint32_t id = dbc->getUInt32(i, 0);
|
|
|
|
|
if (id == 0) continue;
|
|
|
|
|
std::string title = dbc->getString(i, titleField);
|
|
|
|
|
if (!title.empty()) achievementNameCache_[id] = std::move(title);
|
2026-03-12 12:49:38 -07:00
|
|
|
if (descField != 0xFFFFFFFF && descField < fieldCount) {
|
|
|
|
|
std::string desc = dbc->getString(i, descField);
|
|
|
|
|
if (!desc.empty()) achievementDescCache_[id] = std::move(desc);
|
|
|
|
|
}
|
|
|
|
|
if (ptsField != 0xFFFFFFFF && ptsField < fieldCount) {
|
|
|
|
|
uint32_t pts = dbc->getUInt32(i, ptsField);
|
|
|
|
|
if (pts > 0) achievementPointsCache_[id] = pts;
|
|
|
|
|
}
|
2026-03-09 19:34:33 -07:00
|
|
|
}
|
|
|
|
|
LOG_INFO("Achievement: loaded ", achievementNameCache_.size(), " names from Achievement.dbc");
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 20:53:21 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// SMSG_ALL_ACHIEVEMENT_DATA (WotLK 3.3.5a)
|
|
|
|
|
// Achievement records: repeated { uint32 id, uint32 packedDate } until 0xFFFFFFFF sentinel
|
|
|
|
|
// Criteria records: repeated { uint32 id, uint64 counter, uint32 packedDate, ... } until 0xFFFFFFFF
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
void GameHandler::handleAllAchievementData(network::Packet& packet) {
|
|
|
|
|
loadAchievementNameCache();
|
|
|
|
|
earnedAchievements_.clear();
|
2026-03-12 07:22:36 -07:00
|
|
|
achievementDates_.clear();
|
2026-03-10 20:53:21 -07:00
|
|
|
|
|
|
|
|
// Parse achievement entries (id + packedDate pairs, sentinel 0xFFFFFFFF)
|
2026-03-25 16:22:47 -07:00
|
|
|
while (packet.hasRemaining(4)) {
|
2026-03-10 20:53:21 -07:00
|
|
|
uint32_t id = packet.readUInt32();
|
|
|
|
|
if (id == 0xFFFFFFFF) break;
|
2026-03-25 16:22:47 -07:00
|
|
|
if (!packet.hasRemaining(4)) break;
|
2026-03-12 07:22:36 -07:00
|
|
|
uint32_t date = packet.readUInt32();
|
2026-03-10 20:53:21 -07:00
|
|
|
earnedAchievements_.insert(id);
|
2026-03-12 07:22:36 -07:00
|
|
|
achievementDates_[id] = date;
|
2026-03-10 20:53:21 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-12 03:03:02 -07:00
|
|
|
// Parse criteria block: id + uint64 counter + uint32 date + uint32 flags, sentinel 0xFFFFFFFF
|
|
|
|
|
criteriaProgress_.clear();
|
2026-03-25 16:22:47 -07:00
|
|
|
while (packet.hasRemaining(4)) {
|
2026-03-10 20:53:21 -07:00
|
|
|
uint32_t id = packet.readUInt32();
|
|
|
|
|
if (id == 0xFFFFFFFF) break;
|
|
|
|
|
// counter(8) + date(4) + unknown(4) = 16 bytes
|
2026-03-25 16:22:47 -07:00
|
|
|
if (!packet.hasRemaining(16)) break;
|
2026-03-12 03:03:02 -07:00
|
|
|
uint64_t counter = packet.readUInt64();
|
2026-03-10 20:53:21 -07:00
|
|
|
packet.readUInt32(); // date
|
|
|
|
|
packet.readUInt32(); // unknown / flags
|
2026-03-12 03:03:02 -07:00
|
|
|
criteriaProgress_[id] = counter;
|
2026-03-10 20:53:21 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-12 03:03:02 -07:00
|
|
|
LOG_INFO("SMSG_ALL_ACHIEVEMENT_DATA: loaded ", earnedAchievements_.size(),
|
|
|
|
|
" achievements, ", criteriaProgress_.size(), " criteria");
|
2026-03-10 20:53:21 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-12 23:23:02 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// SMSG_RESPOND_INSPECT_ACHIEVEMENTS (WotLK 3.3.5a)
|
|
|
|
|
// Wire format: packed_guid (inspected player) + same achievement/criteria
|
|
|
|
|
// blocks as SMSG_ALL_ACHIEVEMENT_DATA:
|
|
|
|
|
// Achievement records: repeated { uint32 id, uint32 packedDate } until 0xFFFFFFFF sentinel
|
|
|
|
|
// Criteria records: repeated { uint32 id, uint64 counter, uint32 date, uint32 unk }
|
|
|
|
|
// until 0xFFFFFFFF sentinel
|
|
|
|
|
// We store only the earned achievement IDs (not criteria) per inspected player.
|
|
|
|
|
// ---------------------------------------------------------------------------
|
2026-03-09 14:48:30 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Faction name cache (lazily loaded from Faction.dbc)
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
2026-03-25 13:21:02 -07:00
|
|
|
void GameHandler::loadFactionNameCache() const {
|
2026-03-09 14:48:30 -07:00
|
|
|
if (factionNameCacheLoaded_) return;
|
|
|
|
|
factionNameCacheLoaded_ = true;
|
|
|
|
|
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
auto* am = services_.assetManager;
|
2026-03-09 14:48:30 -07:00
|
|
|
if (!am || !am->isInitialized()) return;
|
|
|
|
|
|
|
|
|
|
auto dbc = am->loadDBC("Faction.dbc");
|
|
|
|
|
if (!dbc || !dbc->isLoaded()) return;
|
|
|
|
|
|
|
|
|
|
// Faction.dbc WotLK 3.3.5a field layout:
|
|
|
|
|
// 0: ID
|
2026-03-12 23:30:44 -07:00
|
|
|
// 1: ReputationListID (-1 / 0xFFFFFFFF = no reputation tracking)
|
|
|
|
|
// 2-5: ReputationRaceMask[4]
|
|
|
|
|
// 6-9: ReputationClassMask[4]
|
|
|
|
|
// 10-13: ReputationBase[4]
|
|
|
|
|
// 14-17: ReputationFlags[4]
|
|
|
|
|
// 18: ParentFactionID
|
|
|
|
|
// 19-20: SpilloverRateIn, SpilloverRateOut (floats)
|
|
|
|
|
// 21-22: SpilloverMaxRankIn, SpilloverMaxRankOut
|
|
|
|
|
// 23: Name (English locale, string ref)
|
|
|
|
|
constexpr uint32_t ID_FIELD = 0;
|
|
|
|
|
constexpr uint32_t REPLIST_FIELD = 1;
|
|
|
|
|
constexpr uint32_t NAME_FIELD = 23; // enUS name string
|
|
|
|
|
|
|
|
|
|
// Classic/TBC have fewer fields; fall back gracefully
|
|
|
|
|
const bool hasRepListField = dbc->getFieldCount() > REPLIST_FIELD;
|
2026-03-09 14:48:30 -07:00
|
|
|
if (dbc->getFieldCount() <= NAME_FIELD) {
|
|
|
|
|
LOG_WARNING("Faction.dbc: unexpected field count ", dbc->getFieldCount());
|
2026-03-12 23:30:44 -07:00
|
|
|
// Don't abort — still try to load names from a shorter layout
|
2026-03-09 14:48:30 -07:00
|
|
|
}
|
2026-03-12 23:30:44 -07:00
|
|
|
const uint32_t nameField = (dbc->getFieldCount() > NAME_FIELD) ? NAME_FIELD : 22u;
|
2026-03-09 14:48:30 -07:00
|
|
|
|
|
|
|
|
uint32_t count = dbc->getRecordCount();
|
|
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
|
|
|
uint32_t factionId = dbc->getUInt32(i, ID_FIELD);
|
|
|
|
|
if (factionId == 0) continue;
|
2026-03-12 23:30:44 -07:00
|
|
|
if (dbc->getFieldCount() > nameField) {
|
|
|
|
|
std::string name = dbc->getString(i, nameField);
|
|
|
|
|
if (!name.empty()) {
|
|
|
|
|
factionNameCache_[factionId] = std::move(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Build repListId ↔ factionId mapping (WotLK field 1)
|
|
|
|
|
if (hasRepListField) {
|
|
|
|
|
uint32_t repListId = dbc->getUInt32(i, REPLIST_FIELD);
|
|
|
|
|
if (repListId != 0xFFFFFFFFu) {
|
|
|
|
|
factionRepListToId_[repListId] = factionId;
|
|
|
|
|
factionIdToRepList_[factionId] = repListId;
|
|
|
|
|
}
|
2026-03-09 14:48:30 -07:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-12 23:30:44 -07:00
|
|
|
LOG_INFO("Faction.dbc: loaded ", factionNameCache_.size(), " faction names, ",
|
|
|
|
|
factionRepListToId_.size(), " with reputation tracking");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getFactionIdByRepListId(uint32_t repListId) const {
|
2026-03-25 13:21:02 -07:00
|
|
|
loadFactionNameCache();
|
2026-03-12 23:30:44 -07:00
|
|
|
auto it = factionRepListToId_.find(repListId);
|
|
|
|
|
return (it != factionRepListToId_.end()) ? it->second : 0u;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getRepListIdByFactionId(uint32_t factionId) const {
|
2026-03-25 13:21:02 -07:00
|
|
|
loadFactionNameCache();
|
2026-03-12 23:30:44 -07:00
|
|
|
auto it = factionIdToRepList_.find(factionId);
|
|
|
|
|
return (it != factionIdToRepList_.end()) ? it->second : 0xFFFFFFFFu;
|
2026-03-09 14:48:30 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-20 04:50:49 -07:00
|
|
|
void GameHandler::setWatchedFactionId(uint32_t factionId) {
|
|
|
|
|
watchedFactionId_ = factionId;
|
2026-03-25 14:21:19 -07:00
|
|
|
if (!isInWorld()) return;
|
2026-03-20 04:50:49 -07:00
|
|
|
// CMSG_SET_WATCHED_FACTION: int32 repListId (-1 = unwatch)
|
|
|
|
|
int32_t repListId = -1;
|
|
|
|
|
if (factionId != 0) {
|
|
|
|
|
uint32_t rl = getRepListIdByFactionId(factionId);
|
|
|
|
|
if (rl != 0xFFFFFFFFu) repListId = static_cast<int32_t>(rl);
|
|
|
|
|
}
|
|
|
|
|
network::Packet pkt(wireOpcode(Opcode::CMSG_SET_WATCHED_FACTION));
|
|
|
|
|
pkt.writeUInt32(static_cast<uint32_t>(repListId));
|
|
|
|
|
socket->send(pkt);
|
|
|
|
|
LOG_DEBUG("CMSG_SET_WATCHED_FACTION: repListId=", repListId, " (factionId=", factionId, ")");
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 14:48:30 -07:00
|
|
|
std::string GameHandler::getFactionName(uint32_t factionId) const {
|
|
|
|
|
auto it = factionNameCache_.find(factionId);
|
|
|
|
|
if (it != factionNameCache_.end()) return it->second;
|
|
|
|
|
return "faction #" + std::to_string(factionId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 14:52:13 -07:00
|
|
|
const std::string& GameHandler::getFactionNamePublic(uint32_t factionId) const {
|
2026-03-25 13:21:02 -07:00
|
|
|
loadFactionNameCache();
|
2026-03-09 14:52:13 -07:00
|
|
|
auto it = factionNameCache_.find(factionId);
|
|
|
|
|
if (it != factionNameCache_.end()) return it->second;
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 08:06:21 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Area name cache (lazy-loaded from WorldMapArea.dbc)
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
2026-03-25 13:21:02 -07:00
|
|
|
void GameHandler::loadAreaNameCache() const {
|
2026-03-10 08:06:21 -07:00
|
|
|
if (areaNameCacheLoaded_) return;
|
|
|
|
|
areaNameCacheLoaded_ = true;
|
|
|
|
|
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
auto* am = services_.assetManager;
|
2026-03-10 08:06:21 -07:00
|
|
|
if (!am || !am->isInitialized()) return;
|
|
|
|
|
|
2026-04-05 04:28:36 -07:00
|
|
|
// AreaTable.dbc has the canonical zone/area names keyed by AreaID.
|
|
|
|
|
// Field 0 = ID, field 11 = AreaName (enUS locale).
|
|
|
|
|
auto areaDbc = am->loadDBC("AreaTable.dbc");
|
|
|
|
|
if (areaDbc && areaDbc->isLoaded() && areaDbc->getFieldCount() > 11) {
|
|
|
|
|
for (uint32_t i = 0; i < areaDbc->getRecordCount(); ++i) {
|
|
|
|
|
uint32_t areaId = areaDbc->getUInt32(i, 0);
|
|
|
|
|
if (areaId == 0) continue;
|
|
|
|
|
std::string name = areaDbc->getString(i, 11);
|
|
|
|
|
if (!name.empty()) {
|
|
|
|
|
areaNameCache_[areaId] = std::move(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-10 08:06:21 -07:00
|
|
|
|
2026-04-05 04:28:36 -07:00
|
|
|
// WorldMapArea.dbc supplements with map-UI area names (different ID space).
|
|
|
|
|
auto dbc = am->loadDBC("WorldMapArea.dbc");
|
|
|
|
|
if (dbc && dbc->isLoaded()) {
|
|
|
|
|
const auto* layout = pipeline::getActiveDBCLayout()
|
|
|
|
|
? pipeline::getActiveDBCLayout()->getLayout("WorldMapArea") : nullptr;
|
|
|
|
|
const uint32_t areaIdField = layout ? (*layout)["AreaID"] : 2;
|
|
|
|
|
const uint32_t areaNameField = layout ? (*layout)["AreaName"] : 3;
|
|
|
|
|
|
|
|
|
|
if (dbc->getFieldCount() > areaNameField) {
|
|
|
|
|
for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) {
|
|
|
|
|
uint32_t areaId = dbc->getUInt32(i, areaIdField);
|
|
|
|
|
if (areaId == 0) continue;
|
|
|
|
|
std::string name = dbc->getString(i, areaNameField);
|
|
|
|
|
// Don't overwrite AreaTable names — those are authoritative
|
|
|
|
|
if (!name.empty() && !areaNameCache_.count(areaId)) {
|
|
|
|
|
areaNameCache_[areaId] = std::move(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-10 08:06:21 -07:00
|
|
|
}
|
|
|
|
|
}
|
2026-04-05 04:28:36 -07:00
|
|
|
|
|
|
|
|
LOG_INFO("Area name cache: loaded ", areaNameCache_.size(), " entries");
|
2026-03-10 08:06:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string GameHandler::getAreaName(uint32_t areaId) const {
|
|
|
|
|
if (areaId == 0) return {};
|
2026-03-25 13:21:02 -07:00
|
|
|
loadAreaNameCache();
|
2026-03-10 08:06:21 -07:00
|
|
|
auto it = areaNameCache_.find(areaId);
|
|
|
|
|
return (it != areaNameCache_.end()) ? it->second : std::string{};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 13:21:02 -07:00
|
|
|
void GameHandler::loadMapNameCache() const {
|
2026-03-13 07:10:10 -07:00
|
|
|
if (mapNameCacheLoaded_) return;
|
|
|
|
|
mapNameCacheLoaded_ = true;
|
|
|
|
|
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
auto* am = services_.assetManager;
|
2026-03-13 07:10:10 -07:00
|
|
|
if (!am || !am->isInitialized()) return;
|
|
|
|
|
|
|
|
|
|
auto dbc = am->loadDBC("Map.dbc");
|
|
|
|
|
if (!dbc || !dbc->isLoaded()) return;
|
|
|
|
|
|
2026-04-05 04:32:10 -07:00
|
|
|
// Map.dbc layout: 0=ID, 1=InternalName, 2=InstanceType, 3=Flags,
|
|
|
|
|
// 4=MapName_enUS (display name), fields 5+ = other locales
|
2026-03-13 07:10:10 -07:00
|
|
|
for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) {
|
|
|
|
|
uint32_t id = dbc->getUInt32(i, 0);
|
2026-04-05 04:32:10 -07:00
|
|
|
std::string name = dbc->getString(i, 4);
|
|
|
|
|
if (name.empty()) name = dbc->getString(i, 1); // internal name fallback
|
2026-03-13 07:10:10 -07:00
|
|
|
if (!name.empty() && !mapNameCache_.count(id)) {
|
|
|
|
|
mapNameCache_[id] = std::move(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Map.dbc: loaded ", mapNameCache_.size(), " map names");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string GameHandler::getMapName(uint32_t mapId) const {
|
2026-03-25 13:21:02 -07:00
|
|
|
loadMapNameCache();
|
2026-03-13 07:10:10 -07:00
|
|
|
auto it = mapNameCache_.find(mapId);
|
|
|
|
|
return (it != mapNameCache_.end()) ? it->second : std::string{};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 08:14:47 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// LFG dungeon name cache (WotLK: LFGDungeons.dbc)
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
2026-03-25 13:21:02 -07:00
|
|
|
void GameHandler::loadLfgDungeonDbc() const {
|
2026-03-13 08:14:47 -07:00
|
|
|
if (lfgDungeonNameCacheLoaded_) return;
|
|
|
|
|
lfgDungeonNameCacheLoaded_ = true;
|
|
|
|
|
|
[refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.
Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
`services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
after all subsystems are created, then constructs `GameHandler`
(fixes latent init-order bug: `GameHandler` was previously created
before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
`isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
`CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
`Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
|
|
|
auto* am = services_.assetManager;
|
2026-03-13 08:14:47 -07:00
|
|
|
if (!am || !am->isInitialized()) return;
|
|
|
|
|
|
|
|
|
|
auto dbc = am->loadDBC("LFGDungeons.dbc");
|
|
|
|
|
if (!dbc || !dbc->isLoaded()) return;
|
|
|
|
|
|
|
|
|
|
const auto* layout = pipeline::getActiveDBCLayout()
|
|
|
|
|
? pipeline::getActiveDBCLayout()->getLayout("LFGDungeons") : nullptr;
|
|
|
|
|
const uint32_t idField = layout ? (*layout)["ID"] : 0;
|
|
|
|
|
const uint32_t nameField = layout ? (*layout)["Name"] : 1;
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) {
|
|
|
|
|
uint32_t id = dbc->getUInt32(i, idField);
|
|
|
|
|
if (id == 0) continue;
|
|
|
|
|
std::string name = dbc->getString(i, nameField);
|
|
|
|
|
if (!name.empty())
|
|
|
|
|
lfgDungeonNameCache_[id] = std::move(name);
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("LFGDungeons.dbc: loaded ", lfgDungeonNameCache_.size(), " dungeon names");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string GameHandler::getLfgDungeonName(uint32_t dungeonId) const {
|
|
|
|
|
if (dungeonId == 0) return {};
|
2026-03-25 13:21:02 -07:00
|
|
|
loadLfgDungeonDbc();
|
2026-03-13 08:14:47 -07:00
|
|
|
auto it = lfgDungeonNameCache_.find(dungeonId);
|
|
|
|
|
return (it != lfgDungeonNameCache_.end()) ? it->second : std::string{};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 15:23:02 -07:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Aura duration update
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GameHandler::handleUpdateAuraDuration(uint8_t slot, uint32_t durationMs) {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (spellHandler_) spellHandler_->handleUpdateAuraDuration(slot, durationMs);
|
2026-03-09 15:23:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Equipment set list
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
2026-03-12 22:25:46 -07:00
|
|
|
// ---- Battlefield Manager (WotLK Wintergrasp / outdoor battlefields) ----
|
|
|
|
|
|
|
|
|
|
void GameHandler::acceptBfMgrInvite() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (socialHandler_) socialHandler_->acceptBfMgrInvite();
|
2026-03-12 22:25:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameHandler::declineBfMgrInvite() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (socialHandler_) socialHandler_->declineBfMgrInvite();
|
2026-03-12 22:25:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- WotLK Calendar ----
|
|
|
|
|
|
|
|
|
|
void GameHandler::requestCalendar() {
|
refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00
|
|
|
if (socialHandler_) socialHandler_->requestCalendar();
|
2026-03-12 22:25:46 -07:00
|
|
|
}
|
|
|
|
|
|
fix: delegate all 113 stale GameHandler getters to domain handlers
PR #23 split GameHandler into 8 domain handlers but left 113 inline
getters reading stale duplicate member variables. Every feature that
relied on these getters was silently broken (showing empty/stale data):
InventoryHandler (32): bank, mail, auction house, guild bank, trainer,
loot rolls, vendor, buyback, item text, master loot candidates
SocialHandler (43): guild roster, battlegrounds, LFG, duels, petitions,
arena teams, instance lockouts, ready check, who results, played time
SpellHandler (10): talents, craft queue, GCD, pet unlearn, queued spell
QuestHandler (13): quest log, gossip POIs, quest offer/request windows,
tracked quests, shared quests, NPC quest statuses
MovementHandler (15): all 8 server speeds, taxi state, taxi nodes/data
All converted from inline `{ return member_; }` to out-of-line
delegations: `return handler_ ? handler_->getter() : fallback;`
2026-03-28 12:18:14 -07:00
|
|
|
// ============================================================
|
|
|
|
|
// Delegating getters — SocialHandler owns the canonical state
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getTotalTimePlayed() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getTotalTimePlayed() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getLevelTimePlayed() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLevelTimePlayed() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<GameHandler::WhoEntry>& GameHandler::getWhoResults() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getWhoResults();
|
|
|
|
|
static const std::vector<WhoEntry> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getWhoOnlineCount() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getWhoOnlineCount() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::array<GameHandler::BgQueueSlot, 3>& GameHandler::getBgQueues() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getBgQueues();
|
|
|
|
|
static const std::array<BgQueueSlot, 3> empty{};
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<GameHandler::AvailableBgInfo>& GameHandler::getAvailableBgs() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getAvailableBgs();
|
|
|
|
|
static const std::vector<AvailableBgInfo> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const GameHandler::BgScoreboardData* GameHandler::getBgScoreboard() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getBgScoreboard() : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<GameHandler::BgPlayerPosition>& GameHandler::getBgPlayerPositions() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getBgPlayerPositions();
|
|
|
|
|
static const std::vector<BgPlayerPosition> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::isLoggingOut() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->isLoggingOut() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float GameHandler::getLogoutCountdown() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLogoutCountdown() : 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::isInGuild() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->isInGuild();
|
|
|
|
|
const Character* ch = getActiveCharacter();
|
|
|
|
|
return ch && ch->hasGuild();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-28 15:29:19 -07:00
|
|
|
bool GameHandler::hasPendingGroupInvite() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->hasPendingGroupInvite() : pendingGroupInvite;
|
|
|
|
|
}
|
|
|
|
|
const std::string& GameHandler::getPendingInviterName() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getPendingInviterName();
|
|
|
|
|
return pendingInviterName;
|
|
|
|
|
}
|
|
|
|
|
|
fix: delegate all 113 stale GameHandler getters to domain handlers
PR #23 split GameHandler into 8 domain handlers but left 113 inline
getters reading stale duplicate member variables. Every feature that
relied on these getters was silently broken (showing empty/stale data):
InventoryHandler (32): bank, mail, auction house, guild bank, trainer,
loot rolls, vendor, buyback, item text, master loot candidates
SocialHandler (43): guild roster, battlegrounds, LFG, duels, petitions,
arena teams, instance lockouts, ready check, who results, played time
SpellHandler (10): talents, craft queue, GCD, pet unlearn, queued spell
QuestHandler (13): quest log, gossip POIs, quest offer/request windows,
tracked quests, shared quests, NPC quest statuses
MovementHandler (15): all 8 server speeds, taxi state, taxi nodes/data
All converted from inline `{ return member_; }` to out-of-line
delegations: `return handler_ ? handler_->getter() : fallback;`
2026-03-28 12:18:14 -07:00
|
|
|
const std::string& GameHandler::getGuildName() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getGuildName();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const GuildRosterData& GameHandler::getGuildRoster() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getGuildRoster();
|
|
|
|
|
static const GuildRosterData empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::hasGuildRoster() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->hasGuildRoster() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<std::string>& GameHandler::getGuildRankNames() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getGuildRankNames();
|
|
|
|
|
static const std::vector<std::string> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::hasPendingGuildInvite() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->hasPendingGuildInvite() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string& GameHandler::getPendingGuildInviterName() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getPendingGuildInviterName();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string& GameHandler::getPendingGuildInviteGuildName() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getPendingGuildInviteGuildName();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const GuildInfoData& GameHandler::getGuildInfoData() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getGuildInfoData();
|
|
|
|
|
static const GuildInfoData empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const GuildQueryResponseData& GameHandler::getGuildQueryData() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getGuildQueryData();
|
|
|
|
|
static const GuildQueryResponseData empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::hasGuildInfoData() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->hasGuildInfoData() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::hasPetitionShowlist() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->hasPetitionShowlist() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getPetitionCost() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getPetitionCost() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t GameHandler::getPetitionNpcGuid() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getPetitionNpcGuid() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const GameHandler::PetitionInfo& GameHandler::getPetitionInfo() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getPetitionInfo();
|
|
|
|
|
static const PetitionInfo empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::hasPetitionSignaturesUI() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->hasPetitionSignaturesUI() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::hasPendingReadyCheck() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->hasPendingReadyCheck() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string& GameHandler::getReadyCheckInitiator() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getReadyCheckInitiator();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<GameHandler::ReadyCheckResult>& GameHandler::getReadyCheckResults() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getReadyCheckResults();
|
|
|
|
|
static const std::vector<ReadyCheckResult> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getInstanceDifficulty() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getInstanceDifficulty() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::isInstanceHeroic() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->isInstanceHeroic() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::isInInstance() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->isInInstance() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::hasPendingDuelRequest() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->hasPendingDuelRequest() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string& GameHandler::getDuelChallengerName() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getDuelChallengerName();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float GameHandler::getDuelCountdownRemaining() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getDuelCountdownRemaining() : 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<GameHandler::InstanceLockout>& GameHandler::getInstanceLockouts() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getInstanceLockouts();
|
|
|
|
|
static const std::vector<InstanceLockout> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GameHandler::LfgState GameHandler::getLfgState() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgState() : LfgState::None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::isLfgQueued() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->isLfgQueued() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameHandler::isLfgInDungeon() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->isLfgInDungeon() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getLfgDungeonId() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgDungeonId() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string GameHandler::getCurrentLfgDungeonName() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getCurrentLfgDungeonName() : std::string{};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getLfgProposalId() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgProposalId() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t GameHandler::getLfgAvgWaitSec() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgAvgWaitSec() : -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getLfgTimeInQueueMs() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgTimeInQueueMs() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getLfgBootVotes() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgBootVotes() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getLfgBootTotal() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgBootTotal() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getLfgBootTimeLeft() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgBootTimeLeft() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t GameHandler::getLfgBootNeeded() const {
|
|
|
|
|
return socialHandler_ ? socialHandler_->getLfgBootNeeded() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string& GameHandler::getLfgBootTargetName() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getLfgBootTargetName();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string& GameHandler::getLfgBootReason() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getLfgBootReason();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<GameHandler::ArenaTeamStats>& GameHandler::getArenaTeamStats() const {
|
|
|
|
|
if (socialHandler_) return socialHandler_->getArenaTeamStats();
|
|
|
|
|
static const std::vector<ArenaTeamStats> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- SpellHandler delegating getters ----
|
|
|
|
|
|
|
|
|
|
int GameHandler::getCraftQueueRemaining() const {
|
|
|
|
|
return spellHandler_ ? spellHandler_->getCraftQueueRemaining() : 0;
|
|
|
|
|
}
|
|
|
|
|
uint32_t GameHandler::getCraftQueueSpellId() const {
|
|
|
|
|
return spellHandler_ ? spellHandler_->getCraftQueueSpellId() : 0;
|
|
|
|
|
}
|
|
|
|
|
uint32_t GameHandler::getQueuedSpellId() const {
|
|
|
|
|
return spellHandler_ ? spellHandler_->getQueuedSpellId() : 0;
|
|
|
|
|
}
|
|
|
|
|
const std::unordered_map<uint32_t, TalentEntry>& GameHandler::getAllTalents() const {
|
|
|
|
|
if (spellHandler_) return spellHandler_->getAllTalents();
|
|
|
|
|
static const std::unordered_map<uint32_t, TalentEntry> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
const std::unordered_map<uint32_t, TalentTabEntry>& GameHandler::getAllTalentTabs() const {
|
|
|
|
|
if (spellHandler_) return spellHandler_->getAllTalentTabs();
|
|
|
|
|
static const std::unordered_map<uint32_t, TalentTabEntry> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
float GameHandler::getGCDTotal() const {
|
|
|
|
|
return spellHandler_ ? spellHandler_->getGCDTotal() : 0.0f;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::showTalentWipeConfirmDialog() const {
|
|
|
|
|
return spellHandler_ ? spellHandler_->showTalentWipeConfirmDialog() : false;
|
|
|
|
|
}
|
|
|
|
|
uint32_t GameHandler::getTalentWipeCost() const {
|
|
|
|
|
return spellHandler_ ? spellHandler_->getTalentWipeCost() : 0;
|
|
|
|
|
}
|
|
|
|
|
void GameHandler::cancelTalentWipe() {
|
|
|
|
|
if (spellHandler_) spellHandler_->cancelTalentWipe();
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::showPetUnlearnDialog() const {
|
|
|
|
|
return spellHandler_ ? spellHandler_->showPetUnlearnDialog() : false;
|
|
|
|
|
}
|
|
|
|
|
uint32_t GameHandler::getPetUnlearnCost() const {
|
|
|
|
|
return spellHandler_ ? spellHandler_->getPetUnlearnCost() : 0;
|
|
|
|
|
}
|
|
|
|
|
void GameHandler::cancelPetUnlearn() {
|
|
|
|
|
if (spellHandler_) spellHandler_->cancelPetUnlearn();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- QuestHandler delegating getters ----
|
|
|
|
|
|
2026-03-28 12:43:44 -07:00
|
|
|
bool GameHandler::isGossipWindowOpen() const {
|
|
|
|
|
return questHandler_ ? questHandler_->isGossipWindowOpen() : gossipWindowOpen;
|
|
|
|
|
}
|
|
|
|
|
const GossipMessageData& GameHandler::getCurrentGossip() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getCurrentGossip();
|
|
|
|
|
return currentGossip;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::isQuestDetailsOpen() {
|
|
|
|
|
if (questHandler_) return questHandler_->isQuestDetailsOpen();
|
|
|
|
|
return questDetailsOpen;
|
|
|
|
|
}
|
|
|
|
|
const QuestDetailsData& GameHandler::getQuestDetails() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getQuestDetails();
|
|
|
|
|
return currentQuestDetails;
|
|
|
|
|
}
|
|
|
|
|
|
fix: delegate all 113 stale GameHandler getters to domain handlers
PR #23 split GameHandler into 8 domain handlers but left 113 inline
getters reading stale duplicate member variables. Every feature that
relied on these getters was silently broken (showing empty/stale data):
InventoryHandler (32): bank, mail, auction house, guild bank, trainer,
loot rolls, vendor, buyback, item text, master loot candidates
SocialHandler (43): guild roster, battlegrounds, LFG, duels, petitions,
arena teams, instance lockouts, ready check, who results, played time
SpellHandler (10): talents, craft queue, GCD, pet unlearn, queued spell
QuestHandler (13): quest log, gossip POIs, quest offer/request windows,
tracked quests, shared quests, NPC quest statuses
MovementHandler (15): all 8 server speeds, taxi state, taxi nodes/data
All converted from inline `{ return member_; }` to out-of-line
delegations: `return handler_ ? handler_->getter() : fallback;`
2026-03-28 12:18:14 -07:00
|
|
|
const std::vector<GossipPoi>& GameHandler::getGossipPois() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getGossipPois();
|
|
|
|
|
static const std::vector<GossipPoi> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
const std::unordered_map<uint64_t, QuestGiverStatus>& GameHandler::getNpcQuestStatuses() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getNpcQuestStatuses();
|
|
|
|
|
static const std::unordered_map<uint64_t, QuestGiverStatus> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
2026-04-10 19:50:56 +03:00
|
|
|
QuestGiverStatus GameHandler::getQuestGiverStatus(uint64_t guid) const {
|
|
|
|
|
if (questHandler_) return questHandler_->getQuestGiverStatus(guid);
|
|
|
|
|
return QuestGiverStatus::NONE;
|
|
|
|
|
}
|
fix: delegate all 113 stale GameHandler getters to domain handlers
PR #23 split GameHandler into 8 domain handlers but left 113 inline
getters reading stale duplicate member variables. Every feature that
relied on these getters was silently broken (showing empty/stale data):
InventoryHandler (32): bank, mail, auction house, guild bank, trainer,
loot rolls, vendor, buyback, item text, master loot candidates
SocialHandler (43): guild roster, battlegrounds, LFG, duels, petitions,
arena teams, instance lockouts, ready check, who results, played time
SpellHandler (10): talents, craft queue, GCD, pet unlearn, queued spell
QuestHandler (13): quest log, gossip POIs, quest offer/request windows,
tracked quests, shared quests, NPC quest statuses
MovementHandler (15): all 8 server speeds, taxi state, taxi nodes/data
All converted from inline `{ return member_; }` to out-of-line
delegations: `return handler_ ? handler_->getter() : fallback;`
2026-03-28 12:18:14 -07:00
|
|
|
const std::vector<GameHandler::QuestLogEntry>& GameHandler::getQuestLog() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getQuestLog();
|
|
|
|
|
static const std::vector<QuestLogEntry> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::isQuestOfferRewardOpen() const {
|
|
|
|
|
return questHandler_ ? questHandler_->isQuestOfferRewardOpen() : false;
|
|
|
|
|
}
|
|
|
|
|
const QuestOfferRewardData& GameHandler::getQuestOfferReward() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getQuestOfferReward();
|
|
|
|
|
static const QuestOfferRewardData empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::isQuestRequestItemsOpen() const {
|
|
|
|
|
return questHandler_ ? questHandler_->isQuestRequestItemsOpen() : false;
|
|
|
|
|
}
|
|
|
|
|
const QuestRequestItemsData& GameHandler::getQuestRequestItems() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getQuestRequestItems();
|
|
|
|
|
static const QuestRequestItemsData empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
int GameHandler::getSelectedQuestLogIndex() const {
|
|
|
|
|
return questHandler_ ? questHandler_->getSelectedQuestLogIndex() : 0;
|
|
|
|
|
}
|
|
|
|
|
uint32_t GameHandler::getSharedQuestId() const {
|
|
|
|
|
return questHandler_ ? questHandler_->getSharedQuestId() : 0;
|
|
|
|
|
}
|
|
|
|
|
const std::string& GameHandler::getSharedQuestSharerName() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getSharedQuestSharerName();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
const std::string& GameHandler::getSharedQuestTitle() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getSharedQuestTitle();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
const std::unordered_set<uint32_t>& GameHandler::getTrackedQuestIds() const {
|
|
|
|
|
if (questHandler_) return questHandler_->getTrackedQuestIds();
|
|
|
|
|
static const std::unordered_set<uint32_t> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::hasPendingSharedQuest() const {
|
|
|
|
|
return questHandler_ ? questHandler_->hasPendingSharedQuest() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- MovementHandler delegating getters ----
|
|
|
|
|
|
|
|
|
|
float GameHandler::getServerRunSpeed() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->getServerRunSpeed() : 7.0f;
|
|
|
|
|
}
|
|
|
|
|
float GameHandler::getServerWalkSpeed() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->getServerWalkSpeed() : 2.5f;
|
|
|
|
|
}
|
|
|
|
|
float GameHandler::getServerSwimSpeed() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->getServerSwimSpeed() : 4.722f;
|
|
|
|
|
}
|
|
|
|
|
float GameHandler::getServerSwimBackSpeed() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->getServerSwimBackSpeed() : 2.5f;
|
|
|
|
|
}
|
|
|
|
|
float GameHandler::getServerFlightSpeed() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->getServerFlightSpeed() : 7.0f;
|
|
|
|
|
}
|
|
|
|
|
float GameHandler::getServerFlightBackSpeed() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->getServerFlightBackSpeed() : 4.5f;
|
|
|
|
|
}
|
|
|
|
|
float GameHandler::getServerRunBackSpeed() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->getServerRunBackSpeed() : 4.5f;
|
|
|
|
|
}
|
|
|
|
|
float GameHandler::getServerTurnRate() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->getServerTurnRate() : 3.14159f;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::isTaxiWindowOpen() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->isTaxiWindowOpen() : false;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::isOnTaxiFlight() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->isOnTaxiFlight() : false;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::isTaxiMountActive() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->isTaxiMountActive() : false;
|
|
|
|
|
}
|
|
|
|
|
bool GameHandler::isTaxiActivationPending() const {
|
|
|
|
|
return movementHandler_ ? movementHandler_->isTaxiActivationPending() : false;
|
|
|
|
|
}
|
|
|
|
|
const std::string& GameHandler::getTaxiDestName() const {
|
|
|
|
|
if (movementHandler_) return movementHandler_->getTaxiDestName();
|
|
|
|
|
static const std::string empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
const ShowTaxiNodesData& GameHandler::getTaxiData() const {
|
|
|
|
|
if (movementHandler_) return movementHandler_->getTaxiData();
|
|
|
|
|
static const ShowTaxiNodesData empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
uint32_t GameHandler::getTaxiCurrentNode() const {
|
|
|
|
|
if (movementHandler_) return movementHandler_->getTaxiData().nearestNode;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
const std::unordered_map<uint32_t, GameHandler::TaxiNode>& GameHandler::getTaxiNodes() const {
|
|
|
|
|
if (movementHandler_) return movementHandler_->getTaxiNodes();
|
|
|
|
|
static const std::unordered_map<uint32_t, TaxiNode> empty;
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
} // namespace game
|
|
|
|
|
} // namespace wowee
|