From 4043e47fd501e8bf5cda5c6c7325c47f04926feb Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Feb 2026 18:16:53 -0800 Subject: [PATCH] Fix SMSG_MONSTER_MOVE spline flag values causing NPCs to stand still MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parser used wrong SplineFlag bitmask values that don't match WotLK 3.3.5a: - Animation: 0x00000100 → 0x00400000 (was matching SPLINEFLAG_DONE) - Parabolic: 0x00000200 → 0x00000800 (was matching SPLINEFLAG_FALLING) - Uncompressed path: 0x00040000 → Catmullrom|Flying (0x00082000) The critical bug: SPLINEFLAG_FALLING (0x00000200) is set when NPCs move over sloped terrain during combat. The parser mistook it for parabolic and read 8 extra bytes, misaligning pointCount and the destination coords. hasDest stayed false, the move callback never fired, and NPCs appeared frozen in place. Also fix Animation field read: uint8+int32 (5 bytes) not uint32+uint32 (8 bytes). --- src/game/world_packets.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 1dc41409..fefccb47 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -2194,21 +2194,27 @@ bool MonsterMoveParser::parse(network::Packet& packet, MonsterMoveData& data) { if (packet.getReadPos() + 4 > packet.getSize()) return false; data.splineFlags = packet.readUInt32(); - // Check for animation flag (0x00000100) - if (data.splineFlags & 0x00000100) { - if (packet.getReadPos() + 8 > packet.getSize()) return false; - packet.readUInt32(); // animId - packet.readUInt32(); // effectStartTime + // WotLK 3.3.5a SplineFlags (from TrinityCore/MaNGOS MoveSplineFlag.h): + // Animation = 0x00400000 + // Parabolic = 0x00000800 + // Catmullrom = 0x00080000 \ either means uncompressed (absolute) waypoints + // Flying = 0x00002000 / + + // [if Animation] uint8 animationType + int32 effectStartTime (5 bytes) + if (data.splineFlags & 0x00400000) { + if (packet.getReadPos() + 5 > packet.getSize()) return false; + packet.readUInt8(); // animationType + packet.readUInt32(); // effectStartTime (int32, read as uint32 same size) } // uint32 duration if (packet.getReadPos() + 4 > packet.getSize()) return false; data.duration = packet.readUInt32(); - // Check for parabolic flag (0x00000200) - if (data.splineFlags & 0x00000200) { + // [if Parabolic] float verticalAcceleration + int32 effectStartTime (8 bytes) + if (data.splineFlags & 0x00000800) { if (packet.getReadPos() + 8 > packet.getSize()) return false; - packet.readFloat(); // vertAccel + packet.readFloat(); // verticalAcceleration packet.readUInt32(); // effectStartTime } @@ -2218,10 +2224,9 @@ bool MonsterMoveParser::parse(network::Packet& packet, MonsterMoveData& data) { if (pointCount == 0) return true; - // Read destination point(s) - // If UncompressedPath flag (0x00040000): all points are full float x,y,z - // Otherwise: first is packed destination, rest are packed deltas - bool uncompressed = (data.splineFlags & 0x00040000) != 0; + // Catmullrom or Flying → all waypoints stored as absolute float3 (uncompressed). + // Otherwise: first float3 is final destination, remaining are packed deltas. + bool uncompressed = (data.splineFlags & (0x00080000 | 0x00002000)) != 0; if (uncompressed) { // Read last point as destination