mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
TBC 2.4.3 SMSG_SPELL_START and SMSG_SPELL_GO send full uint64 GUIDs for casterGuid/casterUnit and hit targets. WotLK uses packed (variable-length) GUIDs. Using readPackedGuid() on a full uint64 reads the first byte as the bitmask, consuming 1-8 wrong bytes, which shifts all subsequent fields (spellId, castFlags, castTime) and causes: - Cast bar to never show for the player's own spells - Sound effects to use the wrong spell ID - Hit/miss target tracking to be completely wrong Additionally, TBC SMSG_SPELL_GO lacks the WotLK timestamp field after castFlags. Add TbcPacketParsers::parseSpellStart and ::parseSpellGo using full GUIDs, add virtual base methods, and route both handlers through virtual dispatch.
432 lines
19 KiB
C++
432 lines
19 KiB
C++
#pragma once
|
||
|
||
#include "game/world_packets.hpp"
|
||
#include <memory>
|
||
#include <string>
|
||
|
||
namespace wowee {
|
||
namespace game {
|
||
|
||
/**
|
||
* PacketParsers - Polymorphic interface for expansion-specific packet parsing.
|
||
*
|
||
* Binary packet formats differ significantly between WoW expansions
|
||
* (movement flags, update fields, character enum layout, etc.).
|
||
* Each expansion implements this interface with its specific parsing logic.
|
||
*
|
||
* The base PacketParsers delegates to the existing static parser classes
|
||
* in world_packets.hpp. Expansion subclasses override the methods that
|
||
* differ from WotLK.
|
||
*/
|
||
class PacketParsers {
|
||
public:
|
||
virtual ~PacketParsers() = default;
|
||
|
||
// Size of MovementInfo.flags2 in bytes for MSG_MOVE_* payloads.
|
||
// Classic: none, TBC: u8, WotLK: u16.
|
||
virtual uint8_t movementFlags2Size() const { return 2; }
|
||
|
||
// --- Movement ---
|
||
|
||
/** Parse movement block from SMSG_UPDATE_OBJECT */
|
||
virtual bool parseMovementBlock(network::Packet& packet, UpdateBlock& block) {
|
||
return UpdateObjectParser::parseMovementBlock(packet, block);
|
||
}
|
||
|
||
/** Write movement payload for CMSG_MOVE_* packets */
|
||
virtual void writeMovementPayload(network::Packet& packet, const MovementInfo& info) {
|
||
MovementPacket::writeMovementPayload(packet, info);
|
||
}
|
||
|
||
/** Build a complete movement packet with packed GUID + payload */
|
||
virtual network::Packet buildMovementPacket(LogicalOpcode opcode,
|
||
const MovementInfo& info,
|
||
uint64_t playerGuid = 0) {
|
||
return MovementPacket::build(opcode, info, playerGuid);
|
||
}
|
||
|
||
/** Build CMSG_CAST_SPELL (WotLK default: castCount + spellId + castFlags + targets) */
|
||
virtual network::Packet buildCastSpell(uint32_t spellId, uint64_t targetGuid, uint8_t castCount) {
|
||
return CastSpellPacket::build(spellId, targetGuid, castCount);
|
||
}
|
||
|
||
/** Build CMSG_USE_ITEM (WotLK default: bag + slot + castCount + spellId + itemGuid + glyphIndex + castFlags + targets) */
|
||
virtual network::Packet buildUseItem(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid, uint32_t spellId = 0) {
|
||
return UseItemPacket::build(bagIndex, slotIndex, itemGuid, spellId);
|
||
}
|
||
|
||
// --- Character Enumeration ---
|
||
|
||
/** Parse SMSG_CHAR_ENUM */
|
||
virtual bool parseCharEnum(network::Packet& packet, CharEnumResponse& response) {
|
||
return CharEnumParser::parse(packet, response);
|
||
}
|
||
|
||
// --- Update Object ---
|
||
|
||
/** Parse a full SMSG_UPDATE_OBJECT packet */
|
||
virtual bool parseUpdateObject(network::Packet& packet, UpdateObjectData& data) {
|
||
return UpdateObjectParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse update fields block (value mask + field values) */
|
||
virtual bool parseUpdateFields(network::Packet& packet, UpdateBlock& block) {
|
||
return UpdateObjectParser::parseUpdateFields(packet, block);
|
||
}
|
||
|
||
// --- Monster Movement ---
|
||
|
||
/** Parse SMSG_MONSTER_MOVE */
|
||
virtual bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) {
|
||
return MonsterMoveParser::parse(packet, data);
|
||
}
|
||
|
||
// --- Combat ---
|
||
|
||
/** Parse SMSG_ATTACKERSTATEUPDATE */
|
||
virtual bool parseAttackerStateUpdate(network::Packet& packet, AttackerStateUpdateData& data) {
|
||
return AttackerStateUpdateParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse SMSG_SPELLNONMELEEDAMAGELOG */
|
||
virtual bool parseSpellDamageLog(network::Packet& packet, SpellDamageLogData& data) {
|
||
return SpellDamageLogParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse SMSG_SPELLHEALLOG */
|
||
virtual bool parseSpellHealLog(network::Packet& packet, SpellHealLogData& data) {
|
||
return SpellHealLogParser::parse(packet, data);
|
||
}
|
||
|
||
// --- Spells ---
|
||
|
||
/** Parse SMSG_INITIAL_SPELLS */
|
||
virtual bool parseInitialSpells(network::Packet& packet, InitialSpellsData& data) {
|
||
return InitialSpellsParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse SMSG_SPELL_START */
|
||
virtual bool parseSpellStart(network::Packet& packet, SpellStartData& data) {
|
||
return SpellStartParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse SMSG_SPELL_GO */
|
||
virtual bool parseSpellGo(network::Packet& packet, SpellGoData& data) {
|
||
return SpellGoParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse SMSG_CAST_FAILED */
|
||
virtual bool parseCastFailed(network::Packet& packet, CastFailedData& data) {
|
||
return CastFailedParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse SMSG_CAST_RESULT header (spellId + result), expansion-aware.
|
||
* WotLK: castCount(u8) + spellId(u32) + result(u8)
|
||
* TBC/Classic: spellId(u32) + result(u8) (no castCount prefix)
|
||
*/
|
||
virtual bool parseCastResult(network::Packet& packet, uint32_t& spellId, uint8_t& result) {
|
||
// WotLK default: skip castCount, read spellId + result
|
||
if (packet.getSize() - packet.getReadPos() < 6) return false;
|
||
packet.readUInt8(); // castCount
|
||
spellId = packet.readUInt32();
|
||
result = packet.readUInt8();
|
||
return true;
|
||
}
|
||
|
||
/** Parse SMSG_AURA_UPDATE / SMSG_AURA_UPDATE_ALL */
|
||
virtual bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) {
|
||
return AuraUpdateParser::parse(packet, data, isAll);
|
||
}
|
||
|
||
// --- Chat ---
|
||
|
||
/** Parse SMSG_MESSAGECHAT */
|
||
virtual bool parseMessageChat(network::Packet& packet, MessageChatData& data) {
|
||
return MessageChatParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse SMSG_NAME_QUERY_RESPONSE */
|
||
virtual bool parseNameQueryResponse(network::Packet& packet, NameQueryResponseData& data) {
|
||
return NameQueryResponseParser::parse(packet, data);
|
||
}
|
||
|
||
// --- Creature Query ---
|
||
|
||
/** Parse SMSG_CREATURE_QUERY_RESPONSE */
|
||
virtual bool parseCreatureQueryResponse(network::Packet& packet, CreatureQueryResponseData& data) {
|
||
return CreatureQueryResponseParser::parse(packet, data);
|
||
}
|
||
|
||
// --- Item Query ---
|
||
|
||
/** Build CMSG_ITEM_QUERY_SINGLE */
|
||
virtual network::Packet buildItemQuery(uint32_t entry, uint64_t guid) {
|
||
return ItemQueryPacket::build(entry, guid);
|
||
}
|
||
|
||
/** Parse SMSG_ITEM_QUERY_SINGLE_RESPONSE */
|
||
virtual bool parseItemQueryResponse(network::Packet& packet, ItemQueryResponseData& data) {
|
||
return ItemQueryResponseParser::parse(packet, data);
|
||
}
|
||
|
||
// --- GameObject Query ---
|
||
|
||
/** Parse SMSG_GAMEOBJECT_QUERY_RESPONSE */
|
||
virtual bool parseGameObjectQueryResponse(network::Packet& packet, GameObjectQueryResponseData& data) {
|
||
return GameObjectQueryResponseParser::parse(packet, data);
|
||
}
|
||
|
||
// --- Gossip ---
|
||
|
||
/** Parse SMSG_GOSSIP_MESSAGE */
|
||
virtual bool parseGossipMessage(network::Packet& packet, GossipMessageData& data) {
|
||
return GossipMessageParser::parse(packet, data);
|
||
}
|
||
|
||
// --- Quest details ---
|
||
|
||
/** Build CMSG_QUESTGIVER_QUERY_QUEST.
|
||
* WotLK appends a trailing unk1 byte; Vanilla/Classic do not. */
|
||
virtual network::Packet buildQueryQuestPacket(uint64_t npcGuid, uint32_t questId) {
|
||
return QuestgiverQueryQuestPacket::build(npcGuid, questId); // includes unk1
|
||
}
|
||
|
||
/** Build CMSG_QUESTGIVER_ACCEPT_QUEST.
|
||
* WotLK/AzerothCore expects trailing unk1 uint32; older expansions may not. */
|
||
virtual network::Packet buildAcceptQuestPacket(uint64_t npcGuid, uint32_t questId) {
|
||
return QuestgiverAcceptQuestPacket::build(npcGuid, questId);
|
||
}
|
||
|
||
/** Parse SMSG_QUESTGIVER_QUEST_DETAILS.
|
||
* WotLK has an extra informUnit GUID before questId; Vanilla/Classic do not. */
|
||
virtual bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) {
|
||
return QuestDetailsParser::parse(packet, data); // WotLK auto-detect
|
||
}
|
||
|
||
/** Stride of PLAYER_QUEST_LOG fields in update-object blocks.
|
||
* WotLK: 5 fields per slot, Classic/Vanilla: 3. */
|
||
virtual uint8_t questLogStride() const { return 5; }
|
||
|
||
// --- Quest Giver Status ---
|
||
|
||
/** Read quest giver status from packet.
|
||
* WotLK: uint8, vanilla/classic: uint32 with different enum values.
|
||
* Returns the status value normalized to WotLK enum values. */
|
||
virtual uint8_t readQuestGiverStatus(network::Packet& packet) {
|
||
return packet.readUInt8();
|
||
}
|
||
|
||
// --- Destroy Object ---
|
||
|
||
/** Parse SMSG_DESTROY_OBJECT */
|
||
virtual bool parseDestroyObject(network::Packet& packet, DestroyObjectData& data) {
|
||
return DestroyObjectParser::parse(packet, data);
|
||
}
|
||
|
||
// --- Guild ---
|
||
|
||
/** Parse SMSG_GUILD_ROSTER */
|
||
virtual bool parseGuildRoster(network::Packet& packet, GuildRosterData& data) {
|
||
return GuildRosterParser::parse(packet, data);
|
||
}
|
||
|
||
/** Parse SMSG_GUILD_QUERY_RESPONSE */
|
||
virtual bool parseGuildQueryResponse(network::Packet& packet, GuildQueryResponseData& data) {
|
||
return GuildQueryResponseParser::parse(packet, data);
|
||
}
|
||
|
||
// --- Channels ---
|
||
|
||
/** Build CMSG_JOIN_CHANNEL */
|
||
virtual network::Packet buildJoinChannel(const std::string& channelName, const std::string& password) {
|
||
return JoinChannelPacket::build(channelName, password);
|
||
}
|
||
|
||
/** Build CMSG_LEAVE_CHANNEL */
|
||
virtual network::Packet buildLeaveChannel(const std::string& channelName) {
|
||
return LeaveChannelPacket::build(channelName);
|
||
}
|
||
|
||
// --- Mail ---
|
||
|
||
/** Build CMSG_SEND_MAIL */
|
||
virtual network::Packet buildSendMail(uint64_t mailboxGuid, const std::string& recipient,
|
||
const std::string& subject, const std::string& body,
|
||
uint32_t money, uint32_t cod,
|
||
const std::vector<uint64_t>& itemGuids = {}) {
|
||
return SendMailPacket::build(mailboxGuid, recipient, subject, body, money, cod, itemGuids);
|
||
}
|
||
|
||
/** Parse SMSG_MAIL_LIST_RESULT into a vector of MailMessage */
|
||
virtual bool parseMailList(network::Packet& packet, std::vector<MailMessage>& inbox);
|
||
|
||
/** Build CMSG_MAIL_TAKE_ITEM */
|
||
virtual network::Packet buildMailTakeItem(uint64_t mailboxGuid, uint32_t mailId, uint32_t itemSlot) {
|
||
return MailTakeItemPacket::build(mailboxGuid, mailId, itemSlot);
|
||
}
|
||
|
||
/** Build CMSG_MAIL_DELETE */
|
||
virtual network::Packet buildMailDelete(uint64_t mailboxGuid, uint32_t mailId, uint32_t mailTemplateId) {
|
||
return MailDeletePacket::build(mailboxGuid, mailId, mailTemplateId);
|
||
}
|
||
|
||
// --- Utility ---
|
||
|
||
/** Read a packed GUID from the packet */
|
||
virtual uint64_t readPackedGuid(network::Packet& packet) {
|
||
return UpdateObjectParser::readPackedGuid(packet);
|
||
}
|
||
|
||
/** Write a packed GUID to the packet */
|
||
virtual void writePackedGuid(network::Packet& packet, uint64_t guid) {
|
||
MovementPacket::writePackedGuid(packet, guid);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* WotLK 3.3.5a packet parsers.
|
||
*
|
||
* Uses the default implementations which delegate to the existing
|
||
* static parser classes. All current parsing code is WotLK-specific,
|
||
* so no overrides are needed.
|
||
*/
|
||
class WotlkPacketParsers : public PacketParsers {
|
||
// All methods use the defaults from PacketParsers base class,
|
||
// which delegate to the existing WotLK static parsers.
|
||
};
|
||
|
||
/**
|
||
* TBC 2.4.3 packet parsers.
|
||
*
|
||
* Overrides methods where TBC binary format differs from WotLK:
|
||
* - SMSG_UPDATE_OBJECT: u8 has_transport after blockCount (WotLK removed it)
|
||
* - UpdateFlags is u8 (not u16), no VEHICLE/ROTATION/POSITION flags
|
||
* - Movement flags2 is u8 (not u16), no transport seat byte
|
||
* - Movement flags: JUMPING=0x2000 gates jump data (WotLK: FALLING=0x1000)
|
||
* - SPLINE_ENABLED=0x08000000, SPLINE_ELEVATION=0x04000000 (same as WotLK)
|
||
* - Pitch: SWIMMING or else ONTRANSPORT(0x02000000)
|
||
* - CharEnum: uint8 firstLogin (not uint32+uint8), 20 equipment items (not 23)
|
||
* - Aura updates use inline update fields, not SMSG_AURA_UPDATE
|
||
*/
|
||
class TbcPacketParsers : public PacketParsers {
|
||
public:
|
||
uint8_t movementFlags2Size() const override { return 1; }
|
||
bool parseMovementBlock(network::Packet& packet, UpdateBlock& block) override;
|
||
void writeMovementPayload(network::Packet& packet, const MovementInfo& info) override;
|
||
network::Packet buildMovementPacket(LogicalOpcode opcode,
|
||
const MovementInfo& info,
|
||
uint64_t playerGuid = 0) override;
|
||
bool parseUpdateObject(network::Packet& packet, UpdateObjectData& data) override;
|
||
bool parseCharEnum(network::Packet& packet, CharEnumResponse& response) override;
|
||
bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) override;
|
||
bool parseNameQueryResponse(network::Packet& packet, NameQueryResponseData& data) override;
|
||
bool parseItemQueryResponse(network::Packet& packet, ItemQueryResponseData& data) override;
|
||
network::Packet buildAcceptQuestPacket(uint64_t npcGuid, uint32_t questId) override;
|
||
// TBC 2.4.3 CMSG_CAST_SPELL has no castFlags byte (WotLK added it)
|
||
network::Packet buildCastSpell(uint32_t spellId, uint64_t targetGuid, uint8_t castCount) override;
|
||
// TBC 2.4.3 CMSG_USE_ITEM has no glyphIndex field (WotLK added it)
|
||
network::Packet buildUseItem(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid, uint32_t spellId = 0) override;
|
||
// TBC 2.4.3 SMSG_MONSTER_MOVE has no unk byte after packed GUID (WotLK added it)
|
||
bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) override;
|
||
// TBC 2.4.3 SMSG_GOSSIP_MESSAGE quests lack questFlags(u32)+isRepeatable(u8) (WotLK added them)
|
||
bool parseGossipMessage(network::Packet& packet, GossipMessageData& data) override;
|
||
// TBC 2.4.3 SMSG_CAST_RESULT: spellId(u32) + result(u8) — WotLK added castCount(u8) prefix
|
||
bool parseCastResult(network::Packet& packet, uint32_t& spellId, uint8_t& result) override;
|
||
// TBC 2.4.3 SMSG_SPELL_START: full uint64 GUIDs (WotLK uses packed GUIDs)
|
||
bool parseSpellStart(network::Packet& packet, SpellStartData& data) override;
|
||
// TBC 2.4.3 SMSG_SPELL_GO: full uint64 GUIDs, no timestamp field (WotLK added one)
|
||
bool parseSpellGo(network::Packet& packet, SpellGoData& data) override;
|
||
// TBC 2.4.3 SMSG_MAIL_LIST_RESULT: uint8 count (not uint32+uint8), no body field,
|
||
// attachment uses uint64 itemGuid (not uint32), enchants are 7×u32 id-only (not 7×{id+dur+charges})
|
||
bool parseMailList(network::Packet& packet, std::vector<MailMessage>& inbox) override;
|
||
// TBC 2.4.3 SMSG_ATTACKERSTATEUPDATE uses full uint64 GUIDs (WotLK uses packed GUIDs)
|
||
bool parseAttackerStateUpdate(network::Packet& packet, AttackerStateUpdateData& data) override;
|
||
// TBC 2.4.3 SMSG_SPELLNONMELEEDAMAGELOG uses full uint64 GUIDs (WotLK uses packed GUIDs)
|
||
bool parseSpellDamageLog(network::Packet& packet, SpellDamageLogData& data) override;
|
||
// TBC 2.4.3 SMSG_SPELLHEALLOG uses full uint64 GUIDs (WotLK uses packed GUIDs)
|
||
bool parseSpellHealLog(network::Packet& packet, SpellHealLogData& data) override;
|
||
};
|
||
|
||
/**
|
||
* Classic 1.12.1 packet parsers.
|
||
*
|
||
* Inherits from TBC (shared: u8 UpdateFlags, has_transport byte).
|
||
*
|
||
* Differences from TBC:
|
||
* - No moveFlags2 byte (TBC has u8, Classic has none)
|
||
* - Only 6 speed fields (no flight speeds — flying added in TBC)
|
||
* - SPLINE_ENABLED at 0x00400000 (TBC/WotLK: 0x08000000)
|
||
* - Transport data has no timestamp (TBC adds u32 timestamp)
|
||
* - Pitch: only SWIMMING (no ONTRANSPORT secondary pitch)
|
||
* - CharEnum: no enchantment field per equipment slot
|
||
* - No SMSG_AURA_UPDATE (uses update fields, same as TBC)
|
||
*/
|
||
class ClassicPacketParsers : public TbcPacketParsers {
|
||
public:
|
||
uint8_t movementFlags2Size() const override { return 0; }
|
||
bool parseCharEnum(network::Packet& packet, CharEnumResponse& response) override;
|
||
bool parseMovementBlock(network::Packet& packet, UpdateBlock& block) override;
|
||
void writeMovementPayload(network::Packet& packet, const MovementInfo& info) override;
|
||
network::Packet buildMovementPacket(LogicalOpcode opcode,
|
||
const MovementInfo& info,
|
||
uint64_t playerGuid = 0) override;
|
||
network::Packet buildCastSpell(uint32_t spellId, uint64_t targetGuid, uint8_t castCount) override;
|
||
network::Packet buildUseItem(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid, uint32_t spellId = 0) override;
|
||
bool parseCastFailed(network::Packet& packet, CastFailedData& data) override;
|
||
bool parseMessageChat(network::Packet& packet, MessageChatData& data) override;
|
||
bool parseGameObjectQueryResponse(network::Packet& packet, GameObjectQueryResponseData& data) override;
|
||
// Classic 1.12 SMSG_CREATURE_QUERY_RESPONSE lacks the iconName string that TBC/WotLK include
|
||
bool parseCreatureQueryResponse(network::Packet& packet, CreatureQueryResponseData& data) override;
|
||
bool parseGossipMessage(network::Packet& packet, GossipMessageData& data) override;
|
||
bool parseGuildRoster(network::Packet& packet, GuildRosterData& data) override;
|
||
bool parseGuildQueryResponse(network::Packet& packet, GuildQueryResponseData& data) override;
|
||
network::Packet buildJoinChannel(const std::string& channelName, const std::string& password) override;
|
||
network::Packet buildLeaveChannel(const std::string& channelName) override;
|
||
network::Packet buildSendMail(uint64_t mailboxGuid, const std::string& recipient,
|
||
const std::string& subject, const std::string& body,
|
||
uint32_t money, uint32_t cod,
|
||
const std::vector<uint64_t>& itemGuids = {}) override;
|
||
bool parseMailList(network::Packet& packet, std::vector<MailMessage>& inbox) override;
|
||
network::Packet buildMailTakeItem(uint64_t mailboxGuid, uint32_t mailId, uint32_t itemSlot) override;
|
||
network::Packet buildMailDelete(uint64_t mailboxGuid, uint32_t mailId, uint32_t mailTemplateId) override;
|
||
network::Packet buildItemQuery(uint32_t entry, uint64_t guid) override;
|
||
bool parseItemQueryResponse(network::Packet& packet, ItemQueryResponseData& data) override;
|
||
uint8_t readQuestGiverStatus(network::Packet& packet) override;
|
||
network::Packet buildQueryQuestPacket(uint64_t npcGuid, uint32_t questId) override;
|
||
network::Packet buildAcceptQuestPacket(uint64_t npcGuid, uint32_t questId) override;
|
||
bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) override;
|
||
uint8_t questLogStride() const override { return 3; }
|
||
bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) override {
|
||
return MonsterMoveParser::parseVanilla(packet, data);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Turtle WoW (build 7234) packet parsers.
|
||
*
|
||
* Turtle WoW is a heavily modified vanilla server that sends TBC-style
|
||
* movement blocks (moveFlags2, transport timestamps, 8 speeds including flight)
|
||
* while keeping all other Classic packet formats.
|
||
*
|
||
* Inherits all Classic overrides (charEnum, chat, gossip, mail, items, etc.)
|
||
* but delegates movement block parsing to TBC format.
|
||
*/
|
||
class TurtlePacketParsers : public ClassicPacketParsers {
|
||
public:
|
||
uint8_t movementFlags2Size() const override { return 0; }
|
||
bool parseMovementBlock(network::Packet& packet, UpdateBlock& block) override;
|
||
bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) override;
|
||
};
|
||
|
||
/**
|
||
* Factory function to create the right parser set for an expansion.
|
||
*/
|
||
inline std::unique_ptr<PacketParsers> createPacketParsers(const std::string& expansionId) {
|
||
if (expansionId == "classic") return std::make_unique<ClassicPacketParsers>();
|
||
if (expansionId == "turtle") return std::make_unique<TurtlePacketParsers>();
|
||
if (expansionId == "tbc") return std::make_unique<TbcPacketParsers>();
|
||
return std::make_unique<WotlkPacketParsers>();
|
||
}
|
||
|
||
} // namespace game
|
||
} // namespace wowee
|