From b46246b74f0fc9edae56ceae89d99c23d3ff3b85 Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Sun, 30 Nov 2025 15:52:39 +0100 Subject: [PATCH] fixed assertion --- src/main.cpp | 43 ++++++++++++++++++++++++++++++---------- src/states/MenuState.cpp | 4 +++- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 5c71528..65747c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "audio/Audio.h" #include "audio/SoundEffect.h" @@ -696,7 +697,6 @@ int main(int, char **) exeDir.string().c_str(), ec.message().c_str()); } #endif - SDL_free(const_cast(basePathRaw)); } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetBasePath() failed; asset lookups rely on current directory: %s", @@ -711,15 +711,13 @@ int main(int, char **) pixelFont.init("assets/fonts/PressStart2P-Regular.ttf", 16); ScoreManager scores; + std::atomic scoresLoadComplete{false}; // Load scores asynchronously but keep the worker alive until shutdown to avoid lifetime issues - std::jthread scoreLoader([&scores]() { + std::jthread scoreLoader([&scores, &scoresLoadComplete]() { scores.load(); + scoresLoadComplete.store(true, std::memory_order_release); }); - const auto ensureScoresLoaded = [&]() { - if (scoreLoader.joinable()) { - scoreLoader.join(); - } - }; + std::jthread menuTrackLoader; Starfield starfield; starfield.init(200, LOGICAL_W, LOGICAL_H); Starfield3D starfield3D; @@ -904,7 +902,7 @@ int main(int, char **) // Allow states to access the state manager for transitions ctx.stateManager = &stateMgr; ctx.game = &game; - ctx.scores = &scores; + ctx.scores = nullptr; // populated once async load finishes ctx.starfield = &starfield; ctx.starfield3D = &starfield3D; ctx.font = &font; @@ -938,6 +936,15 @@ int main(int, char **) running = false; }; + auto ensureScoresLoaded = [&]() { + if (scoreLoader.joinable()) { + scoreLoader.join(); + } + if (!ctx.scores) { + ctx.scores = &scores; + } + }; + auto beginStateFade = [&](AppState targetState, bool armGameplayCountdown) { if (!ctx.stateManager) { return; @@ -1023,6 +1030,10 @@ int main(int, char **) // Playing, LevelSelect and GameOver currently use inline logic in main; we'll migrate later while (running) { + if (!ctx.scores && scoresLoadComplete.load(std::memory_order_acquire)) { + ensureScoresLoaded(); + } + int winW = 0, winH = 0; SDL_GetWindowSize(window, &winW, &winH); @@ -1460,14 +1471,17 @@ int main(int, char **) // Load menu track once on first menu entry (in background to avoid blocking) static bool menuTrackLoaded = false; if (!menuTrackLoaded) { - std::thread([]() { + if (menuTrackLoader.joinable()) { + menuTrackLoader.join(); + } + menuTrackLoader = std::jthread([]() { std::string menuTrack = AssetPath::resolveWithExtensions("assets/music/Every Block You Take", { ".mp3" }); if (!menuTrack.empty()) { Audio::instance().setMenuTrack(menuTrack); } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Menu track not found (Every Block You Take)"); } - }).detach(); + }); menuTrackLoaded = true; } @@ -1959,6 +1973,15 @@ int main(int, char **) // Save settings on exit Settings::instance().save(); + if (scoreLoader.joinable()) { + scoreLoader.join(); + if (!ctx.scores) { + ctx.scores = &scores; + } + } + if (menuTrackLoader.joinable()) { + menuTrackLoader.join(); + } lineEffect.shutdown(); Audio::instance().shutdown(); SoundEffectManager::instance().shutdown(); diff --git a/src/states/MenuState.cpp b/src/states/MenuState.cpp index 40572b7..11a6adc 100644 --- a/src/states/MenuState.cpp +++ b/src/states/MenuState.cpp @@ -9,6 +9,7 @@ #include #include #include +#include // Use dynamic logical dimensions from GlobalState instead of hardcoded values // This allows the UI to adapt when the window is resized or goes fullscreen @@ -239,7 +240,8 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi // High scores table with wave offset float scoresStartY = topPlayersY + 70; // more spacing under title - const auto &hs = ctx.scores ? ctx.scores->all() : *(new std::vector()); + static const std::vector EMPTY_SCORES; + const auto& hs = ctx.scores ? ctx.scores->all() : EMPTY_SCORES; size_t maxDisplay = std::min(hs.size(), size_t(12)); // Draw table header