mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 09:33:51 +00: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/.
This commit is contained in:
parent
3762dceaa6
commit
b2710258dc
21 changed files with 20771 additions and 17858 deletions
272
include/game/movement_handler.hpp
Normal file
272
include/game/movement_handler.hpp
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
#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
|
||||
Loading…
Add table
Add a link
Reference in a new issue