fix: restore Classic spline fallback to prevent UPDATE_OBJECT packet loss

The previous fix (b8a9efb7) that returned false on spline failure was too
aggressive — it aborted the ENTIRE UPDATE_OBJECT packet, not just one
block. Since many entity spawns (NPCs, other players) share the same
packet, a single spline parse failure killed ALL entities in the batch.

Restored the Classic-format fallback as a last resort after WotLK format
fails. The key difference from the original bug is that WotLK is now
tried FIRST (with proper position save/restore), and Classic only fires
if WotLK fails. This prevents the false-positive match that originally
caused corruption while still handling edge-case spline formats.
This commit is contained in:
Kelsi 2026-03-28 10:52:26 -07:00
parent f4a2a631ab
commit 559f100204

View file

@ -1015,27 +1015,41 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
return true;
};
// WotLK format: durationMod+durationModNext+[ANIMATION]+vertAccel+effectStart+points
if (!bytesAvailable(8)) return false; // durationMod + durationModNext
/*float durationMod =*/ packet.readFloat();
/*float durationModNext =*/ packet.readFloat();
if (splineFlags & 0x00400000) { // SPLINEFLAG_ANIMATION
if (!bytesAvailable(5)) return false;
packet.readUInt8(); packet.readUInt32();
}
// AzerothCore/ChromieCraft always writes verticalAcceleration(float)
// + effectStartTime(uint32) unconditionally -- NOT gated by PARABOLIC flag.
if (!bytesAvailable(8)) return false;
/*float vertAccel =*/ packet.readFloat();
/*uint32_t effectStart =*/ packet.readUInt32();
// Save position before WotLK spline header for fallback
size_t beforeSplineHeader = packet.getReadPos();
// WotLK: compressed unless CYCLIC(0x80000) or ENTER_CYCLE(0x2000) set
bool useCompressed = (splineFlags & (0x00080000 | 0x00002000)) == 0;
bool splineParsed = tryParseSplinePoints(useCompressed, "wotlk-compressed");
// Fallback: try uncompressed WotLK if compressed didn't work
if (!splineParsed) {
splineParsed = tryParseSplinePoints(false, "wotlk-uncompressed");
// Try 1: WotLK format (durationMod+durationModNext+[ANIMATION]+vertAccel+effectStart+points)
bool splineParsed = false;
if (bytesAvailable(8)) {
/*float durationMod =*/ packet.readFloat();
/*float durationModNext =*/ packet.readFloat();
bool wotlkOk = true;
if (splineFlags & 0x00400000) { // SPLINEFLAG_ANIMATION
if (!bytesAvailable(5)) { wotlkOk = false; }
else { packet.readUInt8(); packet.readUInt32(); }
}
// AzerothCore/ChromieCraft always writes verticalAcceleration(float)
// + effectStartTime(uint32) unconditionally -- NOT gated by PARABOLIC flag.
if (wotlkOk) {
if (!bytesAvailable(8)) { wotlkOk = false; }
else { /*float vertAccel =*/ packet.readFloat(); /*uint32_t effectStart =*/ packet.readUInt32(); }
}
if (wotlkOk) {
// WotLK: compressed unless CYCLIC(0x80000) or ENTER_CYCLE(0x2000) set
bool useCompressed = (splineFlags & (0x00080000 | 0x00002000)) == 0;
splineParsed = tryParseSplinePoints(useCompressed, "wotlk-compressed");
if (!splineParsed) {
splineParsed = tryParseSplinePoints(false, "wotlk-uncompressed");
}
}
}
// Try 2: Classic/fallback format (uncompressed points immediately after splineId)
if (!splineParsed) {
packet.setReadPos(beforeSplineHeader);
splineParsed = tryParseSplinePoints(false, "classic-fallback");
}
if (!splineParsed) {
LOG_WARNING("Spline parse failed for guid=0x", std::hex, block.guid, std::dec,
" splineFlags=0x", std::hex, splineFlags, std::dec,