From f44ef7b9ea9b0471e6888289310e08669f19171e Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 14 Mar 2026 22:01:26 -0700 Subject: [PATCH] fix: optimize turtle monster move wrapped parsing --- src/game/game_handler.cpp | 49 ++++++++++++++++++++++++++++---------- src/game/world_packets.cpp | 19 ++++++--------- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 40c4d34b..072944e0 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -16290,6 +16290,22 @@ void GameHandler::handleMonsterMove(network::Packet& packet) { LOG_WARNING(msg, " (occurrence=", failCount, ")"); } }; + auto logWrappedFallbackUsed = [&]() { + static uint32_t wrappedFallbackCount = 0; + ++wrappedFallbackCount; + if (wrappedFallbackCount <= 10 || (wrappedFallbackCount % 100) == 0) { + LOG_WARNING("SMSG_MONSTER_MOVE parsed via wrapped-subpacket fallback", + " (occurrence=", wrappedFallbackCount, ")"); + } + }; + auto logWrappedUncompressedFallbackUsed = [&]() { + static uint32_t wrappedUncompressedFallbackCount = 0; + ++wrappedUncompressedFallbackCount; + if (wrappedUncompressedFallbackCount <= 10 || (wrappedUncompressedFallbackCount % 100) == 0) { + LOG_WARNING("SMSG_MONSTER_MOVE parsed via uncompressed wrapped-subpacket fallback", + " (occurrence=", wrappedUncompressedFallbackCount, ")"); + } + }; auto stripWrappedSubpacket = [&](const std::vector& bytes, std::vector& stripped) -> bool { if (bytes.size() < 3) return false; uint8_t subSize = bytes[0]; @@ -16331,22 +16347,31 @@ void GameHandler::handleMonsterMove(network::Packet& packet) { std::vector stripped; bool hasWrappedForm = stripWrappedSubpacket(decompressed, stripped); - // Try unwrapped payload first (common form), then wrapped-subpacket fallback. - network::Packet decompPacket(packet.getOpcode(), decompressed); - if (!packetParsers_->parseMonsterMove(decompPacket, data)) { - if (!hasWrappedForm) { - logMonsterMoveParseFailure("Failed to parse SMSG_MONSTER_MOVE (decompressed " + - std::to_string(destLen) + " bytes)"); - return; - } + bool parsed = false; + if (hasWrappedForm) { network::Packet wrappedPacket(packet.getOpcode(), stripped); - if (!packetParsers_->parseMonsterMove(wrappedPacket, data)) { + if (packetParsers_->parseMonsterMove(wrappedPacket, data)) { + parsed = true; + logWrappedFallbackUsed(); + } + } + if (!parsed) { + network::Packet decompPacket(packet.getOpcode(), decompressed); + if (packetParsers_->parseMonsterMove(decompPacket, data)) { + parsed = true; + } + } + + if (!parsed) { + if (hasWrappedForm) { logMonsterMoveParseFailure("Failed to parse SMSG_MONSTER_MOVE (decompressed " + std::to_string(destLen) + " bytes, wrapped payload " + std::to_string(stripped.size()) + " bytes)"); - return; + } else { + logMonsterMoveParseFailure("Failed to parse SMSG_MONSTER_MOVE (decompressed " + + std::to_string(destLen) + " bytes)"); } - LOG_WARNING("SMSG_MONSTER_MOVE parsed via wrapped-subpacket fallback"); + return; } } else if (!packetParsers_->parseMonsterMove(packet, data)) { // Some realms occasionally embed an extra [size|opcode] wrapper even when the @@ -16355,7 +16380,7 @@ void GameHandler::handleMonsterMove(network::Packet& packet) { if (stripWrappedSubpacket(rawData, stripped)) { network::Packet wrappedPacket(packet.getOpcode(), stripped); if (packetParsers_->parseMonsterMove(wrappedPacket, data)) { - LOG_WARNING("SMSG_MONSTER_MOVE parsed via uncompressed wrapped-subpacket fallback"); + logWrappedUncompressedFallbackUsed(); } else { logMonsterMoveParseFailure("Failed to parse SMSG_MONSTER_MOVE"); return; diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 23efb3bc..ba036f2d 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -3172,10 +3172,12 @@ bool MonsterMoveParser::parse(network::Packet& packet, MonsterMoveData& data) { if (pointCount == 0) return true; - // Reject extreme point counts from malformed packets. + // Cap pointCount to prevent excessive iteration from malformed packets. constexpr uint32_t kMaxSplinePoints = 1000; if (pointCount > kMaxSplinePoints) { - return false; + LOG_WARNING("SMSG_MONSTER_MOVE: pointCount=", pointCount, " exceeds max ", kMaxSplinePoints, + " (guid=0x", std::hex, data.guid, std::dec, "), capping"); + pointCount = kMaxSplinePoints; } // Catmullrom or Flying → all waypoints stored as absolute float3 (uncompressed). @@ -3183,27 +3185,20 @@ bool MonsterMoveParser::parse(network::Packet& packet, MonsterMoveData& data) { bool uncompressed = (data.splineFlags & (0x00080000 | 0x00002000)) != 0; if (uncompressed) { - const size_t requiredBytes = static_cast(pointCount) * 12ull; - if (packet.getReadPos() + requiredBytes > packet.getSize()) return false; - // Read last point as destination // Skip to last point: each point is 12 bytes for (uint32_t i = 0; i < pointCount - 1; i++) { - if (packet.getReadPos() + 12 > packet.getSize()) return false; + if (packet.getReadPos() + 12 > packet.getSize()) return true; packet.readFloat(); packet.readFloat(); packet.readFloat(); } - if (packet.getReadPos() + 12 > packet.getSize()) return false; + if (packet.getReadPos() + 12 > packet.getSize()) return true; data.destX = packet.readFloat(); data.destY = packet.readFloat(); data.destZ = packet.readFloat(); data.hasDest = true; } else { // Compressed: first 3 floats are the destination (final point) - size_t requiredBytes = 12; - if (pointCount > 1) { - requiredBytes += static_cast(pointCount - 1) * 4ull; - } - if (packet.getReadPos() + requiredBytes > packet.getSize()) return false; + if (packet.getReadPos() + 12 > packet.getSize()) return true; data.destX = packet.readFloat(); data.destY = packet.readFloat(); data.destZ = packet.readFloat();