mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-01 19:23:51 +00:00
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
272 lines
11 KiB
C++
272 lines
11 KiB
C++
#pragma once
|
|
|
|
#include "game/world_packets.hpp"
|
|
#include "game/opcode_table.hpp"
|
|
#include "network/packet.hpp"
|
|
#include <glm/glm.hpp>
|
|
#include <chrono>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
namespace wowee {
|
|
namespace game {
|
|
|
|
class GameHandler;
|
|
|
|
class MovementHandler {
|
|
public:
|
|
using PacketHandler = std::function<void(network::Packet&)>;
|
|
using DispatchTable = std::unordered_map<LogicalOpcode, PacketHandler>;
|
|
|
|
explicit MovementHandler(GameHandler& owner);
|
|
|
|
void registerOpcodes(DispatchTable& table);
|
|
|
|
// --- Public API (delegated from GameHandler) ---
|
|
|
|
void sendMovement(Opcode opcode);
|
|
void setPosition(float x, float y, float z);
|
|
void setOrientation(float orientation);
|
|
void setMovementPitch(float radians) { movementInfo.pitch = radians; }
|
|
void dismount();
|
|
|
|
// Follow target (moved from GameHandler)
|
|
void followTarget();
|
|
void cancelFollow();
|
|
|
|
// Area trigger detection
|
|
void loadAreaTriggerDbc();
|
|
void checkAreaTriggers();
|
|
|
|
// Transport attachment
|
|
void setTransportAttachment(uint64_t childGuid, ObjectType type, uint64_t transportGuid,
|
|
const glm::vec3& localOffset, bool hasLocalOrientation,
|
|
float localOrientation);
|
|
void clearTransportAttachment(uint64_t childGuid);
|
|
void updateAttachedTransportChildren(float deltaTime);
|
|
|
|
// Movement info accessors
|
|
const MovementInfo& getMovementInfo() const { return movementInfo; }
|
|
MovementInfo& getMovementInfoMut() { return movementInfo; }
|
|
|
|
// Speed accessors
|
|
float getServerRunSpeed() const { return serverRunSpeed_; }
|
|
float getServerWalkSpeed() const { return serverWalkSpeed_; }
|
|
float getServerSwimSpeed() const { return serverSwimSpeed_; }
|
|
float getServerSwimBackSpeed() const { return serverSwimBackSpeed_; }
|
|
float getServerFlightSpeed() const { return serverFlightSpeed_; }
|
|
float getServerFlightBackSpeed() const { return serverFlightBackSpeed_; }
|
|
float getServerRunBackSpeed() const { return serverRunBackSpeed_; }
|
|
float getServerTurnRate() const { return serverTurnRate_; }
|
|
|
|
// Movement flag queries
|
|
bool isPlayerRooted() const {
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::ROOT)) != 0;
|
|
}
|
|
bool isGravityDisabled() const {
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::LEVITATING)) != 0;
|
|
}
|
|
bool isFeatherFalling() const {
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::FEATHER_FALL)) != 0;
|
|
}
|
|
bool isWaterWalking() const {
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::WATER_WALK)) != 0;
|
|
}
|
|
bool isPlayerFlying() const {
|
|
const uint32_t flyMask = static_cast<uint32_t>(MovementFlags::CAN_FLY) |
|
|
static_cast<uint32_t>(MovementFlags::FLYING);
|
|
return (movementInfo.flags & flyMask) == flyMask;
|
|
}
|
|
bool isHovering() const {
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::HOVER)) != 0;
|
|
}
|
|
bool isSwimming() const {
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::SWIMMING)) != 0;
|
|
}
|
|
|
|
// Taxi / Flight Paths
|
|
bool isTaxiWindowOpen() const { return taxiWindowOpen_; }
|
|
void closeTaxi();
|
|
void activateTaxi(uint32_t destNodeId);
|
|
bool isOnTaxiFlight() const { return onTaxiFlight_; }
|
|
bool isTaxiMountActive() const { return taxiMountActive_; }
|
|
bool isTaxiActivationPending() const { return taxiActivatePending_; }
|
|
void forceClearTaxiAndMovementState();
|
|
const std::string& getTaxiDestName() const { return taxiDestName_; }
|
|
const ShowTaxiNodesData& getTaxiData() const { return currentTaxiData_; }
|
|
uint32_t getTaxiCurrentNode() const { return currentTaxiData_.nearestNode; }
|
|
|
|
struct TaxiNode {
|
|
uint32_t id = 0;
|
|
uint32_t mapId = 0;
|
|
float x = 0, y = 0, z = 0;
|
|
std::string name;
|
|
uint32_t mountDisplayIdAlliance = 0;
|
|
uint32_t mountDisplayIdHorde = 0;
|
|
};
|
|
struct TaxiPathEdge {
|
|
uint32_t pathId = 0;
|
|
uint32_t fromNode = 0, toNode = 0;
|
|
uint32_t cost = 0;
|
|
};
|
|
struct TaxiPathNode {
|
|
uint32_t id = 0;
|
|
uint32_t pathId = 0;
|
|
uint32_t nodeIndex = 0;
|
|
uint32_t mapId = 0;
|
|
float x = 0, y = 0, z = 0;
|
|
};
|
|
|
|
const std::unordered_map<uint32_t, TaxiNode>& getTaxiNodes() const { return taxiNodes_; }
|
|
bool isKnownTaxiNode(uint32_t nodeId) const {
|
|
if (nodeId == 0 || nodeId > 384) return false;
|
|
uint32_t idx = nodeId - 1;
|
|
return (knownTaxiMask_[idx / 32] & (1u << (idx % 32))) != 0;
|
|
}
|
|
uint32_t getTaxiCostTo(uint32_t destNodeId) const;
|
|
bool taxiNpcHasRoutes(uint64_t guid) const {
|
|
auto it = taxiNpcHasRoutes_.find(guid);
|
|
return it != taxiNpcHasRoutes_.end() && it->second;
|
|
}
|
|
|
|
void updateClientTaxi(float deltaTime);
|
|
uint32_t nextMovementTimestampMs();
|
|
void sanitizeMovementForTaxi();
|
|
|
|
// Heartbeat / movement timing (for GameHandler::update())
|
|
float& timeSinceLastMoveHeartbeatRef() { return timeSinceLastMoveHeartbeat_; }
|
|
float getMoveHeartbeatInterval() const { return moveHeartbeatInterval_; }
|
|
bool isServerMovementAllowed() const { return serverMovementAllowed_; }
|
|
void setServerMovementAllowed(bool v) { serverMovementAllowed_ = v; }
|
|
uint32_t& monsterMovePacketsThisTickRef() { return monsterMovePacketsThisTick_; }
|
|
uint32_t& monsterMovePacketsDroppedThisTickRef() { return monsterMovePacketsDroppedThisTick_; }
|
|
|
|
// Taxi state references for GameHandler update/processing
|
|
bool& onTaxiFlightRef() { return onTaxiFlight_; }
|
|
bool& taxiMountActiveRef() { return taxiMountActive_; }
|
|
uint32_t& taxiMountDisplayIdRef() { return taxiMountDisplayId_; }
|
|
bool& taxiActivatePendingRef() { return taxiActivatePending_; }
|
|
float& taxiActivateTimerRef() { return taxiActivateTimer_; }
|
|
bool& taxiClientActiveRef() { return taxiClientActive_; }
|
|
float& taxiLandingCooldownRef() { return taxiLandingCooldown_; }
|
|
float& taxiStartGraceRef() { return taxiStartGrace_; }
|
|
bool& taxiRecoverPendingRef() { return taxiRecoverPending_; }
|
|
uint32_t& taxiRecoverMapIdRef() { return taxiRecoverMapId_; }
|
|
glm::vec3& taxiRecoverPosRef() { return taxiRecoverPos_; }
|
|
std::unordered_map<uint64_t, bool>& taxiNpcHasRoutesRef() { return taxiNpcHasRoutes_; }
|
|
uint32_t* knownTaxiMaskPtr() { return knownTaxiMask_; }
|
|
bool& taxiMaskInitializedRef() { return taxiMaskInitialized_; }
|
|
uint64_t& taxiNpcGuidRef() { return taxiNpcGuid_; }
|
|
|
|
// Other-player movement timing (for cleanup on despawn etc.)
|
|
std::unordered_map<uint64_t, uint32_t>& otherPlayerMoveTimeMsRef() { return otherPlayerMoveTimeMs_; }
|
|
std::unordered_map<uint64_t, float>& otherPlayerSmoothedIntervalMsRef() { return otherPlayerSmoothedIntervalMs_; }
|
|
|
|
// Methods also called from GameHandler's registerOpcodeHandlers
|
|
void handleCompressedMoves(network::Packet& packet);
|
|
void handleForceMoveFlagChange(network::Packet& packet, const char* name, Opcode ackOpcode, uint32_t flag, bool set);
|
|
void handleMoveSetCollisionHeight(network::Packet& packet);
|
|
void applyTaxiMountForCurrentNode();
|
|
|
|
private:
|
|
// --- Packet handlers ---
|
|
void handleMonsterMove(network::Packet& packet);
|
|
void handleMonsterMoveTransport(network::Packet& packet);
|
|
void handleOtherPlayerMovement(network::Packet& packet);
|
|
void handleMoveSetSpeed(network::Packet& packet);
|
|
void handleForceRunSpeedChange(network::Packet& packet);
|
|
void handleForceSpeedChange(network::Packet& packet, const char* name, Opcode ackOpcode, float* speedStorage);
|
|
void handleForceMoveRootState(network::Packet& packet, bool rooted);
|
|
void handleMoveKnockBack(network::Packet& packet);
|
|
void handleTeleportAck(network::Packet& packet);
|
|
void handleNewWorld(network::Packet& packet);
|
|
void handleShowTaxiNodes(network::Packet& packet);
|
|
void handleClientControlUpdate(network::Packet& packet);
|
|
void handleActivateTaxiReply(network::Packet& packet);
|
|
void loadTaxiDbc();
|
|
|
|
// --- Private helpers ---
|
|
void buildTaxiCostMap();
|
|
void startClientTaxiPath(const std::vector<uint32_t>& pathNodes);
|
|
|
|
friend class GameHandler;
|
|
|
|
GameHandler& owner_;
|
|
|
|
// --- Movement state ---
|
|
// Reference to GameHandler's movementInfo to avoid desync
|
|
MovementInfo& movementInfo;
|
|
std::chrono::steady_clock::time_point movementClockStart_ = std::chrono::steady_clock::now();
|
|
uint32_t lastMovementTimestampMs_ = 0;
|
|
bool serverMovementAllowed_ = true;
|
|
uint32_t monsterMovePacketsThisTick_ = 0;
|
|
uint32_t monsterMovePacketsDroppedThisTick_ = 0;
|
|
|
|
// Fall/jump tracking
|
|
bool isFalling_ = false;
|
|
uint32_t fallStartMs_ = 0;
|
|
|
|
// Heartbeat timing
|
|
float timeSinceLastMoveHeartbeat_ = 0.0f;
|
|
float moveHeartbeatInterval_ = 0.5f;
|
|
uint32_t lastHeartbeatSendTimeMs_ = 0;
|
|
float lastHeartbeatX_ = 0.0f;
|
|
float lastHeartbeatY_ = 0.0f;
|
|
float lastHeartbeatZ_ = 0.0f;
|
|
uint32_t lastHeartbeatFlags_ = 0;
|
|
uint64_t lastHeartbeatTransportGuid_ = 0;
|
|
uint32_t lastNonHeartbeatMoveSendTimeMs_ = 0;
|
|
uint32_t lastFacingSendTimeMs_ = 0;
|
|
float lastFacingSentOrientation_ = 0.0f;
|
|
|
|
// Speed state
|
|
float serverRunSpeed_ = 7.0f;
|
|
float serverWalkSpeed_ = 2.5f;
|
|
float serverRunBackSpeed_ = 4.5f;
|
|
float serverSwimSpeed_ = 4.722f;
|
|
float serverSwimBackSpeed_ = 2.5f;
|
|
float serverFlightSpeed_ = 7.0f;
|
|
float serverFlightBackSpeed_ = 4.5f;
|
|
float serverTurnRate_ = 3.14159f;
|
|
float serverPitchRate_ = 3.14159f;
|
|
|
|
// Other-player movement smoothing
|
|
std::unordered_map<uint64_t, uint32_t> otherPlayerMoveTimeMs_;
|
|
std::unordered_map<uint64_t, float> otherPlayerSmoothedIntervalMs_;
|
|
|
|
// --- Taxi / Flight Path state ---
|
|
std::unordered_map<uint64_t, bool> taxiNpcHasRoutes_;
|
|
std::unordered_map<uint32_t, TaxiNode> taxiNodes_;
|
|
std::vector<TaxiPathEdge> taxiPathEdges_;
|
|
std::unordered_map<uint32_t, std::vector<TaxiPathNode>> taxiPathNodes_;
|
|
bool taxiDbcLoaded_ = false;
|
|
bool taxiWindowOpen_ = false;
|
|
ShowTaxiNodesData currentTaxiData_;
|
|
uint64_t taxiNpcGuid_ = 0;
|
|
bool onTaxiFlight_ = false;
|
|
std::string taxiDestName_;
|
|
bool taxiMountActive_ = false;
|
|
uint32_t taxiMountDisplayId_ = 0;
|
|
bool taxiActivatePending_ = false;
|
|
float taxiActivateTimer_ = 0.0f;
|
|
bool taxiClientActive_ = false;
|
|
float taxiLandingCooldown_ = 0.0f;
|
|
float taxiStartGrace_ = 0.0f;
|
|
size_t taxiClientIndex_ = 0;
|
|
std::vector<glm::vec3> taxiClientPath_;
|
|
float taxiClientSpeed_ = 32.0f;
|
|
float taxiClientSegmentProgress_ = 0.0f;
|
|
bool taxiRecoverPending_ = false;
|
|
uint32_t taxiRecoverMapId_ = 0;
|
|
glm::vec3 taxiRecoverPos_{0.0f};
|
|
uint32_t knownTaxiMask_[12] = {};
|
|
bool taxiMaskInitialized_ = false;
|
|
std::unordered_map<uint32_t, uint32_t> taxiCostMap_;
|
|
};
|
|
|
|
} // namespace game
|
|
} // namespace wowee
|