mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-02 15:53:51 +00:00
Initial commit: wowee native WoW 3.3.5a client
This commit is contained in:
commit
ce6cb8f38e
147 changed files with 32347 additions and 0 deletions
129
include/game/character.hpp
Normal file
129
include/game/character.hpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* Race IDs (WoW 3.3.5a)
|
||||
*/
|
||||
enum class Race : uint8_t {
|
||||
HUMAN = 1,
|
||||
ORC = 2,
|
||||
DWARF = 3,
|
||||
NIGHT_ELF = 4,
|
||||
UNDEAD = 5,
|
||||
TAUREN = 6,
|
||||
GNOME = 7,
|
||||
TROLL = 8,
|
||||
GOBLIN = 9,
|
||||
BLOOD_ELF = 10,
|
||||
DRAENEI = 11
|
||||
};
|
||||
|
||||
/**
|
||||
* Class IDs (WoW 3.3.5a)
|
||||
*/
|
||||
enum class Class : uint8_t {
|
||||
WARRIOR = 1,
|
||||
PALADIN = 2,
|
||||
HUNTER = 3,
|
||||
ROGUE = 4,
|
||||
PRIEST = 5,
|
||||
DEATH_KNIGHT = 6,
|
||||
SHAMAN = 7,
|
||||
MAGE = 8,
|
||||
WARLOCK = 9,
|
||||
DRUID = 11
|
||||
};
|
||||
|
||||
/**
|
||||
* Gender IDs
|
||||
*/
|
||||
enum class Gender : uint8_t {
|
||||
MALE = 0,
|
||||
FEMALE = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Equipment item data
|
||||
*/
|
||||
struct EquipmentItem {
|
||||
uint32_t displayModel; // Display model ID
|
||||
uint8_t inventoryType; // Inventory slot type
|
||||
uint32_t enchantment; // Enchantment/effect ID
|
||||
|
||||
bool isEmpty() const { return displayModel == 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Pet data (optional)
|
||||
*/
|
||||
struct PetData {
|
||||
uint32_t displayModel; // Pet display model ID
|
||||
uint32_t level; // Pet level
|
||||
uint32_t family; // Pet family ID
|
||||
|
||||
bool exists() const { return displayModel != 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Complete character data from SMSG_CHAR_ENUM
|
||||
*/
|
||||
struct Character {
|
||||
// Identity
|
||||
uint64_t guid; // Character GUID (unique identifier)
|
||||
std::string name; // Character name
|
||||
|
||||
// Basics
|
||||
Race race; // Character race
|
||||
Class characterClass; // Character class (renamed from 'class' keyword)
|
||||
Gender gender; // Character gender
|
||||
uint8_t level; // Character level (1-80)
|
||||
|
||||
// Appearance
|
||||
uint32_t appearanceBytes; // Custom appearance (skin, hair color, hair style, face)
|
||||
uint8_t facialFeatures; // Facial features
|
||||
|
||||
// Location
|
||||
uint32_t zoneId; // Current zone ID
|
||||
uint32_t mapId; // Current map ID
|
||||
float x; // X coordinate
|
||||
float y; // Y coordinate
|
||||
float z; // Z coordinate
|
||||
|
||||
// Affiliations
|
||||
uint32_t guildId; // Guild ID (0 if no guild)
|
||||
|
||||
// State
|
||||
uint32_t flags; // Character flags (PvP, dead, etc.)
|
||||
|
||||
// Optional data
|
||||
PetData pet; // Pet information (if exists)
|
||||
std::vector<EquipmentItem> equipment; // Equipment (23 slots)
|
||||
|
||||
// Helper methods
|
||||
bool hasGuild() const { return guildId != 0; }
|
||||
bool hasPet() const { return pet.exists(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Get human-readable race name
|
||||
*/
|
||||
const char* getRaceName(Race race);
|
||||
|
||||
/**
|
||||
* Get human-readable class name
|
||||
*/
|
||||
const char* getClassName(Class characterClass);
|
||||
|
||||
/**
|
||||
* Get human-readable gender name
|
||||
*/
|
||||
const char* getGenderName(Gender gender);
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
211
include/game/entity.hpp
Normal file
211
include/game/entity.hpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* Object type IDs for WoW 3.3.5a
|
||||
*/
|
||||
enum class ObjectType : uint8_t {
|
||||
OBJECT = 0,
|
||||
ITEM = 1,
|
||||
CONTAINER = 2,
|
||||
UNIT = 3,
|
||||
PLAYER = 4,
|
||||
GAMEOBJECT = 5,
|
||||
DYNAMICOBJECT = 6,
|
||||
CORPSE = 7
|
||||
};
|
||||
|
||||
/**
|
||||
* Object type masks for update packets
|
||||
*/
|
||||
enum class TypeMask : uint16_t {
|
||||
OBJECT = 0x0001,
|
||||
ITEM = 0x0002,
|
||||
CONTAINER = 0x0004,
|
||||
UNIT = 0x0008,
|
||||
PLAYER = 0x0010,
|
||||
GAMEOBJECT = 0x0020,
|
||||
DYNAMICOBJECT = 0x0040,
|
||||
CORPSE = 0x0080
|
||||
};
|
||||
|
||||
/**
|
||||
* Update types for SMSG_UPDATE_OBJECT
|
||||
*/
|
||||
enum class UpdateType : uint8_t {
|
||||
VALUES = 0, // Partial update (changed fields only)
|
||||
MOVEMENT = 1, // Movement update
|
||||
CREATE_OBJECT = 2, // Create new object (full data)
|
||||
CREATE_OBJECT2 = 3, // Create new object (alternate format)
|
||||
OUT_OF_RANGE_OBJECTS = 4, // Objects left view range
|
||||
NEAR_OBJECTS = 5 // Objects entered view range
|
||||
};
|
||||
|
||||
/**
|
||||
* Base entity class for all game objects
|
||||
*/
|
||||
class Entity {
|
||||
public:
|
||||
Entity() = default;
|
||||
explicit Entity(uint64_t guid) : guid(guid) {}
|
||||
virtual ~Entity() = default;
|
||||
|
||||
// GUID access
|
||||
uint64_t getGuid() const { return guid; }
|
||||
void setGuid(uint64_t g) { guid = g; }
|
||||
|
||||
// Position
|
||||
float getX() const { return x; }
|
||||
float getY() const { return y; }
|
||||
float getZ() const { return z; }
|
||||
float getOrientation() const { return orientation; }
|
||||
|
||||
void setPosition(float px, float py, float pz, float o) {
|
||||
x = px;
|
||||
y = py;
|
||||
z = pz;
|
||||
orientation = o;
|
||||
}
|
||||
|
||||
// Object type
|
||||
ObjectType getType() const { return type; }
|
||||
void setType(ObjectType t) { type = t; }
|
||||
|
||||
// Fields (for update values)
|
||||
void setField(uint16_t index, uint32_t value) {
|
||||
fields[index] = value;
|
||||
}
|
||||
|
||||
uint32_t getField(uint16_t index) const {
|
||||
auto it = fields.find(index);
|
||||
return (it != fields.end()) ? it->second : 0;
|
||||
}
|
||||
|
||||
bool hasField(uint16_t index) const {
|
||||
return fields.find(index) != fields.end();
|
||||
}
|
||||
|
||||
const std::map<uint16_t, uint32_t>& getFields() const {
|
||||
return fields;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint64_t guid = 0;
|
||||
ObjectType type = ObjectType::OBJECT;
|
||||
|
||||
// Position
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float z = 0.0f;
|
||||
float orientation = 0.0f;
|
||||
|
||||
// Update fields (dynamic values)
|
||||
std::map<uint16_t, uint32_t> fields;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unit entity (NPCs, creatures, players)
|
||||
*/
|
||||
class Unit : public Entity {
|
||||
public:
|
||||
Unit() { type = ObjectType::UNIT; }
|
||||
explicit Unit(uint64_t guid) : Entity(guid) { type = ObjectType::UNIT; }
|
||||
|
||||
// Name
|
||||
const std::string& getName() const { return name; }
|
||||
void setName(const std::string& n) { name = n; }
|
||||
|
||||
// Health
|
||||
uint32_t getHealth() const { return health; }
|
||||
void setHealth(uint32_t h) { health = h; }
|
||||
|
||||
uint32_t getMaxHealth() const { return maxHealth; }
|
||||
void setMaxHealth(uint32_t h) { maxHealth = h; }
|
||||
|
||||
// Level
|
||||
uint32_t getLevel() const { return level; }
|
||||
void setLevel(uint32_t l) { level = l; }
|
||||
|
||||
protected:
|
||||
std::string name;
|
||||
uint32_t health = 0;
|
||||
uint32_t maxHealth = 0;
|
||||
uint32_t level = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Player entity
|
||||
*/
|
||||
class Player : public Unit {
|
||||
public:
|
||||
Player() { type = ObjectType::PLAYER; }
|
||||
explicit Player(uint64_t guid) : Unit(guid) { type = ObjectType::PLAYER; }
|
||||
|
||||
// Name
|
||||
const std::string& getName() const { return name; }
|
||||
void setName(const std::string& n) { name = n; }
|
||||
|
||||
protected:
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/**
|
||||
* GameObject entity (doors, chests, etc.)
|
||||
*/
|
||||
class GameObject : public Entity {
|
||||
public:
|
||||
GameObject() { type = ObjectType::GAMEOBJECT; }
|
||||
explicit GameObject(uint64_t guid) : Entity(guid) { type = ObjectType::GAMEOBJECT; }
|
||||
|
||||
uint32_t getDisplayId() const { return displayId; }
|
||||
void setDisplayId(uint32_t id) { displayId = id; }
|
||||
|
||||
protected:
|
||||
uint32_t displayId = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Entity manager for tracking all entities in view
|
||||
*/
|
||||
class EntityManager {
|
||||
public:
|
||||
// Add entity
|
||||
void addEntity(uint64_t guid, std::shared_ptr<Entity> entity);
|
||||
|
||||
// Remove entity
|
||||
void removeEntity(uint64_t guid);
|
||||
|
||||
// Get entity
|
||||
std::shared_ptr<Entity> getEntity(uint64_t guid) const;
|
||||
|
||||
// Check if entity exists
|
||||
bool hasEntity(uint64_t guid) const;
|
||||
|
||||
// Get all entities
|
||||
const std::map<uint64_t, std::shared_ptr<Entity>>& getEntities() const {
|
||||
return entities;
|
||||
}
|
||||
|
||||
// Clear all entities
|
||||
void clear() {
|
||||
entities.clear();
|
||||
}
|
||||
|
||||
// Get entity count
|
||||
size_t getEntityCount() const {
|
||||
return entities.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<uint64_t, std::shared_ptr<Entity>> entities;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
310
include/game/game_handler.hpp
Normal file
310
include/game/game_handler.hpp
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
#pragma once
|
||||
|
||||
#include "game/world_packets.hpp"
|
||||
#include "game/character.hpp"
|
||||
#include "game/inventory.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
|
||||
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<void()>;
|
||||
using WorldConnectFailureCallback = std::function<void(const std::string& reason)>;
|
||||
|
||||
/**
|
||||
* 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<uint8_t>& 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<Character>& getCharacters() const { return characters; }
|
||||
|
||||
/**
|
||||
* Select and log in with a character
|
||||
* @param characterGuid GUID of character to log in with
|
||||
*/
|
||||
void selectCharacter(uint64_t characterGuid);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
std::vector<MessageChatData> getChatHistory(size_t maxMessages = 50) const;
|
||||
|
||||
/**
|
||||
* Add a locally-generated chat message (e.g., emote feedback)
|
||||
*/
|
||||
void addLocalChatMessage(const MessageChatData& msg);
|
||||
|
||||
// Inventory
|
||||
Inventory& getInventory() { return inventory; }
|
||||
const Inventory& getInventory() const { return inventory; }
|
||||
|
||||
// Targeting
|
||||
void setTarget(uint64_t guid);
|
||||
void clearTarget();
|
||||
uint64_t getTargetGuid() const { return targetGuid; }
|
||||
std::shared_ptr<Entity> getTarget() const;
|
||||
bool hasTarget() const { return targetGuid != 0; }
|
||||
void tabTarget(float playerX, float playerY, float playerZ);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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_DESTROY_OBJECT from server
|
||||
*/
|
||||
void handleDestroyObject(network::Packet& packet);
|
||||
|
||||
/**
|
||||
* Handle SMSG_MESSAGECHAT from server
|
||||
*/
|
||||
void handleMessageChat(network::Packet& packet);
|
||||
|
||||
/**
|
||||
* 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<network::WorldSocket> socket;
|
||||
|
||||
// State
|
||||
WorldState state = WorldState::DISCONNECTED;
|
||||
|
||||
// Authentication data
|
||||
std::vector<uint8_t> 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<Character> 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::vector<MessageChatData> chatHistory; // Recent chat messages
|
||||
size_t maxChatHistory = 100; // Maximum chat messages to keep
|
||||
|
||||
// Targeting
|
||||
uint64_t targetGuid = 0;
|
||||
std::vector<uint64_t> 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)
|
||||
|
||||
// Callbacks
|
||||
WorldConnectSuccessCallback onSuccess;
|
||||
WorldConnectFailureCallback onFailure;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
99
include/game/inventory.hpp
Normal file
99
include/game/inventory.hpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
enum class ItemQuality : uint8_t {
|
||||
POOR = 0, // Grey
|
||||
COMMON = 1, // White
|
||||
UNCOMMON = 2, // Green
|
||||
RARE = 3, // Blue
|
||||
EPIC = 4, // Purple
|
||||
LEGENDARY = 5, // Orange
|
||||
};
|
||||
|
||||
enum class EquipSlot : uint8_t {
|
||||
HEAD = 0, NECK, SHOULDERS, SHIRT, CHEST,
|
||||
WAIST, LEGS, FEET, WRISTS, HANDS,
|
||||
RING1, RING2, TRINKET1, TRINKET2,
|
||||
BACK, MAIN_HAND, OFF_HAND, RANGED, TABARD,
|
||||
BAG1, BAG2, BAG3, BAG4,
|
||||
NUM_SLOTS // = 23
|
||||
};
|
||||
|
||||
struct ItemDef {
|
||||
uint32_t itemId = 0;
|
||||
std::string name;
|
||||
std::string subclassName; // "Sword", "Mace", "Shield", etc.
|
||||
ItemQuality quality = ItemQuality::COMMON;
|
||||
uint8_t inventoryType = 0;
|
||||
uint32_t stackCount = 1;
|
||||
uint32_t maxStack = 1;
|
||||
uint32_t bagSlots = 0;
|
||||
// Stats
|
||||
int32_t armor = 0;
|
||||
int32_t stamina = 0;
|
||||
int32_t strength = 0;
|
||||
int32_t agility = 0;
|
||||
int32_t intellect = 0;
|
||||
int32_t spirit = 0;
|
||||
uint32_t displayInfoId = 0;
|
||||
};
|
||||
|
||||
struct ItemSlot {
|
||||
ItemDef item;
|
||||
bool empty() const { return item.itemId == 0; }
|
||||
};
|
||||
|
||||
class Inventory {
|
||||
public:
|
||||
static constexpr int BACKPACK_SLOTS = 16;
|
||||
static constexpr int NUM_EQUIP_SLOTS = 23;
|
||||
static constexpr int NUM_BAG_SLOTS = 4;
|
||||
static constexpr int MAX_BAG_SIZE = 36;
|
||||
|
||||
Inventory();
|
||||
|
||||
// Backpack
|
||||
const ItemSlot& getBackpackSlot(int index) const;
|
||||
bool setBackpackSlot(int index, const ItemDef& item);
|
||||
bool clearBackpackSlot(int index);
|
||||
int getBackpackSize() const { return BACKPACK_SLOTS; }
|
||||
|
||||
// Equipment
|
||||
const ItemSlot& getEquipSlot(EquipSlot slot) const;
|
||||
bool setEquipSlot(EquipSlot slot, const ItemDef& item);
|
||||
bool clearEquipSlot(EquipSlot slot);
|
||||
|
||||
// Extra bags
|
||||
int getBagSize(int bagIndex) const;
|
||||
const ItemSlot& getBagSlot(int bagIndex, int slotIndex) const;
|
||||
bool setBagSlot(int bagIndex, int slotIndex, const ItemDef& item);
|
||||
|
||||
// Utility
|
||||
int findFreeBackpackSlot() const;
|
||||
bool addItem(const ItemDef& item);
|
||||
|
||||
// Test data
|
||||
void populateTestItems();
|
||||
|
||||
private:
|
||||
std::array<ItemSlot, BACKPACK_SLOTS> backpack{};
|
||||
std::array<ItemSlot, NUM_EQUIP_SLOTS> equipment{};
|
||||
|
||||
struct BagData {
|
||||
int size = 0;
|
||||
std::array<ItemSlot, MAX_BAG_SIZE> slots{};
|
||||
};
|
||||
std::array<BagData, NUM_BAG_SLOTS> bags{};
|
||||
};
|
||||
|
||||
const char* getQualityName(ItemQuality quality);
|
||||
const char* getEquipSlotName(EquipSlot slot);
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
57
include/game/npc_manager.hpp
Normal file
57
include/game/npc_manager.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering { class CharacterRenderer; }
|
||||
namespace game {
|
||||
|
||||
class EntityManager;
|
||||
|
||||
struct NpcSpawnDef {
|
||||
std::string name;
|
||||
std::string m2Path;
|
||||
uint32_t level;
|
||||
uint32_t health;
|
||||
glm::vec3 glPosition; // GL world coords (pre-converted)
|
||||
float rotation; // radians around Z
|
||||
float scale;
|
||||
bool isCritter; // critters don't do humanoid emotes
|
||||
};
|
||||
|
||||
struct NpcInstance {
|
||||
uint64_t guid;
|
||||
uint32_t renderInstanceId;
|
||||
float emoteTimer; // countdown to next random emote
|
||||
float emoteEndTimer; // countdown until emote animation finishes
|
||||
bool isEmoting;
|
||||
bool isCritter;
|
||||
};
|
||||
|
||||
class NpcManager {
|
||||
public:
|
||||
void initialize(pipeline::AssetManager* am,
|
||||
rendering::CharacterRenderer* cr,
|
||||
EntityManager& em,
|
||||
const glm::vec3& playerSpawnGL);
|
||||
void update(float deltaTime, rendering::CharacterRenderer* cr);
|
||||
|
||||
private:
|
||||
void loadCreatureModel(pipeline::AssetManager* am,
|
||||
rendering::CharacterRenderer* cr,
|
||||
const std::string& m2Path,
|
||||
uint32_t modelId);
|
||||
|
||||
std::vector<NpcInstance> npcs;
|
||||
std::unordered_map<std::string, uint32_t> loadedModels; // path -> modelId
|
||||
uint64_t nextGuid = 0xF1300000DEAD0001ULL;
|
||||
uint32_t nextModelId = 100;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
55
include/game/opcodes.hpp
Normal file
55
include/game/opcodes.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
// World of Warcraft 3.3.5a opcodes
|
||||
enum class Opcode : uint16_t {
|
||||
// Client to Server
|
||||
CMSG_PING = 0x1DC,
|
||||
CMSG_AUTH_SESSION = 0x1ED,
|
||||
CMSG_CHAR_ENUM = 0x037,
|
||||
CMSG_PLAYER_LOGIN = 0x03D,
|
||||
|
||||
// Movement
|
||||
CMSG_MOVE_START_FORWARD = 0x0B5,
|
||||
CMSG_MOVE_START_BACKWARD = 0x0B6,
|
||||
CMSG_MOVE_STOP = 0x0B7,
|
||||
CMSG_MOVE_START_STRAFE_LEFT = 0x0B8,
|
||||
CMSG_MOVE_START_STRAFE_RIGHT = 0x0B9,
|
||||
CMSG_MOVE_STOP_STRAFE = 0x0BA,
|
||||
CMSG_MOVE_JUMP = 0x0BB,
|
||||
CMSG_MOVE_START_TURN_LEFT = 0x0BC,
|
||||
CMSG_MOVE_START_TURN_RIGHT = 0x0BD,
|
||||
CMSG_MOVE_STOP_TURN = 0x0BE,
|
||||
CMSG_MOVE_SET_FACING = 0x0DA,
|
||||
CMSG_MOVE_FALL_LAND = 0x0C9,
|
||||
CMSG_MOVE_START_SWIM = 0x0CA,
|
||||
CMSG_MOVE_STOP_SWIM = 0x0CB,
|
||||
CMSG_MOVE_HEARTBEAT = 0x0EE,
|
||||
|
||||
// Server to Client
|
||||
SMSG_AUTH_CHALLENGE = 0x1EC,
|
||||
SMSG_AUTH_RESPONSE = 0x1EE,
|
||||
SMSG_CHAR_ENUM = 0x03B,
|
||||
SMSG_PONG = 0x1DD,
|
||||
SMSG_LOGIN_VERIFY_WORLD = 0x236,
|
||||
SMSG_ACCOUNT_DATA_TIMES = 0x209,
|
||||
SMSG_FEATURE_SYSTEM_STATUS = 0x3ED,
|
||||
SMSG_MOTD = 0x33D,
|
||||
|
||||
// Entity/Object updates
|
||||
SMSG_UPDATE_OBJECT = 0x0A9,
|
||||
SMSG_DESTROY_OBJECT = 0x0AA,
|
||||
|
||||
// Chat
|
||||
CMSG_MESSAGECHAT = 0x095,
|
||||
SMSG_MESSAGECHAT = 0x096,
|
||||
|
||||
// TODO: Add more opcodes as needed
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
18
include/game/player.hpp
Normal file
18
include/game/player.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
class Player {
|
||||
public:
|
||||
void setPosition(const glm::vec3& pos) { position = pos; }
|
||||
const glm::vec3& getPosition() const { return position; }
|
||||
|
||||
private:
|
||||
glm::vec3 position = glm::vec3(0.0f);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
18
include/game/world.hpp
Normal file
18
include/game/world.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
class World {
|
||||
public:
|
||||
World() = default;
|
||||
~World() = default;
|
||||
|
||||
void update(float deltaTime);
|
||||
void loadMap(uint32_t mapId);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
566
include/game/world_packets.hpp
Normal file
566
include/game/world_packets.hpp
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
#pragma once
|
||||
|
||||
#include "network/packet.hpp"
|
||||
#include "game/opcodes.hpp"
|
||||
#include "game/entity.hpp"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* SMSG_AUTH_CHALLENGE data (from server)
|
||||
*
|
||||
* Sent by world server immediately after TCP connect
|
||||
* Contains server seed/salt for authentication hash
|
||||
*/
|
||||
struct AuthChallengeData {
|
||||
uint32_t unknown1; // Always seems to be 0x00000001
|
||||
uint32_t serverSeed; // Random seed from server
|
||||
// Note: 3.3.5a has additional data after this
|
||||
|
||||
bool isValid() const { return unknown1 != 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* CMSG_AUTH_SESSION packet builder
|
||||
*
|
||||
* Client authentication to world server using session key from auth server
|
||||
*/
|
||||
class AuthSessionPacket {
|
||||
public:
|
||||
/**
|
||||
* Build CMSG_AUTH_SESSION packet
|
||||
*
|
||||
* @param build Client build number (12340 for 3.3.5a)
|
||||
* @param accountName Account name (uppercase)
|
||||
* @param clientSeed Random 4-byte seed generated by client
|
||||
* @param sessionKey 40-byte session key from auth server
|
||||
* @param serverSeed 4-byte seed from SMSG_AUTH_CHALLENGE
|
||||
* @return Packet ready to send
|
||||
*/
|
||||
static network::Packet build(uint32_t build,
|
||||
const std::string& accountName,
|
||||
uint32_t clientSeed,
|
||||
const std::vector<uint8_t>& sessionKey,
|
||||
uint32_t serverSeed);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Compute authentication hash
|
||||
*
|
||||
* SHA1(account + [0,0,0,0] + clientSeed + serverSeed + sessionKey)
|
||||
*
|
||||
* @param accountName Account name
|
||||
* @param clientSeed Client seed
|
||||
* @param serverSeed Server seed
|
||||
* @param sessionKey 40-byte session key
|
||||
* @return 20-byte SHA1 hash
|
||||
*/
|
||||
static std::vector<uint8_t> computeAuthHash(
|
||||
const std::string& accountName,
|
||||
uint32_t clientSeed,
|
||||
uint32_t serverSeed,
|
||||
const std::vector<uint8_t>& sessionKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_AUTH_CHALLENGE response parser
|
||||
*/
|
||||
class AuthChallengeParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, AuthChallengeData& data);
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_AUTH_RESPONSE result codes
|
||||
*/
|
||||
enum class AuthResult : uint8_t {
|
||||
OK = 0x00, // Success, proceed to character screen
|
||||
FAILED = 0x01, // Generic failure
|
||||
REJECT = 0x02, // Reject
|
||||
BAD_SERVER_PROOF = 0x03, // Bad server proof
|
||||
UNAVAILABLE = 0x04, // Unavailable
|
||||
SYSTEM_ERROR = 0x05, // System error
|
||||
BILLING_ERROR = 0x06, // Billing error
|
||||
BILLING_EXPIRED = 0x07, // Billing expired
|
||||
VERSION_MISMATCH = 0x08, // Version mismatch
|
||||
UNKNOWN_ACCOUNT = 0x09, // Unknown account
|
||||
INCORRECT_PASSWORD = 0x0A, // Incorrect password
|
||||
SESSION_EXPIRED = 0x0B, // Session expired
|
||||
SERVER_SHUTTING_DOWN = 0x0C, // Server shutting down
|
||||
ALREADY_LOGGING_IN = 0x0D, // Already logging in
|
||||
LOGIN_SERVER_NOT_FOUND = 0x0E, // Login server not found
|
||||
WAIT_QUEUE = 0x0F, // Wait queue
|
||||
BANNED = 0x10, // Banned
|
||||
ALREADY_ONLINE = 0x11, // Already online
|
||||
NO_TIME = 0x12, // No game time
|
||||
DB_BUSY = 0x13, // DB busy
|
||||
SUSPENDED = 0x14, // Suspended
|
||||
PARENTAL_CONTROL = 0x15, // Parental control
|
||||
LOCKED_ENFORCED = 0x16 // Account locked
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_AUTH_RESPONSE data (from server)
|
||||
*/
|
||||
struct AuthResponseData {
|
||||
AuthResult result;
|
||||
|
||||
bool isSuccess() const { return result == AuthResult::OK; }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_AUTH_RESPONSE parser
|
||||
*/
|
||||
class AuthResponseParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, AuthResponseData& data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get human-readable string for auth result
|
||||
*/
|
||||
const char* getAuthResultString(AuthResult result);
|
||||
|
||||
// Forward declare Character
|
||||
struct Character;
|
||||
|
||||
/**
|
||||
* CMSG_CHAR_ENUM packet builder
|
||||
*
|
||||
* Request list of characters on account
|
||||
*/
|
||||
class CharEnumPacket {
|
||||
public:
|
||||
/**
|
||||
* Build CMSG_CHAR_ENUM packet
|
||||
*
|
||||
* This packet has no body - just the opcode
|
||||
*/
|
||||
static network::Packet build();
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_CHAR_ENUM response data (from server)
|
||||
*
|
||||
* Contains list of all characters on the account
|
||||
*/
|
||||
struct CharEnumResponse {
|
||||
std::vector<Character> characters;
|
||||
|
||||
bool isEmpty() const { return characters.empty(); }
|
||||
size_t count() const { return characters.size(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_CHAR_ENUM response parser
|
||||
*/
|
||||
class CharEnumParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, CharEnumResponse& response);
|
||||
};
|
||||
|
||||
/**
|
||||
* CMSG_PLAYER_LOGIN packet builder
|
||||
*
|
||||
* Select character and enter world
|
||||
*/
|
||||
class PlayerLoginPacket {
|
||||
public:
|
||||
/**
|
||||
* Build CMSG_PLAYER_LOGIN packet
|
||||
*
|
||||
* @param characterGuid GUID of character to log in with
|
||||
*/
|
||||
static network::Packet build(uint64_t characterGuid);
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_LOGIN_VERIFY_WORLD data (from server)
|
||||
*
|
||||
* Confirms successful world entry with initial position and map info
|
||||
*/
|
||||
struct LoginVerifyWorldData {
|
||||
uint32_t mapId; // Map ID where character spawned
|
||||
float x, y, z; // Initial position coordinates
|
||||
float orientation; // Initial orientation (facing direction)
|
||||
|
||||
bool isValid() const { return mapId != 0xFFFFFFFF; }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_LOGIN_VERIFY_WORLD parser
|
||||
*/
|
||||
class LoginVerifyWorldParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, LoginVerifyWorldData& data);
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_ACCOUNT_DATA_TIMES data (from server)
|
||||
*
|
||||
* Contains timestamps for account data (macros, keybindings, etc.)
|
||||
*/
|
||||
struct AccountDataTimesData {
|
||||
uint32_t serverTime; // Current server time (Unix timestamp)
|
||||
uint8_t unknown; // Unknown (always 1?)
|
||||
uint32_t accountDataTimes[8]; // Timestamps for 8 account data slots
|
||||
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_ACCOUNT_DATA_TIMES parser
|
||||
*/
|
||||
class AccountDataTimesParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, AccountDataTimesData& data);
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_MOTD data (from server)
|
||||
*
|
||||
* Message of the Day from server
|
||||
*/
|
||||
struct MotdData {
|
||||
std::vector<std::string> lines; // MOTD text lines
|
||||
|
||||
bool isEmpty() const { return lines.empty(); }
|
||||
size_t lineCount() const { return lines.size(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_MOTD parser
|
||||
*/
|
||||
class MotdParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, MotdData& data);
|
||||
};
|
||||
|
||||
/**
|
||||
* CMSG_PING packet builder
|
||||
*
|
||||
* Heartbeat packet sent periodically to keep connection alive
|
||||
*/
|
||||
class PingPacket {
|
||||
public:
|
||||
/**
|
||||
* Build CMSG_PING packet
|
||||
*
|
||||
* @param sequence Sequence number (increments with each ping)
|
||||
* @param latency Client-side latency estimate in milliseconds
|
||||
* @return Packet ready to send
|
||||
*/
|
||||
static network::Packet build(uint32_t sequence, uint32_t latency);
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_PONG data (from server)
|
||||
*
|
||||
* Response to CMSG_PING, echoes back the sequence number
|
||||
*/
|
||||
struct PongData {
|
||||
uint32_t sequence; // Sequence number from CMSG_PING
|
||||
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_PONG parser
|
||||
*/
|
||||
class PongParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, PongData& data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Movement flags for player movement
|
||||
*/
|
||||
enum class MovementFlags : uint32_t {
|
||||
NONE = 0x00000000,
|
||||
FORWARD = 0x00000001,
|
||||
BACKWARD = 0x00000002,
|
||||
STRAFE_LEFT = 0x00000004,
|
||||
STRAFE_RIGHT = 0x00000008,
|
||||
TURN_LEFT = 0x00000010,
|
||||
TURN_RIGHT = 0x00000020,
|
||||
PITCH_UP = 0x00000040,
|
||||
PITCH_DOWN = 0x00000080,
|
||||
WALKING = 0x00000100,
|
||||
ONTRANSPORT = 0x00000200,
|
||||
LEVITATING = 0x00000400,
|
||||
ROOT = 0x00000800,
|
||||
FALLING = 0x00001000,
|
||||
FALLINGFAR = 0x00002000,
|
||||
SWIMMING = 0x00200000,
|
||||
ASCENDING = 0x00400000,
|
||||
CAN_FLY = 0x00800000,
|
||||
FLYING = 0x01000000,
|
||||
};
|
||||
|
||||
/**
|
||||
* Movement info structure
|
||||
*
|
||||
* Contains all movement-related data sent in movement packets
|
||||
*/
|
||||
struct MovementInfo {
|
||||
uint32_t flags = 0; // Movement flags
|
||||
uint16_t flags2 = 0; // Extra movement flags
|
||||
uint32_t time = 0; // Movement timestamp (milliseconds)
|
||||
float x = 0.0f; // Position X
|
||||
float y = 0.0f; // Position Y
|
||||
float z = 0.0f; // Position Z
|
||||
float orientation = 0.0f; // Facing direction (radians)
|
||||
|
||||
// Optional fields (based on flags)
|
||||
float pitch = 0.0f; // Pitch angle (swimming/flying)
|
||||
uint32_t fallTime = 0; // Time falling (milliseconds)
|
||||
float jumpVelocity = 0.0f; // Jump vertical velocity
|
||||
float jumpSinAngle = 0.0f; // Jump horizontal sin
|
||||
float jumpCosAngle = 0.0f; // Jump horizontal cos
|
||||
float jumpXYSpeed = 0.0f; // Jump horizontal speed
|
||||
|
||||
bool hasFlag(MovementFlags flag) const {
|
||||
return (flags & static_cast<uint32_t>(flag)) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Movement packet builder
|
||||
*
|
||||
* Builds CMSG_MOVE_* packets with movement info
|
||||
*/
|
||||
class MovementPacket {
|
||||
public:
|
||||
/**
|
||||
* Build a movement packet
|
||||
*
|
||||
* @param opcode Movement opcode (CMSG_MOVE_START_FORWARD, etc.)
|
||||
* @param info Movement info
|
||||
* @return Packet ready to send
|
||||
*/
|
||||
static network::Packet build(Opcode opcode, const MovementInfo& info);
|
||||
};
|
||||
|
||||
// Forward declare Entity types
|
||||
class Entity;
|
||||
class EntityManager;
|
||||
enum class ObjectType : uint8_t;
|
||||
enum class UpdateType : uint8_t;
|
||||
|
||||
/**
|
||||
* Update block for a single object in SMSG_UPDATE_OBJECT
|
||||
*/
|
||||
struct UpdateBlock {
|
||||
UpdateType updateType;
|
||||
uint64_t guid = 0;
|
||||
ObjectType objectType;
|
||||
|
||||
// Movement data (for MOVEMENT updates)
|
||||
bool hasMovement = false;
|
||||
float x = 0.0f, y = 0.0f, z = 0.0f, orientation = 0.0f;
|
||||
|
||||
// Field data (for VALUES and CREATE updates)
|
||||
std::map<uint16_t, uint32_t> fields;
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_UPDATE_OBJECT data
|
||||
*
|
||||
* Contains all update blocks in the packet
|
||||
*/
|
||||
struct UpdateObjectData {
|
||||
uint32_t blockCount = 0;
|
||||
std::vector<UpdateBlock> blocks;
|
||||
|
||||
// Out-of-range GUIDs (for OUT_OF_RANGE_OBJECTS)
|
||||
std::vector<uint64_t> outOfRangeGuids;
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_UPDATE_OBJECT parser
|
||||
*
|
||||
* Parses object updates from server
|
||||
*/
|
||||
class UpdateObjectParser {
|
||||
public:
|
||||
/**
|
||||
* Parse SMSG_UPDATE_OBJECT packet
|
||||
*
|
||||
* @param packet Packet to parse
|
||||
* @param data Output data
|
||||
* @return true if successful
|
||||
*/
|
||||
static bool parse(network::Packet& packet, UpdateObjectData& data);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parse a single update block
|
||||
*
|
||||
* @param packet Packet to read from
|
||||
* @param block Output block
|
||||
* @return true if successful
|
||||
*/
|
||||
static bool parseUpdateBlock(network::Packet& packet, UpdateBlock& block);
|
||||
|
||||
/**
|
||||
* Parse movement block
|
||||
*
|
||||
* @param packet Packet to read from
|
||||
* @param block Output block
|
||||
* @return true if successful
|
||||
*/
|
||||
static bool parseMovementBlock(network::Packet& packet, UpdateBlock& block);
|
||||
|
||||
/**
|
||||
* Parse update fields (mask + values)
|
||||
*
|
||||
* @param packet Packet to read from
|
||||
* @param block Output block
|
||||
* @return true if successful
|
||||
*/
|
||||
static bool parseUpdateFields(network::Packet& packet, UpdateBlock& block);
|
||||
|
||||
/**
|
||||
* Read packed GUID from packet
|
||||
*
|
||||
* @param packet Packet to read from
|
||||
* @return GUID value
|
||||
*/
|
||||
static uint64_t readPackedGuid(network::Packet& packet);
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_DESTROY_OBJECT data
|
||||
*/
|
||||
struct DestroyObjectData {
|
||||
uint64_t guid = 0;
|
||||
bool isDeath = false; // true if unit died, false if despawned
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_DESTROY_OBJECT parser
|
||||
*/
|
||||
class DestroyObjectParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, DestroyObjectData& data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Chat message types
|
||||
*/
|
||||
enum class ChatType : uint8_t {
|
||||
SAY = 0,
|
||||
PARTY = 1,
|
||||
RAID = 2,
|
||||
GUILD = 3,
|
||||
OFFICER = 4,
|
||||
YELL = 5,
|
||||
WHISPER = 6,
|
||||
WHISPER_INFORM = 7,
|
||||
EMOTE = 8,
|
||||
TEXT_EMOTE = 9,
|
||||
SYSTEM = 10,
|
||||
MONSTER_SAY = 11,
|
||||
MONSTER_YELL = 12,
|
||||
MONSTER_EMOTE = 13,
|
||||
CHANNEL = 14,
|
||||
CHANNEL_JOIN = 15,
|
||||
CHANNEL_LEAVE = 16,
|
||||
CHANNEL_LIST = 17,
|
||||
CHANNEL_NOTICE = 18,
|
||||
CHANNEL_NOTICE_USER = 19,
|
||||
AFK = 20,
|
||||
DND = 21,
|
||||
IGNORED = 22,
|
||||
SKILL = 23,
|
||||
LOOT = 24,
|
||||
BATTLEGROUND = 25,
|
||||
BATTLEGROUND_LEADER = 26,
|
||||
RAID_LEADER = 27,
|
||||
RAID_WARNING = 28,
|
||||
ACHIEVEMENT = 29,
|
||||
GUILD_ACHIEVEMENT = 30
|
||||
};
|
||||
|
||||
/**
|
||||
* Chat language IDs
|
||||
*/
|
||||
enum class ChatLanguage : uint32_t {
|
||||
UNIVERSAL = 0,
|
||||
ORCISH = 1,
|
||||
DARNASSIAN = 2,
|
||||
TAURAHE = 3,
|
||||
DWARVISH = 6,
|
||||
COMMON = 7,
|
||||
DEMONIC = 8,
|
||||
TITAN = 9,
|
||||
THALASSIAN = 10,
|
||||
DRACONIC = 11,
|
||||
KALIMAG = 12,
|
||||
GNOMISH = 13,
|
||||
TROLL = 14,
|
||||
GUTTERSPEAK = 33,
|
||||
DRAENEI = 35,
|
||||
ZOMBIE = 36,
|
||||
GNOMISH_BINARY = 37,
|
||||
GOBLIN_BINARY = 38,
|
||||
ADDON = 0xFFFFFFFF // Used for addon communication
|
||||
};
|
||||
|
||||
/**
|
||||
* CMSG_MESSAGECHAT packet builder
|
||||
*/
|
||||
class MessageChatPacket {
|
||||
public:
|
||||
/**
|
||||
* Build CMSG_MESSAGECHAT packet
|
||||
*
|
||||
* @param type Chat type (SAY, YELL, etc.)
|
||||
* @param language Language ID
|
||||
* @param message Message text
|
||||
* @param target Target name (for whispers, empty otherwise)
|
||||
* @return Packet ready to send
|
||||
*/
|
||||
static network::Packet build(ChatType type,
|
||||
ChatLanguage language,
|
||||
const std::string& message,
|
||||
const std::string& target = "");
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_MESSAGECHAT data
|
||||
*/
|
||||
struct MessageChatData {
|
||||
ChatType type;
|
||||
ChatLanguage language;
|
||||
uint64_t senderGuid = 0;
|
||||
std::string senderName;
|
||||
uint64_t receiverGuid = 0;
|
||||
std::string receiverName;
|
||||
std::string message;
|
||||
std::string channelName; // For channel messages
|
||||
uint8_t chatTag = 0; // Player flags (AFK, DND, GM, etc.)
|
||||
|
||||
bool isValid() const { return !message.empty(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_MESSAGECHAT parser
|
||||
*/
|
||||
class MessageChatParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, MessageChatData& data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get human-readable string for chat type
|
||||
*/
|
||||
const char* getChatTypeString(ChatType type);
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
32
include/game/zone_manager.hpp
Normal file
32
include/game/zone_manager.hpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
struct ZoneInfo {
|
||||
uint32_t id;
|
||||
std::string name;
|
||||
std::vector<std::string> musicPaths; // MPQ paths to music files
|
||||
};
|
||||
|
||||
class ZoneManager {
|
||||
public:
|
||||
void initialize();
|
||||
|
||||
uint32_t getZoneId(int tileX, int tileY) const;
|
||||
const ZoneInfo* getZoneInfo(uint32_t zoneId) const;
|
||||
std::string getRandomMusic(uint32_t zoneId) const;
|
||||
|
||||
private:
|
||||
// tile key = tileX * 100 + tileY
|
||||
std::unordered_map<int, uint32_t> tileToZone;
|
||||
std::unordered_map<uint32_t, ZoneInfo> zones;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue