Reduce Stormwind stutter from spawn retry churn and log I/O

- Cache non-renderable creature display IDs and fail-fast future spawn attempts

- Mark GUIDs tied to non-renderable displays as permanent failures to avoid long retry loops

- Skip queued spawn retry work immediately for known non-renderable display IDs

- Clear non-renderable display cache on expansion reload/logout

- Downgrade high-volume UNIT spawn logs to debug and fix mislabeled time-sync log
This commit is contained in:
Kelsi 2026-02-21 03:29:13 -08:00
parent f8fc34ff2c
commit d40cfcad90
2 changed files with 29 additions and 6 deletions

View file

@ -495,6 +495,7 @@ void Application::reloadExpansionData() {
humanoidExtraMap_.clear();
creatureModelIds_.clear();
creatureRenderPosCache_.clear();
nonRenderableCreatureDisplayIds_.clear();
buildCreatureDisplayLookups();
}
@ -508,6 +509,7 @@ void Application::logoutToLogin() {
weaponsSheathed_ = false;
wasAutoAttacking_ = false;
loadedMapId_ = 0xFFFFFFFF;
nonRenderableCreatureDisplayIds_.clear();
world.reset();
if (renderer) {
// Remove old player model so it doesn't persist into next session
@ -3629,10 +3631,16 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
// Skip if already spawned
if (creatureInstances_.count(guid)) return;
if (nonRenderableCreatureDisplayIds_.count(displayId)) {
creaturePermanentFailureGuids_.insert(guid);
return;
}
// Get model path from displayId
std::string m2Path = getModelPathForDisplayId(displayId);
if (m2Path.empty()) {
nonRenderableCreatureDisplayIds_.insert(displayId);
creaturePermanentFailureGuids_.insert(guid);
return;
}
{
@ -3642,6 +3650,7 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
if (lowerPath.find("invisiblestalker") != std::string::npos ||
lowerPath.find("invisible_stalker") != std::string::npos) {
nonRenderableCreatureDisplayIds_.insert(displayId);
creaturePermanentFailureGuids_.insert(guid);
return;
}
@ -3663,12 +3672,16 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
auto m2Data = assetManager->readFile(m2Path);
if (m2Data.empty()) {
LOG_WARNING("Failed to read creature M2: ", m2Path);
nonRenderableCreatureDisplayIds_.insert(displayId);
creaturePermanentFailureGuids_.insert(guid);
return;
}
pipeline::M2Model model = pipeline::M2Loader::load(m2Data);
if (model.vertices.empty()) {
LOG_WARNING("Failed to parse creature M2: ", m2Path);
nonRenderableCreatureDisplayIds_.insert(displayId);
creaturePermanentFailureGuids_.insert(guid);
return;
}
@ -3695,6 +3708,8 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
if (!charRenderer->loadModel(model, modelId)) {
LOG_WARNING("Failed to load creature model: ", m2Path);
nonRenderableCreatureDisplayIds_.insert(displayId);
creaturePermanentFailureGuids_.insert(guid);
return;
}
@ -5688,6 +5703,14 @@ void Application::processCreatureSpawnQueue() {
PendingCreatureSpawn s = pendingCreatureSpawns_.front();
pendingCreatureSpawns_.erase(pendingCreatureSpawns_.begin());
if (nonRenderableCreatureDisplayIds_.count(s.displayId)) {
pendingCreatureSpawnGuids_.erase(s.guid);
creatureSpawnRetryCounts_.erase(s.guid);
processed++;
rotationsLeft = pendingCreatureSpawns_.size();
continue;
}
const bool needsNewModel = (displayIdModelCache_.find(s.displayId) == displayIdModelCache_.end());
if (needsNewModel && newModelLoads >= MAX_NEW_CREATURE_MODELS_PER_FRAME) {
// Defer additional first-time model/texture loads to later frames so

View file

@ -1885,7 +1885,7 @@ void GameHandler::handlePacket(network::Packet& packet) {
break;
}
uint32_t reason = packet.readUInt32();
LOG_INFO("Resurrect cancel reason: ", reason);
LOG_DEBUG("Time sync request reason: ", reason);
resurrectPending_ = false;
resurrectRequestPending_ = false;
break;
@ -4986,8 +4986,8 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
}
// Trigger creature spawn callback for units/players with displayId
if (block.objectType == ObjectType::UNIT && unit->getDisplayId() == 0) {
LOG_INFO("[Spawn] UNIT guid=0x", std::hex, block.guid, std::dec,
" has displayId=0 — no spawn (entry=", unit->getEntry(), ")");
LOG_DEBUG("[Spawn] UNIT guid=0x", std::hex, block.guid, std::dec,
" has displayId=0 — no spawn (entry=", unit->getEntry(), ")");
}
if ((block.objectType == ObjectType::UNIT || block.objectType == ObjectType::PLAYER) && unit->getDisplayId() != 0) {
if (block.objectType == ObjectType::PLAYER && block.guid == playerGuid) {
@ -5004,9 +5004,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
}
}
} else if (creatureSpawnCallback_) {
LOG_INFO("[Spawn] UNIT guid=0x", std::hex, block.guid, std::dec,
" displayId=", unit->getDisplayId(), " at (",
unit->getX(), ",", unit->getY(), ",", unit->getZ(), ")");
LOG_DEBUG("[Spawn] UNIT guid=0x", std::hex, block.guid, std::dec,
" displayId=", unit->getDisplayId(), " at (",
unit->getX(), ",", unit->getY(), ",", unit->getZ(), ")");
creatureSpawnCallback_(block.guid, unit->getDisplayId(),
unit->getX(), unit->getY(), unit->getZ(), unit->getOrientation());
if (unitInitiallyDead && npcDeathCallback_) {