From f1caf8c03e9fd30fa456860aff291ed4e1e8f6fc Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 2 Mar 2026 08:19:14 -0800 Subject: [PATCH] Fix Stockades crash: suppress area triggers on initial login, handle VK_ERROR_DEVICE_LOST Root cause: LOGIN_VERIFY_WORLD path did not set areaTriggerCheckTimer_ or areaTriggerSuppressFirst_, so the Stockades exit portal (AT 503) fired immediately on login, teleporting the player back to Stormwind and crashing the GPU during the unexpected map transition. Fixes: - Set 5s area trigger cooldown + suppress-first in handleLoginVerifyWorld (same as SMSG_NEW_WORLD handler already did for teleports) - Add deviceLost_ flag to VkContext so beginFrame returns immediately once VK_ERROR_DEVICE_LOST is detected, preventing infinite retry loops - Track device lost from both fence wait and queue submit paths --- include/rendering/vk_context.hpp | 3 +++ src/game/game_handler.cpp | 6 ++++++ src/rendering/renderer.cpp | 1 + src/rendering/vk_context.cpp | 8 ++++++++ 4 files changed, 18 insertions(+) diff --git a/include/rendering/vk_context.hpp b/include/rendering/vk_context.hpp index 4f405073..3a242940 100644 --- a/include/rendering/vk_context.hpp +++ b/include/rendering/vk_context.hpp @@ -73,6 +73,8 @@ public: bool isSwapchainDirty() const { return swapchainDirty; } void markSwapchainDirty() { swapchainDirty = true; } + bool isDeviceLost() const { return deviceLost_; } + // MSAA VkSampleCountFlagBits getMsaaSamples() const { return msaaSamples_; } void setMsaaSamples(VkSampleCountFlagBits samples); @@ -131,6 +133,7 @@ private: std::vector swapchainImageViews; std::vector swapchainFramebuffers; bool swapchainDirty = false; + bool deviceLost_ = false; // Per-frame resources FrameData frames[MAX_FRAMES_IN_FLIGHT]; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 34d2dcb4..d824ab9f 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -3456,6 +3456,12 @@ void GameHandler::handleLoginVerifyWorld(network::Packet& packet) { mountCallback_(0); } + // Suppress area triggers on initial login — prevents exit portals from + // immediately firing when spawning inside a dungeon/instance. + activeAreaTriggers_.clear(); + areaTriggerCheckTimer_ = -5.0f; + areaTriggerSuppressFirst_ = true; + // Send CMSG_SET_ACTIVE_MOVER (required by some servers) if (playerGuid != 0 && socket) { auto activeMoverPacket = SetActiveMoverPacket::build(playerGuid); diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 1bce7855..ceb98ac0 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -946,6 +946,7 @@ void Renderer::applyMsaaChange() { void Renderer::beginFrame() { if (!vkCtx) return; + if (vkCtx->isDeviceLost()) return; // Apply deferred MSAA change between frames (before any rendering state is used) if (msaaChangePending_) { diff --git a/src/rendering/vk_context.cpp b/src/rendering/vk_context.cpp index 8a7fd505..e1a76cee 100644 --- a/src/rendering/vk_context.cpp +++ b/src/rendering/vk_context.cpp @@ -1297,6 +1297,8 @@ bool VkContext::recreateSwapchain(int width, int height) { } VkCommandBuffer VkContext::beginFrame(uint32_t& imageIndex) { + if (deviceLost_) return VK_NULL_HANDLE; + auto& frame = frames[currentFrame]; // Wait for this frame's fence (with timeout to detect GPU hangs) @@ -1309,6 +1311,9 @@ VkCommandBuffer VkContext::beginFrame(uint32_t& imageIndex) { } if (fenceResult != VK_SUCCESS) { LOG_ERROR("beginFrame[", beginFrameCounter, "] fence wait failed: ", (int)fenceResult); + if (fenceResult == VK_ERROR_DEVICE_LOST) { + deviceLost_ = true; + } return VK_NULL_HANDLE; } @@ -1363,6 +1368,9 @@ void VkContext::endFrame(VkCommandBuffer cmd, uint32_t imageIndex) { VkResult submitResult = vkQueueSubmit(graphicsQueue, 1, &submitInfo, frame.inFlightFence); if (submitResult != VK_SUCCESS) { LOG_ERROR("endFrame[", endFrameCounter, "] vkQueueSubmit FAILED: ", (int)submitResult); + if (submitResult == VK_ERROR_DEVICE_LOST) { + deviceLost_ = true; + } } VkPresentInfoKHR presentInfo{};