From aa11ffda727dfedcb48245d2d25ed2d95bbb3748 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 6 Feb 2026 14:56:26 -0800 Subject: [PATCH] Fix terrain streaming loop and auto-select single realm/character Use getRemainingTileCount (pending + readyQueue) and processAllReadyTiles to prevent loading screen from exiting before tiles are finalized. Auto-select realm and character when only one is available. --- include/rendering/terrain_manager.hpp | 6 +++++ src/core/application.cpp | 36 ++++++++++++++++----------- src/rendering/terrain_manager.cpp | 17 +++++++++++++ src/ui/character_screen.cpp | 14 +++++++++++ src/ui/realm_screen.cpp | 10 ++++++++ 5 files changed, 68 insertions(+), 15 deletions(-) diff --git a/include/rendering/terrain_manager.hpp b/include/rendering/terrain_manager.hpp index cee40c12..954872d5 100644 --- a/include/rendering/terrain_manager.hpp +++ b/include/rendering/terrain_manager.hpp @@ -184,8 +184,14 @@ public: */ int getLoadedTileCount() const { return static_cast(loadedTiles.size()); } int getPendingTileCount() const { return static_cast(pendingTiles.size()); } + int getReadyQueueCount() const { return static_cast(readyQueue.size()); } + /** Total unfinished tiles (worker threads + ready queue) */ + int getRemainingTileCount() const { return static_cast(pendingTiles.size() + readyQueue.size()); } TileCoord getCurrentTile() const { return currentTile; } + /** Process all ready tiles immediately (use during loading screens) */ + void processAllReadyTiles(); + private: /** * Get tile coordinates from GL world position diff --git a/src/core/application.cpp b/src/core/application.cpp index 34411ca4..f047cc72 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1434,9 +1434,9 @@ void Application::startSinglePlayer() { auto startTime = std::chrono::high_resolution_clock::now(); const float maxWaitSeconds = 15.0f; - int initialPending = terrainMgr->getPendingTileCount(); + int initialRemaining = terrainMgr->getRemainingTileCount(); - while (terrainMgr->getPendingTileCount() > 0) { + while (terrainMgr->getRemainingTileCount() > 0) { // Poll events to keep window responsive SDL_Event event; while (SDL_PollEvent(&event)) { @@ -1459,18 +1459,19 @@ void Application::startSinglePlayer() { // Process ready tiles from worker threads terrainMgr->update(*camera, 0.016f); + terrainMgr->processAllReadyTiles(); // Update loading screen with tile progress (40% - 85% range) if (loadingScreenOk) { int loaded = terrainMgr->getLoadedTileCount(); - int pending = terrainMgr->getPendingTileCount(); - float tileProgress = (initialPending > 0) - ? static_cast(initialPending - pending) / initialPending + int remaining = terrainMgr->getRemainingTileCount(); + float tileProgress = (initialRemaining > 0) + ? static_cast(initialRemaining - remaining) / initialRemaining : 1.0f; float progress = 0.40f + tileProgress * 0.45f; char buf[128]; snprintf(buf, sizeof(buf), "Loading terrain... %d tiles loaded, %d remaining", - loaded, pending); + loaded, remaining); loadingScreen.setStatus(buf); loadingScreen.setProgress(progress); loadingScreen.render(); @@ -1616,7 +1617,7 @@ void Application::teleportTo(int presetIndex) { auto startTime = std::chrono::high_resolution_clock::now(); const float maxWaitSeconds = 8.0f; - while (terrainMgr->getPendingTileCount() > 0) { + while (terrainMgr->getRemainingTileCount() > 0) { SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { @@ -1626,6 +1627,7 @@ void Application::teleportTo(int presetIndex) { } terrainMgr->update(*camera, 0.016f); + terrainMgr->processAllReadyTiles(); auto elapsed = std::chrono::high_resolution_clock::now() - startTime; if (std::chrono::duration(elapsed).count() > maxWaitSeconds) { @@ -1785,13 +1787,16 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float auto* terrainMgr = renderer->getTerrainManager(); auto* camera = renderer->getCamera(); + // Trigger tile streaming for surrounding area terrainMgr->update(*camera, 1.0f); auto startTime = std::chrono::high_resolution_clock::now(); - const float maxWaitSeconds = 20.0f; - int initialPending = terrainMgr->getPendingTileCount(); + const float maxWaitSeconds = 30.0f; + int initialRemaining = terrainMgr->getRemainingTileCount(); + if (initialRemaining < 1) initialRemaining = 1; - while (terrainMgr->getPendingTileCount() > 0) { + // Wait until all pending + ready-queue tiles are finalized + while (terrainMgr->getRemainingTileCount() > 0) { SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { @@ -1811,18 +1816,19 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float } } + // Trigger new streaming and process ALL ready tiles (not just 2) terrainMgr->update(*camera, 0.016f); + terrainMgr->processAllReadyTiles(); if (loadingScreenOk) { + int remaining = terrainMgr->getRemainingTileCount(); int loaded = terrainMgr->getLoadedTileCount(); - int pending = terrainMgr->getPendingTileCount(); - float tileProgress = (initialPending > 0) - ? static_cast(initialPending - pending) / initialPending - : 1.0f; + float tileProgress = static_cast(initialRemaining - remaining) / initialRemaining; + if (tileProgress < 0.0f) tileProgress = 0.0f; float progress = 0.35f + tileProgress * 0.50f; char buf[128]; snprintf(buf, sizeof(buf), "Loading terrain... %d tiles loaded, %d remaining", - loaded, pending); + loaded, remaining); loadingScreen.setStatus(buf); loadingScreen.setProgress(progress); loadingScreen.render(); diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index 8f569407..22ab20f0 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -661,6 +661,23 @@ void TerrainManager::processReadyTiles() { } } +void TerrainManager::processAllReadyTiles() { + while (true) { + std::unique_ptr pending; + { + std::lock_guard lock(queueMutex); + if (readyQueue.empty()) break; + pending = std::move(readyQueue.front()); + readyQueue.pop(); + } + if (pending) { + TileCoord coord = pending->coord; + finalizeTile(std::move(pending)); + pendingTiles.erase(coord); + } + } +} + void TerrainManager::unloadTile(int x, int y) { TileCoord coord = {x, y}; diff --git a/src/ui/character_screen.cpp b/src/ui/character_screen.cpp index b907ce39..53b9b129 100644 --- a/src/ui/character_screen.cpp +++ b/src/ui/character_screen.cpp @@ -33,6 +33,20 @@ void CharacterScreen::render(game::GameHandler& gameHandler) { gameHandler.requestCharacterList(); } else if (characters.empty()) { ImGui::Text("No characters available."); + } else if (characters.size() == 1 && !characterSelected) { + // Auto-select the only available character + selectedCharacterIndex = 0; + selectedCharacterGuid = characters[0].guid; + characterSelected = true; + std::stringstream ss; + ss << "Entering world with " << characters[0].name << "..."; + setStatus(ss.str()); + if (!gameHandler.isSinglePlayerMode()) { + gameHandler.selectCharacter(characters[0].guid); + } + if (onCharacterSelected) { + onCharacterSelected(characters[0].guid); + } } else { // Character table if (ImGui::BeginTable("CharactersTable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { diff --git a/src/ui/realm_screen.cpp b/src/ui/realm_screen.cpp index 6ae86bc7..9d6549bc 100644 --- a/src/ui/realm_screen.cpp +++ b/src/ui/realm_screen.cpp @@ -28,6 +28,16 @@ void RealmScreen::render(auth::AuthHandler& authHandler) { if (realms.empty()) { ImGui::Text("No realms available. Requesting realm list..."); authHandler.requestRealmList(); + } else if (realms.size() == 1 && !realmSelected && !realms[0].lock) { + // Auto-select the only available realm + selectedRealmIndex = 0; + realmSelected = true; + selectedRealmName = realms[0].name; + selectedRealmAddress = realms[0].address; + setStatus("Auto-selecting realm: " + realms[0].name); + if (onRealmSelected) { + onRealmSelected(selectedRealmName, selectedRealmAddress); + } } else { // Realm table if (ImGui::BeginTable("RealmsTable", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {