fix(parsing): correct UPDATE_OBJECT PackedGuid, cape textures, and missing asset guards
Some checks failed
Build / Build (arm64) (push) Has been cancelled
Build / Build (x86-64) (push) Has been cancelled
Build / Build (macOS arm64) (push) Has been cancelled
Build / Build (windows-arm64) (push) Has been cancelled
Build / Build (windows-x86-64) (push) Has been cancelled
Security / CodeQL (C/C++) (push) Has been cancelled
Security / Semgrep (push) Has been cancelled
Security / Sanitizer Build (ASan/UBSan) (push) Has been cancelled

- Fix MOVEMENT update type to use readPackedGuid() instead of readUInt64() (WotLK 3.3.5a)
- Add desync diagnostic logging to UPDATE_OBJECT parser for future debugging
- Register MSG_MOVE_SET_COLLISION_HGT (0x518) as skip handler
- Fix cape texture lookup to only try .blp extension variants (4 files)
- Add fileExists() guards for underwear textures referencing missing BLP files (4 files)
- Add spell visual impact→cast M2 path fallback
- Skip WMO doodad instance creation when model load fails
- Demote spell caster position warning to debug level
This commit is contained in:
Kelsi 2026-04-14 06:06:50 -07:00
parent 83eef878fb
commit 01fecbf3e0
9 changed files with 105 additions and 38 deletions

View file

@ -2523,6 +2523,8 @@ void GameHandler::registerOpcodeHandlers() {
};
// GM ticket status (new/updated); no ticket UI yet
registerSkipHandler(Opcode::SMSG_GM_TICKET_STATUS_UPDATE);
// Broadcast of another player's collision height change — cosmetic only.
registerSkipHandler(Opcode::MSG_MOVE_SET_COLLISION_HGT);
// Client uses this outbound; treat inbound variant as no-op for robustness.
registerSkipHandler(Opcode::MSG_MOVE_WORLDPORT_ACK);
// Observed custom server packet (8 bytes). Safe-consume for now.

View file

@ -120,7 +120,7 @@ void SpellHandler::triggerCastVisual(uint32_t spellId, uint64_t casterGuid, uint
uint32_t visualId = resolveSpellVisualId(spellId);
if (visualId == 0) { LOG_WARNING("SpellVisual: triggerCastVisual — visualId=0 for spellId=", spellId); return; }
glm::vec3 casterPos;
if (!resolveUnitPosition(casterGuid, casterPos)) { LOG_WARNING("SpellVisual: triggerCastVisual — cannot resolve caster position"); return; }
if (!resolveUnitPosition(casterGuid, casterPos)) { LOG_DEBUG("SpellVisual: triggerCastVisual — cannot resolve caster position for guid=0x", std::hex, casterGuid, std::dec); return; }
LOG_INFO("SpellVisual: triggerCastVisual visualId=", visualId, " pos=(", casterPos.x, ",", casterPos.y, ",", casterPos.z, ") castTimeMs=", castTimeMs);
svs->playSpellVisualPrecast(visualId, casterPos, castTimeMs);
}

View file

@ -1183,9 +1183,9 @@ bool UpdateObjectParser::parseUpdateBlock(network::Packet& packet, UpdateBlock&
}
case UpdateType::MOVEMENT: {
// Movement update
if (!packet.hasRemaining(8)) return false;
block.guid = packet.readUInt64();
// Movement update — WotLK 3.3.5a uses PackedGuid (NOT full uint64)
if (!packet.hasData()) return false;
block.guid = packet.readPackedGuid();
LOG_DEBUG(" MOVEMENT update for GUID: 0x", std::hex, block.guid, std::dec);
return parseMovementBlock(packet, block);
@ -1288,9 +1288,18 @@ bool UpdateObjectParser::parse(network::Packet& packet, UpdateObjectData& data)
data.blockCount = remainingBlockCount;
data.blocks.reserve(data.blockCount);
// Track last block state for desync diagnostics
uint8_t prevUpdateType = 0;
uint8_t prevObjectType = 0;
uint16_t prevUpdateFlags = 0;
uint32_t prevMoveFlags = 0;
uint64_t prevGuid = 0;
size_t prevReadPos = packet.getReadPos();
for (uint32_t i = 0; i < data.blockCount; ++i) {
LOG_DEBUG("Parsing block ", i + 1, " / ", data.blockCount);
size_t blockStartPos = packet.getReadPos();
UpdateBlock block;
if (!parseUpdateBlock(packet, block)) {
static int parseBlockErrors = 0;
@ -1299,6 +1308,31 @@ bool UpdateObjectParser::parse(network::Packet& packet, UpdateObjectData& data)
LOG_ERROR("Failed to parse update block ", i + 1, " of ", data.blockCount,
" (", i, " blocks parsed, ", lostBlocks, " blocks LOST",
", remaining=", packet.getRemainingSize(), " bytes)");
LOG_ERROR(" blockStartPos=", blockStartPos, " packetSize=", packet.getSize());
if (i > 0) {
LOG_ERROR(" prevBlock: type=", static_cast<int>(prevUpdateType),
" objType=", static_cast<int>(prevObjectType),
" updateFlags=0x", std::hex, prevUpdateFlags,
" moveFlags=0x", prevMoveFlags,
" guid=0x", prevGuid, std::dec,
" startPos=", prevReadPos,
" consumed=", blockStartPos - prevReadPos, " bytes");
}
// Peek at the failing byte(s) for format diagnosis
packet.setReadPos(blockStartPos);
uint8_t peekBytes[8] = {};
size_t peekCount = std::min<size_t>(8, packet.getRemainingSize());
for (size_t p = 0; p < peekCount; ++p)
peekBytes[p] = packet.readUInt8();
LOG_ERROR(" failBytes: ",
std::hex, static_cast<int>(peekBytes[0]), " ",
static_cast<int>(peekBytes[1]), " ",
static_cast<int>(peekBytes[2]), " ",
static_cast<int>(peekBytes[3]), " ",
static_cast<int>(peekBytes[4]), " ",
static_cast<int>(peekBytes[5]), " ",
static_cast<int>(peekBytes[6]), " ",
static_cast<int>(peekBytes[7]), std::dec);
if (parseBlockErrors == 10)
LOG_ERROR("(suppressing further update block parse errors)");
}
@ -1307,6 +1341,12 @@ bool UpdateObjectParser::parse(network::Packet& packet, UpdateObjectData& data)
break;
}
prevUpdateType = static_cast<uint8_t>(block.updateType);
prevObjectType = static_cast<uint8_t>(block.objectType);
prevUpdateFlags = block.updateFlags;
prevMoveFlags = block.moveFlags;
prevGuid = block.guid;
prevReadPos = blockStartPos;
data.blocks.emplace_back(std::move(block));
}