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.
This commit is contained in:
Kelsi 2026-02-06 14:56:26 -08:00
parent 81d712121e
commit aa11ffda72
5 changed files with 68 additions and 15 deletions

View file

@ -184,8 +184,14 @@ public:
*/ */
int getLoadedTileCount() const { return static_cast<int>(loadedTiles.size()); } int getLoadedTileCount() const { return static_cast<int>(loadedTiles.size()); }
int getPendingTileCount() const { return static_cast<int>(pendingTiles.size()); } int getPendingTileCount() const { return static_cast<int>(pendingTiles.size()); }
int getReadyQueueCount() const { return static_cast<int>(readyQueue.size()); }
/** Total unfinished tiles (worker threads + ready queue) */
int getRemainingTileCount() const { return static_cast<int>(pendingTiles.size() + readyQueue.size()); }
TileCoord getCurrentTile() const { return currentTile; } TileCoord getCurrentTile() const { return currentTile; }
/** Process all ready tiles immediately (use during loading screens) */
void processAllReadyTiles();
private: private:
/** /**
* Get tile coordinates from GL world position * Get tile coordinates from GL world position

View file

@ -1434,9 +1434,9 @@ void Application::startSinglePlayer() {
auto startTime = std::chrono::high_resolution_clock::now(); auto startTime = std::chrono::high_resolution_clock::now();
const float maxWaitSeconds = 15.0f; 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 // Poll events to keep window responsive
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
@ -1459,18 +1459,19 @@ void Application::startSinglePlayer() {
// Process ready tiles from worker threads // Process ready tiles from worker threads
terrainMgr->update(*camera, 0.016f); terrainMgr->update(*camera, 0.016f);
terrainMgr->processAllReadyTiles();
// Update loading screen with tile progress (40% - 85% range) // Update loading screen with tile progress (40% - 85% range)
if (loadingScreenOk) { if (loadingScreenOk) {
int loaded = terrainMgr->getLoadedTileCount(); int loaded = terrainMgr->getLoadedTileCount();
int pending = terrainMgr->getPendingTileCount(); int remaining = terrainMgr->getRemainingTileCount();
float tileProgress = (initialPending > 0) float tileProgress = (initialRemaining > 0)
? static_cast<float>(initialPending - pending) / initialPending ? static_cast<float>(initialRemaining - remaining) / initialRemaining
: 1.0f; : 1.0f;
float progress = 0.40f + tileProgress * 0.45f; float progress = 0.40f + tileProgress * 0.45f;
char buf[128]; char buf[128];
snprintf(buf, sizeof(buf), "Loading terrain... %d tiles loaded, %d remaining", snprintf(buf, sizeof(buf), "Loading terrain... %d tiles loaded, %d remaining",
loaded, pending); loaded, remaining);
loadingScreen.setStatus(buf); loadingScreen.setStatus(buf);
loadingScreen.setProgress(progress); loadingScreen.setProgress(progress);
loadingScreen.render(); loadingScreen.render();
@ -1616,7 +1617,7 @@ void Application::teleportTo(int presetIndex) {
auto startTime = std::chrono::high_resolution_clock::now(); auto startTime = std::chrono::high_resolution_clock::now();
const float maxWaitSeconds = 8.0f; const float maxWaitSeconds = 8.0f;
while (terrainMgr->getPendingTileCount() > 0) { while (terrainMgr->getRemainingTileCount() > 0) {
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) { if (event.type == SDL_QUIT) {
@ -1626,6 +1627,7 @@ void Application::teleportTo(int presetIndex) {
} }
terrainMgr->update(*camera, 0.016f); terrainMgr->update(*camera, 0.016f);
terrainMgr->processAllReadyTiles();
auto elapsed = std::chrono::high_resolution_clock::now() - startTime; auto elapsed = std::chrono::high_resolution_clock::now() - startTime;
if (std::chrono::duration<float>(elapsed).count() > maxWaitSeconds) { if (std::chrono::duration<float>(elapsed).count() > maxWaitSeconds) {
@ -1785,13 +1787,16 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
auto* terrainMgr = renderer->getTerrainManager(); auto* terrainMgr = renderer->getTerrainManager();
auto* camera = renderer->getCamera(); auto* camera = renderer->getCamera();
// Trigger tile streaming for surrounding area
terrainMgr->update(*camera, 1.0f); terrainMgr->update(*camera, 1.0f);
auto startTime = std::chrono::high_resolution_clock::now(); auto startTime = std::chrono::high_resolution_clock::now();
const float maxWaitSeconds = 20.0f; const float maxWaitSeconds = 30.0f;
int initialPending = terrainMgr->getPendingTileCount(); 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; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) { 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->update(*camera, 0.016f);
terrainMgr->processAllReadyTiles();
if (loadingScreenOk) { if (loadingScreenOk) {
int remaining = terrainMgr->getRemainingTileCount();
int loaded = terrainMgr->getLoadedTileCount(); int loaded = terrainMgr->getLoadedTileCount();
int pending = terrainMgr->getPendingTileCount(); float tileProgress = static_cast<float>(initialRemaining - remaining) / initialRemaining;
float tileProgress = (initialPending > 0) if (tileProgress < 0.0f) tileProgress = 0.0f;
? static_cast<float>(initialPending - pending) / initialPending
: 1.0f;
float progress = 0.35f + tileProgress * 0.50f; float progress = 0.35f + tileProgress * 0.50f;
char buf[128]; char buf[128];
snprintf(buf, sizeof(buf), "Loading terrain... %d tiles loaded, %d remaining", snprintf(buf, sizeof(buf), "Loading terrain... %d tiles loaded, %d remaining",
loaded, pending); loaded, remaining);
loadingScreen.setStatus(buf); loadingScreen.setStatus(buf);
loadingScreen.setProgress(progress); loadingScreen.setProgress(progress);
loadingScreen.render(); loadingScreen.render();

View file

@ -661,6 +661,23 @@ void TerrainManager::processReadyTiles() {
} }
} }
void TerrainManager::processAllReadyTiles() {
while (true) {
std::unique_ptr<PendingTile> pending;
{
std::lock_guard<std::mutex> 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) { void TerrainManager::unloadTile(int x, int y) {
TileCoord coord = {x, y}; TileCoord coord = {x, y};

View file

@ -33,6 +33,20 @@ void CharacterScreen::render(game::GameHandler& gameHandler) {
gameHandler.requestCharacterList(); gameHandler.requestCharacterList();
} else if (characters.empty()) { } else if (characters.empty()) {
ImGui::Text("No characters available."); 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 { } else {
// Character table // Character table
if (ImGui::BeginTable("CharactersTable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { if (ImGui::BeginTable("CharactersTable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {

View file

@ -28,6 +28,16 @@ void RealmScreen::render(auth::AuthHandler& authHandler) {
if (realms.empty()) { if (realms.empty()) {
ImGui::Text("No realms available. Requesting realm list..."); ImGui::Text("No realms available. Requesting realm list...");
authHandler.requestRealmList(); 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 { } else {
// Realm table // Realm table
if (ImGui::BeginTable("RealmsTable", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { if (ImGui::BeginTable("RealmsTable", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {