fix(parsing): bail on suspicious maskBlockCount in CREATE_OBJECT blocks

When spline parsing consumes the wrong number of bytes, the subsequent
blockCount read lands on garbage data (e.g. 71 instead of ~5 for UNIT).
Previously the parser logged a warning but continued, reading garbage
mask/field data until hitting truncation. Now it returns false for
CREATE_OBJECT blocks with suspicious counts, letting the block loop
skip cleanly to the next entity.

Also downgrade ~44 diagnostic LOG_WARNING messages to LOG_DEBUG across
17 files (equipment, transport, DBC, heartbeat, chat, GO raypick, etc.)
to reduce log noise and make real warnings visible.
This commit is contained in:
Kelsi 2026-04-05 20:12:17 -07:00
parent e32f4fbff9
commit 069dd36698
18 changed files with 46 additions and 43 deletions

View file

@ -41,7 +41,7 @@ void EntitySpawnCallbackHandler::setupCallbacks() {
uint32_t appearanceBytes,
uint8_t facialFeatures,
float x, float y, float z, float orientation) {
LOG_WARNING("playerSpawnCallback: guid=0x", std::hex, guid, std::dec,
LOG_DEBUG("playerSpawnCallback: guid=0x", std::hex, guid, std::dec,
" race=", static_cast<int>(raceId), " gender=", static_cast<int>(genderId),
" pos=(", x, ",", y, ",", z, ")");
// Skip local player — already spawned as the main character

View file

@ -544,7 +544,7 @@ void EntitySpawner::buildCreatureDisplayLookups() {
if (!extra.bakeName.empty()) withBakeName++;
humanoidExtraMap_[cdie->getUInt32(i, cdieL ? (*cdieL)["ID"] : 0)] = extra;
}
LOG_WARNING("Loaded ", humanoidExtraMap_.size(), " humanoid display extra entries (",
LOG_DEBUG("Loaded ", humanoidExtraMap_.size(), " humanoid display extra entries (",
withBakeName, " with baked textures, ", numEquipSlots, " equip slots, ",
dbcFieldCount, " DBC fields, bakeField=", bakeField, ")");
}

View file

@ -340,14 +340,14 @@ void EntitySpawner::setOnlinePlayerEquipment(uint64_t guid,
if (st.instanceId == 0 || st.modelId == 0) return;
if (st.bodySkinPath.empty()) {
LOG_WARNING("setOnlinePlayerEquipment: bodySkinPath empty for guid=0x", std::hex, guid, std::dec,
LOG_DEBUG("setOnlinePlayerEquipment: bodySkinPath empty for guid=0x", std::hex, guid, std::dec,
" instanceId=", st.instanceId, " — skipping equipment");
return;
}
int nonZeroDisplay = 0;
for (uint32_t d : displayInfoIds) if (d != 0) nonZeroDisplay++;
LOG_WARNING("setOnlinePlayerEquipment: guid=0x", std::hex, guid, std::dec,
LOG_DEBUG("setOnlinePlayerEquipment: guid=0x", std::hex, guid, std::dec,
" instanceId=", st.instanceId, " nonZeroDisplayIds=", nonZeroDisplay,
" head=", displayInfoIds[0], " chest=", displayInfoIds[4],
" legs=", displayInfoIds[6], " mainhand=", displayInfoIds[15]);
@ -1099,7 +1099,7 @@ void EntitySpawner::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_
// (e.g. elevators/lifts). If the server marks it as a transport, always
// notify so TransportManager can animate/carry passengers.
bool isTG = gameHandler_ && gameHandler_->isTransportGuid(guid);
LOG_WARNING("WMO GO spawned: guid=0x", std::hex, guid, std::dec,
LOG_DEBUG("WMO GO spawned: guid=0x", std::hex, guid, std::dec,
" entry=", entry, " displayId=", displayId,
" isTransport=", isTG,
" pos=(", x, ", ", y, ", ", z, ")");
@ -1215,7 +1215,7 @@ void EntitySpawner::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_
// Notify transport system for M2 transports (e.g. Deeprun Tram cars)
if (gameHandler_ && gameHandler_->isTransportGuid(guid)) {
LOG_WARNING("M2 transport spawned: guid=0x", std::hex, guid, std::dec,
LOG_DEBUG("M2 transport spawned: guid=0x", std::hex, guid, std::dec,
" entry=", entry, " displayId=", displayId,
" instanceId=", instanceId);
gameHandler_->notifyTransportSpawned(guid, entry, displayId, x, y, z, orientation);

View file

@ -722,7 +722,7 @@ void EntitySpawner::processDeferredEquipmentQueue() {
setOnlinePlayerEquipment(guid, equipData.first, equipData.second);
return;
}
LOG_WARNING("Equipment async pre-decode for guid=0x", std::hex, guid, std::dec,
LOG_DEBUG("Equipment async pre-decode for guid=0x", std::hex, guid, std::dec,
" textures=", texturePaths.size());
// Launch background BLP pre-decode
@ -991,7 +991,7 @@ void EntitySpawner::processPendingTransportRegistrations() {
}
const uint32_t wmoInstanceId = goIt->second.instanceId;
LOG_WARNING("Registering server transport: GUID=0x", std::hex, pending.guid, std::dec,
LOG_DEBUG("Registering server transport: GUID=0x", std::hex, pending.guid, std::dec,
" entry=", pending.entry, " displayId=", pending.displayId, " wmoInstance=", wmoInstanceId,
" pos=(", pending.x, ", ", pending.y, ", ", pending.z, ")");
@ -1015,17 +1015,17 @@ void EntitySpawner::processPendingTransportRegistrations() {
hasUsablePath = transportManager->hasUsableMovingPathForEntry(pending.entry, 25.0f);
}
LOG_WARNING("Transport path check: entry=", pending.entry, " hasUsablePath=", hasUsablePath,
LOG_DEBUG("Transport path check: entry=", pending.entry, " hasUsablePath=", hasUsablePath,
" preferServerData=", preferServerData, " shipOrZepDisplay=", shipOrZeppelinDisplay);
if (preferServerData) {
if (!hasUsablePath) {
std::vector<glm::vec3> path = { canonicalSpawnPos };
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
LOG_WARNING("Server-first strict registration: stationary fallback for GUID 0x",
LOG_DEBUG("Server-first strict registration: stationary fallback for GUID 0x",
std::hex, pending.guid, std::dec, " entry=", pending.entry);
} else {
LOG_WARNING("Server-first transport registration: using entry DBC path for entry ", pending.entry);
LOG_DEBUG("Server-first transport registration: using entry DBC path for entry ", pending.entry);
}
} else if (!hasUsablePath) {
bool allowZOnly = (pending.displayId == 455 || pending.displayId == 462);
@ -1087,7 +1087,7 @@ void EntitySpawner::processPendingTransportRegistrations() {
}
if (auto* tr = transportManager->getTransport(pending.guid); tr) {
LOG_WARNING("Transport registered: guid=0x", std::hex, pending.guid, std::dec,
LOG_DEBUG("Transport registered: guid=0x", std::hex, pending.guid, std::dec,
" entry=", pending.entry, " displayId=", pending.displayId,
" pathId=", tr->pathId,
" mode=", (tr->useClientAnimation ? "client" : "server"),

View file

@ -228,7 +228,7 @@ void WorldEntryCallbackHandler::setupCallbacks() {
// drives `gameHandler->update()` during warmup. Queue the load here so
// it runs after the current packet handler returns instead of recursing
// from `SMSG_LOGIN_VERIFY_WORLD` / `SMSG_NEW_WORLD`.
LOG_WARNING("Queued world entry: map ", mapId, " pos=(", x, ", ", y, ", ", z, ")");
LOG_DEBUG("Queued world entry: map ", mapId, " pos=(", x, ", ", y, ", ", z, ")");
if (worldLoader_) worldLoader_->setPendingEntry(mapId, x, y, z);
});

View file

@ -174,7 +174,7 @@ void WorldLoader::processPendingEntry() {
if (!pendingWorldEntry_ || loadingWorld_) return;
auto entry = *pendingWorldEntry_;
pendingWorldEntry_.reset();
LOG_WARNING("Processing deferred world entry: map ", entry.mapId);
LOG_DEBUG("Processing deferred world entry: map ", entry.mapId);
if (app_.worldEntryCallbacks_) {
app_.worldEntryCallbacks_->setWorldEntryMovementGraceTimer(2.0f);
app_.worldEntryCallbacks_->setTaxiLandingClampTimer(0.0f);
@ -250,7 +250,7 @@ void WorldLoader::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
// --- Clean up previous map's state on map change ---
// (Same cleanup as logout, but preserves player identity and renderer objects.)
LOG_WARNING("loadOnlineWorldTerrain: mapId=", mapId, " loadedMapId_=", loadedMapId_);
LOG_DEBUG("loadOnlineWorldTerrain: mapId=", mapId, " loadedMapId_=", loadedMapId_);
bool hasRendererData = renderer_ && (renderer_->getWMORenderer() || renderer_->getM2Renderer());
if (loadedMapId_ != 0xFFFFFFFF || hasRendererData) {
LOG_WARNING("Map change: cleaning up old map ", loadedMapId_, " before loading map ", mapId);
@ -448,12 +448,12 @@ void WorldLoader::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
pipeline::WDTInfo wdtInfo;
{
std::string wdtPath = "World\\Maps\\" + mapName + "\\" + mapName + ".wdt";
LOG_WARNING("Reading WDT: ", wdtPath);
LOG_DEBUG("Reading WDT: ", wdtPath);
std::vector<uint8_t> wdtData = assetManager_->readFile(wdtPath);
if (!wdtData.empty()) {
wdtInfo = pipeline::parseWDT(wdtData);
isWMOOnlyMap = wdtInfo.isWMOOnly() && !wdtInfo.rootWMOPath.empty();
LOG_WARNING("WDT result: isWMOOnly=", isWMOOnlyMap, " rootWMO='", wdtInfo.rootWMOPath, "'");
LOG_DEBUG("WDT result: isWMOOnly=", isWMOOnlyMap, " rootWMO='", wdtInfo.rootWMOPath, "'");
} else {
LOG_WARNING("No WDT file found at ", wdtPath);
}
@ -954,7 +954,7 @@ void WorldLoader::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
// If a new world entry was deferred during packet processing,
// stop warming up this map — we'll load the new one after cleanup.
if (pendingWorldEntry_) {
LOG_WARNING("loadOnlineWorldTerrain(map ", mapId,
LOG_DEBUG("loadOnlineWorldTerrain(map ", mapId,
") — deferred world entry pending, stopping warmup");
break;
}
@ -1021,7 +1021,7 @@ void WorldLoader::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
if (auto* tm = renderer_->getTerrainManager()) {
if (tm->getLoadedTileCount() >= 4) {
groundReady = true;
LOG_WARNING("Warmup: using tile-count fallback (", tm->getLoadedTileCount(), " tiles) after ", elapsed, "s");
LOG_DEBUG("Warmup: using tile-count fallback (", tm->getLoadedTileCount(), " tiles) after ", elapsed, "s");
}
}
}
@ -1078,7 +1078,7 @@ void WorldLoader::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
if (pendingWorldEntry_) {
auto entry = *pendingWorldEntry_;
pendingWorldEntry_.reset();
LOG_WARNING("Processing deferred world entry: map ", entry.mapId);
LOG_DEBUG("Processing deferred world entry: map ", entry.mapId);
if (app_.worldEntryCallbacks_) {
app_.worldEntryCallbacks_->setWorldEntryMovementGraceTimer(2.0f);
app_.worldEntryCallbacks_->setTaxiLandingClampTimer(0.0f);