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/movement_handler.hpp"
|
|
|
|
|
#include "game/game_handler.hpp"
|
|
|
|
|
#include "game/game_utils.hpp"
|
|
|
|
|
#include "game/packet_parsers.hpp"
|
|
|
|
|
#include "game/transport_manager.hpp"
|
|
|
|
|
#include "game/entity.hpp"
|
|
|
|
|
#include "network/world_socket.hpp"
|
|
|
|
|
#include "network/packet.hpp"
|
|
|
|
|
#include "core/coordinates.hpp"
|
|
|
|
|
#include "core/application.hpp"
|
|
|
|
|
#include "pipeline/asset_manager.hpp"
|
|
|
|
|
#include "pipeline/dbc_layout.hpp"
|
|
|
|
|
#include "core/logger.hpp"
|
|
|
|
|
#include <glm/gtx/quaternion.hpp>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <zlib.h>
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <unordered_set>
|
|
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace game {
|
|
|
|
|
|
|
|
|
|
MovementHandler::MovementHandler(GameHandler& owner)
|
2026-04-05 19:30:44 +03:00
|
|
|
: owner_(owner), movementInfo(owner.movementInfoRef()) {}
|
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 MovementHandler::registerOpcodes(DispatchTable& table) {
|
|
|
|
|
// Creature movement
|
|
|
|
|
table[Opcode::SMSG_MONSTER_MOVE] = [this](network::Packet& packet) { handleMonsterMove(packet); };
|
|
|
|
|
table[Opcode::SMSG_COMPRESSED_MOVES] = [this](network::Packet& packet) { handleCompressedMoves(packet); };
|
|
|
|
|
table[Opcode::SMSG_MONSTER_MOVE_TRANSPORT] = [this](network::Packet& packet) { handleMonsterMoveTransport(packet); };
|
|
|
|
|
|
|
|
|
|
// Spline move: consume-only (no state change)
|
|
|
|
|
for (auto op : { Opcode::SMSG_SPLINE_MOVE_FEATHER_FALL,
|
|
|
|
|
Opcode::SMSG_SPLINE_MOVE_GRAVITY_DISABLE,
|
|
|
|
|
Opcode::SMSG_SPLINE_MOVE_GRAVITY_ENABLE,
|
|
|
|
|
Opcode::SMSG_SPLINE_MOVE_LAND_WALK,
|
|
|
|
|
Opcode::SMSG_SPLINE_MOVE_NORMAL_FALL,
|
|
|
|
|
Opcode::SMSG_SPLINE_MOVE_ROOT,
|
|
|
|
|
Opcode::SMSG_SPLINE_MOVE_SET_HOVER }) {
|
|
|
|
|
table[op] = [this](network::Packet& packet) {
|
2026-03-29 20:53:26 -07:00
|
|
|
if (packet.hasRemaining(1))
|
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)packet.readPackedGuid();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spline move: synth flags (each opcode produces different flags)
|
|
|
|
|
{
|
|
|
|
|
auto makeSynthHandler = [this](uint32_t synthFlags) {
|
|
|
|
|
return [this, synthFlags](network::Packet& packet) {
|
2026-03-29 20:53:26 -07:00
|
|
|
if (!packet.hasRemaining(1)) return;
|
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
|
|
|
uint64_t guid = packet.readPackedGuid();
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid == 0 || guid == owner_.getPlayerGuid() || !owner_.unitMoveFlagsCallbackRef()) return;
|
|
|
|
|
owner_.unitMoveFlagsCallbackRef()(guid, synthFlags);
|
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
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_SPLINE_MOVE_SET_WALK_MODE] = makeSynthHandler(0x00000100u);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_MOVE_SET_RUN_MODE] = makeSynthHandler(0u);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_MOVE_SET_FLYING] = makeSynthHandler(0x01000000u | 0x00800000u);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_MOVE_START_SWIM] = makeSynthHandler(0x00200000u);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_MOVE_STOP_SWIM] = makeSynthHandler(0u);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 17:59:51 -07:00
|
|
|
// Spline speed: all opcodes share the same PackedGuid+float format, differing
|
|
|
|
|
// only in which member receives the value. Factory avoids 8 copy-pasted lambdas.
|
|
|
|
|
auto makeSplineSpeedHandler = [this](float MovementHandler::* member) {
|
|
|
|
|
return [this, member](network::Packet& packet) {
|
|
|
|
|
if (!packet.hasRemaining(5)) return;
|
|
|
|
|
uint64_t guid = packet.readPackedGuid();
|
|
|
|
|
if (!packet.hasRemaining(4)) return;
|
|
|
|
|
float speed = packet.readFloat();
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid == owner_.getPlayerGuid() && std::isfinite(speed) && speed > 0.01f && speed < 200.0f)
|
2026-03-29 17:59:51 -07:00
|
|
|
this->*member = speed;
|
|
|
|
|
};
|
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-29 17:59:51 -07:00
|
|
|
table[Opcode::SMSG_SPLINE_SET_RUN_SPEED] = makeSplineSpeedHandler(&MovementHandler::serverRunSpeed_);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_SET_RUN_BACK_SPEED] = makeSplineSpeedHandler(&MovementHandler::serverRunBackSpeed_);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_SET_SWIM_SPEED] = makeSplineSpeedHandler(&MovementHandler::serverSwimSpeed_);
|
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
|
|
|
|
|
|
|
|
// Force speed changes
|
|
|
|
|
table[Opcode::SMSG_FORCE_RUN_SPEED_CHANGE] = [this](network::Packet& packet) { handleForceRunSpeedChange(packet); };
|
|
|
|
|
table[Opcode::SMSG_FORCE_MOVE_ROOT] = [this](network::Packet& packet) { handleForceMoveRootState(packet, true); };
|
|
|
|
|
table[Opcode::SMSG_FORCE_MOVE_UNROOT] = [this](network::Packet& packet) { handleForceMoveRootState(packet, false); };
|
|
|
|
|
table[Opcode::SMSG_FORCE_WALK_SPEED_CHANGE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "WALK_SPEED", Opcode::CMSG_FORCE_WALK_SPEED_CHANGE_ACK, &serverWalkSpeed_);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_FORCE_RUN_BACK_SPEED_CHANGE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "RUN_BACK_SPEED", Opcode::CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK, &serverRunBackSpeed_);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_FORCE_SWIM_SPEED_CHANGE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "SWIM_SPEED", Opcode::CMSG_FORCE_SWIM_SPEED_CHANGE_ACK, &serverSwimSpeed_);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_FORCE_SWIM_BACK_SPEED_CHANGE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "SWIM_BACK_SPEED", Opcode::CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK, &serverSwimBackSpeed_);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_FORCE_FLIGHT_SPEED_CHANGE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "FLIGHT_SPEED", Opcode::CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK, &serverFlightSpeed_);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "FLIGHT_BACK_SPEED", Opcode::CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK, &serverFlightBackSpeed_);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_FORCE_TURN_RATE_CHANGE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "TURN_RATE", Opcode::CMSG_FORCE_TURN_RATE_CHANGE_ACK, &serverTurnRate_);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_FORCE_PITCH_RATE_CHANGE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "PITCH_RATE", Opcode::CMSG_FORCE_PITCH_RATE_CHANGE_ACK, &serverPitchRate_);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Movement flag toggles
|
|
|
|
|
table[Opcode::SMSG_MOVE_SET_CAN_FLY] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "SET_CAN_FLY", Opcode::CMSG_MOVE_SET_CAN_FLY_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::CAN_FLY), true);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_UNSET_CAN_FLY] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "UNSET_CAN_FLY", Opcode::CMSG_MOVE_SET_CAN_FLY_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::CAN_FLY), false);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_FEATHER_FALL] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "FEATHER_FALL", Opcode::CMSG_MOVE_FEATHER_FALL_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FEATHER_FALL), true);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_WATER_WALK] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "WATER_WALK", Opcode::CMSG_MOVE_WATER_WALK_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::WATER_WALK), true);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_SET_HOVER] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "SET_HOVER", Opcode::CMSG_MOVE_HOVER_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::HOVER), true);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_UNSET_HOVER] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "UNSET_HOVER", Opcode::CMSG_MOVE_HOVER_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::HOVER), false);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_KNOCK_BACK] = [this](network::Packet& packet) { handleMoveKnockBack(packet); };
|
|
|
|
|
|
|
|
|
|
// Teleport
|
|
|
|
|
for (auto op : { Opcode::MSG_MOVE_TELEPORT, Opcode::MSG_MOVE_TELEPORT_ACK }) {
|
|
|
|
|
table[op] = [this](network::Packet& packet) { handleTeleportAck(packet); };
|
|
|
|
|
}
|
|
|
|
|
table[Opcode::SMSG_NEW_WORLD] = [this](network::Packet& packet) { handleNewWorld(packet); };
|
|
|
|
|
|
|
|
|
|
// Taxi
|
|
|
|
|
table[Opcode::SMSG_SHOWTAXINODES] = [this](network::Packet& packet) { handleShowTaxiNodes(packet); };
|
|
|
|
|
table[Opcode::SMSG_ACTIVATETAXIREPLY] = [this](network::Packet& packet) { handleActivateTaxiReply(packet); };
|
|
|
|
|
|
|
|
|
|
// MSG_MOVE_* relay (other player movement)
|
|
|
|
|
for (auto op : { Opcode::MSG_MOVE_START_FORWARD, Opcode::MSG_MOVE_START_BACKWARD,
|
|
|
|
|
Opcode::MSG_MOVE_STOP, Opcode::MSG_MOVE_START_STRAFE_LEFT,
|
|
|
|
|
Opcode::MSG_MOVE_START_STRAFE_RIGHT, Opcode::MSG_MOVE_STOP_STRAFE,
|
|
|
|
|
Opcode::MSG_MOVE_JUMP, Opcode::MSG_MOVE_START_TURN_LEFT,
|
|
|
|
|
Opcode::MSG_MOVE_START_TURN_RIGHT, Opcode::MSG_MOVE_STOP_TURN,
|
|
|
|
|
Opcode::MSG_MOVE_SET_FACING, Opcode::MSG_MOVE_FALL_LAND,
|
|
|
|
|
Opcode::MSG_MOVE_HEARTBEAT, Opcode::MSG_MOVE_START_SWIM,
|
|
|
|
|
Opcode::MSG_MOVE_STOP_SWIM, Opcode::MSG_MOVE_SET_WALK_MODE,
|
|
|
|
|
Opcode::MSG_MOVE_SET_RUN_MODE, Opcode::MSG_MOVE_START_PITCH_UP,
|
|
|
|
|
Opcode::MSG_MOVE_START_PITCH_DOWN, Opcode::MSG_MOVE_STOP_PITCH,
|
|
|
|
|
Opcode::MSG_MOVE_START_ASCEND, Opcode::MSG_MOVE_STOP_ASCEND,
|
|
|
|
|
Opcode::MSG_MOVE_START_DESCEND, Opcode::MSG_MOVE_SET_PITCH,
|
|
|
|
|
Opcode::MSG_MOVE_GRAVITY_CHNG, Opcode::MSG_MOVE_UPDATE_CAN_FLY,
|
|
|
|
|
Opcode::MSG_MOVE_UPDATE_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY,
|
|
|
|
|
Opcode::MSG_MOVE_ROOT, Opcode::MSG_MOVE_UNROOT }) {
|
|
|
|
|
table[op] = [this](network::Packet& packet) {
|
|
|
|
|
if (owner_.getState() == WorldState::IN_WORLD) handleOtherPlayerMovement(packet);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MSG_MOVE_SET_*_SPEED relay
|
|
|
|
|
for (auto op : { Opcode::MSG_MOVE_SET_RUN_SPEED, Opcode::MSG_MOVE_SET_RUN_BACK_SPEED,
|
|
|
|
|
Opcode::MSG_MOVE_SET_WALK_SPEED, Opcode::MSG_MOVE_SET_SWIM_SPEED,
|
|
|
|
|
Opcode::MSG_MOVE_SET_SWIM_BACK_SPEED, Opcode::MSG_MOVE_SET_FLIGHT_SPEED,
|
|
|
|
|
Opcode::MSG_MOVE_SET_FLIGHT_BACK_SPEED }) {
|
|
|
|
|
table[op] = [this](network::Packet& packet) {
|
|
|
|
|
if (owner_.getState() == WorldState::IN_WORLD) handleMoveSetSpeed(packet);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Client control & spline speed/flag changes ----
|
|
|
|
|
|
|
|
|
|
// Client control update
|
|
|
|
|
table[Opcode::SMSG_CLIENT_CONTROL_UPDATE] = [this](network::Packet& packet) {
|
|
|
|
|
handleClientControlUpdate(packet);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Spline move flag changes for other units (unroot/unset_hover/water_walk)
|
|
|
|
|
for (auto op : {Opcode::SMSG_SPLINE_MOVE_UNROOT,
|
|
|
|
|
Opcode::SMSG_SPLINE_MOVE_UNSET_HOVER,
|
|
|
|
|
Opcode::SMSG_SPLINE_MOVE_WATER_WALK}) {
|
|
|
|
|
table[op] = [this](network::Packet& packet) {
|
|
|
|
|
// Minimal parse: PackedGuid only — no animation-relevant state change.
|
|
|
|
|
if (packet.hasRemaining(1)) {
|
|
|
|
|
(void)packet.readPackedGuid();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table[Opcode::SMSG_SPLINE_MOVE_UNSET_FLYING] = [this](network::Packet& packet) {
|
|
|
|
|
// PackedGuid + synthesised move-flags=0 → clears flying animation.
|
|
|
|
|
if (!packet.hasRemaining(1)) return;
|
|
|
|
|
uint64_t guid = packet.readPackedGuid();
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid == 0 || guid == owner_.getPlayerGuid() || !owner_.unitMoveFlagsCallbackRef()) return;
|
|
|
|
|
owner_.unitMoveFlagsCallbackRef()(guid, 0u); // clear flying/CAN_FLY
|
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-29 17:59:51 -07:00
|
|
|
// Remaining spline speed opcodes — same factory as above.
|
|
|
|
|
table[Opcode::SMSG_SPLINE_SET_FLIGHT_SPEED] = makeSplineSpeedHandler(&MovementHandler::serverFlightSpeed_);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_SET_FLIGHT_BACK_SPEED] = makeSplineSpeedHandler(&MovementHandler::serverFlightBackSpeed_);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_SET_SWIM_BACK_SPEED] = makeSplineSpeedHandler(&MovementHandler::serverSwimBackSpeed_);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_SET_WALK_SPEED] = makeSplineSpeedHandler(&MovementHandler::serverWalkSpeed_);
|
|
|
|
|
table[Opcode::SMSG_SPLINE_SET_TURN_RATE] = makeSplineSpeedHandler(&MovementHandler::serverTurnRate_);
|
|
|
|
|
// Pitch rate not stored locally — consume packet to keep stream aligned.
|
|
|
|
|
table[Opcode::SMSG_SPLINE_SET_PITCH_RATE] = [](network::Packet& packet) { packet.skipAll(); };
|
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
|
|
|
|
|
|
|
|
// ---- Player movement flag changes (server-pushed) ----
|
|
|
|
|
table[Opcode::SMSG_MOVE_GRAVITY_DISABLE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "GRAVITY_DISABLE", Opcode::CMSG_MOVE_GRAVITY_DISABLE_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::LEVITATING), true);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_GRAVITY_ENABLE] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "GRAVITY_ENABLE", Opcode::CMSG_MOVE_GRAVITY_ENABLE_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::LEVITATING), false);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_LAND_WALK] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "LAND_WALK", Opcode::CMSG_MOVE_WATER_WALK_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::WATER_WALK), false);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_NORMAL_FALL] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "NORMAL_FALL", Opcode::CMSG_MOVE_FEATHER_FALL_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FEATHER_FALL), false);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "SET_CAN_TRANSITION_SWIM_FLY",
|
|
|
|
|
Opcode::CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK, 0, true);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_UNSET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "UNSET_CAN_TRANSITION_SWIM_FLY",
|
|
|
|
|
Opcode::CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK, 0, false);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_SET_COLLISION_HGT] = [this](network::Packet& packet) {
|
|
|
|
|
handleMoveSetCollisionHeight(packet);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_SET_FLIGHT] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "SET_FLIGHT", Opcode::CMSG_MOVE_FLIGHT_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FLYING), true);
|
|
|
|
|
};
|
|
|
|
|
table[Opcode::SMSG_MOVE_UNSET_FLIGHT] = [this](network::Packet& packet) {
|
|
|
|
|
handleForceMoveFlagChange(packet, "UNSET_FLIGHT", Opcode::CMSG_MOVE_FLIGHT_ACK,
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FLYING), false);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// handleClientControlUpdate
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleClientControlUpdate(network::Packet& packet) {
|
|
|
|
|
// Minimal parse: PackedGuid + uint8 allowMovement.
|
|
|
|
|
if (!packet.hasRemaining(2)) {
|
|
|
|
|
LOG_WARNING("SMSG_CLIENT_CONTROL_UPDATE too short: ", packet.getSize(), " bytes");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uint8_t guidMask = packet.readUInt8();
|
|
|
|
|
size_t guidBytes = 0;
|
|
|
|
|
uint64_t controlGuid = 0;
|
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
|
if (guidMask & (1u << i)) ++guidBytes;
|
|
|
|
|
}
|
|
|
|
|
if (!packet.hasRemaining(guidBytes) + 1) {
|
|
|
|
|
LOG_WARNING("SMSG_CLIENT_CONTROL_UPDATE malformed (truncated packed guid)");
|
|
|
|
|
packet.skipAll();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
|
if (guidMask & (1u << i)) {
|
|
|
|
|
uint8_t b = packet.readUInt8();
|
|
|
|
|
controlGuid |= (static_cast<uint64_t>(b) << (i * 8));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool allowMovement = (packet.readUInt8() != 0);
|
2026-04-05 19:30:44 +03:00
|
|
|
if (controlGuid == 0 || controlGuid == owner_.getPlayerGuid()) {
|
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 changed = (serverMovementAllowed_ != allowMovement);
|
|
|
|
|
serverMovementAllowed_ = allowMovement;
|
|
|
|
|
if (changed && !allowMovement) {
|
|
|
|
|
// Force-stop local movement immediately when server revokes control.
|
|
|
|
|
movementInfo.flags &= ~(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));
|
|
|
|
|
owner_.sendMovement(Opcode::MSG_MOVE_STOP);
|
|
|
|
|
owner_.sendMovement(Opcode::MSG_MOVE_STOP_STRAFE);
|
|
|
|
|
owner_.sendMovement(Opcode::MSG_MOVE_STOP_TURN);
|
|
|
|
|
owner_.sendMovement(Opcode::MSG_MOVE_STOP_SWIM);
|
|
|
|
|
owner_.addSystemChatMessage("Movement disabled by server.");
|
|
|
|
|
owner_.fireAddonEvent("PLAYER_CONTROL_LOST", {});
|
|
|
|
|
} else if (changed && allowMovement) {
|
|
|
|
|
owner_.addSystemChatMessage("Movement re-enabled.");
|
|
|
|
|
owner_.fireAddonEvent("PLAYER_CONTROL_GAINED", {});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Movement Timestamp
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
uint32_t MovementHandler::nextMovementTimestampMs() {
|
|
|
|
|
auto now = std::chrono::steady_clock::now();
|
|
|
|
|
uint64_t elapsed = static_cast<uint64_t>(
|
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(now - movementClockStart_).count()) + 1ULL;
|
|
|
|
|
if (elapsed > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
|
movementClockStart_ = now;
|
|
|
|
|
elapsed = 1ULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t candidate = static_cast<uint32_t>(elapsed);
|
|
|
|
|
if (candidate <= lastMovementTimestampMs_) {
|
|
|
|
|
candidate = lastMovementTimestampMs_ + 1U;
|
|
|
|
|
if (candidate == 0) {
|
|
|
|
|
movementClockStart_ = now;
|
|
|
|
|
candidate = 1U;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lastMovementTimestampMs_ = candidate;
|
|
|
|
|
return candidate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// sendMovement
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::sendMovement(Opcode opcode) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getState() != WorldState::IN_WORLD) {
|
|
|
|
|
LOG_WARNING("Cannot send movement in state: ", (int)owner_.getState());
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Block manual movement while taxi is active/mounted, but always allow
|
|
|
|
|
// stop/heartbeat opcodes so stuck states can be recovered.
|
|
|
|
|
bool taxiAllowed =
|
|
|
|
|
(opcode == Opcode::MSG_MOVE_HEARTBEAT) ||
|
|
|
|
|
(opcode == Opcode::MSG_MOVE_STOP) ||
|
|
|
|
|
(opcode == Opcode::MSG_MOVE_STOP_STRAFE) ||
|
|
|
|
|
(opcode == Opcode::MSG_MOVE_STOP_TURN) ||
|
|
|
|
|
(opcode == Opcode::MSG_MOVE_STOP_SWIM);
|
|
|
|
|
if (!serverMovementAllowed_ && !taxiAllowed) return;
|
|
|
|
|
if ((onTaxiFlight_ || taxiMountActive_) && !taxiAllowed) return;
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.resurrectPendingRef() && !taxiAllowed) return;
|
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
|
|
|
|
|
|
|
|
// Always send a strictly increasing non-zero client movement clock value.
|
|
|
|
|
const uint32_t movementTime = nextMovementTimestampMs();
|
|
|
|
|
movementInfo.time = movementTime;
|
|
|
|
|
|
|
|
|
|
if (opcode == Opcode::MSG_MOVE_SET_FACING &&
|
|
|
|
|
(isClassicLikeExpansion() || isActiveExpansion("tbc"))) {
|
|
|
|
|
const float facingDelta = core::coords::normalizeAngleRad(
|
|
|
|
|
movementInfo.orientation - lastFacingSentOrientation_);
|
|
|
|
|
const uint32_t sinceLastFacingMs =
|
|
|
|
|
lastFacingSendTimeMs_ != 0 && movementTime >= lastFacingSendTimeMs_
|
|
|
|
|
? (movementTime - lastFacingSendTimeMs_)
|
|
|
|
|
: std::numeric_limits<uint32_t>::max();
|
|
|
|
|
if (std::abs(facingDelta) < 0.02f && sinceLastFacingMs < 200U) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Track movement state transition for PLAYER_STARTED/STOPPED_MOVING events
|
|
|
|
|
const uint32_t kMoveMask = 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);
|
|
|
|
|
const bool wasMoving = (movementInfo.flags & kMoveMask) != 0;
|
|
|
|
|
|
|
|
|
|
// Cancel any timed (non-channeled) cast the moment the player starts moving.
|
2026-03-28 11:35:10 +03:00
|
|
|
if (owner_.isCasting() && !owner_.isChanneling()) {
|
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
|
|
|
const bool isPositionalMove =
|
|
|
|
|
opcode == Opcode::MSG_MOVE_START_FORWARD ||
|
|
|
|
|
opcode == Opcode::MSG_MOVE_START_BACKWARD ||
|
|
|
|
|
opcode == Opcode::MSG_MOVE_START_STRAFE_LEFT ||
|
|
|
|
|
opcode == Opcode::MSG_MOVE_START_STRAFE_RIGHT ||
|
|
|
|
|
opcode == Opcode::MSG_MOVE_JUMP;
|
|
|
|
|
if (isPositionalMove) {
|
|
|
|
|
owner_.cancelCast();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update movement flags based on opcode
|
|
|
|
|
switch (opcode) {
|
|
|
|
|
case Opcode::MSG_MOVE_START_FORWARD:
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::FORWARD);
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_START_BACKWARD:
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::BACKWARD);
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_STOP:
|
|
|
|
|
movementInfo.flags &= ~(static_cast<uint32_t>(MovementFlags::FORWARD) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::BACKWARD));
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_START_STRAFE_LEFT:
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::STRAFE_LEFT);
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_START_STRAFE_RIGHT:
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::STRAFE_RIGHT);
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_STOP_STRAFE:
|
|
|
|
|
movementInfo.flags &= ~(static_cast<uint32_t>(MovementFlags::STRAFE_LEFT) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::STRAFE_RIGHT));
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_JUMP:
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::FALLING);
|
|
|
|
|
isFalling_ = true;
|
|
|
|
|
fallStartMs_ = movementInfo.time;
|
|
|
|
|
movementInfo.fallTime = 0;
|
|
|
|
|
movementInfo.jumpVelocity = 7.96f;
|
|
|
|
|
{
|
|
|
|
|
const float facingRad = movementInfo.orientation;
|
|
|
|
|
movementInfo.jumpCosAngle = std::cos(facingRad);
|
|
|
|
|
movementInfo.jumpSinAngle = std::sin(facingRad);
|
|
|
|
|
const uint32_t horizFlags =
|
|
|
|
|
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);
|
|
|
|
|
const bool movingHoriz = (movementInfo.flags & horizFlags) != 0;
|
|
|
|
|
if (movingHoriz) {
|
|
|
|
|
const bool isWalking = (movementInfo.flags & static_cast<uint32_t>(MovementFlags::WALKING)) != 0;
|
|
|
|
|
movementInfo.jumpXYSpeed = isWalking ? 2.5f : (serverRunSpeed_ > 0.0f ? serverRunSpeed_ : 7.0f);
|
|
|
|
|
} else {
|
|
|
|
|
movementInfo.jumpXYSpeed = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_START_TURN_LEFT:
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::TURN_LEFT);
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_START_TURN_RIGHT:
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::TURN_RIGHT);
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_STOP_TURN:
|
|
|
|
|
movementInfo.flags &= ~(static_cast<uint32_t>(MovementFlags::TURN_LEFT) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::TURN_RIGHT));
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_FALL_LAND:
|
|
|
|
|
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::FALLING);
|
|
|
|
|
isFalling_ = false;
|
|
|
|
|
fallStartMs_ = 0;
|
|
|
|
|
movementInfo.fallTime = 0;
|
|
|
|
|
movementInfo.jumpVelocity = 0.0f;
|
|
|
|
|
movementInfo.jumpSinAngle = 0.0f;
|
|
|
|
|
movementInfo.jumpCosAngle = 0.0f;
|
|
|
|
|
movementInfo.jumpXYSpeed = 0.0f;
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_HEARTBEAT:
|
|
|
|
|
timeSinceLastMoveHeartbeat_ = 0.0f;
|
|
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_START_ASCEND:
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::ASCENDING);
|
2026-03-29 18:28:49 -07:00
|
|
|
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::DESCENDING);
|
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
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_STOP_ASCEND:
|
|
|
|
|
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::ASCENDING);
|
2026-03-29 18:28:49 -07:00
|
|
|
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::DESCENDING);
|
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
|
|
|
break;
|
|
|
|
|
case Opcode::MSG_MOVE_START_DESCEND:
|
2026-03-29 18:28:49 -07:00
|
|
|
// Must set DESCENDING so outgoing movement packets carry the correct
|
|
|
|
|
// flag during flight descent. Only clearing ASCENDING left the flag
|
|
|
|
|
// field ambiguous (neither ascending nor descending).
|
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
|
|
|
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::ASCENDING);
|
2026-03-29 18:28:49 -07:00
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::DESCENDING);
|
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
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fire PLAYER_STARTED/STOPPED_MOVING on movement state transitions
|
|
|
|
|
{
|
|
|
|
|
const bool isMoving = (movementInfo.flags & kMoveMask) != 0;
|
2026-04-05 19:30:44 +03:00
|
|
|
if (isMoving && !wasMoving && owner_.addonEventCallbackRef())
|
|
|
|
|
owner_.addonEventCallbackRef()("PLAYER_STARTED_MOVING", {});
|
|
|
|
|
else if (!isMoving && wasMoving && owner_.addonEventCallbackRef())
|
|
|
|
|
owner_.addonEventCallbackRef()("PLAYER_STOPPED_MOVING", {});
|
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 (opcode == Opcode::MSG_MOVE_SET_FACING) {
|
|
|
|
|
lastFacingSendTimeMs_ = movementInfo.time;
|
|
|
|
|
lastFacingSentOrientation_ = movementInfo.orientation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keep fallTime current
|
|
|
|
|
if (isFalling_ && movementInfo.hasFlag(MovementFlags::FALLING)) {
|
|
|
|
|
uint32_t elapsed = (movementInfo.time >= fallStartMs_)
|
|
|
|
|
? (movementInfo.time - fallStartMs_)
|
|
|
|
|
: 0u;
|
|
|
|
|
movementInfo.fallTime = elapsed;
|
|
|
|
|
} else if (!movementInfo.hasFlag(MovementFlags::FALLING)) {
|
|
|
|
|
if (isFalling_) {
|
|
|
|
|
isFalling_ = false;
|
|
|
|
|
fallStartMs_ = 0;
|
|
|
|
|
}
|
|
|
|
|
movementInfo.fallTime = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (onTaxiFlight_ || taxiMountActive_ || taxiActivatePending_ || taxiClientActive_) {
|
|
|
|
|
sanitizeMovementForTaxi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool includeTransportInWire = owner_.isOnTransport();
|
2026-04-05 19:30:44 +03:00
|
|
|
if (includeTransportInWire && owner_.getTransportManager()) {
|
|
|
|
|
if (auto* tr = owner_.getTransportManager()->getTransport(owner_.playerTransportGuidRef()); tr && tr->isM2) {
|
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
|
|
|
includeTransportInWire = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add transport data if player is on a server-recognized transport
|
|
|
|
|
if (includeTransportInWire) {
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
bool transportResolved = false;
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getTransportManager()) {
|
2026-04-05 19:42:25 -07:00
|
|
|
auto* tr = owner_.getTransportManager()->getTransport(owner_.playerTransportGuidRef());
|
2026-04-05 16:01:14 -07:00
|
|
|
if (tr) {
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
transportResolved = true;
|
2026-04-05 19:42:25 -07:00
|
|
|
glm::vec3 composed = owner_.getTransportManager()->getPlayerWorldPosition(owner_.playerTransportGuidRef(), owner_.playerTransportOffsetRef());
|
2026-04-05 16:01:14 -07:00
|
|
|
movementInfo.x = composed.x;
|
|
|
|
|
movementInfo.y = composed.y;
|
|
|
|
|
movementInfo.z = composed.z;
|
|
|
|
|
}
|
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
|
|
|
}
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
if (!transportResolved) {
|
|
|
|
|
// Transport not tracked — don't send ONTRANSPORT to the server.
|
|
|
|
|
// Sending stale transport GUID + local offset causes the server to
|
|
|
|
|
// compute a bad world position and teleport us to map origin.
|
2026-04-05 19:42:25 -07:00
|
|
|
LOG_WARNING("sendMovement: transport 0x", std::hex, owner_.playerTransportGuidRef(),
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
std::dec, " not found — clearing transport state");
|
|
|
|
|
includeTransportInWire = false;
|
|
|
|
|
owner_.clearPlayerTransport();
|
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
|
|
|
}
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
}
|
|
|
|
|
if (includeTransportInWire) {
|
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
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::ONTRANSPORT);
|
2026-04-05 19:30:44 +03:00
|
|
|
movementInfo.transportGuid = owner_.playerTransportGuidRef();
|
|
|
|
|
movementInfo.transportX = owner_.playerTransportOffsetRef().x;
|
|
|
|
|
movementInfo.transportY = owner_.playerTransportOffsetRef().y;
|
|
|
|
|
movementInfo.transportZ = owner_.playerTransportOffsetRef().z;
|
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
|
|
|
movementInfo.transportTime = movementInfo.time;
|
|
|
|
|
movementInfo.transportSeat = -1;
|
|
|
|
|
movementInfo.transportTime2 = movementInfo.time;
|
|
|
|
|
|
|
|
|
|
float transportYawCanonical = 0.0f;
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getTransportManager()) {
|
|
|
|
|
if (auto* tr = owner_.getTransportManager()->getTransport(owner_.playerTransportGuidRef()); tr) {
|
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 (tr->hasServerYaw) {
|
|
|
|
|
transportYawCanonical = tr->serverYaw;
|
|
|
|
|
} else {
|
|
|
|
|
transportYawCanonical = glm::eulerAngles(tr->rotation).z;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
movementInfo.transportO =
|
|
|
|
|
core::coords::normalizeAngleRad(movementInfo.orientation - transportYawCanonical);
|
|
|
|
|
} else {
|
|
|
|
|
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::ONTRANSPORT);
|
|
|
|
|
movementInfo.transportGuid = 0;
|
|
|
|
|
movementInfo.transportSeat = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opcode == Opcode::MSG_MOVE_HEARTBEAT && isClassicLikeExpansion()) {
|
|
|
|
|
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) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FALLING) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FALLINGFAR) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::SWIMMING);
|
|
|
|
|
const bool stationaryIdle =
|
|
|
|
|
!onTaxiFlight_ &&
|
|
|
|
|
!taxiMountActive_ &&
|
|
|
|
|
!taxiActivatePending_ &&
|
|
|
|
|
!taxiClientActive_ &&
|
|
|
|
|
!includeTransportInWire &&
|
|
|
|
|
(movementInfo.flags & locomotionFlags) == 0;
|
|
|
|
|
const uint32_t sinceLastHeartbeatMs =
|
|
|
|
|
lastHeartbeatSendTimeMs_ != 0 && movementTime >= lastHeartbeatSendTimeMs_
|
|
|
|
|
? (movementTime - lastHeartbeatSendTimeMs_)
|
|
|
|
|
: std::numeric_limits<uint32_t>::max();
|
|
|
|
|
const bool unchangedState =
|
|
|
|
|
std::abs(movementInfo.x - lastHeartbeatX_) < 0.01f &&
|
|
|
|
|
std::abs(movementInfo.y - lastHeartbeatY_) < 0.01f &&
|
|
|
|
|
std::abs(movementInfo.z - lastHeartbeatZ_) < 0.01f &&
|
|
|
|
|
movementInfo.flags == lastHeartbeatFlags_ &&
|
|
|
|
|
movementInfo.transportGuid == lastHeartbeatTransportGuid_;
|
|
|
|
|
if (stationaryIdle && unchangedState && sinceLastHeartbeatMs < 1500U) {
|
|
|
|
|
timeSinceLastMoveHeartbeat_ = 0.0f;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const uint32_t sinceLastNonHeartbeatMoveMs =
|
|
|
|
|
lastNonHeartbeatMoveSendTimeMs_ != 0 && movementTime >= lastNonHeartbeatMoveSendTimeMs_
|
|
|
|
|
? (movementTime - lastNonHeartbeatMoveSendTimeMs_)
|
|
|
|
|
: std::numeric_limits<uint32_t>::max();
|
|
|
|
|
if (sinceLastNonHeartbeatMoveMs < 350U) {
|
|
|
|
|
timeSinceLastMoveHeartbeat_ = 0.0f;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("Sending movement packet: opcode=0x", std::hex,
|
|
|
|
|
wireOpcode(opcode), std::dec,
|
|
|
|
|
(includeTransportInWire ? " ONTRANSPORT" : ""));
|
|
|
|
|
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
// Detect near-origin position on Eastern Kingdoms (map 0) — this would place
|
|
|
|
|
// the player near Alterac Mountains and is almost certainly a bug.
|
2026-04-05 19:42:25 -07:00
|
|
|
if (owner_.getCurrentMapId() == 0 &&
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
std::abs(movementInfo.x) < 500.0f && std::abs(movementInfo.y) < 500.0f) {
|
|
|
|
|
LOG_WARNING("sendMovement: position near map origin! canonical=(",
|
|
|
|
|
movementInfo.x, ", ", movementInfo.y, ", ", movementInfo.z,
|
|
|
|
|
") onTransport=", owner_.isOnTransport(),
|
2026-04-05 19:42:25 -07:00
|
|
|
" transportGuid=0x", std::hex, owner_.playerTransportGuidRef(), std::dec,
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
" flags=0x", std::hex, movementInfo.flags, std::dec);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// Convert canonical → server coordinates for the wire
|
|
|
|
|
MovementInfo wireInfo = movementInfo;
|
|
|
|
|
glm::vec3 serverPos = core::coords::canonicalToServer(glm::vec3(wireInfo.x, wireInfo.y, wireInfo.z));
|
|
|
|
|
wireInfo.x = serverPos.x;
|
|
|
|
|
wireInfo.y = serverPos.y;
|
|
|
|
|
wireInfo.z = serverPos.z;
|
|
|
|
|
|
2026-04-05 17:39:56 -07:00
|
|
|
// Periodic position audit — log every ~60 heartbeats (~30s) to trace position drift.
|
|
|
|
|
if (opcode == Opcode::MSG_MOVE_HEARTBEAT && ++heartbeatLogCount_ % 60 == 0) {
|
|
|
|
|
LOG_WARNING("HEARTBEAT #", heartbeatLogCount_, " canonical=(",
|
|
|
|
|
movementInfo.x, ",", movementInfo.y, ",", movementInfo.z,
|
|
|
|
|
") server=(", wireInfo.x, ",", wireInfo.y, ",", wireInfo.z,
|
|
|
|
|
") flags=0x", std::hex, movementInfo.flags, std::dec);
|
2026-03-28 16:02:36 -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
|
|
|
wireInfo.orientation = core::coords::canonicalToServerYaw(wireInfo.orientation);
|
|
|
|
|
|
|
|
|
|
if (includeTransportInWire) {
|
|
|
|
|
glm::vec3 serverTransportPos = core::coords::canonicalToServer(
|
|
|
|
|
glm::vec3(wireInfo.transportX, wireInfo.transportY, wireInfo.transportZ));
|
|
|
|
|
wireInfo.transportX = serverTransportPos.x;
|
|
|
|
|
wireInfo.transportY = serverTransportPos.y;
|
|
|
|
|
wireInfo.transportZ = serverTransportPos.z;
|
|
|
|
|
wireInfo.transportO = core::coords::normalizeAngleRad(-wireInfo.transportO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build and send movement packet (expansion-specific format)
|
2026-04-05 19:30:44 +03:00
|
|
|
auto packet = owner_.getPacketParsers()
|
|
|
|
|
? owner_.getPacketParsers()->buildMovementPacket(opcode, wireInfo, owner_.getPlayerGuid())
|
|
|
|
|
: MovementPacket::build(opcode, wireInfo, owner_.getPlayerGuid());
|
|
|
|
|
owner_.getSocket()->send(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 (opcode == Opcode::MSG_MOVE_HEARTBEAT) {
|
|
|
|
|
lastHeartbeatSendTimeMs_ = movementInfo.time;
|
|
|
|
|
lastHeartbeatX_ = movementInfo.x;
|
|
|
|
|
lastHeartbeatY_ = movementInfo.y;
|
|
|
|
|
lastHeartbeatZ_ = movementInfo.z;
|
|
|
|
|
lastHeartbeatFlags_ = movementInfo.flags;
|
|
|
|
|
lastHeartbeatTransportGuid_ = movementInfo.transportGuid;
|
|
|
|
|
} else {
|
|
|
|
|
lastNonHeartbeatMoveSendTimeMs_ = movementInfo.time;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// sanitizeMovementForTaxi
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::sanitizeMovementForTaxi() {
|
|
|
|
|
constexpr uint32_t kClearTaxiFlags =
|
|
|
|
|
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::PITCH_UP) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::PITCH_DOWN) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FALLING) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FALLINGFAR) |
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::SWIMMING);
|
|
|
|
|
|
|
|
|
|
movementInfo.flags &= ~kClearTaxiFlags;
|
|
|
|
|
movementInfo.fallTime = 0;
|
|
|
|
|
movementInfo.jumpVelocity = 0.0f;
|
|
|
|
|
movementInfo.jumpSinAngle = 0.0f;
|
|
|
|
|
movementInfo.jumpCosAngle = 0.0f;
|
|
|
|
|
movementInfo.jumpXYSpeed = 0.0f;
|
|
|
|
|
movementInfo.pitch = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// forceClearTaxiAndMovementState
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::forceClearTaxiAndMovementState() {
|
|
|
|
|
taxiActivatePending_ = false;
|
|
|
|
|
taxiActivateTimer_ = 0.0f;
|
|
|
|
|
taxiClientActive_ = false;
|
|
|
|
|
taxiClientPath_.clear();
|
|
|
|
|
taxiRecoverPending_ = false;
|
|
|
|
|
taxiStartGrace_ = 0.0f;
|
|
|
|
|
onTaxiFlight_ = false;
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (taxiMountActive_ && owner_.mountCallbackRef()) {
|
|
|
|
|
owner_.mountCallbackRef()(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
|
|
|
}
|
|
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiMountDisplayId_ = 0;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.currentMountDisplayIdRef() = 0;
|
|
|
|
|
owner_.vehicleIdRef() = 0;
|
2026-03-29 19:01:34 -07:00
|
|
|
// Death/resurrect state is intentionally NOT cleared here.
|
|
|
|
|
// Previously this method reset 10 death-related fields despite being named
|
|
|
|
|
// "forceClearTaxiAndMovementState", which could cancel pending resurrections
|
|
|
|
|
// or clear death state on taxi dismount. Death state is managed by
|
|
|
|
|
// entity_controller (markPlayerDead) and the resurrect packet handlers.
|
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
|
|
|
|
|
|
|
|
movementInfo.flags = 0;
|
|
|
|
|
movementInfo.flags2 = 0;
|
|
|
|
|
movementInfo.transportGuid = 0;
|
|
|
|
|
owner_.clearPlayerTransport();
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSocket() && owner_.getState() == WorldState::IN_WORLD) {
|
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
|
|
|
sendMovement(Opcode::MSG_MOVE_STOP);
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_STOP_STRAFE);
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_STOP_TURN);
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_STOP_SWIM);
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Force-cleared taxi/movement state");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// setPosition / setOrientation
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::setPosition(float x, float y, float z) {
|
|
|
|
|
movementInfo.x = x;
|
|
|
|
|
movementInfo.y = y;
|
|
|
|
|
movementInfo.z = z;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::setOrientation(float orientation) {
|
|
|
|
|
movementInfo.orientation = orientation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// dismount
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::dismount() {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (!owner_.getSocket()) return;
|
|
|
|
|
uint32_t savedMountAura = owner_.mountAuraSpellIdRef();
|
|
|
|
|
if (owner_.currentMountDisplayIdRef() != 0 || taxiMountActive_) {
|
|
|
|
|
if (owner_.mountCallbackRef()) {
|
|
|
|
|
owner_.mountCallbackRef()(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-04-05 19:30:44 +03:00
|
|
|
owner_.currentMountDisplayIdRef() = 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
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiMountDisplayId_ = 0;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.mountAuraSpellIdRef() = 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
|
|
|
LOG_INFO("Dismount: cleared local mount state");
|
|
|
|
|
}
|
|
|
|
|
uint16_t cancelMountWire = wireOpcode(Opcode::CMSG_CANCEL_MOUNT_AURA);
|
|
|
|
|
if (cancelMountWire != 0xFFFF) {
|
|
|
|
|
network::Packet pkt(cancelMountWire);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(pkt);
|
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
|
|
|
LOG_INFO("Sent CMSG_CANCEL_MOUNT_AURA");
|
|
|
|
|
} else if (savedMountAura != 0) {
|
|
|
|
|
auto pkt = CancelAuraPacket::build(savedMountAura);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(pkt);
|
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
|
|
|
LOG_INFO("Sent CMSG_CANCEL_AURA (mount spell ", savedMountAura, ") — Classic fallback");
|
|
|
|
|
} else {
|
2026-03-28 11:35:10 +03:00
|
|
|
for (const auto& a : owner_.getPlayerAuras()) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (!a.isEmpty() && a.maxDurationMs < 0 && a.casterGuid == owner_.getPlayerGuid()) {
|
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
|
|
|
auto pkt = CancelAuraPacket::build(a.spellId);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(pkt);
|
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
|
|
|
LOG_INFO("Sent CMSG_CANCEL_AURA (spell ", a.spellId, ") — brute force dismount");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Force Speed / Root / Flag Change Handlers
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
2026-03-29 19:08:42 -07:00
|
|
|
// Shared force-ACK packet builder. All server-forced movement changes (speed,
|
|
|
|
|
// root, flag, collision-height, knockback) require the same ACK structure:
|
|
|
|
|
// GUID + counter + movement payload with server-space coordinates. Centralised
|
|
|
|
|
// here so transport coordinate conversion can't diverge between handlers.
|
|
|
|
|
network::Packet MovementHandler::buildForceAck(Opcode ackOpcode, uint32_t counter) {
|
|
|
|
|
network::Packet ack(wireOpcode(ackOpcode));
|
|
|
|
|
const bool legacyGuid =
|
|
|
|
|
isActiveExpansion("classic") || isActiveExpansion("tbc") || isActiveExpansion("turtle");
|
|
|
|
|
if (legacyGuid) {
|
2026-04-05 19:30:44 +03:00
|
|
|
ack.writeUInt64(owner_.getPlayerGuid());
|
2026-03-29 19:08:42 -07:00
|
|
|
} else {
|
2026-04-05 19:30:44 +03:00
|
|
|
ack.writePackedGuid(owner_.getPlayerGuid());
|
2026-03-29 19:08:42 -07:00
|
|
|
}
|
|
|
|
|
ack.writeUInt32(counter);
|
|
|
|
|
|
|
|
|
|
MovementInfo wire = movementInfo;
|
|
|
|
|
wire.time = nextMovementTimestampMs();
|
|
|
|
|
if (wire.hasFlag(MovementFlags::ONTRANSPORT)) {
|
|
|
|
|
wire.transportTime = wire.time;
|
|
|
|
|
wire.transportTime2 = wire.time;
|
|
|
|
|
}
|
|
|
|
|
glm::vec3 serverPos = core::coords::canonicalToServer(glm::vec3(wire.x, wire.y, wire.z));
|
|
|
|
|
wire.x = serverPos.x;
|
|
|
|
|
wire.y = serverPos.y;
|
|
|
|
|
wire.z = serverPos.z;
|
|
|
|
|
if (wire.hasFlag(MovementFlags::ONTRANSPORT)) {
|
|
|
|
|
glm::vec3 serverTransport =
|
|
|
|
|
core::coords::canonicalToServer(glm::vec3(wire.transportX, wire.transportY, wire.transportZ));
|
|
|
|
|
wire.transportX = serverTransport.x;
|
|
|
|
|
wire.transportY = serverTransport.y;
|
|
|
|
|
wire.transportZ = serverTransport.z;
|
|
|
|
|
}
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getPacketParsers()) {
|
|
|
|
|
owner_.getPacketParsers()->writeMovementPayload(ack, wire);
|
2026-03-29 19:08:42 -07:00
|
|
|
} else {
|
|
|
|
|
MovementPacket::writeMovementPayload(ack, wire);
|
|
|
|
|
}
|
|
|
|
|
return ack;
|
|
|
|
|
}
|
|
|
|
|
|
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 MovementHandler::handleForceSpeedChange(network::Packet& packet, const char* name,
|
|
|
|
|
Opcode ackOpcode, float* speedStorage) {
|
|
|
|
|
const bool fscTbcLike = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
|
|
|
|
uint64_t guid = fscTbcLike
|
|
|
|
|
? packet.readUInt64() : packet.readPackedGuid();
|
|
|
|
|
uint32_t counter = packet.readUInt32();
|
|
|
|
|
|
2026-03-29 20:53:26 -07:00
|
|
|
size_t remaining = packet.getRemainingSize();
|
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 (remaining >= 8) {
|
|
|
|
|
packet.readUInt32();
|
|
|
|
|
} else if (remaining >= 5) {
|
|
|
|
|
packet.readUInt8();
|
|
|
|
|
}
|
|
|
|
|
float newSpeed = packet.readFloat();
|
|
|
|
|
|
|
|
|
|
LOG_INFO("SMSG_FORCE_", name, "_CHANGE: guid=0x", std::hex, guid, std::dec,
|
|
|
|
|
" counter=", counter, " speed=", newSpeed);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid != owner_.getPlayerGuid()) return;
|
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-29 18:53:16 -07:00
|
|
|
// Validate BEFORE sending ACK — if we echo a bad speed back to the server
|
|
|
|
|
// but don't apply it locally, the client and server desync on movement speed.
|
|
|
|
|
if (std::isnan(newSpeed) || newSpeed < 0.1f || newSpeed > 100.0f) {
|
|
|
|
|
LOG_WARNING("Ignoring invalid ", name, " speed: ", newSpeed);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSocket()) {
|
2026-03-29 19:08:42 -07:00
|
|
|
auto ack = buildForceAck(ackOpcode, counter);
|
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
|
|
|
ack.writeFloat(newSpeed);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(ack);
|
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 (speedStorage) *speedStorage = newSpeed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleForceRunSpeedChange(network::Packet& packet) {
|
|
|
|
|
handleForceSpeedChange(packet, "RUN_SPEED", Opcode::CMSG_FORCE_RUN_SPEED_CHANGE_ACK, &serverRunSpeed_);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (!onTaxiFlight_ && !taxiMountActive_ && owner_.currentMountDisplayIdRef() != 0 && serverRunSpeed_ <= 8.5f) {
|
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
|
|
|
LOG_INFO("Auto-clearing mount from speed change: speed=", serverRunSpeed_,
|
2026-04-05 19:30:44 +03:00
|
|
|
" displayId=", owner_.currentMountDisplayIdRef());
|
|
|
|
|
owner_.currentMountDisplayIdRef() = 0;
|
|
|
|
|
if (owner_.mountCallbackRef()) {
|
|
|
|
|
owner_.mountCallbackRef()(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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleForceMoveRootState(network::Packet& packet, bool rooted) {
|
|
|
|
|
const bool rootTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
2026-03-29 20:53:26 -07:00
|
|
|
if (packet.getRemainingSize() < (rootTbc ? 8u : 2u)) return;
|
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
|
|
|
uint64_t guid = rootTbc
|
|
|
|
|
? packet.readUInt64() : packet.readPackedGuid();
|
2026-03-29 20:53:26 -07:00
|
|
|
if (!packet.hasRemaining(4)) return;
|
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 counter = packet.readUInt32();
|
|
|
|
|
|
|
|
|
|
LOG_INFO(rooted ? "SMSG_FORCE_MOVE_ROOT" : "SMSG_FORCE_MOVE_UNROOT",
|
|
|
|
|
": guid=0x", std::hex, guid, std::dec, " counter=", counter);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid != owner_.getPlayerGuid()) return;
|
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 (rooted) {
|
|
|
|
|
movementInfo.flags |= static_cast<uint32_t>(MovementFlags::ROOT);
|
|
|
|
|
} else {
|
|
|
|
|
movementInfo.flags &= ~static_cast<uint32_t>(MovementFlags::ROOT);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (!owner_.getSocket()) return;
|
2026-03-29 19:08:42 -07:00
|
|
|
Opcode ackOp = rooted ? Opcode::CMSG_FORCE_MOVE_ROOT_ACK : Opcode::CMSG_FORCE_MOVE_UNROOT_ACK;
|
|
|
|
|
if (wireOpcode(ackOp) == 0xFFFF) return;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(buildForceAck(ackOp, counter));
|
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 MovementHandler::handleForceMoveFlagChange(network::Packet& packet, const char* name,
|
|
|
|
|
Opcode ackOpcode, uint32_t flag, bool set) {
|
|
|
|
|
const bool fmfTbcLike = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
2026-03-29 20:53:26 -07:00
|
|
|
if (packet.getRemainingSize() < (fmfTbcLike ? 8u : 2u)) return;
|
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
|
|
|
uint64_t guid = fmfTbcLike
|
|
|
|
|
? packet.readUInt64() : packet.readPackedGuid();
|
2026-03-29 20:53:26 -07:00
|
|
|
if (!packet.hasRemaining(4)) return;
|
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 counter = packet.readUInt32();
|
|
|
|
|
|
|
|
|
|
LOG_INFO("SMSG_FORCE_", name, ": guid=0x", std::hex, guid, std::dec, " counter=", counter);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid != owner_.getPlayerGuid()) return;
|
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 (flag != 0) {
|
|
|
|
|
if (set) {
|
|
|
|
|
movementInfo.flags |= flag;
|
|
|
|
|
} else {
|
|
|
|
|
movementInfo.flags &= ~flag;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (!owner_.getSocket()) return;
|
2026-03-29 19:08:42 -07:00
|
|
|
if (wireOpcode(ackOpcode) == 0xFFFF) return;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(buildForceAck(ackOpcode, counter));
|
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 MovementHandler::handleMoveSetCollisionHeight(network::Packet& packet) {
|
|
|
|
|
const bool legacyGuid = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
2026-03-29 20:53:26 -07:00
|
|
|
if (packet.getRemainingSize() < (legacyGuid ? 8u : 2u)) return;
|
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
|
|
|
uint64_t guid = legacyGuid ? packet.readUInt64() : packet.readPackedGuid();
|
2026-03-29 20:53:26 -07:00
|
|
|
if (!packet.hasRemaining(8)) return;
|
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 counter = packet.readUInt32();
|
|
|
|
|
float height = packet.readFloat();
|
|
|
|
|
|
|
|
|
|
LOG_INFO("SMSG_MOVE_SET_COLLISION_HGT: guid=0x", std::hex, guid, std::dec,
|
|
|
|
|
" counter=", counter, " height=", height);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid != owner_.getPlayerGuid()) return;
|
|
|
|
|
if (!owner_.getSocket()) return;
|
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-29 19:08:42 -07:00
|
|
|
if (wireOpcode(Opcode::CMSG_MOVE_SET_COLLISION_HGT_ACK) == 0xFFFF) return;
|
|
|
|
|
// buildForceAck now handles transport coordinate conversion, fixing the
|
|
|
|
|
// previous omission that caused desync when riding boats/zeppelins.
|
|
|
|
|
auto ack = buildForceAck(Opcode::CMSG_MOVE_SET_COLLISION_HGT_ACK, counter);
|
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
|
|
|
ack.writeFloat(height);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(ack);
|
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 MovementHandler::handleMoveKnockBack(network::Packet& packet) {
|
|
|
|
|
const bool mkbTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
2026-03-29 20:53:26 -07:00
|
|
|
if (packet.getRemainingSize() < (mkbTbc ? 8u : 2u)) return;
|
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
|
|
|
uint64_t guid = mkbTbc
|
|
|
|
|
? packet.readUInt64() : packet.readPackedGuid();
|
2026-03-29 20:53:26 -07:00
|
|
|
if (!packet.hasRemaining(20)) return;
|
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 counter = packet.readUInt32();
|
|
|
|
|
float vcos = packet.readFloat();
|
|
|
|
|
float vsin = packet.readFloat();
|
|
|
|
|
float hspeed = packet.readFloat();
|
|
|
|
|
float vspeed = packet.readFloat();
|
|
|
|
|
|
|
|
|
|
LOG_INFO("SMSG_MOVE_KNOCK_BACK: guid=0x", std::hex, guid, std::dec,
|
|
|
|
|
" counter=", counter, " vcos=", vcos, " vsin=", vsin,
|
|
|
|
|
" hspeed=", hspeed, " vspeed=", vspeed);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid != owner_.getPlayerGuid()) return;
|
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-04-05 19:30:44 +03:00
|
|
|
if (owner_.knockBackCallbackRef()) {
|
|
|
|
|
owner_.knockBackCallbackRef()(vcos, vsin, hspeed, vspeed);
|
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-04-05 19:30:44 +03:00
|
|
|
if (!owner_.getSocket()) return;
|
2026-03-29 19:08:42 -07:00
|
|
|
if (wireOpcode(Opcode::CMSG_MOVE_KNOCK_BACK_ACK) == 0xFFFF) return;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(buildForceAck(Opcode::CMSG_MOVE_KNOCK_BACK_ACK, counter));
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Other Player / Creature Movement Handlers
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleMoveSetSpeed(network::Packet& packet) {
|
|
|
|
|
const bool useFull = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
|
|
|
|
uint64_t moverGuid = useFull
|
|
|
|
|
? packet.readUInt64() : packet.readPackedGuid();
|
|
|
|
|
|
2026-03-29 20:53:26 -07:00
|
|
|
const size_t remaining = packet.getRemainingSize();
|
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 (remaining < 4) return;
|
|
|
|
|
if (remaining > 4) {
|
|
|
|
|
packet.setReadPos(packet.getSize() - 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float speed = packet.readFloat();
|
|
|
|
|
if (!std::isfinite(speed) || speed <= 0.01f || speed > 200.0f) return;
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (moverGuid != owner_.getPlayerGuid()) return;
|
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
|
|
|
const uint16_t wireOp = packet.getOpcode();
|
|
|
|
|
if (wireOp == wireOpcode(Opcode::MSG_MOVE_SET_RUN_SPEED)) serverRunSpeed_ = speed;
|
|
|
|
|
else if (wireOp == wireOpcode(Opcode::MSG_MOVE_SET_RUN_BACK_SPEED)) serverRunBackSpeed_ = speed;
|
|
|
|
|
else if (wireOp == wireOpcode(Opcode::MSG_MOVE_SET_WALK_SPEED)) serverWalkSpeed_ = speed;
|
|
|
|
|
else if (wireOp == wireOpcode(Opcode::MSG_MOVE_SET_SWIM_SPEED)) serverSwimSpeed_ = speed;
|
|
|
|
|
else if (wireOp == wireOpcode(Opcode::MSG_MOVE_SET_SWIM_BACK_SPEED)) serverSwimBackSpeed_ = speed;
|
|
|
|
|
else if (wireOp == wireOpcode(Opcode::MSG_MOVE_SET_FLIGHT_SPEED)) serverFlightSpeed_ = speed;
|
|
|
|
|
else if (wireOp == wireOpcode(Opcode::MSG_MOVE_SET_FLIGHT_BACK_SPEED))serverFlightBackSpeed_= speed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleOtherPlayerMovement(network::Packet& packet) {
|
|
|
|
|
const bool otherMoveTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
|
|
|
|
uint64_t moverGuid = otherMoveTbc
|
|
|
|
|
? packet.readUInt64() : packet.readPackedGuid();
|
2026-04-05 19:30:44 +03:00
|
|
|
if (moverGuid == owner_.getPlayerGuid() || moverGuid == 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
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MovementInfo info = {};
|
|
|
|
|
info.flags = packet.readUInt32();
|
2026-04-05 19:30:44 +03:00
|
|
|
uint8_t flags2Size = owner_.getPacketParsers() ? owner_.getPacketParsers()->movementFlags2Size() : 2;
|
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 (flags2Size == 2) info.flags2 = packet.readUInt16();
|
|
|
|
|
else if (flags2Size == 1) info.flags2 = packet.readUInt8();
|
|
|
|
|
info.time = packet.readUInt32();
|
|
|
|
|
info.x = packet.readFloat();
|
|
|
|
|
info.y = packet.readFloat();
|
|
|
|
|
info.z = packet.readFloat();
|
|
|
|
|
info.orientation = packet.readFloat();
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
const uint32_t wireTransportFlag = owner_.getPacketParsers() ? owner_.getPacketParsers()->wireOnTransportFlag() : 0x00000200;
|
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
|
|
|
const bool onTransport = (info.flags & wireTransportFlag) != 0;
|
|
|
|
|
uint64_t transportGuid = 0;
|
|
|
|
|
float tLocalX = 0, tLocalY = 0, tLocalZ = 0, tLocalO = 0;
|
|
|
|
|
if (onTransport) {
|
|
|
|
|
transportGuid = packet.readPackedGuid();
|
|
|
|
|
tLocalX = packet.readFloat();
|
|
|
|
|
tLocalY = packet.readFloat();
|
|
|
|
|
tLocalZ = packet.readFloat();
|
|
|
|
|
tLocalO = packet.readFloat();
|
|
|
|
|
if (flags2Size >= 1) {
|
|
|
|
|
/*uint32_t transportTime =*/ packet.readUInt32();
|
|
|
|
|
}
|
|
|
|
|
if (flags2Size >= 2) {
|
|
|
|
|
/*int8_t transportSeat =*/ packet.readUInt8();
|
|
|
|
|
if (info.flags2 & 0x0200) {
|
|
|
|
|
/*uint32_t transportTime2 =*/ packet.readUInt32();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 entity = owner_.getEntityManager().getEntity(moverGuid);
|
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 (!entity) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 canonical = core::coords::serverToCanonical(glm::vec3(info.x, info.y, info.z));
|
|
|
|
|
float canYaw = core::coords::serverToCanonicalYaw(info.orientation);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (onTransport && transportGuid != 0 && owner_.getTransportManager()) {
|
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
|
|
|
glm::vec3 localCanonical = core::coords::serverToCanonical(glm::vec3(tLocalX, tLocalY, tLocalZ));
|
|
|
|
|
owner_.setTransportAttachment(moverGuid, entity->getType(), transportGuid, localCanonical, true,
|
|
|
|
|
core::coords::serverToCanonicalYaw(tLocalO));
|
2026-04-05 19:30:44 +03:00
|
|
|
glm::vec3 worldPos = owner_.getTransportManager()->getPlayerWorldPosition(transportGuid, localCanonical);
|
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
|
|
|
canonical = worldPos;
|
|
|
|
|
} else if (!onTransport) {
|
|
|
|
|
owner_.clearTransportAttachment(moverGuid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t durationMs = 120;
|
|
|
|
|
auto itPrev = otherPlayerMoveTimeMs_.find(moverGuid);
|
|
|
|
|
if (itPrev != otherPlayerMoveTimeMs_.end()) {
|
|
|
|
|
uint32_t rawDt = info.time - itPrev->second;
|
|
|
|
|
if (rawDt >= 20 && rawDt <= 2000) {
|
|
|
|
|
float fDt = static_cast<float>(rawDt);
|
|
|
|
|
auto& smoothed = otherPlayerSmoothedIntervalMs_[moverGuid];
|
|
|
|
|
if (smoothed < 1.0f) smoothed = fDt;
|
|
|
|
|
smoothed = 0.7f * smoothed + 0.3f * fDt;
|
|
|
|
|
float clamped = std::max(60.0f, std::min(500.0f, smoothed));
|
|
|
|
|
durationMs = static_cast<uint32_t>(clamped);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
otherPlayerMoveTimeMs_[moverGuid] = info.time;
|
|
|
|
|
|
|
|
|
|
const uint16_t wireOp = packet.getOpcode();
|
|
|
|
|
const bool isStopOpcode =
|
|
|
|
|
(wireOp == wireOpcode(Opcode::MSG_MOVE_STOP)) ||
|
|
|
|
|
(wireOp == wireOpcode(Opcode::MSG_MOVE_STOP_STRAFE)) ||
|
|
|
|
|
(wireOp == wireOpcode(Opcode::MSG_MOVE_STOP_TURN)) ||
|
|
|
|
|
(wireOp == wireOpcode(Opcode::MSG_MOVE_STOP_SWIM)) ||
|
|
|
|
|
(wireOp == wireOpcode(Opcode::MSG_MOVE_FALL_LAND));
|
|
|
|
|
const bool isJumpOpcode = (wireOp == wireOpcode(Opcode::MSG_MOVE_JUMP));
|
|
|
|
|
|
|
|
|
|
const float entityDuration = isStopOpcode ? 0.0f : (durationMs / 1000.0f);
|
|
|
|
|
entity->startMoveTo(canonical.x, canonical.y, canonical.z, canYaw, entityDuration);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.creatureMoveCallbackRef()) {
|
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
|
|
|
const uint32_t notifyDuration = isStopOpcode ? 0u : durationMs;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.creatureMoveCallbackRef()(moverGuid, canonical.x, canonical.y, canonical.z, notifyDuration);
|
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-04-05 19:30:44 +03:00
|
|
|
if (owner_.unitAnimHintCallbackRef() && isJumpOpcode) {
|
|
|
|
|
owner_.unitAnimHintCallbackRef()(moverGuid, 38u);
|
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-04-05 19:30:44 +03:00
|
|
|
if (owner_.unitMoveFlagsCallbackRef()) {
|
|
|
|
|
owner_.unitMoveFlagsCallbackRef()(moverGuid, info.flags);
|
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 MovementHandler::handleCompressedMoves(network::Packet& packet) {
|
|
|
|
|
std::vector<uint8_t> decompressedStorage;
|
|
|
|
|
const std::vector<uint8_t>* dataPtr = &packet.getData();
|
|
|
|
|
|
|
|
|
|
const auto& rawData = packet.getData();
|
|
|
|
|
const bool hasCompressedWrapper =
|
|
|
|
|
rawData.size() >= 6 &&
|
|
|
|
|
rawData[4] == 0x78 &&
|
|
|
|
|
(rawData[5] == 0x01 || rawData[5] == 0x9C ||
|
|
|
|
|
rawData[5] == 0xDA || rawData[5] == 0x5E);
|
|
|
|
|
if (hasCompressedWrapper) {
|
|
|
|
|
uint32_t decompressedSize = static_cast<uint32_t>(rawData[0]) |
|
|
|
|
|
(static_cast<uint32_t>(rawData[1]) << 8) |
|
|
|
|
|
(static_cast<uint32_t>(rawData[2]) << 16) |
|
|
|
|
|
(static_cast<uint32_t>(rawData[3]) << 24);
|
|
|
|
|
if (decompressedSize == 0 || decompressedSize > 65536) {
|
|
|
|
|
LOG_WARNING("SMSG_COMPRESSED_MOVES: bad decompressedSize=", decompressedSize);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decompressedStorage.resize(decompressedSize);
|
|
|
|
|
uLongf destLen = decompressedSize;
|
|
|
|
|
int ret = uncompress(decompressedStorage.data(), &destLen,
|
|
|
|
|
rawData.data() + 4, rawData.size() - 4);
|
|
|
|
|
if (ret != Z_OK) {
|
|
|
|
|
LOG_WARNING("SMSG_COMPRESSED_MOVES: zlib error ", ret);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decompressedStorage.resize(destLen);
|
|
|
|
|
dataPtr = &decompressedStorage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& data = *dataPtr;
|
|
|
|
|
const size_t dataLen = data.size();
|
|
|
|
|
|
|
|
|
|
uint16_t monsterMoveWire = wireOpcode(Opcode::SMSG_MONSTER_MOVE);
|
|
|
|
|
uint16_t monsterMoveTransportWire = wireOpcode(Opcode::SMSG_MONSTER_MOVE_TRANSPORT);
|
|
|
|
|
|
|
|
|
|
const std::array<uint16_t, 29> kMoveOpcodes = {
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_FORWARD),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_BACKWARD),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_STOP),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_STRAFE_LEFT),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_STRAFE_RIGHT),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_STOP_STRAFE),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_JUMP),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_TURN_LEFT),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_TURN_RIGHT),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_STOP_TURN),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_SET_FACING),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_FALL_LAND),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_HEARTBEAT),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_SWIM),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_STOP_SWIM),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_SET_WALK_MODE),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_SET_RUN_MODE),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_PITCH_UP),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_PITCH_DOWN),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_STOP_PITCH),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_ASCEND),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_STOP_ASCEND),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_START_DESCEND),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_SET_PITCH),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_GRAVITY_CHNG),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_UPDATE_CAN_FLY),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_UPDATE_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_ROOT),
|
|
|
|
|
wireOpcode(Opcode::MSG_MOVE_UNROOT),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct CompressedMoveSubPacket {
|
|
|
|
|
uint16_t opcode = 0;
|
|
|
|
|
std::vector<uint8_t> payload;
|
|
|
|
|
};
|
|
|
|
|
struct DecodeResult {
|
|
|
|
|
bool ok = false;
|
|
|
|
|
bool overrun = false;
|
|
|
|
|
bool usedPayloadOnlySize = false;
|
|
|
|
|
size_t endPos = 0;
|
|
|
|
|
size_t recognizedCount = 0;
|
|
|
|
|
size_t subPacketCount = 0;
|
|
|
|
|
std::vector<CompressedMoveSubPacket> packets;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto isRecognizedSubOpcode = [&](uint16_t subOpcode) {
|
|
|
|
|
return subOpcode == monsterMoveWire ||
|
|
|
|
|
subOpcode == monsterMoveTransportWire ||
|
|
|
|
|
std::find(kMoveOpcodes.begin(), kMoveOpcodes.end(), subOpcode) != kMoveOpcodes.end();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto decodeSubPackets = [&](bool payloadOnlySize) -> DecodeResult {
|
|
|
|
|
DecodeResult result;
|
|
|
|
|
result.usedPayloadOnlySize = payloadOnlySize;
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
while (pos < dataLen) {
|
|
|
|
|
if (pos + 1 > dataLen) break;
|
|
|
|
|
uint8_t subSize = data[pos];
|
|
|
|
|
if (subSize == 0) {
|
|
|
|
|
result.ok = true;
|
|
|
|
|
result.endPos = pos + 1;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const size_t payloadLen = payloadOnlySize
|
|
|
|
|
? static_cast<size_t>(subSize)
|
|
|
|
|
: (subSize >= 2 ? static_cast<size_t>(subSize) - 2 : 0);
|
|
|
|
|
if (!payloadOnlySize && subSize < 2) {
|
|
|
|
|
result.endPos = pos;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const size_t packetLen = 1 + 2 + payloadLen;
|
|
|
|
|
if (pos + packetLen > dataLen) {
|
|
|
|
|
result.overrun = true;
|
|
|
|
|
result.endPos = pos;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t subOpcode = static_cast<uint16_t>(data[pos + 1]) |
|
|
|
|
|
(static_cast<uint16_t>(data[pos + 2]) << 8);
|
|
|
|
|
size_t payloadStart = pos + 3;
|
|
|
|
|
|
|
|
|
|
CompressedMoveSubPacket subPacket;
|
|
|
|
|
subPacket.opcode = subOpcode;
|
|
|
|
|
subPacket.payload.assign(data.begin() + payloadStart,
|
|
|
|
|
data.begin() + payloadStart + payloadLen);
|
|
|
|
|
result.packets.push_back(std::move(subPacket));
|
|
|
|
|
++result.subPacketCount;
|
|
|
|
|
if (isRecognizedSubOpcode(subOpcode)) {
|
|
|
|
|
++result.recognizedCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pos += packetLen;
|
|
|
|
|
}
|
|
|
|
|
result.ok = (result.endPos == 0 || result.endPos == dataLen);
|
|
|
|
|
result.endPos = dataLen;
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DecodeResult decoded = decodeSubPackets(false);
|
|
|
|
|
if (!decoded.ok || decoded.overrun) {
|
|
|
|
|
DecodeResult payloadOnlyDecoded = decodeSubPackets(true);
|
|
|
|
|
const bool preferPayloadOnly =
|
|
|
|
|
payloadOnlyDecoded.ok &&
|
|
|
|
|
(!decoded.ok || decoded.overrun || payloadOnlyDecoded.recognizedCount > decoded.recognizedCount);
|
|
|
|
|
if (preferPayloadOnly) {
|
|
|
|
|
decoded = std::move(payloadOnlyDecoded);
|
|
|
|
|
static uint32_t payloadOnlyFallbackCount = 0;
|
|
|
|
|
++payloadOnlyFallbackCount;
|
|
|
|
|
if (payloadOnlyFallbackCount <= 10 || (payloadOnlyFallbackCount % 100) == 0) {
|
|
|
|
|
LOG_WARNING("SMSG_COMPRESSED_MOVES decoded via payload-only size fallback",
|
|
|
|
|
" (occurrence=", payloadOnlyFallbackCount, ")");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!decoded.ok || decoded.overrun) {
|
|
|
|
|
LOG_WARNING("SMSG_COMPRESSED_MOVES: sub-packet overruns buffer at pos=", decoded.endPos);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unordered_set<uint16_t> unhandledSeen;
|
|
|
|
|
|
|
|
|
|
for (const auto& entry : decoded.packets) {
|
|
|
|
|
network::Packet subPacket(entry.opcode, entry.payload);
|
|
|
|
|
|
|
|
|
|
if (entry.opcode == monsterMoveWire) {
|
|
|
|
|
handleMonsterMove(subPacket);
|
|
|
|
|
} else if (entry.opcode == monsterMoveTransportWire) {
|
|
|
|
|
handleMonsterMoveTransport(subPacket);
|
2026-04-05 19:30:44 +03:00
|
|
|
} else if (owner_.getState() == WorldState::IN_WORLD &&
|
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
|
|
|
std::find(kMoveOpcodes.begin(), kMoveOpcodes.end(), entry.opcode) != kMoveOpcodes.end()) {
|
|
|
|
|
handleOtherPlayerMovement(subPacket);
|
|
|
|
|
} else {
|
|
|
|
|
if (unhandledSeen.insert(entry.opcode).second) {
|
|
|
|
|
LOG_INFO("SMSG_COMPRESSED_MOVES: unhandled sub-opcode 0x",
|
|
|
|
|
std::hex, entry.opcode, std::dec, " payloadLen=", entry.payload.size());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Monster Move Handlers
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleMonsterMove(network::Packet& packet) {
|
|
|
|
|
if (isActiveExpansion("classic") || isActiveExpansion("turtle")) {
|
|
|
|
|
constexpr uint32_t kMaxMonsterMovesPerTick = 256;
|
|
|
|
|
++monsterMovePacketsThisTick_;
|
|
|
|
|
if (monsterMovePacketsThisTick_ > kMaxMonsterMovesPerTick) {
|
|
|
|
|
++monsterMovePacketsDroppedThisTick_;
|
|
|
|
|
if (monsterMovePacketsDroppedThisTick_ <= 3 ||
|
|
|
|
|
(monsterMovePacketsDroppedThisTick_ % 100) == 0) {
|
|
|
|
|
LOG_WARNING("SMSG_MONSTER_MOVE: per-tick cap exceeded, dropping packet",
|
|
|
|
|
" (processed=", monsterMovePacketsThisTick_,
|
|
|
|
|
" dropped=", monsterMovePacketsDroppedThisTick_, ")");
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MonsterMoveData data;
|
|
|
|
|
auto logMonsterMoveParseFailure = [&](const std::string& msg) {
|
|
|
|
|
static uint32_t failCount = 0;
|
|
|
|
|
++failCount;
|
|
|
|
|
if (failCount <= 10 || (failCount % 100) == 0) {
|
|
|
|
|
LOG_WARNING(msg, " (occurrence=", failCount, ")");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
auto logWrappedUncompressedFallbackUsed = [&]() {
|
|
|
|
|
static uint32_t wrappedUncompressedFallbackCount = 0;
|
|
|
|
|
++wrappedUncompressedFallbackCount;
|
|
|
|
|
if (wrappedUncompressedFallbackCount <= 10 || (wrappedUncompressedFallbackCount % 100) == 0) {
|
|
|
|
|
LOG_WARNING("SMSG_MONSTER_MOVE parsed via uncompressed wrapped-subpacket fallback",
|
|
|
|
|
" (occurrence=", wrappedUncompressedFallbackCount, ")");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
auto stripWrappedSubpacket = [&](const std::vector<uint8_t>& bytes, std::vector<uint8_t>& stripped) -> bool {
|
|
|
|
|
if (bytes.size() < 3) return false;
|
|
|
|
|
uint8_t subSize = bytes[0];
|
|
|
|
|
if (subSize < 2) return false;
|
|
|
|
|
size_t wrappedLen = static_cast<size_t>(subSize) + 1;
|
|
|
|
|
if (wrappedLen != bytes.size()) return false;
|
|
|
|
|
size_t payloadLen = static_cast<size_t>(subSize) - 2;
|
|
|
|
|
if (3 + payloadLen > bytes.size()) return false;
|
|
|
|
|
stripped.assign(bytes.begin() + 3, bytes.begin() + 3 + payloadLen);
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const auto& rawData = packet.getData();
|
|
|
|
|
const bool allowTurtleMoveCompression = isActiveExpansion("turtle");
|
|
|
|
|
bool isCompressed = allowTurtleMoveCompression &&
|
|
|
|
|
rawData.size() >= 6 &&
|
|
|
|
|
rawData[4] == 0x78 &&
|
|
|
|
|
(rawData[5] == 0x01 || rawData[5] == 0x9C ||
|
|
|
|
|
rawData[5] == 0xDA || rawData[5] == 0x5E);
|
|
|
|
|
if (isCompressed) {
|
|
|
|
|
uint32_t decompSize = static_cast<uint32_t>(rawData[0]) |
|
|
|
|
|
(static_cast<uint32_t>(rawData[1]) << 8) |
|
|
|
|
|
(static_cast<uint32_t>(rawData[2]) << 16) |
|
|
|
|
|
(static_cast<uint32_t>(rawData[3]) << 24);
|
|
|
|
|
if (decompSize == 0 || decompSize > 65536) {
|
|
|
|
|
LOG_WARNING("SMSG_MONSTER_MOVE: bad decompSize=", decompSize);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
std::vector<uint8_t> decompressed(decompSize);
|
|
|
|
|
uLongf destLen = decompSize;
|
|
|
|
|
int ret = uncompress(decompressed.data(), &destLen,
|
|
|
|
|
rawData.data() + 4, rawData.size() - 4);
|
|
|
|
|
if (ret != Z_OK) {
|
|
|
|
|
LOG_WARNING("SMSG_MONSTER_MOVE: zlib error ", ret);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
decompressed.resize(destLen);
|
|
|
|
|
std::vector<uint8_t> stripped;
|
|
|
|
|
bool hasWrappedForm = stripWrappedSubpacket(decompressed, stripped);
|
|
|
|
|
|
|
|
|
|
bool parsed = false;
|
|
|
|
|
if (hasWrappedForm) {
|
|
|
|
|
network::Packet wrappedPacket(packet.getOpcode(), stripped);
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getPacketParsers()->parseMonsterMove(wrappedPacket, data)) {
|
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
|
|
|
parsed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!parsed) {
|
|
|
|
|
network::Packet decompPacket(packet.getOpcode(), decompressed);
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getPacketParsers()->parseMonsterMove(decompPacket, data)) {
|
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
|
|
|
parsed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!parsed) {
|
|
|
|
|
if (hasWrappedForm) {
|
|
|
|
|
logMonsterMoveParseFailure("Failed to parse SMSG_MONSTER_MOVE (decompressed " +
|
|
|
|
|
std::to_string(destLen) + " bytes, wrapped payload " +
|
|
|
|
|
std::to_string(stripped.size()) + " bytes)");
|
|
|
|
|
} else {
|
|
|
|
|
logMonsterMoveParseFailure("Failed to parse SMSG_MONSTER_MOVE (decompressed " +
|
|
|
|
|
std::to_string(destLen) + " bytes)");
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-04-05 19:30:44 +03:00
|
|
|
} else if (!owner_.getPacketParsers()->parseMonsterMove(packet, data)) {
|
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
|
|
|
std::vector<uint8_t> stripped;
|
|
|
|
|
if (stripWrappedSubpacket(rawData, stripped)) {
|
|
|
|
|
network::Packet wrappedPacket(packet.getOpcode(), stripped);
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getPacketParsers()->parseMonsterMove(wrappedPacket, data)) {
|
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
|
|
|
logWrappedUncompressedFallbackUsed();
|
|
|
|
|
} else {
|
|
|
|
|
logMonsterMoveParseFailure("Failed to parse SMSG_MONSTER_MOVE");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
logMonsterMoveParseFailure("Failed to parse SMSG_MONSTER_MOVE");
|
|
|
|
|
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 entity = owner_.getEntityManager().getEntity(data.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 (!entity) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.hasDest) {
|
|
|
|
|
glm::vec3 destCanonical = core::coords::serverToCanonical(
|
|
|
|
|
glm::vec3(data.destX, data.destY, data.destZ));
|
|
|
|
|
|
|
|
|
|
float orientation = entity->getOrientation();
|
|
|
|
|
if (data.moveType == 4) {
|
|
|
|
|
orientation = core::coords::serverToCanonicalYaw(data.facingAngle);
|
|
|
|
|
} else if (data.moveType == 3) {
|
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 target = owner_.getEntityManager().getEntity(data.facingTarget);
|
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 (target) {
|
|
|
|
|
float dx = target->getX() - entity->getX();
|
|
|
|
|
float dy = target->getY() - entity->getY();
|
|
|
|
|
if (std::abs(dx) > 0.01f || std::abs(dy) > 0.01f) {
|
|
|
|
|
orientation = std::atan2(-dy, dx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
float dx = destCanonical.x - entity->getX();
|
|
|
|
|
float dy = destCanonical.y - entity->getY();
|
|
|
|
|
if (std::abs(dx) > 0.01f || std::abs(dy) > 0.01f) {
|
|
|
|
|
orientation = std::atan2(-dy, dx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.moveType != 3) {
|
|
|
|
|
glm::vec3 startCanonical = core::coords::serverToCanonical(
|
|
|
|
|
glm::vec3(data.x, data.y, data.z));
|
|
|
|
|
float travelDx = destCanonical.x - startCanonical.x;
|
|
|
|
|
float travelDy = destCanonical.y - startCanonical.y;
|
|
|
|
|
float travelLen = std::sqrt(travelDx * travelDx + travelDy * travelDy);
|
|
|
|
|
if (travelLen > 0.5f) {
|
|
|
|
|
float travelAngle = std::atan2(-travelDy, travelDx);
|
|
|
|
|
float diff = orientation - travelAngle;
|
|
|
|
|
while (diff > static_cast<float>(M_PI)) diff -= 2.0f * static_cast<float>(M_PI);
|
|
|
|
|
while (diff < -static_cast<float>(M_PI)) diff += 2.0f * static_cast<float>(M_PI);
|
|
|
|
|
if (std::abs(diff) > static_cast<float>(M_PI) * 0.5f) {
|
|
|
|
|
orientation = travelAngle;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entity->startMoveTo(destCanonical.x, destCanonical.y, destCanonical.z,
|
|
|
|
|
orientation, data.duration / 1000.0f);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.creatureMoveCallbackRef()) {
|
|
|
|
|
owner_.creatureMoveCallbackRef()(data.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
|
|
|
destCanonical.x, destCanonical.y, destCanonical.z,
|
|
|
|
|
data.duration);
|
|
|
|
|
}
|
|
|
|
|
} else if (data.moveType == 1) {
|
|
|
|
|
glm::vec3 posCanonical = core::coords::serverToCanonical(
|
|
|
|
|
glm::vec3(data.x, data.y, data.z));
|
|
|
|
|
entity->setPosition(posCanonical.x, posCanonical.y, posCanonical.z,
|
|
|
|
|
entity->getOrientation());
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.creatureMoveCallbackRef()) {
|
|
|
|
|
owner_.creatureMoveCallbackRef()(data.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
|
|
|
posCanonical.x, posCanonical.y, posCanonical.z, 0);
|
|
|
|
|
}
|
|
|
|
|
} else if (data.moveType == 4) {
|
|
|
|
|
float orientation = core::coords::serverToCanonicalYaw(data.facingAngle);
|
|
|
|
|
glm::vec3 posCanonical = core::coords::serverToCanonical(
|
|
|
|
|
glm::vec3(data.x, data.y, data.z));
|
|
|
|
|
entity->setPosition(posCanonical.x, posCanonical.y, posCanonical.z, orientation);
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.creatureMoveCallbackRef()) {
|
|
|
|
|
owner_.creatureMoveCallbackRef()(data.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
|
|
|
posCanonical.x, posCanonical.y, posCanonical.z, 0);
|
|
|
|
|
}
|
|
|
|
|
} else if (data.moveType == 3 && data.facingTarget != 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 target = owner_.getEntityManager().getEntity(data.facingTarget);
|
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 (target) {
|
|
|
|
|
float dx = target->getX() - entity->getX();
|
|
|
|
|
float dy = target->getY() - entity->getY();
|
|
|
|
|
if (std::abs(dx) > 0.01f || std::abs(dy) > 0.01f) {
|
|
|
|
|
float orientation = std::atan2(-dy, dx);
|
|
|
|
|
entity->setOrientation(orientation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleMonsterMoveTransport(network::Packet& packet) {
|
2026-03-29 20:53:26 -07:00
|
|
|
if (packet.getRemainingSize() < 8 + 1 + 8 + 12) return;
|
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
|
|
|
uint64_t moverGuid = packet.readUInt64();
|
|
|
|
|
/*uint8_t unk =*/ packet.readUInt8();
|
|
|
|
|
uint64_t transportGuid = packet.readUInt64();
|
|
|
|
|
|
|
|
|
|
float localX = packet.readFloat();
|
|
|
|
|
float localY = packet.readFloat();
|
|
|
|
|
float localZ = packet.readFloat();
|
|
|
|
|
|
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 entity = owner_.getEntityManager().getEntity(moverGuid);
|
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 (!entity) return;
|
|
|
|
|
|
|
|
|
|
if (packet.getReadPos() + 5 > packet.getSize()) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getTransportManager()) {
|
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
|
|
|
glm::vec3 localCanonical = core::coords::serverToCanonical(glm::vec3(localX, localY, localZ));
|
|
|
|
|
owner_.setTransportAttachment(moverGuid, entity->getType(), transportGuid, localCanonical, false, 0.0f);
|
2026-04-05 19:30:44 +03:00
|
|
|
glm::vec3 worldPos = owner_.getTransportManager()->getPlayerWorldPosition(transportGuid, localCanonical);
|
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
|
|
|
entity->setPosition(worldPos.x, worldPos.y, worldPos.z, entity->getOrientation());
|
2026-04-05 19:30:44 +03:00
|
|
|
if (entity->getType() == ObjectType::UNIT && owner_.creatureMoveCallbackRef())
|
|
|
|
|
owner_.creatureMoveCallbackRef()(moverGuid, worldPos.x, worldPos.y, worldPos.z, 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
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*uint32_t splineId =*/ packet.readUInt32();
|
|
|
|
|
uint8_t moveType = packet.readUInt8();
|
|
|
|
|
|
|
|
|
|
if (moveType == 1) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getTransportManager()) {
|
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
|
|
|
glm::vec3 localCanonical = core::coords::serverToCanonical(glm::vec3(localX, localY, localZ));
|
|
|
|
|
owner_.setTransportAttachment(moverGuid, entity->getType(), transportGuid, localCanonical, false, 0.0f);
|
2026-04-05 19:30:44 +03:00
|
|
|
glm::vec3 worldPos = owner_.getTransportManager()->getPlayerWorldPosition(transportGuid, localCanonical);
|
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
|
|
|
entity->setPosition(worldPos.x, worldPos.y, worldPos.z, entity->getOrientation());
|
2026-04-05 19:30:44 +03:00
|
|
|
if (entity->getType() == ObjectType::UNIT && owner_.creatureMoveCallbackRef())
|
|
|
|
|
owner_.creatureMoveCallbackRef()(moverGuid, worldPos.x, worldPos.y, worldPos.z, 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
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float facingAngle = entity->getOrientation();
|
|
|
|
|
if (moveType == 2) {
|
|
|
|
|
if (packet.getReadPos() + 12 > packet.getSize()) return;
|
|
|
|
|
float sx = packet.readFloat(), sy = packet.readFloat(), sz = packet.readFloat();
|
|
|
|
|
facingAngle = std::atan2(-(sy - localY), sx - localX);
|
|
|
|
|
(void)sz;
|
|
|
|
|
} else if (moveType == 3) {
|
|
|
|
|
if (packet.getReadPos() + 8 > packet.getSize()) return;
|
|
|
|
|
uint64_t tgtGuid = packet.readUInt64();
|
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 (auto tgt = owner_.getEntityManager().getEntity(tgtGuid)) {
|
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
|
|
|
float dx = tgt->getX() - entity->getX();
|
|
|
|
|
float dy = tgt->getY() - entity->getY();
|
|
|
|
|
if (std::abs(dx) > 0.01f || std::abs(dy) > 0.01f)
|
|
|
|
|
facingAngle = std::atan2(-dy, dx);
|
|
|
|
|
}
|
|
|
|
|
} else if (moveType == 4) {
|
|
|
|
|
if (packet.getReadPos() + 4 > packet.getSize()) return;
|
|
|
|
|
facingAngle = core::coords::serverToCanonicalYaw(packet.readFloat());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (packet.getReadPos() + 4 > packet.getSize()) return;
|
|
|
|
|
uint32_t splineFlags = packet.readUInt32();
|
|
|
|
|
|
|
|
|
|
if (splineFlags & 0x00400000) {
|
|
|
|
|
if (packet.getReadPos() + 5 > packet.getSize()) return;
|
|
|
|
|
packet.readUInt8(); packet.readUInt32();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (packet.getReadPos() + 4 > packet.getSize()) return;
|
|
|
|
|
uint32_t duration = packet.readUInt32();
|
|
|
|
|
|
|
|
|
|
if (splineFlags & 0x00000800) {
|
|
|
|
|
if (packet.getReadPos() + 8 > packet.getSize()) return;
|
|
|
|
|
packet.readFloat(); packet.readUInt32();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (packet.getReadPos() + 4 > packet.getSize()) return;
|
|
|
|
|
uint32_t pointCount = packet.readUInt32();
|
|
|
|
|
constexpr uint32_t kMaxTransportSplinePoints = 1000;
|
|
|
|
|
if (pointCount > kMaxTransportSplinePoints) {
|
|
|
|
|
LOG_WARNING("SMSG_MONSTER_MOVE_TRANSPORT: pointCount=", pointCount,
|
|
|
|
|
" clamped to ", kMaxTransportSplinePoints);
|
|
|
|
|
pointCount = kMaxTransportSplinePoints;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float destLocalX = localX, destLocalY = localY, destLocalZ = localZ;
|
|
|
|
|
bool hasDest = false;
|
|
|
|
|
if (pointCount > 0) {
|
|
|
|
|
const bool uncompressed = (splineFlags & (0x00080000 | 0x00002000)) != 0;
|
|
|
|
|
if (uncompressed) {
|
|
|
|
|
for (uint32_t i = 0; i < pointCount - 1; ++i) {
|
|
|
|
|
if (packet.getReadPos() + 12 > packet.getSize()) break;
|
|
|
|
|
packet.readFloat(); packet.readFloat(); packet.readFloat();
|
|
|
|
|
}
|
|
|
|
|
if (packet.getReadPos() + 12 <= packet.getSize()) {
|
|
|
|
|
destLocalX = packet.readFloat();
|
|
|
|
|
destLocalY = packet.readFloat();
|
|
|
|
|
destLocalZ = packet.readFloat();
|
|
|
|
|
hasDest = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (packet.getReadPos() + 12 <= packet.getSize()) {
|
|
|
|
|
destLocalX = packet.readFloat();
|
|
|
|
|
destLocalY = packet.readFloat();
|
|
|
|
|
destLocalZ = packet.readFloat();
|
|
|
|
|
hasDest = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (!owner_.getTransportManager()) {
|
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
|
|
|
LOG_WARNING("SMSG_MONSTER_MOVE_TRANSPORT: TransportManager not available for mover 0x",
|
|
|
|
|
std::hex, moverGuid, std::dec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 startLocalCanonical = core::coords::serverToCanonical(glm::vec3(localX, localY, localZ));
|
|
|
|
|
|
|
|
|
|
if (hasDest && duration > 0) {
|
|
|
|
|
glm::vec3 destLocalCanonical = core::coords::serverToCanonical(glm::vec3(destLocalX, destLocalY, destLocalZ));
|
2026-04-05 19:30:44 +03:00
|
|
|
glm::vec3 destWorld = owner_.getTransportManager()->getPlayerWorldPosition(transportGuid, destLocalCanonical);
|
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 (moveType == 0) {
|
|
|
|
|
float dx = destLocalCanonical.x - startLocalCanonical.x;
|
|
|
|
|
float dy = destLocalCanonical.y - startLocalCanonical.y;
|
|
|
|
|
if (std::abs(dx) > 0.01f || std::abs(dy) > 0.01f)
|
|
|
|
|
facingAngle = std::atan2(-dy, dx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
owner_.setTransportAttachment(moverGuid, entity->getType(), transportGuid, destLocalCanonical, false, 0.0f);
|
|
|
|
|
entity->startMoveTo(destWorld.x, destWorld.y, destWorld.z, facingAngle, duration / 1000.0f);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (entity->getType() == ObjectType::UNIT && owner_.creatureMoveCallbackRef())
|
|
|
|
|
owner_.creatureMoveCallbackRef()(moverGuid, destWorld.x, destWorld.y, destWorld.z, duration);
|
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
|
|
|
|
|
|
|
|
LOG_DEBUG("SMSG_MONSTER_MOVE_TRANSPORT: mover=0x", std::hex, moverGuid,
|
|
|
|
|
" transport=0x", transportGuid, std::dec,
|
|
|
|
|
" dur=", duration, "ms dest=(", destWorld.x, ",", destWorld.y, ",", destWorld.z, ")");
|
|
|
|
|
} else {
|
2026-04-05 19:30:44 +03:00
|
|
|
glm::vec3 startWorld = owner_.getTransportManager()->getPlayerWorldPosition(transportGuid, startLocalCanonical);
|
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
|
|
|
owner_.setTransportAttachment(moverGuid, entity->getType(), transportGuid, startLocalCanonical, false, 0.0f);
|
|
|
|
|
entity->setPosition(startWorld.x, startWorld.y, startWorld.z, facingAngle);
|
2026-04-05 19:30:44 +03:00
|
|
|
if (entity->getType() == ObjectType::UNIT && owner_.creatureMoveCallbackRef())
|
|
|
|
|
owner_.creatureMoveCallbackRef()(moverGuid, startWorld.x, startWorld.y, startWorld.z, 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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Teleport Handlers
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleTeleportAck(network::Packet& packet) {
|
|
|
|
|
const bool taTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
2026-03-29 20:53:26 -07:00
|
|
|
if (packet.getRemainingSize() < (taTbc ? 8u : 4u)) {
|
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
|
|
|
LOG_WARNING("MSG_MOVE_TELEPORT_ACK too short");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t guid = taTbc
|
|
|
|
|
? packet.readUInt64() : packet.readPackedGuid();
|
2026-03-29 20:53:26 -07:00
|
|
|
if (!packet.hasRemaining(4)) return;
|
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 counter = packet.readUInt32();
|
|
|
|
|
|
|
|
|
|
const bool taNoFlags2 = isClassicLikeExpansion() || isActiveExpansion("tbc");
|
|
|
|
|
const size_t minMoveSz = taNoFlags2 ? (4 + 4 + 4 * 4) : (4 + 2 + 4 + 4 * 4);
|
2026-03-29 20:53:26 -07:00
|
|
|
if (packet.getRemainingSize() < minMoveSz) {
|
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
|
|
|
LOG_WARNING("MSG_MOVE_TELEPORT_ACK: not enough data for movement info");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
packet.readUInt32(); // moveFlags
|
|
|
|
|
if (!taNoFlags2)
|
|
|
|
|
packet.readUInt16(); // moveFlags2 (WotLK only)
|
|
|
|
|
uint32_t moveTime = packet.readUInt32();
|
|
|
|
|
float serverX = packet.readFloat();
|
|
|
|
|
float serverY = packet.readFloat();
|
|
|
|
|
float serverZ = packet.readFloat();
|
|
|
|
|
float orientation = packet.readFloat();
|
|
|
|
|
|
2026-04-05 17:39:56 -07:00
|
|
|
LOG_WARNING("MSG_MOVE_TELEPORT_ACK: guid=0x", std::hex, guid, std::dec,
|
|
|
|
|
" counter=", counter,
|
|
|
|
|
" pos=(", serverX, ", ", serverY, ", ", serverZ, ")",
|
|
|
|
|
" currentPos=(", movementInfo.x, ", ", movementInfo.y, ", ", movementInfo.z, ")");
|
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
|
|
|
|
|
|
|
|
glm::vec3 canonical = core::coords::serverToCanonical(glm::vec3(serverX, serverY, serverZ));
|
|
|
|
|
movementInfo.x = canonical.x;
|
|
|
|
|
movementInfo.y = canonical.y;
|
|
|
|
|
movementInfo.z = canonical.z;
|
|
|
|
|
movementInfo.orientation = core::coords::serverToCanonicalYaw(orientation);
|
|
|
|
|
movementInfo.flags = 0;
|
|
|
|
|
|
2026-03-28 11:35:10 +03:00
|
|
|
// Clear cast bar on teleport — SpellHandler owns the casting_ flag
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSpellHandler()) owner_.getSpellHandler()->resetCastState();
|
2026-03-28 11:35:10 +03:00
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSocket()) {
|
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
|
|
|
network::Packet ack(wireOpcode(Opcode::MSG_MOVE_TELEPORT_ACK));
|
|
|
|
|
const bool legacyGuidAck =
|
|
|
|
|
isActiveExpansion("classic") || isActiveExpansion("tbc") || isActiveExpansion("turtle");
|
|
|
|
|
if (legacyGuidAck) {
|
2026-04-05 19:30:44 +03:00
|
|
|
ack.writeUInt64(owner_.getPlayerGuid());
|
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
|
|
|
} else {
|
2026-04-05 19:30:44 +03:00
|
|
|
ack.writePackedGuid(owner_.getPlayerGuid());
|
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
|
|
|
}
|
|
|
|
|
ack.writeUInt32(counter);
|
|
|
|
|
ack.writeUInt32(moveTime);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(ack);
|
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
|
|
|
LOG_INFO("Sent MSG_MOVE_TELEPORT_ACK response");
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.worldEntryCallbackRef()) {
|
|
|
|
|
owner_.worldEntryCallbackRef()(owner_.currentMapIdRef(), serverX, serverY, serverZ, false);
|
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 MovementHandler::handleNewWorld(network::Packet& packet) {
|
2026-03-29 20:53:26 -07:00
|
|
|
if (!packet.hasRemaining(20)) {
|
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
|
|
|
LOG_WARNING("SMSG_NEW_WORLD too short");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t mapId = packet.readUInt32();
|
|
|
|
|
float serverX = packet.readFloat();
|
|
|
|
|
float serverY = packet.readFloat();
|
|
|
|
|
float serverZ = packet.readFloat();
|
|
|
|
|
float orientation = packet.readFloat();
|
|
|
|
|
|
|
|
|
|
LOG_INFO("SMSG_NEW_WORLD: mapId=", mapId,
|
|
|
|
|
" pos=(", serverX, ", ", serverY, ", ", serverZ, ")",
|
|
|
|
|
" orient=", orientation);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
const bool isSameMap = (mapId == owner_.currentMapIdRef());
|
|
|
|
|
const bool isResurrection = owner_.resurrectPendingRef();
|
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 (isSameMap && isResurrection) {
|
|
|
|
|
LOG_INFO("SMSG_NEW_WORLD same-map resurrection — skipping world reload");
|
|
|
|
|
|
|
|
|
|
glm::vec3 canonical = core::coords::serverToCanonical(glm::vec3(serverX, serverY, serverZ));
|
|
|
|
|
movementInfo.x = canonical.x;
|
|
|
|
|
movementInfo.y = canonical.y;
|
|
|
|
|
movementInfo.z = canonical.z;
|
|
|
|
|
movementInfo.orientation = core::coords::serverToCanonicalYaw(orientation);
|
|
|
|
|
movementInfo.flags = 0;
|
|
|
|
|
movementInfo.flags2 = 0;
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.resurrectPendingRef() = false;
|
|
|
|
|
owner_.resurrectRequestPendingRef() = false;
|
|
|
|
|
owner_.releasedSpiritRef() = false;
|
|
|
|
|
owner_.playerDeadRef() = false;
|
|
|
|
|
owner_.repopPendingRef() = false;
|
|
|
|
|
owner_.pendingSpiritHealerGuidRef() = 0;
|
|
|
|
|
owner_.resurrectCasterGuidRef() = 0;
|
|
|
|
|
owner_.corpseMapIdRef() = 0;
|
|
|
|
|
owner_.corpseGuidRef() = 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
|
|
|
owner_.clearHostileAttackers();
|
|
|
|
|
owner_.stopAutoAttack();
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.tabCycleStaleRef() = true;
|
2026-03-28 11:35:10 +03:00
|
|
|
owner_.resetCastState();
|
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-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSocket()) {
|
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
|
|
|
network::Packet ack(wireOpcode(Opcode::MSG_MOVE_WORLDPORT_ACK));
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(ack);
|
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
|
|
|
LOG_INFO("Sent MSG_MOVE_WORLDPORT_ACK (resurrection)");
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.currentMapIdRef() = mapId;
|
|
|
|
|
owner_.inInstanceRef() = false;
|
|
|
|
|
if (owner_.getSocket()) {
|
|
|
|
|
owner_.getSocket()->tracePacketsFor(std::chrono::seconds(12), "new_world");
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 canonical = core::coords::serverToCanonical(glm::vec3(serverX, serverY, serverZ));
|
|
|
|
|
movementInfo.x = canonical.x;
|
|
|
|
|
movementInfo.y = canonical.y;
|
|
|
|
|
movementInfo.z = canonical.z;
|
|
|
|
|
movementInfo.orientation = core::coords::serverToCanonicalYaw(orientation);
|
|
|
|
|
movementInfo.flags = 0;
|
|
|
|
|
movementInfo.flags2 = 0;
|
|
|
|
|
serverMovementAllowed_ = true;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.resurrectPendingRef() = false;
|
|
|
|
|
owner_.resurrectRequestPendingRef() = false;
|
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
|
|
|
onTaxiFlight_ = false;
|
|
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiActivatePending_ = false;
|
|
|
|
|
taxiClientActive_ = false;
|
|
|
|
|
taxiClientPath_.clear();
|
|
|
|
|
taxiRecoverPending_ = false;
|
|
|
|
|
taxiStartGrace_ = 0.0f;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.currentMountDisplayIdRef() = 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
|
|
|
taxiMountDisplayId_ = 0;
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.mountCallbackRef()) {
|
|
|
|
|
owner_.mountCallbackRef()(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
|
|
|
}
|
|
|
|
|
|
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] : owner_.getEntityManager().getEntities()) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (guid == owner_.getPlayerGuid()) continue;
|
|
|
|
|
if (entity->getType() == ObjectType::UNIT && owner_.creatureDespawnCallbackRef()) {
|
|
|
|
|
owner_.creatureDespawnCallbackRef()(guid);
|
|
|
|
|
} else if (entity->getType() == ObjectType::PLAYER && owner_.playerDespawnCallbackRef()) {
|
|
|
|
|
owner_.playerDespawnCallbackRef()(guid);
|
|
|
|
|
} else if (entity->getType() == ObjectType::GAMEOBJECT && owner_.gameObjectDespawnCallbackRef()) {
|
|
|
|
|
owner_.gameObjectDespawnCallbackRef()(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
|
|
|
}
|
|
|
|
|
}
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.otherPlayerVisibleItemEntriesRef().clear();
|
|
|
|
|
owner_.otherPlayerVisibleDirtyRef().clear();
|
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
|
|
|
otherPlayerMoveTimeMs_.clear();
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSpellHandler()) owner_.getSpellHandler()->clearUnitCastStates();
|
|
|
|
|
owner_.unitAurasCacheRef().clear();
|
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
|
|
|
owner_.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
|
|
|
owner_.getEntityManager().clear();
|
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
|
|
|
owner_.clearHostileAttackers();
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.worldStatesRef().clear();
|
|
|
|
|
owner_.gossipPoisRef().clear();
|
|
|
|
|
owner_.worldStateMapIdRef() = mapId;
|
|
|
|
|
owner_.worldStateZoneIdRef() = 0;
|
|
|
|
|
owner_.activeAreaTriggersRef().clear();
|
|
|
|
|
owner_.areaTriggerCheckTimerRef() = -5.0f;
|
|
|
|
|
owner_.areaTriggerSuppressFirstRef() = true;
|
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
|
|
|
owner_.stopAutoAttack();
|
2026-03-28 11:35:10 +03:00
|
|
|
owner_.resetCastState();
|
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-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSocket()) {
|
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
|
|
|
network::Packet ack(wireOpcode(Opcode::MSG_MOVE_WORLDPORT_ACK));
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(ack);
|
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
|
|
|
LOG_INFO("Sent MSG_MOVE_WORLDPORT_ACK");
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.timeSinceLastPingRef() = 0.0f;
|
|
|
|
|
if (owner_.getSocket()) {
|
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
|
|
|
LOG_WARNING("World transfer keepalive: sending immediate ping after MSG_MOVE_WORLDPORT_ACK");
|
|
|
|
|
owner_.sendPing();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.worldEntryCallbackRef()) {
|
|
|
|
|
owner_.worldEntryCallbackRef()(mapId, serverX, serverY, serverZ, isSameMap);
|
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-04-05 19:30:44 +03:00
|
|
|
if (owner_.addonEventCallbackRef()) {
|
|
|
|
|
owner_.addonEventCallbackRef()("PLAYER_ENTERING_WORLD", {"0"});
|
|
|
|
|
owner_.addonEventCallbackRef()("ZONE_CHANGED_NEW_AREA", {});
|
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
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::loadTaxiDbc() {
|
|
|
|
|
if (taxiDbcLoaded_) return;
|
|
|
|
|
taxiDbcLoaded_ = 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 = owner_.services().assetManager;
|
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 (!am || !am->isInitialized()) return;
|
|
|
|
|
|
|
|
|
|
auto nodesDbc = am->loadDBC("TaxiNodes.dbc");
|
|
|
|
|
if (nodesDbc && nodesDbc->isLoaded()) {
|
|
|
|
|
const auto* tnL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("TaxiNodes") : nullptr;
|
|
|
|
|
// Cache field indices before the loop
|
|
|
|
|
const uint32_t tnIdField = tnL ? (*tnL)["ID"] : 0;
|
|
|
|
|
const uint32_t tnMapField = tnL ? (*tnL)["MapID"] : 1;
|
|
|
|
|
const uint32_t tnXField = tnL ? (*tnL)["X"] : 2;
|
|
|
|
|
const uint32_t tnYField = tnL ? (*tnL)["Y"] : 3;
|
|
|
|
|
const uint32_t tnZField = tnL ? (*tnL)["Z"] : 4;
|
|
|
|
|
const uint32_t tnNameField = tnL ? (*tnL)["Name"] : 5;
|
|
|
|
|
const uint32_t mountAllianceField = tnL ? (*tnL)["MountDisplayIdAlliance"] : 22;
|
|
|
|
|
const uint32_t mountHordeField = tnL ? (*tnL)["MountDisplayIdHorde"] : 23;
|
|
|
|
|
const uint32_t mountAllianceFB = tnL ? (*tnL)["MountDisplayIdAllianceFallback"] : 20;
|
|
|
|
|
const uint32_t mountHordeFB = tnL ? (*tnL)["MountDisplayIdHordeFallback"] : 21;
|
|
|
|
|
uint32_t fieldCount = nodesDbc->getFieldCount();
|
|
|
|
|
for (uint32_t i = 0; i < nodesDbc->getRecordCount(); i++) {
|
|
|
|
|
TaxiNode node;
|
|
|
|
|
node.id = nodesDbc->getUInt32(i, tnIdField);
|
|
|
|
|
node.mapId = nodesDbc->getUInt32(i, tnMapField);
|
|
|
|
|
node.x = nodesDbc->getFloat(i, tnXField);
|
|
|
|
|
node.y = nodesDbc->getFloat(i, tnYField);
|
|
|
|
|
node.z = nodesDbc->getFloat(i, tnZField);
|
|
|
|
|
node.name = nodesDbc->getString(i, tnNameField);
|
|
|
|
|
if (fieldCount > mountHordeField) {
|
|
|
|
|
node.mountDisplayIdAlliance = nodesDbc->getUInt32(i, mountAllianceField);
|
|
|
|
|
node.mountDisplayIdHorde = nodesDbc->getUInt32(i, mountHordeField);
|
|
|
|
|
if (node.mountDisplayIdAlliance == 0 && node.mountDisplayIdHorde == 0 && fieldCount > mountHordeFB) {
|
|
|
|
|
node.mountDisplayIdAlliance = nodesDbc->getUInt32(i, mountAllianceFB);
|
|
|
|
|
node.mountDisplayIdHorde = nodesDbc->getUInt32(i, mountHordeFB);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
uint32_t nodeId = node.id;
|
|
|
|
|
if (nodeId > 0) {
|
|
|
|
|
taxiNodes_[nodeId] = std::move(node);
|
|
|
|
|
}
|
|
|
|
|
if (nodeId == 195) {
|
|
|
|
|
std::string fields;
|
|
|
|
|
for (uint32_t f = 0; f < fieldCount; f++) {
|
|
|
|
|
fields += std::to_string(f) + ":" + std::to_string(nodesDbc->getUInt32(i, f)) + " ";
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("TaxiNodes[195] fields: ", fields);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Loaded ", taxiNodes_.size(), " taxi nodes from TaxiNodes.dbc");
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARNING("Could not load TaxiNodes.dbc");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pathDbc = am->loadDBC("TaxiPath.dbc");
|
|
|
|
|
if (pathDbc && pathDbc->isLoaded()) {
|
|
|
|
|
const auto* tpL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("TaxiPath") : nullptr;
|
|
|
|
|
const uint32_t tpIdField = tpL ? (*tpL)["ID"] : 0;
|
|
|
|
|
const uint32_t tpFromField = tpL ? (*tpL)["FromNode"] : 1;
|
|
|
|
|
const uint32_t tpToField = tpL ? (*tpL)["ToNode"] : 2;
|
|
|
|
|
const uint32_t tpCostField = tpL ? (*tpL)["Cost"] : 3;
|
|
|
|
|
for (uint32_t i = 0; i < pathDbc->getRecordCount(); i++) {
|
|
|
|
|
TaxiPathEdge edge;
|
|
|
|
|
edge.pathId = pathDbc->getUInt32(i, tpIdField);
|
|
|
|
|
edge.fromNode = pathDbc->getUInt32(i, tpFromField);
|
|
|
|
|
edge.toNode = pathDbc->getUInt32(i, tpToField);
|
|
|
|
|
edge.cost = pathDbc->getUInt32(i, tpCostField);
|
|
|
|
|
taxiPathEdges_.push_back(edge);
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Loaded ", taxiPathEdges_.size(), " taxi path edges from TaxiPath.dbc");
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARNING("Could not load TaxiPath.dbc");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pathNodeDbc = am->loadDBC("TaxiPathNode.dbc");
|
|
|
|
|
if (pathNodeDbc && pathNodeDbc->isLoaded()) {
|
|
|
|
|
const auto* tpnL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("TaxiPathNode") : nullptr;
|
|
|
|
|
const uint32_t tpnIdField = tpnL ? (*tpnL)["ID"] : 0;
|
|
|
|
|
const uint32_t tpnPathField = tpnL ? (*tpnL)["PathID"] : 1;
|
|
|
|
|
const uint32_t tpnIndexField = tpnL ? (*tpnL)["NodeIndex"] : 2;
|
|
|
|
|
const uint32_t tpnMapField = tpnL ? (*tpnL)["MapID"] : 3;
|
|
|
|
|
const uint32_t tpnXField = tpnL ? (*tpnL)["X"] : 4;
|
|
|
|
|
const uint32_t tpnYField = tpnL ? (*tpnL)["Y"] : 5;
|
|
|
|
|
const uint32_t tpnZField = tpnL ? (*tpnL)["Z"] : 6;
|
|
|
|
|
for (uint32_t i = 0; i < pathNodeDbc->getRecordCount(); i++) {
|
|
|
|
|
TaxiPathNode node;
|
|
|
|
|
node.id = pathNodeDbc->getUInt32(i, tpnIdField);
|
|
|
|
|
node.pathId = pathNodeDbc->getUInt32(i, tpnPathField);
|
|
|
|
|
node.nodeIndex = pathNodeDbc->getUInt32(i, tpnIndexField);
|
|
|
|
|
node.mapId = pathNodeDbc->getUInt32(i, tpnMapField);
|
|
|
|
|
node.x = pathNodeDbc->getFloat(i, tpnXField);
|
|
|
|
|
node.y = pathNodeDbc->getFloat(i, tpnYField);
|
|
|
|
|
node.z = pathNodeDbc->getFloat(i, tpnZField);
|
|
|
|
|
taxiPathNodes_[node.pathId].push_back(node);
|
|
|
|
|
}
|
|
|
|
|
for (auto& [pathId, nodes] : taxiPathNodes_) {
|
|
|
|
|
std::sort(nodes.begin(), nodes.end(),
|
|
|
|
|
[](const TaxiPathNode& a, const TaxiPathNode& b) {
|
|
|
|
|
return a.nodeIndex < b.nodeIndex;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Loaded ", pathNodeDbc->getRecordCount(), " taxi path waypoints from TaxiPathNode.dbc");
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARNING("Could not load TaxiPathNode.dbc");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::handleShowTaxiNodes(network::Packet& packet) {
|
|
|
|
|
ShowTaxiNodesData data;
|
|
|
|
|
if (!ShowTaxiNodesParser::parse(packet, data)) {
|
|
|
|
|
LOG_WARNING("Failed to parse SMSG_SHOWTAXINODES");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadTaxiDbc();
|
|
|
|
|
|
|
|
|
|
if (taxiMaskInitialized_) {
|
|
|
|
|
for (uint32_t i = 0; i < TLK_TAXI_MASK_SIZE; ++i) {
|
|
|
|
|
uint32_t newBits = data.nodeMask[i] & ~knownTaxiMask_[i];
|
|
|
|
|
if (newBits == 0) continue;
|
|
|
|
|
for (uint32_t bit = 0; bit < 32; ++bit) {
|
|
|
|
|
if (newBits & (1u << bit)) {
|
|
|
|
|
uint32_t nodeId = i * 32 + bit + 1;
|
|
|
|
|
auto it = taxiNodes_.find(nodeId);
|
|
|
|
|
if (it != taxiNodes_.end()) {
|
|
|
|
|
owner_.addSystemChatMessage("Discovered flight path: " + it->second.name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < TLK_TAXI_MASK_SIZE; ++i) {
|
|
|
|
|
knownTaxiMask_[i] = data.nodeMask[i];
|
|
|
|
|
}
|
|
|
|
|
taxiMaskInitialized_ = true;
|
|
|
|
|
|
|
|
|
|
currentTaxiData_ = data;
|
|
|
|
|
taxiNpcGuid_ = data.npcGuid;
|
|
|
|
|
taxiWindowOpen_ = true;
|
2026-03-28 14:36:14 -07:00
|
|
|
owner_.closeGossip();
|
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
|
|
|
buildTaxiCostMap();
|
|
|
|
|
auto it = taxiNodes_.find(data.nearestNode);
|
|
|
|
|
if (it != taxiNodes_.end()) {
|
|
|
|
|
LOG_INFO("Taxi node ", data.nearestNode, " mounts: A=", it->second.mountDisplayIdAlliance,
|
|
|
|
|
" H=", it->second.mountDisplayIdHorde);
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Taxi window opened, nearest node=", data.nearestNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::applyTaxiMountForCurrentNode() {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (taxiMountActive_ || !owner_.mountCallbackRef()) return;
|
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
|
|
|
auto it = taxiNodes_.find(currentTaxiData_.nearestNode);
|
|
|
|
|
if (it == taxiNodes_.end()) {
|
|
|
|
|
bool isAlliance = true;
|
2026-04-05 19:30:44 +03:00
|
|
|
switch (owner_.playerRaceRef()) {
|
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
|
|
|
case Race::ORC: case Race::UNDEAD: case Race::TAUREN: case Race::TROLL:
|
|
|
|
|
case Race::GOBLIN: case Race::BLOOD_ELF:
|
|
|
|
|
isAlliance = false; break;
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
uint32_t mountId = isAlliance ? 1210u : 1310u;
|
|
|
|
|
taxiMountDisplayId_ = mountId;
|
|
|
|
|
taxiMountActive_ = true;
|
|
|
|
|
LOG_INFO("Taxi mount fallback (node ", currentTaxiData_.nearestNode, " not in DBC): displayId=", mountId);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.mountCallbackRef()(mountId);
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isAlliance = true;
|
2026-04-05 19:30:44 +03:00
|
|
|
switch (owner_.playerRaceRef()) {
|
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
|
|
|
case Race::ORC:
|
|
|
|
|
case Race::UNDEAD:
|
|
|
|
|
case Race::TAUREN:
|
|
|
|
|
case Race::TROLL:
|
|
|
|
|
case Race::GOBLIN:
|
|
|
|
|
case Race::BLOOD_ELF:
|
|
|
|
|
isAlliance = false;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
isAlliance = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
uint32_t mountId = isAlliance ? it->second.mountDisplayIdAlliance
|
|
|
|
|
: it->second.mountDisplayIdHorde;
|
|
|
|
|
if (mountId == 541) mountId = 0;
|
|
|
|
|
if (mountId == 0) {
|
|
|
|
|
mountId = isAlliance ? it->second.mountDisplayIdHorde
|
|
|
|
|
: it->second.mountDisplayIdAlliance;
|
|
|
|
|
if (mountId == 541) mountId = 0;
|
|
|
|
|
}
|
|
|
|
|
if (mountId == 0) {
|
[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
|
|
|
uint32_t gryphonId = owner_.services().gryphonDisplayId;
|
|
|
|
|
uint32_t wyvernId = owner_.services().wyvernDisplayId;
|
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 (isAlliance && gryphonId != 0) mountId = gryphonId;
|
|
|
|
|
if (!isAlliance && wyvernId != 0) mountId = wyvernId;
|
|
|
|
|
if (mountId == 0) {
|
|
|
|
|
mountId = (isAlliance ? wyvernId : gryphonId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mountId == 0) {
|
|
|
|
|
if (it->second.mountDisplayIdAlliance != 0) mountId = it->second.mountDisplayIdAlliance;
|
|
|
|
|
else if (it->second.mountDisplayIdHorde != 0) mountId = it->second.mountDisplayIdHorde;
|
|
|
|
|
}
|
|
|
|
|
if (mountId == 0) {
|
|
|
|
|
static const uint32_t kAllianceTaxiDisplays[] = {1210u, 1211u, 1212u, 1213u};
|
|
|
|
|
static const uint32_t kHordeTaxiDisplays[] = {1310u, 1311u, 1312u};
|
|
|
|
|
mountId = isAlliance ? kAllianceTaxiDisplays[0] : kHordeTaxiDisplays[0];
|
|
|
|
|
}
|
|
|
|
|
if (mountId == 0) {
|
|
|
|
|
mountId = isAlliance ? 30412u : 30413u;
|
|
|
|
|
}
|
|
|
|
|
if (mountId != 0) {
|
|
|
|
|
taxiMountDisplayId_ = mountId;
|
|
|
|
|
taxiMountActive_ = true;
|
|
|
|
|
LOG_INFO("Taxi mount apply: displayId=", mountId);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.mountCallbackRef()(mountId);
|
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 MovementHandler::startClientTaxiPath(const std::vector<uint32_t>& pathNodes) {
|
|
|
|
|
taxiClientPath_.clear();
|
|
|
|
|
taxiClientIndex_ = 0;
|
|
|
|
|
taxiClientActive_ = false;
|
|
|
|
|
taxiClientSegmentProgress_ = 0.0f;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i + 1 < pathNodes.size(); i++) {
|
|
|
|
|
uint32_t fromNode = pathNodes[i];
|
|
|
|
|
uint32_t toNode = pathNodes[i + 1];
|
|
|
|
|
uint32_t pathId = 0;
|
|
|
|
|
for (const auto& edge : taxiPathEdges_) {
|
|
|
|
|
if (edge.fromNode == fromNode && edge.toNode == toNode) {
|
|
|
|
|
pathId = edge.pathId;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (pathId == 0) {
|
|
|
|
|
LOG_WARNING("No taxi path found from node ", fromNode, " to ", toNode);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto pathIt = taxiPathNodes_.find(pathId);
|
|
|
|
|
if (pathIt != taxiPathNodes_.end()) {
|
|
|
|
|
for (const auto& wpNode : pathIt->second) {
|
|
|
|
|
glm::vec3 serverPos(wpNode.x, wpNode.y, wpNode.z);
|
|
|
|
|
glm::vec3 canonical = core::coords::serverToCanonical(serverPos);
|
|
|
|
|
taxiClientPath_.push_back(canonical);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARNING("No spline waypoints found for taxi pathId ", pathId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (taxiClientPath_.size() < 2) {
|
|
|
|
|
taxiClientPath_.clear();
|
|
|
|
|
for (uint32_t nodeId : pathNodes) {
|
|
|
|
|
auto nodeIt = taxiNodes_.find(nodeId);
|
|
|
|
|
if (nodeIt == taxiNodes_.end()) continue;
|
|
|
|
|
glm::vec3 serverPos(nodeIt->second.x, nodeIt->second.y, nodeIt->second.z);
|
|
|
|
|
taxiClientPath_.push_back(core::coords::serverToCanonical(serverPos));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (taxiClientPath_.size() < 2) {
|
|
|
|
|
LOG_WARNING("Taxi path too short: ", taxiClientPath_.size(), " waypoints");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 start = taxiClientPath_[0];
|
|
|
|
|
glm::vec3 dir(0.0f);
|
|
|
|
|
float dirLenSq = 0.0f;
|
|
|
|
|
for (size_t i = 1; i < taxiClientPath_.size(); i++) {
|
|
|
|
|
dir = taxiClientPath_[i] - start;
|
|
|
|
|
dirLenSq = glm::dot(dir, dir);
|
|
|
|
|
if (dirLenSq >= 1e-6f) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float initialOrientation = movementInfo.orientation;
|
|
|
|
|
float initialRenderYaw = movementInfo.orientation;
|
|
|
|
|
float initialPitch = 0.0f;
|
|
|
|
|
float initialRoll = 0.0f;
|
|
|
|
|
if (dirLenSq >= 1e-6f) {
|
|
|
|
|
initialOrientation = std::atan2(dir.y, dir.x);
|
|
|
|
|
glm::vec3 renderDir = core::coords::canonicalToRender(dir);
|
|
|
|
|
initialRenderYaw = std::atan2(renderDir.y, renderDir.x);
|
|
|
|
|
glm::vec3 dirNorm = dir * glm::inversesqrt(dirLenSq);
|
|
|
|
|
initialPitch = std::asin(std::clamp(dirNorm.z, -1.0f, 1.0f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
movementInfo.x = start.x;
|
|
|
|
|
movementInfo.y = start.y;
|
|
|
|
|
movementInfo.z = start.z;
|
|
|
|
|
movementInfo.orientation = initialOrientation;
|
|
|
|
|
sanitizeMovementForTaxi();
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
auto playerEntity = owner_.getEntityManager().getEntity(owner_.getPlayerGuid());
|
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 (playerEntity) {
|
|
|
|
|
playerEntity->setPosition(start.x, start.y, start.z, initialOrientation);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.taxiOrientationCallbackRef()) {
|
|
|
|
|
owner_.taxiOrientationCallbackRef()(initialRenderYaw, initialPitch, initialRoll);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Taxi flight started with ", taxiClientPath_.size(), " spline waypoints");
|
|
|
|
|
taxiClientActive_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::updateClientTaxi(float deltaTime) {
|
|
|
|
|
if (!taxiClientActive_ || taxiClientPath_.size() < 2) return;
|
2026-04-05 19:30:44 +03:00
|
|
|
auto playerEntity = owner_.getEntityManager().getEntity(owner_.getPlayerGuid());
|
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
|
|
|
|
|
|
|
|
auto finishTaxiFlight = [&]() {
|
|
|
|
|
if (!taxiClientPath_.empty()) {
|
|
|
|
|
const auto& landingPos = taxiClientPath_.back();
|
|
|
|
|
if (playerEntity) {
|
|
|
|
|
playerEntity->setPosition(landingPos.x, landingPos.y, landingPos.z,
|
|
|
|
|
movementInfo.orientation);
|
|
|
|
|
}
|
|
|
|
|
movementInfo.x = landingPos.x;
|
|
|
|
|
movementInfo.y = landingPos.y;
|
|
|
|
|
movementInfo.z = landingPos.z;
|
|
|
|
|
LOG_INFO("Taxi landing: snapped to final waypoint (",
|
|
|
|
|
landingPos.x, ", ", landingPos.y, ", ", landingPos.z, ")");
|
|
|
|
|
}
|
|
|
|
|
taxiClientActive_ = false;
|
|
|
|
|
onTaxiFlight_ = false;
|
|
|
|
|
taxiLandingCooldown_ = 2.0f;
|
2026-04-05 19:30:44 +03:00
|
|
|
if (taxiMountActive_ && owner_.mountCallbackRef()) {
|
|
|
|
|
owner_.mountCallbackRef()(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
|
|
|
}
|
|
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiMountDisplayId_ = 0;
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.currentMountDisplayIdRef() = 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
|
|
|
taxiClientPath_.clear();
|
|
|
|
|
taxiRecoverPending_ = false;
|
|
|
|
|
movementInfo.flags = 0;
|
|
|
|
|
movementInfo.flags2 = 0;
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSocket()) {
|
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
|
|
|
sendMovement(Opcode::MSG_MOVE_STOP);
|
|
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Taxi flight landed (client path)");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (taxiClientIndex_ + 1 >= taxiClientPath_.size()) {
|
|
|
|
|
finishTaxiFlight();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float remainingDistance = taxiClientSegmentProgress_ + (taxiClientSpeed_ * deltaTime);
|
|
|
|
|
glm::vec3 start(0.0f);
|
|
|
|
|
glm::vec3 end(0.0f);
|
|
|
|
|
glm::vec3 dir(0.0f);
|
|
|
|
|
float segmentLen = 0.0f;
|
|
|
|
|
float t = 0.0f;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
if (taxiClientIndex_ + 1 >= taxiClientPath_.size()) {
|
|
|
|
|
finishTaxiFlight();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start = taxiClientPath_[taxiClientIndex_];
|
|
|
|
|
end = taxiClientPath_[taxiClientIndex_ + 1];
|
|
|
|
|
dir = end - start;
|
|
|
|
|
float segLenSq = glm::dot(dir, dir);
|
|
|
|
|
|
|
|
|
|
if (segLenSq < 1e-4f) {
|
|
|
|
|
taxiClientIndex_++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
segmentLen = std::sqrt(segLenSq);
|
|
|
|
|
|
|
|
|
|
if (remainingDistance >= segmentLen) {
|
|
|
|
|
remainingDistance -= segmentLen;
|
|
|
|
|
taxiClientIndex_++;
|
|
|
|
|
taxiClientSegmentProgress_ = 0.0f;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
taxiClientSegmentProgress_ = remainingDistance;
|
|
|
|
|
t = taxiClientSegmentProgress_ / segmentLen;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 p0 = (taxiClientIndex_ > 0) ? taxiClientPath_[taxiClientIndex_ - 1] : start;
|
|
|
|
|
glm::vec3 p1 = start;
|
|
|
|
|
glm::vec3 p2 = end;
|
|
|
|
|
glm::vec3 p3 = (taxiClientIndex_ + 2 < taxiClientPath_.size()) ?
|
|
|
|
|
taxiClientPath_[taxiClientIndex_ + 2] : end;
|
|
|
|
|
|
|
|
|
|
float t2 = t * t;
|
|
|
|
|
float t3 = t2 * t;
|
|
|
|
|
glm::vec3 nextPos = 0.5f * (
|
|
|
|
|
(2.0f * p1) +
|
|
|
|
|
(-p0 + p2) * t +
|
|
|
|
|
(2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 +
|
|
|
|
|
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
glm::vec3 tangent = 0.5f * (
|
|
|
|
|
(-p0 + p2) +
|
|
|
|
|
2.0f * (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t +
|
|
|
|
|
3.0f * (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t2
|
|
|
|
|
);
|
|
|
|
|
float tangentLenSq = glm::dot(tangent, tangent);
|
|
|
|
|
if (tangentLenSq < 1e-8f) {
|
|
|
|
|
tangent = dir;
|
|
|
|
|
tangentLenSq = glm::dot(tangent, tangent);
|
|
|
|
|
if (tangentLenSq < 1e-8f) {
|
|
|
|
|
tangent = glm::vec3(std::cos(movementInfo.orientation), std::sin(movementInfo.orientation), 0.0f);
|
|
|
|
|
tangentLenSq = 1.0f; // unit vector
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float targetOrientation = std::atan2(tangent.y, tangent.x);
|
|
|
|
|
|
|
|
|
|
glm::vec3 tangentNorm = tangent * glm::inversesqrt(std::max(tangentLenSq, 1e-8f));
|
|
|
|
|
float pitch = std::asin(std::clamp(tangentNorm.z, -1.0f, 1.0f));
|
|
|
|
|
|
|
|
|
|
float currentOrientation = movementInfo.orientation;
|
|
|
|
|
float orientDiff = targetOrientation - currentOrientation;
|
|
|
|
|
while (orientDiff > 3.14159265f) orientDiff -= 6.28318530f;
|
|
|
|
|
while (orientDiff < -3.14159265f) orientDiff += 6.28318530f;
|
|
|
|
|
float roll = -orientDiff * 2.5f;
|
|
|
|
|
roll = std::clamp(roll, -0.7f, 0.7f);
|
|
|
|
|
|
|
|
|
|
float smoothOrientation = currentOrientation + orientDiff * std::min(1.0f, deltaTime * 3.0f);
|
|
|
|
|
|
|
|
|
|
if (playerEntity) {
|
|
|
|
|
playerEntity->setPosition(nextPos.x, nextPos.y, nextPos.z, smoothOrientation);
|
|
|
|
|
}
|
|
|
|
|
movementInfo.x = nextPos.x;
|
|
|
|
|
movementInfo.y = nextPos.y;
|
|
|
|
|
movementInfo.z = nextPos.z;
|
|
|
|
|
movementInfo.orientation = smoothOrientation;
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.taxiOrientationCallbackRef()) {
|
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
|
|
|
glm::vec3 renderTangent = core::coords::canonicalToRender(tangent);
|
|
|
|
|
float renderYaw = std::atan2(renderTangent.y, renderTangent.x);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.taxiOrientationCallbackRef()(renderYaw, pitch, roll);
|
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 MovementHandler::handleActivateTaxiReply(network::Packet& packet) {
|
|
|
|
|
ActivateTaxiReplyData data;
|
|
|
|
|
if (!ActivateTaxiReplyParser::parse(packet, data)) {
|
|
|
|
|
LOG_WARNING("Failed to parse SMSG_ACTIVATETAXIREPLY");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!taxiActivatePending_) {
|
|
|
|
|
LOG_DEBUG("Ignoring stray taxi reply: result=", data.result);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.result == 0) {
|
|
|
|
|
if (onTaxiFlight_ && !taxiActivatePending_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
onTaxiFlight_ = true;
|
|
|
|
|
taxiStartGrace_ = std::max(taxiStartGrace_, 2.0f);
|
|
|
|
|
sanitizeMovementForTaxi();
|
|
|
|
|
taxiWindowOpen_ = false;
|
|
|
|
|
taxiActivatePending_ = false;
|
|
|
|
|
taxiActivateTimer_ = 0.0f;
|
|
|
|
|
applyTaxiMountForCurrentNode();
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSocket()) {
|
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
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Taxi flight started!");
|
|
|
|
|
} else {
|
|
|
|
|
if (onTaxiFlight_ || taxiClientActive_) {
|
|
|
|
|
LOG_WARNING("Ignoring stale taxi failure reply while flight is active: result=", data.result);
|
|
|
|
|
taxiActivatePending_ = false;
|
|
|
|
|
taxiActivateTimer_ = 0.0f;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
LOG_WARNING("Taxi activation failed, result=", data.result);
|
|
|
|
|
owner_.addSystemChatMessage("Cannot take that flight path.");
|
|
|
|
|
taxiActivatePending_ = false;
|
|
|
|
|
taxiActivateTimer_ = 0.0f;
|
2026-04-05 19:30:44 +03:00
|
|
|
if (taxiMountActive_ && owner_.mountCallbackRef()) {
|
|
|
|
|
owner_.mountCallbackRef()(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
|
|
|
}
|
|
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiMountDisplayId_ = 0;
|
|
|
|
|
onTaxiFlight_ = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::closeTaxi() {
|
|
|
|
|
taxiWindowOpen_ = false;
|
|
|
|
|
|
|
|
|
|
if (taxiActivatePending_ || onTaxiFlight_ || taxiClientActive_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (taxiMountActive_ && owner_.mountCallbackRef()) {
|
|
|
|
|
owner_.mountCallbackRef()(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
|
|
|
}
|
|
|
|
|
taxiMountActive_ = false;
|
|
|
|
|
taxiMountDisplayId_ = 0;
|
|
|
|
|
|
|
|
|
|
taxiActivatePending_ = false;
|
|
|
|
|
onTaxiFlight_ = false;
|
|
|
|
|
|
|
|
|
|
taxiLandingCooldown_ = 2.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::buildTaxiCostMap() {
|
|
|
|
|
taxiCostMap_.clear();
|
|
|
|
|
uint32_t startNode = currentTaxiData_.nearestNode;
|
|
|
|
|
if (startNode == 0) return;
|
|
|
|
|
|
|
|
|
|
struct AdjEntry { uint32_t node; uint32_t cost; };
|
|
|
|
|
std::unordered_map<uint32_t, std::vector<AdjEntry>> adj;
|
|
|
|
|
for (const auto& edge : taxiPathEdges_) {
|
|
|
|
|
adj[edge.fromNode].push_back({edge.toNode, edge.cost});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::deque<uint32_t> queue;
|
|
|
|
|
queue.push_back(startNode);
|
|
|
|
|
taxiCostMap_[startNode] = 0;
|
|
|
|
|
|
|
|
|
|
while (!queue.empty()) {
|
|
|
|
|
uint32_t cur = queue.front();
|
|
|
|
|
queue.pop_front();
|
|
|
|
|
for (const auto& next : adj[cur]) {
|
|
|
|
|
if (taxiCostMap_.find(next.node) == taxiCostMap_.end()) {
|
|
|
|
|
taxiCostMap_[next.node] = taxiCostMap_[cur] + next.cost;
|
|
|
|
|
queue.push_back(next.node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t MovementHandler::getTaxiCostTo(uint32_t destNodeId) const {
|
|
|
|
|
auto it = taxiCostMap_.find(destNodeId);
|
|
|
|
|
return (it != taxiCostMap_.end()) ? it->second : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::activateTaxi(uint32_t destNodeId) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (!owner_.getSocket() || owner_.getState() != WorldState::IN_WORLD) return;
|
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 (taxiActivatePending_ || onTaxiFlight_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t startNode = currentTaxiData_.nearestNode;
|
|
|
|
|
if (startNode == 0 || destNodeId == 0 || startNode == destNodeId) return;
|
|
|
|
|
|
|
|
|
|
if (owner_.isMounted()) {
|
|
|
|
|
LOG_INFO("Taxi activate: dismounting current mount");
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.mountCallbackRef()) owner_.mountCallbackRef()(0);
|
|
|
|
|
owner_.currentMountDisplayIdRef() = 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
|
|
|
dismount();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto destIt = taxiNodes_.find(destNodeId);
|
|
|
|
|
if (destIt != taxiNodes_.end() && !destIt->second.name.empty()) {
|
|
|
|
|
taxiDestName_ = destIt->second.name;
|
|
|
|
|
owner_.addSystemChatMessage("Requesting flight to " + destIt->second.name + "...");
|
|
|
|
|
} else {
|
|
|
|
|
taxiDestName_.clear();
|
|
|
|
|
owner_.addSystemChatMessage("Taxi: requesting flight...");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BFS to find path from startNode to destNodeId
|
|
|
|
|
std::unordered_map<uint32_t, std::vector<uint32_t>> adj;
|
|
|
|
|
for (const auto& edge : taxiPathEdges_) {
|
|
|
|
|
adj[edge.fromNode].push_back(edge.toNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unordered_map<uint32_t, uint32_t> parent;
|
|
|
|
|
std::deque<uint32_t> queue;
|
|
|
|
|
queue.push_back(startNode);
|
|
|
|
|
parent[startNode] = startNode;
|
|
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
|
while (!queue.empty()) {
|
|
|
|
|
uint32_t cur = queue.front();
|
|
|
|
|
queue.pop_front();
|
|
|
|
|
if (cur == destNodeId) { found = true; break; }
|
|
|
|
|
for (uint32_t next : adj[cur]) {
|
|
|
|
|
if (parent.find(next) == parent.end()) {
|
|
|
|
|
parent[next] = cur;
|
|
|
|
|
queue.push_back(next);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
LOG_WARNING("No taxi path found from node ", startNode, " to ", destNodeId);
|
|
|
|
|
owner_.addSystemChatMessage("No flight path available to that destination.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<uint32_t> path;
|
|
|
|
|
for (uint32_t n = destNodeId; n != startNode; n = parent[n]) {
|
|
|
|
|
path.push_back(n);
|
|
|
|
|
}
|
|
|
|
|
path.push_back(startNode);
|
|
|
|
|
std::reverse(path.begin(), path.end());
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Taxi path: ", path.size(), " nodes, from ", startNode, " to ", destNodeId);
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Taxi activate: npc=0x", std::hex, taxiNpcGuid_, std::dec,
|
|
|
|
|
" start=", startNode, " dest=", destNodeId, " pathLen=", path.size());
|
|
|
|
|
if (!path.empty()) {
|
|
|
|
|
std::string pathStr;
|
|
|
|
|
for (size_t i = 0; i < path.size(); i++) {
|
|
|
|
|
pathStr += std::to_string(path[i]);
|
|
|
|
|
if (i + 1 < path.size()) pathStr += "->";
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("Taxi path nodes: ", pathStr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t totalCost = getTaxiCostTo(destNodeId);
|
|
|
|
|
LOG_INFO("Taxi activate: start=", startNode, " dest=", destNodeId, " cost=", totalCost);
|
|
|
|
|
|
|
|
|
|
auto basicPkt = ActivateTaxiPacket::build(taxiNpcGuid_, startNode, destNodeId);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(basicPkt);
|
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
|
|
|
|
|
|
|
|
taxiWindowOpen_ = false;
|
|
|
|
|
taxiActivatePending_ = true;
|
|
|
|
|
taxiActivateTimer_ = 0.0f;
|
|
|
|
|
taxiStartGrace_ = 2.0f;
|
|
|
|
|
if (!onTaxiFlight_) {
|
|
|
|
|
onTaxiFlight_ = true;
|
|
|
|
|
sanitizeMovementForTaxi();
|
|
|
|
|
applyTaxiMountForCurrentNode();
|
|
|
|
|
}
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getSocket()) {
|
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
|
|
|
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.taxiPrecacheCallbackRef()) {
|
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
|
|
|
std::vector<glm::vec3> previewPath;
|
|
|
|
|
for (size_t i = 0; i + 1 < path.size(); i++) {
|
|
|
|
|
uint32_t fromNode = path[i];
|
|
|
|
|
uint32_t toNode = path[i + 1];
|
|
|
|
|
uint32_t pathId = 0;
|
|
|
|
|
for (const auto& edge : taxiPathEdges_) {
|
|
|
|
|
if (edge.fromNode == fromNode && edge.toNode == toNode) {
|
|
|
|
|
pathId = edge.pathId;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (pathId == 0) continue;
|
|
|
|
|
auto pathIt = taxiPathNodes_.find(pathId);
|
|
|
|
|
if (pathIt != taxiPathNodes_.end()) {
|
|
|
|
|
for (const auto& wpNode : pathIt->second) {
|
|
|
|
|
glm::vec3 serverPos(wpNode.x, wpNode.y, wpNode.z);
|
|
|
|
|
glm::vec3 canonical = core::coords::serverToCanonical(serverPos);
|
|
|
|
|
previewPath.push_back(canonical);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (previewPath.size() >= 2) {
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.taxiPrecacheCallbackRef()(previewPath);
|
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-04-05 19:30:44 +03:00
|
|
|
if (owner_.taxiFlightStartCallbackRef()) {
|
|
|
|
|
owner_.taxiFlightStartCallbackRef()();
|
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
|
|
|
}
|
|
|
|
|
startClientTaxiPath(path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Area Trigger Detection (moved from GameHandler)
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::loadAreaTriggerDbc() {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.areaTriggerDbcLoadedRef()) return;
|
|
|
|
|
owner_.areaTriggerDbcLoadedRef() = true;
|
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
|
|
|
|
[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 = owner_.services().assetManager;
|
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 (!am || !am->isInitialized()) return;
|
|
|
|
|
|
|
|
|
|
auto dbc = am->loadDBC("AreaTrigger.dbc");
|
|
|
|
|
if (!dbc || !dbc->isLoaded()) {
|
|
|
|
|
LOG_WARNING("Failed to load AreaTrigger.dbc");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.areaTriggersRef().reserve(dbc->getRecordCount());
|
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
|
|
|
for (uint32_t i = 0; i < dbc->getRecordCount(); i++) {
|
|
|
|
|
GameHandler::AreaTriggerEntry at;
|
|
|
|
|
at.id = dbc->getUInt32(i, 0);
|
|
|
|
|
at.mapId = dbc->getUInt32(i, 1);
|
|
|
|
|
// DBC stores positions in server/wire format (X=west, Y=north) — swap to canonical
|
|
|
|
|
at.x = dbc->getFloat(i, 3); // canonical X (north) = DBC field 3 (Y_wire)
|
|
|
|
|
at.y = dbc->getFloat(i, 2); // canonical Y (west) = DBC field 2 (X_wire)
|
|
|
|
|
at.z = dbc->getFloat(i, 4);
|
|
|
|
|
at.radius = dbc->getFloat(i, 5);
|
|
|
|
|
at.boxLength = dbc->getFloat(i, 6);
|
|
|
|
|
at.boxWidth = dbc->getFloat(i, 7);
|
|
|
|
|
at.boxHeight = dbc->getFloat(i, 8);
|
|
|
|
|
at.boxYaw = dbc->getFloat(i, 9);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.areaTriggersRef().push_back(at);
|
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-04-05 19:30:44 +03:00
|
|
|
LOG_WARNING("Loaded ", owner_.areaTriggersRef().size(), " area triggers from AreaTrigger.dbc");
|
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 MovementHandler::checkAreaTriggers() {
|
|
|
|
|
if (!owner_.isInWorld()) return;
|
|
|
|
|
if (onTaxiFlight_ || taxiClientActive_) return;
|
|
|
|
|
|
|
|
|
|
loadAreaTriggerDbc();
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.areaTriggersRef().empty()) return;
|
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
|
|
|
|
|
|
|
|
const float px = movementInfo.x;
|
|
|
|
|
const float py = movementInfo.y;
|
|
|
|
|
const float pz = movementInfo.z;
|
|
|
|
|
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
// Sanity: if position is near map origin on Eastern Kingdoms (map 0),
|
|
|
|
|
// something has corrupted movementInfo — skip area trigger check to
|
|
|
|
|
// avoid firing Alterac/Hillsbrad triggers and causing a rogue teleport.
|
2026-04-05 19:42:25 -07:00
|
|
|
if (owner_.getCurrentMapId() == 0 && std::abs(px) < 500.0f && std::abs(py) < 500.0f) {
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
LOG_WARNING("checkAreaTriggers: position near map origin (", px, ", ", py, ", ", pz,
|
|
|
|
|
") on map 0 — skipping to avoid rogue teleport. onTransport=",
|
|
|
|
|
owner_.isOnTransport(), " transportGuid=0x", std::hex,
|
2026-04-05 19:42:25 -07:00
|
|
|
owner_.playerTransportGuidRef(), std::dec);
|
fix(rendering): emote animations, WMO portal culling, transport teleport
Emote animations: fix DBC chain for /laugh, /flirt, /sleep, /fart, /stink.
Previously all emotes with AnimID=0 used emoteRef as animId (wrong DBC
record IDs). Now resolves through Emotes.dbc properly, with per-emote
overrides for emotes whose DBC chain yields 0. Adds Emotes.dbc load
failure warning and diagnostic logging.
WMO culling: skip portal culling when camera is outside all groups (fixes
vanishing Stormwind ground tiles). Also handle indoor/outdoor AABB overlap
by showing all groups when position is in both indoor and outdoor AABBs.
Transport: clear ONTRANSPORT flag and transport state when transport not
found, preventing stale transport data from teleporting player to map
origin. Add area trigger safety net near (0,0,0) on Eastern Kingdoms.
2026-04-05 17:25:25 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// On first check after map transfer, just mark which triggers we're inside
|
|
|
|
|
// without firing them — prevents exit portal from immediately sending us back
|
2026-04-05 19:30:44 +03:00
|
|
|
bool suppressFirst = owner_.areaTriggerSuppressFirstRef();
|
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 (suppressFirst) {
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.areaTriggerSuppressFirstRef() = false;
|
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-04-05 19:30:44 +03:00
|
|
|
for (const auto& at : owner_.areaTriggersRef()) {
|
|
|
|
|
if (at.mapId != owner_.currentMapIdRef()) continue;
|
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 inside = false;
|
|
|
|
|
if (at.radius > 0.0f) {
|
2026-04-05 02:35:47 -07:00
|
|
|
// Sphere trigger — use actual DBC radius
|
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
|
|
|
float dx = px - at.x;
|
|
|
|
|
float dy = py - at.y;
|
|
|
|
|
float dz = pz - at.z;
|
|
|
|
|
float distSq = dx * dx + dy * dy + dz * dz;
|
2026-04-05 02:35:47 -07:00
|
|
|
inside = (distSq <= at.radius * at.radius);
|
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
|
|
|
} else if (at.boxLength > 0.0f || at.boxWidth > 0.0f || at.boxHeight > 0.0f) {
|
2026-04-05 02:35:47 -07:00
|
|
|
// Box trigger — use actual DBC dimensions
|
|
|
|
|
float effLength = at.boxLength;
|
|
|
|
|
float effWidth = at.boxWidth;
|
|
|
|
|
float effHeight = at.boxHeight;
|
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
|
|
|
|
|
|
|
|
float dx = px - at.x;
|
|
|
|
|
float dy = py - at.y;
|
|
|
|
|
float dz = pz - at.z;
|
|
|
|
|
|
|
|
|
|
// Rotate into box-local space
|
|
|
|
|
float cosYaw = std::cos(-at.boxYaw);
|
|
|
|
|
float sinYaw = std::sin(-at.boxYaw);
|
|
|
|
|
float localX = dx * cosYaw - dy * sinYaw;
|
|
|
|
|
float localY = dx * sinYaw + dy * cosYaw;
|
|
|
|
|
|
|
|
|
|
inside = (std::abs(localX) <= effLength * 0.5f &&
|
|
|
|
|
std::abs(localY) <= effWidth * 0.5f &&
|
|
|
|
|
std::abs(dz) <= effHeight * 0.5f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inside) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.activeAreaTriggersRef().count(at.id) == 0) {
|
|
|
|
|
owner_.activeAreaTriggersRef().insert(at.id);
|
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 (suppressFirst) {
|
|
|
|
|
// After map transfer: mark triggers we're inside of, but don't fire them.
|
|
|
|
|
// This prevents the exit portal from immediately sending us back.
|
|
|
|
|
LOG_WARNING("AreaTrigger suppressed (post-transfer): AT", at.id);
|
|
|
|
|
} else {
|
2026-04-05 04:40:46 -07:00
|
|
|
// Send CMSG_AREATRIGGER — the player is already inside the
|
|
|
|
|
// trigger radius so the server's distance check should pass.
|
|
|
|
|
// Do NOT spoof position to the trigger center; that tells the
|
|
|
|
|
// server we're somewhere we're not and can cause rogue teleports.
|
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
|
|
|
network::Packet pkt(wireOpcode(Opcode::CMSG_AREATRIGGER));
|
|
|
|
|
pkt.writeUInt32(at.id);
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.getSocket()->send(pkt);
|
2026-04-05 04:40:46 -07:00
|
|
|
LOG_DEBUG("Fired CMSG_AREATRIGGER: id=", at.id);
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Player left the trigger — allow re-fire on re-entry
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.activeAreaTriggersRef().erase(at.id);
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Transport Attachments (moved from GameHandler)
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::setTransportAttachment(uint64_t childGuid, ObjectType type, uint64_t transportGuid,
|
|
|
|
|
const glm::vec3& localOffset, bool hasLocalOrientation,
|
|
|
|
|
float localOrientation) {
|
|
|
|
|
if (childGuid == 0 || transportGuid == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
GameHandler::TransportAttachment& attachment = owner_.transportAttachmentsRef()[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
|
|
|
attachment.type = type;
|
|
|
|
|
attachment.transportGuid = transportGuid;
|
|
|
|
|
attachment.localOffset = localOffset;
|
|
|
|
|
attachment.hasLocalOrientation = hasLocalOrientation;
|
|
|
|
|
attachment.localOrientation = localOrientation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::clearTransportAttachment(uint64_t childGuid) {
|
|
|
|
|
if (childGuid == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.transportAttachmentsRef().erase(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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::updateAttachedTransportChildren(float /*deltaTime*/) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (!owner_.getTransportManager() || owner_.transportAttachmentsRef().empty()) {
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr float kPosEpsilonSq = 0.0001f;
|
|
|
|
|
constexpr float kOriEpsilon = 0.001f;
|
|
|
|
|
std::vector<uint64_t> stale;
|
|
|
|
|
stale.reserve(8);
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
for (const auto& [childGuid, attachment] : owner_.transportAttachmentsRef()) {
|
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 entity = owner_.getEntityManager().getEntity(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 (!entity) {
|
|
|
|
|
stale.push_back(childGuid);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
ActiveTransport* transport = owner_.getTransportManager()->getTransport(attachment.transportGuid);
|
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 (!transport) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
glm::vec3 composed = owner_.getTransportManager()->getPlayerWorldPosition(
|
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
|
|
|
attachment.transportGuid, attachment.localOffset);
|
|
|
|
|
|
|
|
|
|
float composedOrientation = entity->getOrientation();
|
|
|
|
|
if (attachment.hasLocalOrientation) {
|
|
|
|
|
float baseYaw = transport->hasServerYaw ? transport->serverYaw : 0.0f;
|
|
|
|
|
composedOrientation = baseYaw + attachment.localOrientation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 oldPos(entity->getX(), entity->getY(), entity->getZ());
|
|
|
|
|
float oldOrientation = entity->getOrientation();
|
|
|
|
|
glm::vec3 delta = composed - oldPos;
|
|
|
|
|
const bool positionChanged = glm::dot(delta, delta) > kPosEpsilonSq;
|
|
|
|
|
const bool orientationChanged = std::abs(composedOrientation - oldOrientation) > kOriEpsilon;
|
|
|
|
|
if (!positionChanged && !orientationChanged) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entity->setPosition(composed.x, composed.y, composed.z, composedOrientation);
|
|
|
|
|
|
|
|
|
|
if (attachment.type == ObjectType::UNIT) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.creatureMoveCallbackRef()) {
|
|
|
|
|
owner_.creatureMoveCallbackRef()(childGuid, composed.x, composed.y, composed.z, 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
|
|
|
}
|
|
|
|
|
} else if (attachment.type == ObjectType::GAMEOBJECT) {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.gameObjectMoveCallbackRef()) {
|
|
|
|
|
owner_.gameObjectMoveCallbackRef()(childGuid, composed.x, composed.y, composed.z, composedOrientation);
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint64_t guid : stale) {
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.transportAttachmentsRef().erase(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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Follow target (moved from GameHandler)
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
void MovementHandler::followTarget() {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getState() != WorldState::IN_WORLD) {
|
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
|
|
|
LOG_WARNING("Cannot follow: not in world");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.getTargetGuid() == 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
|
|
|
owner_.addSystemChatMessage("You must target someone to follow.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto target = owner_.getTarget();
|
|
|
|
|
if (!target) {
|
|
|
|
|
owner_.addSystemChatMessage("Invalid target.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set follow target
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.followTargetGuidRef() = owner_.getTargetGuid();
|
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 render-space position from entity's canonical coords
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.followRenderPosRef() = core::coords::canonicalToRender(glm::vec3(target->getX(), target->getY(), target->getZ()));
|
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
|
|
|
|
|
|
|
|
// Tell camera controller to start auto-following
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.autoFollowCallbackRef()) {
|
|
|
|
|
owner_.autoFollowCallbackRef()(&owner_.followRenderPosRef());
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get target name
|
|
|
|
|
std::string targetName = "Target";
|
|
|
|
|
if (target->getType() == ObjectType::PLAYER) {
|
|
|
|
|
auto player = std::static_pointer_cast<Player>(target);
|
|
|
|
|
if (!player->getName().empty()) {
|
|
|
|
|
targetName = player->getName();
|
|
|
|
|
}
|
|
|
|
|
} else if (target->getType() == ObjectType::UNIT) {
|
|
|
|
|
auto unit = std::static_pointer_cast<Unit>(target);
|
|
|
|
|
targetName = unit->getName();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
owner_.addSystemChatMessage("Now following " + targetName + ".");
|
2026-04-05 19:30:44 +03:00
|
|
|
LOG_INFO("Following target: ", targetName, " (GUID: 0x", std::hex, owner_.getTargetGuid(), std::dec, ")");
|
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
|
|
|
owner_.fireAddonEvent("AUTOFOLLOW_BEGIN", {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MovementHandler::cancelFollow() {
|
2026-04-05 19:30:44 +03:00
|
|
|
if (owner_.followTargetGuidRef() == 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
|
|
|
return;
|
|
|
|
|
}
|
2026-04-05 19:30:44 +03:00
|
|
|
owner_.followTargetGuidRef() = 0;
|
|
|
|
|
if (owner_.autoFollowCallbackRef()) {
|
|
|
|
|
owner_.autoFollowCallbackRef()(nullptr);
|
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
|
|
|
}
|
|
|
|
|
owner_.addSystemChatMessage("You stop following.");
|
|
|
|
|
owner_.fireAddonEvent("AUTOFOLLOW_END", {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace game
|
|
|
|
|
} // namespace wowee
|