#pragma once #include "game/world_packets.hpp" #include "game/character.hpp" #include "game/inventory.hpp" #include "game/spell_defines.hpp" #include "game/group_defines.hpp" #include #include #include #include #include #include #include #include #include namespace wowee { namespace network { class WorldSocket; class Packet; } namespace game { /** * World connection state */ enum class WorldState { DISCONNECTED, // Not connected CONNECTING, // TCP connection in progress CONNECTED, // Connected, waiting for challenge CHALLENGE_RECEIVED, // Received SMSG_AUTH_CHALLENGE AUTH_SENT, // Sent CMSG_AUTH_SESSION, encryption initialized AUTHENTICATED, // Received SMSG_AUTH_RESPONSE success READY, // Ready for character/world operations CHAR_LIST_REQUESTED, // CMSG_CHAR_ENUM sent CHAR_LIST_RECEIVED, // SMSG_CHAR_ENUM received ENTERING_WORLD, // CMSG_PLAYER_LOGIN sent IN_WORLD, // In game world FAILED // Connection or authentication failed }; /** * World connection callbacks */ using WorldConnectSuccessCallback = std::function; using WorldConnectFailureCallback = std::function; /** * GameHandler - Manages world server connection and game protocol * * Handles: * - Connection to world server * - Authentication with session key from auth server * - RC4 header encryption * - Character enumeration * - World entry * - Game packets */ class GameHandler { public: GameHandler(); ~GameHandler(); /** * Connect to world server * * @param host World server hostname/IP * @param port World server port (default 8085) * @param sessionKey 40-byte session key from auth server * @param accountName Account name (will be uppercased) * @param build Client build number (default 12340 for 3.3.5a) * @return true if connection initiated */ bool connect(const std::string& host, uint16_t port, const std::vector& sessionKey, const std::string& accountName, uint32_t build = 12340); /** * Disconnect from world server */ void disconnect(); /** * Check if connected to world server */ bool isConnected() const; /** * Get current connection state */ WorldState getState() const { return state; } /** * Request character list from server * Must be called when state is READY or AUTHENTICATED */ void requestCharacterList(); /** * Get list of characters (available after CHAR_LIST_RECEIVED state) */ const std::vector& getCharacters() const { return characters; } void createCharacter(const CharCreateData& data); void deleteCharacter(uint64_t characterGuid); using CharCreateCallback = std::function; void setCharCreateCallback(CharCreateCallback cb) { charCreateCallback_ = std::move(cb); } using CharDeleteCallback = std::function; void setCharDeleteCallback(CharDeleteCallback cb) { charDeleteCallback_ = std::move(cb); } /** * Select and log in with a character * @param characterGuid GUID of character to log in with */ void selectCharacter(uint64_t characterGuid); void setActiveCharacterGuid(uint64_t guid) { activeCharacterGuid_ = guid; } uint64_t getActiveCharacterGuid() const { return activeCharacterGuid_; } const Character* getActiveCharacter() const; const Character* getFirstCharacter() const; /** * Get current player movement info */ const MovementInfo& getMovementInfo() const { return movementInfo; } /** * Send a movement packet * @param opcode Movement opcode (CMSG_MOVE_START_FORWARD, etc.) */ void sendMovement(Opcode opcode); /** * Update player position * @param x X coordinate * @param y Y coordinate * @param z Z coordinate */ void setPosition(float x, float y, float z); /** * Update player orientation * @param orientation Facing direction in radians */ void setOrientation(float orientation); /** * Get entity manager (for accessing entities in view) */ EntityManager& getEntityManager() { return entityManager; } const EntityManager& getEntityManager() const { return entityManager; } /** * Send a chat message * @param type Chat type (SAY, YELL, WHISPER, etc.) * @param message Message text * @param target Target name (for whispers, empty otherwise) */ void sendChatMessage(ChatType type, const std::string& message, const std::string& target = ""); /** * Get chat history (recent messages) * @param maxMessages Maximum number of messages to return (0 = all) * @return Vector of chat messages */ const std::deque& getChatHistory() const { return chatHistory; } /** * Add a locally-generated chat message (e.g., emote feedback) */ void addLocalChatMessage(const MessageChatData& msg); // Money (copper) uint64_t getMoneyCopper() const { return playerMoneyCopper_; } // Single-player: mark character list ready for selection UI void setSinglePlayerCharListReady(); struct SinglePlayerSettings { bool fullscreen = false; bool vsync = true; bool shadows = true; int resWidth = 1920; int resHeight = 1080; int musicVolume = 30; int sfxVolume = 100; float mouseSensitivity = 0.2f; bool invertMouse = false; }; bool getSinglePlayerSettings(SinglePlayerSettings& out) const; void setSinglePlayerSettings(const SinglePlayerSettings& settings); // Inventory Inventory& getInventory() { return inventory; } const Inventory& getInventory() const { return inventory; } bool consumeOnlineEquipmentDirty() { bool d = onlineEquipDirty_; onlineEquipDirty_ = false; return d; } // Targeting void setTarget(uint64_t guid); void clearTarget(); uint64_t getTargetGuid() const { return targetGuid; } std::shared_ptr getTarget() const; bool hasTarget() const { return targetGuid != 0; } void tabTarget(float playerX, float playerY, float playerZ); // ---- Phase 1: Name queries ---- void queryPlayerName(uint64_t guid); void queryCreatureInfo(uint32_t entry, uint64_t guid); std::string getCachedPlayerName(uint64_t guid) const; std::string getCachedCreatureName(uint32_t entry) const; // ---- Phase 2: Combat ---- void startAutoAttack(uint64_t targetGuid); void stopAutoAttack(); bool isAutoAttacking() const { return autoAttacking; } const std::vector& getCombatText() const { return combatText; } void updateCombatText(float deltaTime); // ---- Phase 3: Spells ---- void castSpell(uint32_t spellId, uint64_t targetGuid = 0); void cancelCast(); void cancelAura(uint32_t spellId); const std::vector& getKnownSpells() const { return knownSpells; } bool isCasting() const { return casting; } uint32_t getCurrentCastSpellId() const { return currentCastSpellId; } float getCastProgress() const { return castTimeTotal > 0 ? (castTimeTotal - castTimeRemaining) / castTimeTotal : 0.0f; } float getCastTimeRemaining() const { return castTimeRemaining; } // Action bar static constexpr int ACTION_BAR_SLOTS = 12; std::array& getActionBar() { return actionBar; } const std::array& getActionBar() const { return actionBar; } void setActionBarSlot(int slot, ActionBarSlot::Type type, uint32_t id); // Auras const std::vector& getPlayerAuras() const { return playerAuras; } const std::vector& getTargetAuras() const { return targetAuras; } // Single-player mode void setSinglePlayerMode(bool sp) { singlePlayerMode_ = sp; } bool isSinglePlayerMode() const { return singlePlayerMode_; } void simulateMotd(const std::vector& lines); void applySinglePlayerStartData(Race race, Class cls); bool loadSinglePlayerCharacterState(uint64_t guid); void notifyInventoryChanged(); void notifyEquipmentChanged(); void notifyQuestStateChanged(); void flushSinglePlayerSave(); // NPC death callback (single-player) using NpcDeathCallback = std::function; void setNpcDeathCallback(NpcDeathCallback cb) { npcDeathCallback_ = std::move(cb); } // Melee swing callback (for driving animation/SFX) using MeleeSwingCallback = std::function; void setMeleeSwingCallback(MeleeSwingCallback cb) { meleeSwingCallback_ = std::move(cb); } // NPC swing callback (single-player combat: plays attack animation on NPC) using NpcSwingCallback = std::function; void setNpcSwingCallback(NpcSwingCallback cb) { npcSwingCallback_ = std::move(cb); } // Local player stats (single-player) uint32_t getLocalPlayerHealth() const { return localPlayerHealth_; } uint32_t getLocalPlayerMaxHealth() const { return localPlayerMaxHealth_; } void initLocalPlayerStats(uint32_t level, uint32_t hp, uint32_t maxHp) { localPlayerLevel_ = level; localPlayerHealth_ = hp; localPlayerMaxHealth_ = maxHp; playerNextLevelXp_ = xpForLevel(level); playerXp_ = 0; } // XP tracking (works in both single-player and server modes) uint32_t getPlayerXp() const { return playerXp_; } uint32_t getPlayerNextLevelXp() const { return playerNextLevelXp_; } uint32_t getPlayerLevel() const { return singlePlayerMode_ ? localPlayerLevel_ : serverPlayerLevel_; } // Hearthstone callback (single-player teleport) using HearthstoneCallback = std::function; void setHearthstoneCallback(HearthstoneCallback cb) { hearthstoneCallback = std::move(cb); } // World entry callback (online mode - triggered when entering world) // Parameters: mapId, x, y, z (canonical WoW coordinates) using WorldEntryCallback = std::function; void setWorldEntryCallback(WorldEntryCallback cb) { worldEntryCallback_ = std::move(cb); } // Creature spawn callback (online mode - triggered when creature enters view) // Parameters: guid, displayId, x, y, z (canonical), orientation using CreatureSpawnCallback = std::function; void setCreatureSpawnCallback(CreatureSpawnCallback cb) { creatureSpawnCallback_ = std::move(cb); } // Creature despawn callback (online mode - triggered when creature leaves view) using CreatureDespawnCallback = std::function; void setCreatureDespawnCallback(CreatureDespawnCallback cb) { creatureDespawnCallback_ = std::move(cb); } // Cooldowns float getSpellCooldown(uint32_t spellId) const; // Player GUID uint64_t getPlayerGuid() const { return playerGuid; } void setPlayerGuid(uint64_t guid) { playerGuid = guid; } // ---- Phase 4: Group ---- void inviteToGroup(const std::string& playerName); void acceptGroupInvite(); void declineGroupInvite(); void leaveGroup(); bool isInGroup() const { return !partyData.isEmpty(); } const GroupListData& getPartyData() const { return partyData; } bool hasPendingGroupInvite() const { return pendingGroupInvite; } const std::string& getPendingInviterName() const { return pendingInviterName; } // ---- Phase 5: Loot ---- void lootTarget(uint64_t guid); void lootItem(uint8_t slotIndex); void closeLoot(); bool isLootWindowOpen() const { return lootWindowOpen; } const LootResponseData& getCurrentLoot() const { return currentLoot; } // NPC Gossip void interactWithNpc(uint64_t guid); void selectGossipOption(uint32_t optionId); void selectGossipQuest(uint32_t questId); void acceptQuest(); void declineQuest(); void closeGossip(); bool isGossipWindowOpen() const { return gossipWindowOpen; } const GossipMessageData& getCurrentGossip() const { return currentGossip; } bool isQuestDetailsOpen() const { return questDetailsOpen; } const QuestDetailsData& getQuestDetails() const { return currentQuestDetails; } // Vendor void openVendor(uint64_t npcGuid); void closeVendor(); void buyItem(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint8_t count); void sellItem(uint64_t vendorGuid, uint64_t itemGuid, uint8_t count); bool isVendorWindowOpen() const { return vendorWindowOpen; } const ListInventoryData& getVendorItems() const { return currentVendorItems; } const ItemQueryResponseData* getItemInfo(uint32_t itemId) const { auto it = itemInfoCache_.find(itemId); return (it != itemInfoCache_.end()) ? &it->second : nullptr; } /** * Set callbacks */ void setOnSuccess(WorldConnectSuccessCallback callback) { onSuccess = callback; } void setOnFailure(WorldConnectFailureCallback callback) { onFailure = callback; } /** * Update - call regularly (e.g., each frame) * * @param deltaTime Time since last update in seconds */ void update(float deltaTime); struct SinglePlayerCreateInfo { uint32_t mapId = 0; uint32_t zoneId = 0; float x = 0.0f; float y = 0.0f; float z = 0.0f; float orientation = 0.0f; }; bool getSinglePlayerCreateInfo(Race race, Class cls, SinglePlayerCreateInfo& out) const; private: /** * Handle incoming packet from world server */ void handlePacket(network::Packet& packet); /** * Handle SMSG_AUTH_CHALLENGE from server */ void handleAuthChallenge(network::Packet& packet); /** * Handle SMSG_AUTH_RESPONSE from server */ void handleAuthResponse(network::Packet& packet); /** * Handle SMSG_CHAR_ENUM from server */ void handleCharEnum(network::Packet& packet); /** * Handle SMSG_LOGIN_VERIFY_WORLD from server */ void handleLoginVerifyWorld(network::Packet& packet); /** * Handle SMSG_ACCOUNT_DATA_TIMES from server */ void handleAccountDataTimes(network::Packet& packet); /** * Handle SMSG_MOTD from server */ void handleMotd(network::Packet& packet); /** * Handle SMSG_PONG from server */ void handlePong(network::Packet& packet); /** * Handle SMSG_UPDATE_OBJECT from server */ void handleUpdateObject(network::Packet& packet); /** * Handle SMSG_COMPRESSED_UPDATE_OBJECT from server */ void handleCompressedUpdateObject(network::Packet& packet); /** * Handle SMSG_DESTROY_OBJECT from server */ void handleDestroyObject(network::Packet& packet); /** * Handle SMSG_MESSAGECHAT from server */ void handleMessageChat(network::Packet& packet); // ---- Phase 1 handlers ---- void handleNameQueryResponse(network::Packet& packet); void handleCreatureQueryResponse(network::Packet& packet); void handleItemQueryResponse(network::Packet& packet); void queryItemInfo(uint32_t entry, uint64_t guid); void rebuildOnlineInventory(); // ---- Phase 2 handlers ---- void handleAttackStart(network::Packet& packet); void handleAttackStop(network::Packet& packet); void handleAttackerStateUpdate(network::Packet& packet); void handleSpellDamageLog(network::Packet& packet); void handleSpellHealLog(network::Packet& packet); // ---- Phase 3 handlers ---- void handleInitialSpells(network::Packet& packet); void handleCastFailed(network::Packet& packet); void handleSpellStart(network::Packet& packet); void handleSpellGo(network::Packet& packet); void handleSpellCooldown(network::Packet& packet); void handleCooldownEvent(network::Packet& packet); void handleAuraUpdate(network::Packet& packet, bool isAll); void handleLearnedSpell(network::Packet& packet); void handleRemovedSpell(network::Packet& packet); // ---- Phase 4 handlers ---- void handleGroupInvite(network::Packet& packet); void handleGroupDecline(network::Packet& packet); void handleGroupList(network::Packet& packet); void handleGroupUninvite(network::Packet& packet); void handlePartyCommandResult(network::Packet& packet); // ---- Character creation handler ---- void handleCharCreateResponse(network::Packet& packet); // ---- XP handler ---- void handleXpGain(network::Packet& packet); // ---- Phase 5 handlers ---- void handleLootResponse(network::Packet& packet); void handleLootReleaseResponse(network::Packet& packet); void handleLootRemoved(network::Packet& packet); void handleGossipMessage(network::Packet& packet); void handleGossipComplete(network::Packet& packet); void handleQuestDetails(network::Packet& packet); void handleListInventory(network::Packet& packet); LootResponseData generateLocalLoot(uint64_t guid); void simulateLootResponse(const LootResponseData& data); void simulateLootRelease(); void simulateLootRemove(uint8_t slotIndex); void simulateXpGain(uint64_t victimGuid, uint32_t totalXp); void addMoneyCopper(uint32_t amount); void addCombatText(CombatTextEntry::Type type, int32_t amount, uint32_t spellId, bool isPlayerSource); void addSystemChatMessage(const std::string& message); /** * Send CMSG_PING to server (heartbeat) */ void sendPing(); /** * Send CMSG_AUTH_SESSION to server */ void sendAuthSession(); /** * Generate random client seed */ uint32_t generateClientSeed(); /** * Change state with logging */ void setState(WorldState newState); /** * Fail connection with reason */ void fail(const std::string& reason); // Network std::unique_ptr socket; // State WorldState state = WorldState::DISCONNECTED; // Authentication data std::vector sessionKey; // 40-byte session key from auth server std::string accountName; // Account name uint32_t build = 12340; // Client build (3.3.5a) uint32_t clientSeed = 0; // Random seed generated by client uint32_t serverSeed = 0; // Seed from SMSG_AUTH_CHALLENGE // Characters std::vector characters; // Character list from SMSG_CHAR_ENUM // Movement MovementInfo movementInfo; // Current player movement state uint32_t movementTime = 0; // Movement timestamp counter // Inventory Inventory inventory; // Entity tracking EntityManager entityManager; // Manages all entities in view // Chat std::deque chatHistory; // Recent chat messages size_t maxChatHistory = 100; // Maximum chat messages to keep // Targeting uint64_t targetGuid = 0; std::vector tabCycleList; int tabCycleIndex = -1; bool tabCycleStale = true; // Heartbeat uint32_t pingSequence = 0; // Ping sequence number (increments) float timeSinceLastPing = 0.0f; // Time since last ping sent (seconds) float pingInterval = 30.0f; // Ping interval (30 seconds) uint32_t lastLatency = 0; // Last measured latency (milliseconds) // Player GUID uint64_t playerGuid = 0; // ---- Phase 1: Name caches ---- std::unordered_map playerNameCache; std::unordered_set pendingNameQueries; std::unordered_map creatureInfoCache; std::unordered_set pendingCreatureQueries; // ---- Online item tracking ---- struct OnlineItemInfo { uint32_t entry = 0; uint32_t stackCount = 1; }; std::unordered_map onlineItems_; std::unordered_map itemInfoCache_; std::unordered_set pendingItemQueries_; std::array equipSlotGuids_{}; std::array backpackSlotGuids_{}; bool onlineEquipDirty_ = false; // ---- Phase 2: Combat ---- bool autoAttacking = false; uint64_t autoAttackTarget = 0; std::vector combatText; // ---- Phase 3: Spells ---- HearthstoneCallback hearthstoneCallback; WorldEntryCallback worldEntryCallback_; CreatureSpawnCallback creatureSpawnCallback_; CreatureDespawnCallback creatureDespawnCallback_; std::vector knownSpells; std::unordered_map spellCooldowns; // spellId -> remaining seconds uint8_t castCount = 0; bool casting = false; uint32_t currentCastSpellId = 0; float castTimeRemaining = 0.0f; float castTimeTotal = 0.0f; std::array actionBar{}; std::vector playerAuras; std::vector targetAuras; // ---- Phase 4: Group ---- GroupListData partyData; bool pendingGroupInvite = false; std::string pendingInviterName; uint64_t activeCharacterGuid_ = 0; // ---- Phase 5: Loot ---- bool lootWindowOpen = false; LootResponseData currentLoot; struct LocalLootState { LootResponseData data; bool moneyTaken = false; }; std::unordered_map localLootState_; uint64_t playerMoneyCopper_ = 0; // Gossip bool gossipWindowOpen = false; GossipMessageData currentGossip; // Quest details bool questDetailsOpen = false; QuestDetailsData currentQuestDetails; // Vendor bool vendorWindowOpen = false; ListInventoryData currentVendorItems; // Callbacks WorldConnectSuccessCallback onSuccess; WorldConnectFailureCallback onFailure; CharCreateCallback charCreateCallback_; CharDeleteCallback charDeleteCallback_; bool pendingCharCreateResult_ = false; bool pendingCharCreateSuccess_ = false; std::string pendingCharCreateMsg_; // ---- XP tracking ---- uint32_t playerXp_ = 0; uint32_t playerNextLevelXp_ = 0; uint32_t serverPlayerLevel_ = 1; void awardLocalXp(uint64_t victimGuid, uint32_t victimLevel); void levelUp(); static uint32_t xpForLevel(uint32_t level); static uint32_t killXp(uint32_t playerLevel, uint32_t victimLevel); // ---- Single-player combat ---- bool singlePlayerMode_ = false; float swingTimer_ = 0.0f; static constexpr float SWING_SPEED = 2.0f; NpcDeathCallback npcDeathCallback_; MeleeSwingCallback meleeSwingCallback_; NpcSwingCallback npcSwingCallback_; uint32_t localPlayerHealth_ = 0; uint32_t localPlayerMaxHealth_ = 0; uint32_t localPlayerLevel_ = 1; struct NpcAggroEntry { uint64_t guid; float swingTimer; }; std::vector aggroList_; void updateLocalCombat(float deltaTime); void updateNpcAggro(float deltaTime); void performPlayerSwing(); void performNpcSwing(uint64_t guid); void handleNpcDeath(uint64_t guid); void aggroNpc(uint64_t guid); bool isNpcAggroed(uint64_t guid) const; // ---- Single-player persistence ---- enum SinglePlayerDirty : uint32_t { SP_DIRTY_NONE = 0, SP_DIRTY_CHAR = 1 << 0, SP_DIRTY_INVENTORY = 1 << 1, SP_DIRTY_SPELLS = 1 << 2, SP_DIRTY_ACTIONBAR = 1 << 3, SP_DIRTY_AURAS = 1 << 4, SP_DIRTY_QUESTS = 1 << 5, SP_DIRTY_MONEY = 1 << 6, SP_DIRTY_XP = 1 << 7, SP_DIRTY_POSITION = 1 << 8, SP_DIRTY_STATS = 1 << 9, SP_DIRTY_SETTINGS = 1 << 10, SP_DIRTY_ALL = 0xFFFFFFFFu }; void markSinglePlayerDirty(uint32_t flags, bool highPriority); void loadSinglePlayerCharacters(); void saveSinglePlayerCharacterState(bool force); uint32_t spDirtyFlags_ = SP_DIRTY_NONE; bool spDirtyHighPriority_ = false; float spDirtyTimer_ = 0.0f; float spPeriodicTimer_ = 0.0f; float spLastDirtyX_ = 0.0f; float spLastDirtyY_ = 0.0f; float spLastDirtyZ_ = 0.0f; float spLastDirtyOrientation_ = 0.0f; std::unordered_map spHasState_; std::unordered_map spSavedOrientation_; SinglePlayerSettings spSettings_{}; bool spSettingsLoaded_ = false; }; } // namespace game } // namespace wowee