From eeeba977eac6a43b97e1c0d00eb064d15700e920 Mon Sep 17 00:00:00 2001 From: phosit Date: Thu, 26 Jun 2025 13:31:52 +0200 Subject: [PATCH] Don't crash if rl-interface port is already in use Replace `ENSURE` with recoverable error handling. Introduce a new error type so that it's distinguishable from other errors. When such an error occurs the program exits with `EXIT_FAILURE`. Fixes: #7475 --- source/main.cpp | 58 ++++++++++++++++++++---------- source/rlinterface/RLInterface.cpp | 3 +- source/rlinterface/RLInterface.h | 6 ++++ 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/source/main.cpp b/source/main.cpp index a8dc337589..2527be2e90 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -636,6 +636,7 @@ static void RunGameOrAtlas(const PS::span argv) g_frequencyFilter = CreateFrequencyFilter(res, 30.0); // run the game + bool rlInterfaceError{false}; bool firstIteration{true}; do { @@ -687,23 +688,31 @@ static void RunGameOrAtlas(const PS::span argv) else if (!InitNonVisual(args)) g_Shutdown = ShutdownType::Quit; - // MSVC doesn't support copy elision in ternary expressions. So we use a lambda instead. - std::optional rlInterface{[&]() -> std::optional - { - if (g_Shutdown == ShutdownType::None) - return CreateRLInterface(args); - else - return std::nullopt; - }()}; - - while (g_Shutdown == ShutdownType::None) + try { - if (isVisual) - Frame(rlInterface ? &*rlInterface : nullptr, fixedFrameFrequency); - else if(rlInterface) - rlInterface->TryApplyMessage(); - else - NonVisualFrame(); + // MSVC doesn't support copy elision in ternary expressions. So we use a lambda instead. + std::optional rlInterface{[&]() -> std::optional + { + if (g_Shutdown == ShutdownType::None) + return CreateRLInterface(args); + else + return std::nullopt; + }()}; + + while (g_Shutdown == ShutdownType::None) + { + if (isVisual) + Frame(rlInterface ? &*rlInterface : nullptr, fixedFrameFrequency); + else if(rlInterface) + rlInterface->TryApplyMessage(); + else + NonVisualFrame(); + } + + } + catch (const RL::SetupError&) + { + rlInterfaceError = true; } ShutdownNetworkAndUI(); @@ -713,6 +722,9 @@ static void RunGameOrAtlas(const PS::span argv) } while (g_Shutdown == ShutdownType::Restart); + if (rlInterfaceError) + throw RL::SetupError{}; + #if OS_MACOSX if (g_Shutdown == ShutdownType::RestartAsAtlas) startNewAtlasProcess(g_Mods.GetEnabledMods()); @@ -755,8 +767,16 @@ extern "C" int main(int argc, char* argv[]) EarlyInit(); // must come at beginning of main - // static_cast is ok, argc is never negative. - RunGameOrAtlas({argv, static_cast(argc)}); + int returnValue{EXIT_SUCCESS}; + try + { + // static_cast is ok, argc is never negative. + RunGameOrAtlas({argv, static_cast(argc)}); + } + catch (const RL::SetupError&) + { + returnValue = EXIT_FAILURE; + } // Shut down profiler initialised by EarlyInit g_Profiler2.Shutdown(); @@ -769,5 +789,5 @@ extern "C" int main(int argc, char* argv[]) wutil_Shutdown(); #endif - return EXIT_SUCCESS; + return returnValue; } diff --git a/source/rlinterface/RLInterface.cpp b/source/rlinterface/RLInterface.cpp index 2a7f7a1510..f98a4e87c4 100644 --- a/source/rlinterface/RLInterface.cpp +++ b/source/rlinterface/RLInterface.cpp @@ -50,7 +50,8 @@ Interface::Interface(const char* server_address) nullptr }; m_Context = mg_start(MgCallback, this, options); - ENSURE(m_Context); + if (!m_Context) + throw SetupError{}; } Interface::~Interface() diff --git a/source/rlinterface/RLInterface.h b/source/rlinterface/RLInterface.h index ef93885d99..c1cc41fd52 100644 --- a/source/rlinterface/RLInterface.h +++ b/source/rlinterface/RLInterface.h @@ -22,6 +22,7 @@ #include "third_party/mongoose/mongoose.h" #include +#include #include #include @@ -57,6 +58,11 @@ struct GameMessage std::vector commands; }; +struct SetupError : std::exception +{ + using std::exception::exception; +}; + /** * Implements an interface providing fundamental capabilities required for reinforcement * learning (over HTTP).