Classic 1.12 uses uint16 spellId + uint16 slot (4 bytes/spell); TBC and WotLK
use uint32 spellId + uint16 unknown (6 bytes/spell). The old size-based heuristic
could misdetect TBC packets that happened to fit both layouts. Add a vanillaFormat
parameter to InitialSpellsParser::parse and override parseInitialSpells in
ClassicPacketParsers to always pass true, eliminating the ambiguity.
Classic 1.12 SMSG_CAST_RESULT uses an enum starting at 0=AFFECTING_COMBAT
(no SUCCESS entry), while WotLK starts at 0=SUCCESS, 1=AFFECTING_COMBAT.
Without this override, Classic result codes were handled by TBC's
parseCastResult which passed them unshifted, causing result 0
(AFFECTING_COMBAT) to be silently treated as success with no error shown.
This applies the same +1 shift used in parseCastFailed so all Classic
spell failure codes map correctly to getSpellCastResultString.
Classic 1.12 and Turtle WoW have only 64 PLAYER_EXPLORED_ZONES uint32
fields (zone IDs pack into 2048 bits). TBC and WotLK use 128 (needed for
Outland/Northrend zone IDs up to bit 4095).
The hardcoded PLAYER_EXPLORED_ZONES_COUNT=128 caused extractExploredZoneFields
to read 64 extra fields beyond the actual zone block in Classic/Turtle —
consuming PLAYER_REST_STATE_EXPERIENCE, PLAYER_FIELD_COINAGE, and character-
points fields as zone flags. On the world map, this could mark zones as
explored based on random bit patterns in those unrelated fields.
Add `exploredZonesCount()` virtual method to PacketParsers (default=128,
Classic/Turtle override=64) and use it in extractExploredZoneFields to
limit reads to the correct block and zero-fill remaining slots.
TBC 2.4.3 and Classic 1.12 share the same SMSG_QUESTGIVER_QUEST_DETAILS
format. WotLK 3.3.5a adds three extra fields (informUnit u64, flags u32,
isFinished u8) that the base QuestDetailsParser::parse handles. TBC had no
override, so it fell through to the WotLK heuristic which read flags+isFinished
as if they were TBC fields, misaligning choiceCount, rewardMoney, and rewardXp.
Fix: move parseQuestDetails from ClassicPacketParsers to TbcPacketParsers.
Classic inherits it unchanged (formats are identical). Both expansions now
correctly parse: no informUnit, activateAccept(u8), suggestedPlayers(u32),
emote section, variable choice/reward item counts, rewardMoney, and rewardXp.
TBC 2.4.3 SMSG_CAST_FAILED format is spellId(u32) + result(u8), same as
Classic. WotLK added a castCount(u8) prefix before spellId. TbcPacketParsers
lacked a parseCastFailed override, so it fell through to the WotLK base
which read one extra byte as castCount, shifting the spellId read by one
byte and corrupting the spell ID and result for every failed cast on TBC.
- Add TbcPacketParsers::parseCastFailed override: reads spellId(4)+result(1)
- ClassicPacketParsers already overrides this (enum shift +1), so Classic unaffected
Classic 1.12 servers (vmangos/cmangos-classic) send:
uint64 guid + CString name + CString realmName + uint32 race + uint32 gender + uint32 class
TBC's Variant A (which Classic inherited) skipped the realmName CString,
causing the null terminator of the empty realmName to be absorbed into the
low byte of the uint32 race read, producing race=0 and shifted gender/class.
Adds a ClassicPacketParsers::parseNameQueryResponse override that correctly
reads the realmName CString before the race/gender/class uint32 fields.
Classic 1.12 sends SMSG_AURA_UPDATE/SMSG_AURA_UPDATE_ALL, but ClassicPacketParsers
inherited TBC's override which returns false (TBC uses a different aura system
and doesn't send SMSG_AURA_UPDATE at all).
Classic aura format differs from WotLK in two key ways:
- DURATION flag bit is 0x10 in Vanilla, not 0x20 as in WotLK; reading with the
WotLK parser would incorrectly gate duration reads and misparse aura fields
- No caster GUID field in Classic; WotLK parser tries to read one (gated by 0x08)
which would consume spell ID or flag bytes from the next aura slot
With this override, player/target aura bars and buff tracking work correctly
on Classic 1.12 connections for the first time.
Vanilla 1.12 SMSG_ATTACKERSTATEUPDATE, SMSG_SPELLNONMELEEDAMAGELOG, and
SMSG_SPELLHEALLOG use PackedGuid for all entity GUIDs, not full uint64
as TBC and WotLK do.
Without these overrides Classic inherited TBC's implementations, which
over-read PackedGuid fields as fixed 8-byte GUIDs, misaligning all
subsequent damage/heal fields and making combat parsing unusable on
Classic servers.
The Classic override logic is identical to TBC except for the GUID
reads, so combat text, damage numbers, and kill tracking now work
correctly on Vanilla 1.12 connections.
Vanilla 1.12 SMSG_SPELL_START and SMSG_SPELL_GO use:
- PackedGuid (variable-length) for caster and target GUIDs, not full uint64
- uint16 castFlags, not uint32 as in TBC/WotLK
- uint16 targetFlags in SpellCastTargets, not uint32
Without these overrides Classic inherited TBC's implementations which
read 8 bytes for each GUID (over-reading the PackedGuid) and then 4
bytes for castFlags instead of 2, misaligning all subsequent fields
and producing garbage spell IDs, cast times, and target GUIDs.
Hit and miss target GUIDs in SMSG_SPELL_GO are also PackedGuid in
Vanilla (vs full uint64 in TBC), handled by the new parseSpellGo.
TBC 2.4.3 quest log update fields use 4 fields per slot
(questId, state, counts, timer) vs WotLK's 5 (extra counts field).
The wrong stride (5) caused all quest log reads to use wrong indices
beyond the first slot, breaking quest tracking on TBC servers.
TBC 2.4.3 CMSG_QUESTGIVER_QUERY_QUEST is guid(8) + questId(4) = 12
bytes. WotLK added a trailing isDialogContinued(u8) byte that TBC
servers don't expect; sending it caused quest details to not be sent
back on some emulators.
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.
TBC 2.4.3 SMSG_CAST_RESULT sends spellId(u32) + result(u8) = 5 bytes.
WotLK 3.3.5a added a castCount(u8) prefix making it 6 bytes. Without
this fix the WotLK parser was reading spellId[0] as castCount, then the
remaining 3 spellId bytes plus result byte as spellId (wrong), and then
whatever follows as result — producing incorrect failure messages and
potentially not clearing the cast bar on TBC.
Add TbcPacketParsers::parseCastResult override and a virtual base method,
then route SMSG_CAST_RESULT through virtual dispatch in the game handler.
Classic 1.12 SMSG_CREATURE_QUERY_RESPONSE has no iconName CString between
subName and typeFlags. The TBC/WotLK parser was reading the typeFlags
uint32 bytes as the iconName string, then reading the remaining bytes as
typeFlags — producing garbage creature type/family/rank values and
corrupting target frame display for all creatures on Classic servers.
Add ClassicPacketParsers::parseCreatureQueryResponse without the iconName
read, and route the game handler through virtual dispatch so the override
is called.
Add TbcPacketParsers::parseSpellHealLog override using full uint64 GUIDs
(TBC) instead of packed GUIDs (WotLK). Route handleAttackerStateUpdate,
handleSpellDamageLog, and handleSpellHealLog through the virtual
packetParsers_ interface so expansion-specific overrides are actually
called. Previously the game handler bypassed virtual dispatch with
direct static parser calls, making all three TBC overrides dead code.
TBC 2.4.3 SMSG_ATTACKERSTATEUPDATE and SMSG_SPELLNONMELEEDAMAGELOG send
full uint64 GUIDs for attacker/target, while WotLK 3.3.5a uses packed
(variable-length) GUIDs. Using the WotLK reader on TBC packets consumes
1-8 bytes where a fixed 8 are expected, shifting all subsequent reads
and producing completely wrong damage/absorbed/resisted values.
Add TbcPacketParsers overrides that read plain uint64 GUIDs. Also note
that TBC SMSG_SPELLNONMELEEDAMAGELOG lacks the WotLK overkill field.
TBC 2.4.3 differs from WotLK in four ways:
- Header: uint8 count only (WotLK: uint32 totalCount + uint8 shownCount),
so the WotLK parser was reading 4 garbage bytes before the count
- No extra unknown uint32 between itemTextId and stationery in each entry
- Attachment item GUID: full uint64 (WotLK uses uint32 low GUID)
- Attachment enchants: 7 × uint32 id only (WotLK: 7 × {id+duration+charges})
The resulting mis-parse would scramble subject/money/cod/flags for every
mail entry and corrupt all attachment reads. Add TbcPacketParsers::parseMailList
with the correct TBC 2.4.3 format.
SMSG_GOSSIP_MESSAGE quest entries in TBC 2.4.3 do not include
questFlags(u32) or isRepeatable(u8) that WotLK 3.3.5a added.
The WotLK default parser reads these 5 bytes, causing all quest titles
in gossip dialogs to be shifted/corrupted on TBC servers.
Add TbcPacketParsers::parseGossipMessage() which parses quest entries
without those fields, fixing NPC quest list display.
CMSG_CAST_SPELL: WotLK adds a castFlags(u8) byte after spellId that TBC
2.4.3 does not have. Add TbcPacketParsers::buildCastSpell() to omit it,
preventing every spell cast from being rejected by TBC servers.
CMSG_USE_ITEM: WotLK adds a glyphIndex(u32) field between itemGuid and
castFlags that TBC 2.4.3 does not have. Add buildUseItem() override.
SMSG_MONSTER_MOVE: WotLK adds a uint8 unk byte after the packed GUID
(MOVEMENTFLAG2_UNK7 toggle) that TBC 2.4.3 does not have. Add
parseMonsterMove() override to fix NPC movement parsing — without this,
all NPC positions, durations, and waypoints parse from the wrong byte
offset, making all NPC movement appear broken on TBC servers.
- Fix SMSG_ITEM_QUERY_SINGLE_RESPONSE parsing: read statsCount stat
pairs instead of always 10, use 2 damage entries (MAX_ITEM_PROTO_DAMAGES),
and parse item spell data (spellId + spellTrigger per slot)
- Pass item spell ID in CMSG_USE_ITEM packet so server processes item
use requests (spellId=0 caused silent server rejection)
- Add spellId parameter to buildUseItem interface across all expansions
- Fix spellbook mount tab to use SkillLine 777 (Mounts) instead of 762
(Riding), so known mount summon spells appear correctly
- Fix inventory right-click: use IsItemHovered+IsMouseClicked instead
of IsItemClicked for InvisibleButton (which only tracks left-clicks)
- Fix SlotKind enum declaration order in inventory_screen.hpp
- CMSG_SEND_MAIL now includes item GUIDs (up to 12 per WotLK)
- Right-click items in bags to attach when mail compose is open
- Compose window shows 12-slot attachment grid with item icons
- Click attached items to remove them
- Classic/Vanilla falls back to single item GUID format
Normalize WoW quest text tokens during parsing so quest titles/details no longer leak raw markup like and |n into UI. Apply to WotLK and Classic parser paths, including quest list parsing in GameHandler.
Harden quest state handling by mapping abandon requests to authoritative server quest-log slots (PLAYER_QUEST_LOG_START) instead of local vector order, with a guarded fallback when update fields are unavailable.
Improve accept de-duplication by trusting server slot state over stale local cache; allow re-accept when local/server state diverges and trigger resync semantics.
Add expansion-aware CMSG_QUESTGIVER_ACCEPT_QUEST builders: WotLK sends guid+questId+unk1(uint32), while TBC/Classic/Turtle send guid+questId only. Wire GameHandler through PacketParsers for compatibility across expansions and cores.
handleMonsterMove called MonsterMoveParser::parse() directly (WotLK
format with extra uint8 after PackedGuid) instead of going through
the polymorphic packetParsers_->parseMonsterMove(). Added parseVanilla
override to ClassicPacketParsers so classic/turtle use the correct
vanilla format.
TBC 2.4.3 SMSG_ITEM_QUERY_SINGLE_RESPONSE differs from WotLK: no Flags2,
no BuyCount, statsCount-many stat pairs (not always 10), and no
ScalingStatDistribution/ScalingStatValue. Without this override,
TbcPacketParsers fell back to the WotLK parser and misread stats/armor
with a cascading 16-byte offset. Classic (Vanilla) was already safe via
its own independent ClassicPacketParsers::parseItemQueryResponse().
- Add TurtlePacketParsers with dedicated movement block parser (Classic format + transport timestamp)
- Fix quest giver status: read uint32 and translate vanilla enum values for Classic/Turtle
- Fix quest accept packet: remove trailing uint32 that vanilla servers reject
- Fix quest details parser: auto-detect vanilla vs WotLK format (informUnit field)
- Fix spellbook and action bar icons: fallback to WotLK DBC field indices when expansion layout fails
- Fix spell cast failure messages: translate vanilla SpellCastResult codes (+1 offset)
- Fix realm list: correct type values (6=RP, 8=RP-PvP) and population thresholds
- Fix music: disable looping for zone music, auto-advance to next random track when finished
- Add music anti-repeat: avoid playing the same track back-to-back
- Make TBC update block parsing resilient (keep parsed blocks on failure instead of aborting)
- Add right-click attack on hostile mobs
- Add name query diagnostic logging
Vanilla CMSG_ITEM_QUERY_SINGLE has no GUID field (just uint32 entry),
causing servers to reject the oversized WotLK-format packets. Also fix
response parser: remove nonexistent statsCount field and use 5 damage
types instead of 2 to match Vanilla protocol.
Rebuild creature display lookups after expansion-specific DBC layout loads
(was using WotLK defaults before turtle layout was available). Add full
drag-and-drop support for bag items with server-side CMSG_SWAP_ITEM packets.
Add Classic-specific SMSG_ITEM_QUERY_SINGLE_RESPONSE parser for Vanilla
format differences (fewer damage types, no scaling stats, no Flags2).
Fix mailbox right-click (transposed CMSG_GAMEOBJECT_USE opcode, missing
mail opcodes in Turtle WoW JSON, decorative GO type filtering). Add
expansion-aware mail packet handling via PacketParsers: Classic format
(single item, no msgSize prefix, Vanilla field order) vs WotLK format
(attachment arrays, enchant slots). Fix CMSG_MAIL_TAKE_ITEM and
CMSG_MAIL_DELETE for Vanilla (no trailing fields). Add pulsing "New
Mail" indicator below minimap, SMSG_RECEIVED_MAIL and
MSG_QUERY_NEXT_MAIL_TIME handlers, and async sender name backfill.
SpellCastTargets target mask is uint16 in vanilla 1.12.x, not uint32
like WotLK. The 2 extra bytes corrupted every spell packet. Also add
classic CMSG_USE_ITEM builder (bag+slot+spellIndex+targets only, no
spellId/itemGuid/glyphIndex/castFlags fields that WotLK added).
- Parse MCLQ sub-chunks in vanilla ADTs for water rendering (WotLK uses MH2O)
- Load TaxiPathNode.dbc for MO_TRANSPORT world-coordinate paths (vanilla boats)
- Parse data[] from SMSG_GAMEOBJECT_QUERY_RESPONSE (taxiPathId for transports)
- Support vanilla M2 particle emitters (504-byte struct, different from WotLK 476)
- Add character preview texture diagnostic logging
- Fix disconnect handling on character screen (show error only when no chars)
Classic gossip packets lack the menuId field and quest items don't have
questFlags/isRepeatable, causing the WotLK parser to read garbage counts
(541M quests) and hang. Added ClassicPacketParsers::parseGossipMessage
override with the correct vanilla format.
Fix WotLK chat parser not stripping null terminators from messages,
fix channel message local echo missing channelName, expand default
channels to General/Trade/LocalDefense/LookingForGroup with
configurable auto-join, add Classic packet format for join/leave
channel, display channel index prefix in chat, and add Chat settings
tab with timestamps, font size, and auto-join toggles.
Three Classic packet format fixes:
1. CMSG_CAST_SPELL: target flags are uint32 (not uint16). The wrong
size caused the server to misparse the packet as an item enchant
operation, returning "item already enchanted".
2. SMSG_CAST_FAILED: Classic has no castCount byte prefix (added in
TBC). Added parseCastFailed override to ClassicPacketParsers.
Without this, the parser read the wrong bytes and produced
"Spell cast failed (error 0)" for every failure.
3. SMSG_MESSAGECHAT: Removed spurious receiverGuid read for SAY/YELL
types. Classic chat format has no second GUID before the message
body — the extra 8-byte read consumed messageLen + message data,
producing empty chat messages.
Vanilla CMSG_CAST_SPELL target mask is uint16 (not uint32 like WotLK),
the extra 2 bytes were corrupting packets. Also implement full bag
content tracking: extract container slot GUIDs from CONTAINER update
objects, set proper bag sizes, and populate bag items in inventory
rebuild.
- Parse vanilla M2 animation tracks (flat arrays with M2Range indices)
instead of skipping them, fixing T-pose on all vanilla models
- Use C4Quaternion (float[4]) for vanilla bone rotations instead of
CompressedQuat (int16[4]) which produced garbage transforms
- Fix vanilla M2 attachment struct size (48 bytes, not 40) so weapons
attach to correct bones instead of model origin
- Route movement packets through expansion-specific packet parsers
instead of hardcoded WotLK format, fixing server-side position sync
- Fix Spell.dbc field indices for classic/turtle (Name=120, Rank=129,
IconID=117) - were pointing to Portuguese locale column (+7 offset)
- Change guild roster keybind from J to O (WoW default)
- Add guild opcodes for all expansions
- Route SMSG_UPDATE_OBJECT through polymorphic parsers for correct
vanilla format (uint8 updateFlags, 6 speeds vs WotLK uint16/9)
- Fix SMSG_DESTROY_OBJECT for vanilla (8 bytes, no isDeath field)
- Add MSG_MOVE_* handlers for other player movement relay
- Add ClassicPacketParsers::parseMessageChat with targetGuid read
and monster-type name handling
- Resolve chat sender names from player name cache before display
- Fix CSV DBC field 0 always treated as numeric ID (fixes 16+ garbled
Turtle CSVs including Map, AreaTable, Spell, CreatureDisplayInfo)
- Add CSV DBC validation: reject garbled CSVs (>80% zero IDs) and
fall back to binary DBC files
- Fix ItemDisplayInfo texture component field index (14+ not 15+)
for binary DBC with gender-aware suffix resolution
- Spawn other players as visible M2 models via creature callback
- Map name cache dedup prevents overwrites from duplicate CSV records
Replace hardcoded WotLK protocol constants with a data-driven architecture
supporting Classic 1.12.1, TBC 2.4.3, and WotLK 3.3.5a. Each expansion
has JSON profiles for opcodes, update fields, and DBC layouts, plus C++
polymorphic packet parsers for binary format differences (movement flags,
speed fields, transport data, spline format, char enum layout).
Key components:
- ExpansionRegistry: scans Data/expansions/*/expansion.json at startup
- OpcodeTable: logical enum <-> wire values loaded from JSON
- UpdateFieldTable: field indices loaded from JSON per expansion
- DBCLayout: schema-driven DBC field lookups replacing magic numbers
- PacketParsers: WotLK/TBC/Classic parsers with correct flag positions
- Multi-manifest AssetManager: layered manifests with priority ordering
- HDPackManager: overlay texture packs with expansion compatibility
- Auth screen expansion picker replacing hardcoded version dropdown