fix: correct SMSG_ACTION_BUTTONS parsing for Classic and TBC expansions

Classic 1.12 sends 120 action button slots with no leading mode byte
(480 bytes total). TBC 2.4.3 sends 132 slots with no mode byte (528
bytes). WotLK 3.3.5a sends a uint8 mode byte followed by 144 slots
(577 bytes total).

The previous code always consumed a mode byte and assumed 144 slots.
On Classic servers this would misparse the first action button (reading
one byte as the mode, shifting all subsequent entries), causing the
action bar to load garbage spells/items from the server.

Fixed by detecting expansion type at runtime and selecting the
appropriate slot count and presence of mode byte accordingly.
This commit is contained in:
Kelsi 2026-03-10 17:28:20 -07:00
parent 56588e0dad
commit 63c09163dc

View file

@ -3403,15 +3403,28 @@ void GameHandler::handlePacket(network::Packet& packet) {
}
case Opcode::SMSG_ACTION_BUTTONS: {
// uint8 mode (0=initial, 1=update) + 144 × uint32 packed buttons
// packed: bits 0-23 = actionId, bits 24-31 = type
// 0x00 = spell (when id != 0), 0x80 = item, 0x40 = macro (skip)
// Format differences:
// Classic 1.12: no mode byte, 120 slots (480 bytes)
// TBC 2.4.3: no mode byte, 132 slots (528 bytes)
// WotLK 3.3.5a: uint8 mode + 144 slots (577 bytes)
size_t rem = packet.getSize() - packet.getReadPos();
if (rem < 1) break;
/*uint8_t mode =*/ packet.readUInt8();
rem--;
constexpr int SERVER_BAR_SLOTS = 144;
for (int i = 0; i < SERVER_BAR_SLOTS; ++i) {
const bool hasModeByteExp = isActiveExpansion("wotlk");
int serverBarSlots;
if (isClassicLikeExpansion()) {
serverBarSlots = 120;
} else if (isActiveExpansion("tbc")) {
serverBarSlots = 132;
} else {
serverBarSlots = 144;
}
if (hasModeByteExp) {
if (rem < 1) break;
/*uint8_t mode =*/ packet.readUInt8();
rem--;
}
for (int i = 0; i < serverBarSlots; ++i) {
if (rem < 4) break;
uint32_t packed = packet.readUInt32();
rem -= 4;