From 53405ea32243002ac008030be21a5aaceb72a89b Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 25 Feb 2026 12:09:00 -0800 Subject: [PATCH] Fix crash on re-login by clearing all per-session state on logout logoutToLogin() was only clearing a handful of flags, leaving stale entity instance maps, pending spawn queues, transport state, mount state, and charge state from the previous session. On second login, these stale GUIDs and instance IDs caused invalid renderer operations and crashes. Now clears: creature/player/gameObject instance maps, all pending spawn queues, transport doodad batches, mount/charge state, player identity, and renderer world geometry (WMO instances, M2 models, quest markers). Also disconnects TransportManager from WMORenderer before teardown to prevent dangling pointer access. --- src/core/application.cpp | 75 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/core/application.cpp b/src/core/application.cpp index 9e34d7aa..ee2697e7 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -578,25 +578,100 @@ void Application::reloadExpansionData() { void Application::logoutToLogin() { LOG_INFO("Logout requested"); + + // Disconnect TransportManager from WMORenderer before tearing down + if (gameHandler && gameHandler->getTransportManager()) { + gameHandler->getTransportManager()->setWMORenderer(nullptr); + } + if (gameHandler) { gameHandler->disconnect(); } + + // --- Per-session flags --- npcsSpawned = false; playerCharacterSpawned = false; weaponsSheathed_ = false; wasAutoAttacking_ = false; loadedMapId_ = 0xFFFFFFFF; + lastTaxiFlight_ = false; + taxiLandingClampTimer_ = 0.0f; + worldEntryMovementGraceTimer_ = 0.0f; + facingSendCooldown_ = 0.0f; + lastSentCanonicalYaw_ = 1000.0f; + taxiStreamCooldown_ = 0.0f; + idleYawned_ = false; + + // --- Charge state --- + chargeActive_ = false; + chargeTimer_ = 0.0f; + chargeDuration_ = 0.0f; + chargeTargetGuid_ = 0; + + // --- Player identity --- + spawnedPlayerGuid_ = 0; + spawnedAppearanceBytes_ = 0; + spawnedFacialFeatures_ = 0; + + // --- Mount state --- + mountInstanceId_ = 0; + mountModelId_ = 0; + pendingMountDisplayId_ = 0; + + // --- Creature instance tracking --- + creatureInstances_.clear(); + creatureModelIds_.clear(); + creatureRenderPosCache_.clear(); + creatureWeaponsAttached_.clear(); + creatureWeaponAttachAttempts_.clear(); + deadCreatureGuids_.clear(); nonRenderableCreatureDisplayIds_.clear(); + creaturePermanentFailureGuids_.clear(); + + // --- Creature spawn queues --- + pendingCreatureSpawns_.clear(); + pendingCreatureSpawnGuids_.clear(); + creatureSpawnRetryCounts_.clear(); + + // --- Player instance tracking --- + playerInstances_.clear(); + onlinePlayerAppearance_.clear(); + pendingOnlinePlayerEquipment_.clear(); + deferredEquipmentQueue_.clear(); + pendingPlayerSpawns_.clear(); + pendingPlayerSpawnGuids_.clear(); + + // --- GameObject instance tracking --- + gameObjectInstances_.clear(); + pendingGameObjectSpawns_.clear(); + pendingTransportMoves_.clear(); + pendingTransportDoodadBatches_.clear(); + world.reset(); + if (renderer) { // Remove old player model so it doesn't persist into next session if (auto* charRenderer = renderer->getCharacterRenderer()) { charRenderer->removeInstance(1); } + // Clear all world geometry renderers + if (auto* wmo = renderer->getWMORenderer()) { + wmo->clearInstances(); + } + if (auto* m2 = renderer->getM2Renderer()) { + m2->clear(); + } + // TerrainManager will be re-initialized on next world entry + if (auto* questMarkers = renderer->getQuestMarkerRenderer()) { + questMarkers->clear(); + } + renderer->clearMount(); + renderer->setCharacterFollow(0); if (auto* music = renderer->getMusicManager()) { music->stopMusic(0.0f); } } + // Clear stale realm/character selection so switching servers starts fresh if (uiManager) { uiManager->getRealmScreen().reset();