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
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "game/world_packets.hpp"
|
|
|
|
|
#include "game/opcode_table.hpp"
|
|
|
|
|
#include "game/spell_defines.hpp"
|
|
|
|
|
#include "game/handler_types.hpp"
|
2026-04-06 22:43:13 +03:00
|
|
|
#include "audio/spell_sound_manager.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 "network/packet.hpp"
|
2026-04-07 11:27:59 +03:00
|
|
|
#include <glm/glm.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 <array>
|
|
|
|
|
#include <chrono>
|
|
|
|
|
#include <functional>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
#include <unordered_set>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace game {
|
|
|
|
|
|
|
|
|
|
class GameHandler;
|
|
|
|
|
|
|
|
|
|
class SpellHandler {
|
|
|
|
|
public:
|
|
|
|
|
using PacketHandler = std::function<void(network::Packet&)>;
|
|
|
|
|
using DispatchTable = std::unordered_map<LogicalOpcode, PacketHandler>;
|
|
|
|
|
|
|
|
|
|
explicit SpellHandler(GameHandler& owner);
|
|
|
|
|
|
|
|
|
|
void registerOpcodes(DispatchTable& table);
|
|
|
|
|
|
|
|
|
|
// Talent data structures (aliased from handler_types.hpp)
|
|
|
|
|
using TalentEntry = game::TalentEntry;
|
|
|
|
|
using TalentTabEntry = game::TalentTabEntry;
|
|
|
|
|
|
|
|
|
|
// --- Spell book tabs ---
|
|
|
|
|
struct SpellBookTab {
|
|
|
|
|
std::string name;
|
|
|
|
|
std::string texture; // icon path
|
|
|
|
|
std::vector<uint32_t> spellIds; // spells in this tab
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Unit cast state (aliased from handler_types.hpp)
|
|
|
|
|
using UnitCastState = game::UnitCastState;
|
|
|
|
|
|
|
|
|
|
// Equipment set info (aliased from handler_types.hpp)
|
|
|
|
|
using EquipmentSetInfo = game::EquipmentSetInfo;
|
|
|
|
|
|
|
|
|
|
// --- Public API (delegated from GameHandler) ---
|
|
|
|
|
void castSpell(uint32_t spellId, uint64_t targetGuid = 0);
|
|
|
|
|
void cancelCast();
|
|
|
|
|
void cancelAura(uint32_t spellId);
|
|
|
|
|
|
|
|
|
|
// Known spells
|
|
|
|
|
const std::unordered_set<uint32_t>& getKnownSpells() const { return knownSpells_; }
|
|
|
|
|
const std::unordered_map<uint32_t, float>& getSpellCooldowns() const { return spellCooldowns_; }
|
|
|
|
|
float getSpellCooldown(uint32_t spellId) const;
|
|
|
|
|
|
|
|
|
|
// Cast state
|
|
|
|
|
bool isCasting() const { return casting_; }
|
|
|
|
|
bool isChanneling() const { return casting_ && castIsChannel_; }
|
|
|
|
|
bool isGameObjectInteractionCasting() const;
|
|
|
|
|
uint32_t getCurrentCastSpellId() const { return currentCastSpellId_; }
|
|
|
|
|
float getCastProgress() const { return castTimeTotal_ > 0 ? (castTimeTotal_ - castTimeRemaining_) / castTimeTotal_ : 0.0f; }
|
|
|
|
|
float getCastTimeRemaining() const { return castTimeRemaining_; }
|
|
|
|
|
float getCastTimeTotal() const { return castTimeTotal_; }
|
|
|
|
|
|
|
|
|
|
// Repeat-craft queue
|
|
|
|
|
void startCraftQueue(uint32_t spellId, int count);
|
|
|
|
|
void cancelCraftQueue();
|
|
|
|
|
int getCraftQueueRemaining() const { return craftQueueRemaining_; }
|
|
|
|
|
uint32_t getCraftQueueSpellId() const { return craftQueueSpellId_; }
|
|
|
|
|
|
|
|
|
|
// Spell queue (400ms window)
|
|
|
|
|
uint32_t getQueuedSpellId() const { return queuedSpellId_; }
|
|
|
|
|
void cancelQueuedSpell() { queuedSpellId_ = 0; queuedSpellTarget_ = 0; }
|
|
|
|
|
|
|
|
|
|
// Unit cast state (tracked per GUID for target frame + boss frames)
|
|
|
|
|
const UnitCastState* getUnitCastState(uint64_t guid) const {
|
|
|
|
|
auto it = unitCastStates_.find(guid);
|
|
|
|
|
return (it != unitCastStates_.end() && it->second.casting) ? &it->second : nullptr;
|
|
|
|
|
}
|
2026-03-29 16:49:17 -07:00
|
|
|
void clearUnitCastStates() { unitCastStates_.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
|
|
|
void removeUnitCastState(uint64_t guid) { unitCastStates_.erase(guid); }
|
|
|
|
|
|
|
|
|
|
// Aura cache mutation (formerly accessed via friend)
|
|
|
|
|
void clearUnitAurasCache() { unitAurasCache_.clear(); }
|
|
|
|
|
void removeUnitAuraCache(uint64_t guid) { unitAurasCache_.erase(guid); }
|
|
|
|
|
|
|
|
|
|
// Known spells mutation (formerly accessed via friend)
|
|
|
|
|
void addKnownSpell(uint32_t spellId) { knownSpells_.insert(spellId); }
|
|
|
|
|
bool hasKnownSpell(uint32_t spellId) const { return knownSpells_.count(spellId) > 0; }
|
|
|
|
|
|
|
|
|
|
// Target aura mutation (formerly accessed via friend)
|
|
|
|
|
void clearTargetAuras() { for (auto& slot : targetAuras_) slot = AuraSlot{}; }
|
|
|
|
|
|
|
|
|
|
// Player aura mutation (formerly accessed via friend)
|
|
|
|
|
void resetPlayerAuras(size_t capacity) { playerAuras_.clear(); playerAuras_.resize(capacity); }
|
|
|
|
|
AuraSlot& getPlayerAuraSlotRef(size_t slot) { return playerAuras_[slot]; }
|
|
|
|
|
std::vector<AuraSlot>& getPlayerAurasMut() { return playerAuras_; }
|
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
|
|
|
|
|
|
|
|
// Target cast helpers
|
|
|
|
|
bool isTargetCasting() const;
|
|
|
|
|
uint32_t getTargetCastSpellId() const;
|
|
|
|
|
float getTargetCastProgress() const;
|
|
|
|
|
float getTargetCastTimeRemaining() const;
|
|
|
|
|
bool isTargetCastInterruptible() const;
|
|
|
|
|
|
|
|
|
|
// Talents
|
|
|
|
|
uint8_t getActiveTalentSpec() const { return activeTalentSpec_; }
|
|
|
|
|
uint8_t getUnspentTalentPoints() const { return unspentTalentPoints_[activeTalentSpec_]; }
|
|
|
|
|
uint8_t getUnspentTalentPoints(uint8_t spec) const { return spec < 2 ? unspentTalentPoints_[spec] : 0; }
|
|
|
|
|
const std::unordered_map<uint32_t, uint8_t>& getLearnedTalents() const { return learnedTalents_[activeTalentSpec_]; }
|
|
|
|
|
const std::unordered_map<uint32_t, uint8_t>& getLearnedTalents(uint8_t spec) const {
|
|
|
|
|
static std::unordered_map<uint32_t, uint8_t> empty;
|
|
|
|
|
return spec < 2 ? learnedTalents_[spec] : empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static constexpr uint8_t MAX_GLYPH_SLOTS = 6;
|
|
|
|
|
const std::array<uint16_t, MAX_GLYPH_SLOTS>& getGlyphs() const { return learnedGlyphs_[activeTalentSpec_]; }
|
|
|
|
|
const std::array<uint16_t, MAX_GLYPH_SLOTS>& getGlyphs(uint8_t spec) const {
|
|
|
|
|
static std::array<uint16_t, MAX_GLYPH_SLOTS> empty{};
|
|
|
|
|
return spec < 2 ? learnedGlyphs_[spec] : empty;
|
|
|
|
|
}
|
|
|
|
|
uint8_t getTalentRank(uint32_t talentId) const {
|
|
|
|
|
auto it = learnedTalents_[activeTalentSpec_].find(talentId);
|
|
|
|
|
return (it != learnedTalents_[activeTalentSpec_].end()) ? it->second : 0;
|
|
|
|
|
}
|
|
|
|
|
void learnTalent(uint32_t talentId, uint32_t requestedRank);
|
|
|
|
|
void switchTalentSpec(uint8_t newSpec);
|
|
|
|
|
|
|
|
|
|
// Talent DBC access
|
|
|
|
|
const TalentEntry* getTalentEntry(uint32_t talentId) const {
|
|
|
|
|
auto it = talentCache_.find(talentId);
|
|
|
|
|
return (it != talentCache_.end()) ? &it->second : nullptr;
|
|
|
|
|
}
|
|
|
|
|
const TalentTabEntry* getTalentTabEntry(uint32_t tabId) const {
|
|
|
|
|
auto it = talentTabCache_.find(tabId);
|
|
|
|
|
return (it != talentTabCache_.end()) ? &it->second : nullptr;
|
|
|
|
|
}
|
|
|
|
|
const std::unordered_map<uint32_t, TalentEntry>& getAllTalents() const { return talentCache_; }
|
|
|
|
|
const std::unordered_map<uint32_t, TalentTabEntry>& getAllTalentTabs() const { return talentTabCache_; }
|
|
|
|
|
void loadTalentDbc();
|
|
|
|
|
|
|
|
|
|
// Auras
|
|
|
|
|
const std::vector<AuraSlot>& getPlayerAuras() const { return playerAuras_; }
|
|
|
|
|
const std::vector<AuraSlot>& getTargetAuras() const { return targetAuras_; }
|
|
|
|
|
const std::vector<AuraSlot>* getUnitAuras(uint64_t guid) const {
|
|
|
|
|
auto it = unitAurasCache_.find(guid);
|
|
|
|
|
return (it != unitAurasCache_.end()) ? &it->second : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Global Cooldown (GCD)
|
|
|
|
|
float getGCDRemaining() const {
|
|
|
|
|
if (gcdTotal_ <= 0.0f) return 0.0f;
|
|
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
|
std::chrono::steady_clock::now() - gcdStartedAt_).count() / 1000.0f;
|
|
|
|
|
float rem = gcdTotal_ - elapsed;
|
|
|
|
|
return rem > 0.0f ? rem : 0.0f;
|
|
|
|
|
}
|
|
|
|
|
float getGCDTotal() const { return gcdTotal_; }
|
|
|
|
|
bool isGCDActive() const { return getGCDRemaining() > 0.0f; }
|
|
|
|
|
|
|
|
|
|
// Spell book tabs
|
|
|
|
|
const std::vector<SpellBookTab>& getSpellBookTabs();
|
|
|
|
|
|
|
|
|
|
// Talent wipe confirm dialog
|
|
|
|
|
bool showTalentWipeConfirmDialog() const { return talentWipePending_; }
|
|
|
|
|
uint32_t getTalentWipeCost() const { return talentWipeCost_; }
|
|
|
|
|
void confirmTalentWipe();
|
|
|
|
|
void cancelTalentWipe() { talentWipePending_ = false; }
|
|
|
|
|
|
|
|
|
|
// Pet talent respec confirm
|
|
|
|
|
bool showPetUnlearnDialog() const { return petUnlearnPending_; }
|
|
|
|
|
uint32_t getPetUnlearnCost() const { return petUnlearnCost_; }
|
|
|
|
|
void confirmPetUnlearn();
|
|
|
|
|
void cancelPetUnlearn() { petUnlearnPending_ = false; }
|
|
|
|
|
|
|
|
|
|
// Item use
|
|
|
|
|
void useItemBySlot(int backpackIndex);
|
|
|
|
|
void useItemInBag(int bagIndex, int slotIndex);
|
|
|
|
|
void useItemById(uint32_t itemId);
|
|
|
|
|
|
2026-03-28 11:35:10 +03:00
|
|
|
// Equipment sets — canonical data owned by InventoryHandler;
|
|
|
|
|
// GameHandler::getEquipmentSets() delegates to inventoryHandler_.
|
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
|
|
|
|
|
|
|
|
// Pet spells
|
|
|
|
|
void sendPetAction(uint32_t action, uint64_t targetGuid = 0);
|
|
|
|
|
void dismissPet();
|
|
|
|
|
void togglePetSpellAutocast(uint32_t spellId);
|
|
|
|
|
void renamePet(const std::string& newName);
|
|
|
|
|
|
|
|
|
|
// Spell DBC accessors
|
|
|
|
|
const int32_t* getSpellEffectBasePoints(uint32_t spellId) const;
|
|
|
|
|
float getSpellDuration(uint32_t spellId) const;
|
|
|
|
|
const std::string& getSpellName(uint32_t spellId) const;
|
|
|
|
|
const std::string& getSpellRank(uint32_t spellId) const;
|
|
|
|
|
const std::string& getSpellDescription(uint32_t spellId) const;
|
|
|
|
|
std::string getEnchantName(uint32_t enchantId) const;
|
|
|
|
|
uint8_t getSpellDispelType(uint32_t spellId) const;
|
|
|
|
|
bool isSpellInterruptible(uint32_t spellId) const;
|
|
|
|
|
uint32_t getSpellSchoolMask(uint32_t spellId) const;
|
|
|
|
|
const std::string& getSkillLineName(uint32_t spellId) const;
|
|
|
|
|
|
|
|
|
|
// Cast state
|
|
|
|
|
void stopCasting();
|
|
|
|
|
void resetCastState();
|
2026-03-28 11:35:10 +03:00
|
|
|
void resetTalentState();
|
2026-03-29 18:46:42 -07:00
|
|
|
// Full per-character reset (spells, cooldowns, auras, cast state, talents).
|
|
|
|
|
// Called from GameHandler::selectCharacter so spell state doesn't bleed between characters.
|
|
|
|
|
void resetAllState();
|
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 clearUnitCaches();
|
|
|
|
|
|
|
|
|
|
// Aura duration
|
|
|
|
|
void handleUpdateAuraDuration(uint8_t slot, uint32_t durationMs);
|
|
|
|
|
|
|
|
|
|
// Skill DBC
|
|
|
|
|
void loadSkillLineDbc();
|
|
|
|
|
void extractSkillFields(const std::map<uint16_t, uint32_t>& fields);
|
|
|
|
|
void extractExploredZoneFields(const std::map<uint16_t, uint32_t>& fields);
|
|
|
|
|
|
|
|
|
|
// Update per-frame timers (call from GameHandler::update)
|
|
|
|
|
void updateTimers(float dt);
|
|
|
|
|
|
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
|
|
|
// Packet handlers dispatched from GameHandler's opcode table
|
|
|
|
|
void handlePetSpells(network::Packet& packet);
|
|
|
|
|
void handleListStabledPets(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
// Pet stable commands (called via GameHandler delegation)
|
|
|
|
|
void requestStabledPetList();
|
|
|
|
|
void stablePet(uint8_t slot);
|
|
|
|
|
void unstablePet(uint32_t petNumber);
|
|
|
|
|
|
|
|
|
|
// DBC cache loading (called from GameHandler during login)
|
|
|
|
|
void loadSpellNameCache() const;
|
|
|
|
|
void loadSkillLineAbilityDbc();
|
|
|
|
|
void categorizeTrainerSpells();
|
|
|
|
|
|
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
|
|
|
private:
|
|
|
|
|
// --- Packet handlers ---
|
|
|
|
|
void handleInitialSpells(network::Packet& packet);
|
|
|
|
|
void handleCastFailed(network::Packet& packet);
|
|
|
|
|
void handleSpellStart(network::Packet& packet);
|
|
|
|
|
void handleSpellGo(network::Packet& packet);
|
|
|
|
|
void handleSpellCooldown(network::Packet& packet);
|
|
|
|
|
void handleCooldownEvent(network::Packet& packet);
|
|
|
|
|
void handleAuraUpdate(network::Packet& packet, bool isAll);
|
|
|
|
|
void handleLearnedSpell(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
void handleCastResult(network::Packet& packet);
|
|
|
|
|
void handleSpellFailedOther(network::Packet& packet);
|
|
|
|
|
void handleClearCooldown(network::Packet& packet);
|
|
|
|
|
void handleModifyCooldown(network::Packet& packet);
|
|
|
|
|
void handlePlaySpellVisual(network::Packet& packet);
|
|
|
|
|
void handleSpellModifier(network::Packet& packet, bool isFlat);
|
|
|
|
|
void handleSpellDelayed(network::Packet& packet);
|
|
|
|
|
void handleSpellLogMiss(network::Packet& packet);
|
|
|
|
|
void handleSpellFailure(network::Packet& packet);
|
|
|
|
|
void handleItemCooldown(network::Packet& packet);
|
|
|
|
|
void handleDispelFailed(network::Packet& packet);
|
|
|
|
|
void handleTotemCreated(network::Packet& packet);
|
|
|
|
|
void handlePeriodicAuraLog(network::Packet& packet);
|
|
|
|
|
void handleSpellEnergizeLog(network::Packet& packet);
|
|
|
|
|
void handleExtraAuraInfo(network::Packet& packet, bool isInit);
|
|
|
|
|
void handleSpellDispelLog(network::Packet& packet);
|
|
|
|
|
void handleSpellStealLog(network::Packet& packet);
|
|
|
|
|
void handleSpellChanceProcLog(network::Packet& packet);
|
|
|
|
|
void handleSpellInstaKillLog(network::Packet& packet);
|
|
|
|
|
void handleSpellLogExecute(network::Packet& packet);
|
|
|
|
|
void handleClearExtraAuraInfo(network::Packet& packet);
|
|
|
|
|
void handleItemEnchantTimeUpdate(network::Packet& packet);
|
|
|
|
|
void handleResumeCastBar(network::Packet& packet);
|
|
|
|
|
void handleChannelStart(network::Packet& packet);
|
|
|
|
|
void handleChannelUpdate(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
// --- Internal helpers ---
|
2026-04-06 22:43:13 +03:00
|
|
|
|
|
|
|
|
// Resolve the magic school for a spell (for audio playback).
|
|
|
|
|
// Returns MagicSchool from the spell name cache, defaulting to ARCANE.
|
|
|
|
|
audio::SpellSoundManager::MagicSchool resolveSpellSchool(uint32_t spellId);
|
|
|
|
|
|
|
|
|
|
// Play a spell cast or impact sound via audioCoordinator, if available.
|
|
|
|
|
void playSpellCastSound(uint32_t spellId);
|
|
|
|
|
void playSpellImpactSound(uint32_t spellId);
|
|
|
|
|
|
2026-04-07 11:27:59 +03:00
|
|
|
// Resolve SpellVisualID from Spell.dbc cache for a given spellId.
|
|
|
|
|
uint32_t resolveSpellVisualId(uint32_t spellId);
|
|
|
|
|
// Resolve render-space position for a unit GUID (player or entity).
|
|
|
|
|
bool resolveUnitPosition(uint64_t guid, glm::vec3& outPos);
|
|
|
|
|
// Play the cast/precast visual effect at the caster's position.
|
|
|
|
|
void triggerCastVisual(uint32_t spellId, uint64_t casterGuid, uint32_t castTimeMs = 0);
|
|
|
|
|
// Play the impact visual effect at the target's position.
|
|
|
|
|
void triggerImpactVisual(uint32_t spellId, uint64_t targetGuid);
|
|
|
|
|
|
2026-04-06 22:43:13 +03:00
|
|
|
// --- handleSpellLogExecute per-effect parsers (extracted to reduce nesting) ---
|
|
|
|
|
void parseEffectPowerDrain(network::Packet& packet, uint32_t effectLogCount,
|
|
|
|
|
uint64_t caster, uint32_t spellId, bool isPlayerCaster,
|
|
|
|
|
bool usesFullGuid);
|
|
|
|
|
void parseEffectHealthLeech(network::Packet& packet, uint32_t effectLogCount,
|
|
|
|
|
uint64_t caster, uint32_t spellId, bool isPlayerCaster,
|
|
|
|
|
bool usesFullGuid);
|
|
|
|
|
void parseEffectCreateItem(network::Packet& packet, uint32_t effectLogCount,
|
|
|
|
|
uint64_t caster, uint32_t spellId, bool isPlayerCaster);
|
|
|
|
|
void parseEffectInterruptCast(network::Packet& packet, uint32_t effectLogCount,
|
|
|
|
|
uint64_t caster, uint32_t spellId, bool isPlayerCaster,
|
|
|
|
|
bool usesFullGuid);
|
|
|
|
|
void parseEffectFeedPet(network::Packet& packet, uint32_t effectLogCount,
|
|
|
|
|
uint64_t caster, uint32_t spellId, bool isPlayerCaster);
|
|
|
|
|
|
2026-03-30 13:56:45 -07:00
|
|
|
// Find the on-use spell for an item (trigger=0 Use or trigger=5 NoDelay).
|
|
|
|
|
// CMSG_USE_ITEM requires a valid spellId or the server silently ignores it.
|
|
|
|
|
uint32_t findOnUseSpellId(uint32_t itemId) const;
|
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 handleSupercededSpell(network::Packet& packet);
|
|
|
|
|
void handleRemovedSpell(network::Packet& packet);
|
|
|
|
|
void handleUnlearnSpells(network::Packet& packet);
|
|
|
|
|
void handleTalentsInfo(network::Packet& packet);
|
|
|
|
|
void handleAchievementEarned(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
GameHandler& owner_;
|
|
|
|
|
|
|
|
|
|
// --- Spell state ---
|
|
|
|
|
std::unordered_set<uint32_t> knownSpells_;
|
|
|
|
|
std::unordered_map<uint32_t, float> spellCooldowns_; // spellId -> remaining seconds
|
|
|
|
|
uint8_t castCount_ = 0;
|
|
|
|
|
bool casting_ = false;
|
|
|
|
|
bool castIsChannel_ = false;
|
|
|
|
|
uint32_t currentCastSpellId_ = 0;
|
|
|
|
|
float castTimeRemaining_ = 0.0f;
|
|
|
|
|
float castTimeTotal_ = 0.0f;
|
|
|
|
|
|
|
|
|
|
// Repeat-craft queue
|
|
|
|
|
uint32_t craftQueueSpellId_ = 0;
|
|
|
|
|
int craftQueueRemaining_ = 0;
|
|
|
|
|
|
|
|
|
|
// Spell queue (400ms window)
|
|
|
|
|
uint32_t queuedSpellId_ = 0;
|
|
|
|
|
uint64_t queuedSpellTarget_ = 0;
|
|
|
|
|
|
|
|
|
|
// Per-unit cast state
|
|
|
|
|
std::unordered_map<uint64_t, UnitCastState> unitCastStates_;
|
|
|
|
|
|
|
|
|
|
// Talents (dual-spec support)
|
|
|
|
|
uint8_t activeTalentSpec_ = 0;
|
|
|
|
|
uint8_t unspentTalentPoints_[2] = {0, 0};
|
|
|
|
|
std::unordered_map<uint32_t, uint8_t> learnedTalents_[2];
|
|
|
|
|
std::array<std::array<uint16_t, MAX_GLYPH_SLOTS>, 2> learnedGlyphs_{};
|
|
|
|
|
std::unordered_map<uint32_t, TalentEntry> talentCache_;
|
|
|
|
|
std::unordered_map<uint32_t, TalentTabEntry> talentTabCache_;
|
|
|
|
|
bool talentDbcLoaded_ = false;
|
|
|
|
|
bool talentsInitialized_ = false;
|
|
|
|
|
|
|
|
|
|
// Auras
|
|
|
|
|
std::vector<AuraSlot> playerAuras_;
|
|
|
|
|
std::vector<AuraSlot> targetAuras_;
|
|
|
|
|
std::unordered_map<uint64_t, std::vector<AuraSlot>> unitAurasCache_;
|
|
|
|
|
|
|
|
|
|
// Global Cooldown
|
|
|
|
|
float gcdTotal_ = 0.0f;
|
|
|
|
|
std::chrono::steady_clock::time_point gcdStartedAt_{};
|
|
|
|
|
|
|
|
|
|
// Spell book tabs
|
|
|
|
|
std::vector<SpellBookTab> spellBookTabs_;
|
2026-03-29 19:08:58 -07:00
|
|
|
size_t lastSpellCount_ = 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
|
|
|
bool spellBookTabsDirty_ = true;
|
|
|
|
|
|
|
|
|
|
// Talent wipe confirm dialog
|
|
|
|
|
bool talentWipePending_ = false;
|
|
|
|
|
uint64_t talentWipeNpcGuid_ = 0;
|
|
|
|
|
uint32_t talentWipeCost_ = 0;
|
|
|
|
|
|
|
|
|
|
// Pet talent respec confirm dialog
|
|
|
|
|
bool petUnlearnPending_ = false;
|
|
|
|
|
uint64_t petUnlearnGuid_ = 0;
|
|
|
|
|
uint32_t petUnlearnCost_ = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace game
|
|
|
|
|
} // namespace wowee
|