From b24703db308261c4b5eb7bc3dd85b63c26315352 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 20 Feb 2026 02:23:19 -0800 Subject: [PATCH] Drive level-up effects from server packets instead of local field prediction Switch level-up callback triggering to SMSG_LEVELUP_INFO/SMSG_LEVELUP_INFO_ALT so animation and SFX are server-authoritative. Parse the new level directly from the level-up packet, update cached player/character level state, and fire callback only when level increases. Remove duplicate callback firing from UNIT_FIELD_LEVEL update-field handling to prevent double-triggered level-up effects when values sync after the packet. --- src/game/game_handler.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 4b4f9a26..baeeee78 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1777,10 +1777,33 @@ void GameHandler::handlePacket(network::Packet& packet) { case Opcode::SMSG_ENVIRONMENTALDAMAGELOG: case Opcode::SMSG_SET_PROFICIENCY: case Opcode::SMSG_ACTION_BUTTONS: - case Opcode::SMSG_LEVELUP_INFO: - case Opcode::SMSG_LEVELUP_INFO_ALT: break; + case Opcode::SMSG_LEVELUP_INFO: + case Opcode::SMSG_LEVELUP_INFO_ALT: { + // Server-authoritative level-up event. + // First field is always the new level in Classic/TBC/WotLK-era layouts. + if (packet.getSize() - packet.getReadPos() >= 4) { + uint32_t newLevel = packet.readUInt32(); + if (newLevel > 0) { + uint32_t oldLevel = serverPlayerLevel_; + serverPlayerLevel_ = std::max(serverPlayerLevel_, newLevel); + for (auto& ch : characters) { + if (ch.guid == playerGuid) { + ch.level = serverPlayerLevel_; + break; + } + } + if (newLevel > oldLevel && levelUpCallback_) { + levelUpCallback_(newLevel); + } + } + } + // Remaining payload (hp/mana/stat deltas) is optional for our client. + packet.setReadPos(packet.getSize()); + break; + } + case Opcode::SMSG_PLAY_SOUND: if (packet.getSize() - packet.getReadPos() >= 4) { uint32_t soundId = packet.readUInt32(); @@ -5121,7 +5144,6 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { LOG_INFO("Next level XP updated: ", val); } else if (key == ufPlayerLevel) { - uint32_t oldLevel = serverPlayerLevel_; serverPlayerLevel_ = val; LOG_INFO("Level updated: ", val); for (auto& ch : characters) { @@ -5130,9 +5152,6 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { break; } } - if (val > oldLevel && oldLevel > 0 && levelUpCallback_) { - levelUpCallback_(val); - } } else if (key == ufCoinage) { playerMoneyCopper_ = val;