diff --git a/src/base/system.cpp b/src/base/system.cpp index 47528f825..1f4059860 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -169,6 +169,7 @@ static NETSOCKET_INTERNAL invalid_socket = {NETTYPE_INVALID, -1, -1, -1}; #define AF_WEBSOCKET_INET (0xee) std::atomic_bool dbg_assert_failing = false; +DBG_ASSERT_HANDLER dbg_assert_handler; bool dbg_assert_has_failed() { @@ -179,8 +180,17 @@ void dbg_assert_imp(const char *filename, int line, int test, const char *msg) { if(!test) { + const bool already_failing = dbg_assert_has_failed(); dbg_assert_failing.store(true, std::memory_order_release); - dbg_msg("assert", "%s(%d): %s", filename, line, msg); + char error[256]; + str_format(error, sizeof(error), "%s(%d): %s", filename, line, msg); + dbg_msg("assert", "%s", error); + if(!already_failing) + { + DBG_ASSERT_HANDLER handler = dbg_assert_handler; + if(handler) + handler(error); + } log_global_logger_finish(); dbg_break(); } @@ -195,6 +205,11 @@ void dbg_break() #endif } +void dbg_assert_set_handler(DBG_ASSERT_HANDLER handler) +{ + dbg_assert_handler = std::move(handler); +} + void dbg_msg(const char *sys, const char *fmt, ...) { va_list args; diff --git a/src/base/system.h b/src/base/system.h index 765c822f4..7a6b1e5f3 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -108,6 +108,9 @@ bool dbg_assert_has_failed(); void dbg_break(); +typedef std::function DBG_ASSERT_HANDLER; +void dbg_assert_set_handler(DBG_ASSERT_HANDLER handler); + /** * Prints a debug message. * diff --git a/src/engine/client.h b/src/engine/client.h index 466e9e00a..5ae3cac5b 100644 --- a/src/engine/client.h +++ b/src/engine/client.h @@ -285,6 +285,14 @@ public: virtual void ShellRegister() = 0; virtual void ShellUnregister() = 0; #endif + + enum EMessageBoxType + { + MESSAGE_BOX_TYPE_ERROR, + MESSAGE_BOX_TYPE_WARNING, + MESSAGE_BOX_TYPE_INFO, + }; + virtual void ShowMessageBox(const char *pTitle, const char *pMessage, EMessageBoxType Type = MESSAGE_BOX_TYPE_ERROR) = 0; }; class IGameClient : public IInterface diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index a4ca7d332..223862129 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -3007,6 +3007,7 @@ void CClient::Run() if(RegisterFail || m_pGraphics->Init() != 0) { dbg_msg("client", "couldn't init graphics"); + ShowMessageBox("Graphics Error", "The graphics could not be initialized."); return; } } @@ -3024,45 +3025,12 @@ void CClient::Run() #endif #ifndef CONF_WEBASM - // open socket + char aNetworkError[256]; + if(!InitNetworkClient(aNetworkError, sizeof(aNetworkError))) { - NETADDR BindAddr; - if(g_Config.m_Bindaddr[0] == '\0') - { - mem_zero(&BindAddr, sizeof(BindAddr)); - } - else if(net_host_lookup(g_Config.m_Bindaddr, &BindAddr, NETTYPE_ALL) != 0) - { - dbg_msg("client", "The configured bindaddr '%s' cannot be resolved", g_Config.m_Bindaddr); - return; - } - BindAddr.type = NETTYPE_ALL; - for(unsigned int i = 0; i < std::size(m_aNetClient); i++) - { - int &PortRef = i == CONN_MAIN ? g_Config.m_ClPort : i == CONN_DUMMY ? g_Config.m_ClDummyPort : g_Config.m_ClContactPort; - if(PortRef < 1024) // Reject users setting ports that we don't want to use - { - PortRef = 0; - } - BindAddr.port = PortRef; - unsigned RemainingAttempts = 25; - while(BindAddr.port == 0 || !m_aNetClient[i].Open(BindAddr)) - { - if(BindAddr.port != 0) - { - --RemainingAttempts; - if(RemainingAttempts == 0) - { - if(g_Config.m_Bindaddr[0]) - dbg_msg("client", "Could not open network client, try changing or unsetting the bindaddr '%s'", g_Config.m_Bindaddr); - else - dbg_msg("client", "Could not open network client"); - return; - } - } - BindAddr.port = (secure_rand() % 64511) + 1024; - } - } + dbg_msg("client", "%s", aNetworkError); + ShowMessageBox("Network Error", aNetworkError); + return; } #endif @@ -3421,6 +3389,48 @@ void CClient::Run() delete m_pEditor; } +bool CClient::InitNetworkClient(char *pError, size_t ErrorSize) +{ + NETADDR BindAddr; + if(g_Config.m_Bindaddr[0] == '\0') + { + mem_zero(&BindAddr, sizeof(BindAddr)); + } + else if(net_host_lookup(g_Config.m_Bindaddr, &BindAddr, NETTYPE_ALL) != 0) + { + str_format(pError, ErrorSize, "The configured bindaddr '%s' cannot be resolved.", g_Config.m_Bindaddr); + return false; + } + BindAddr.type = NETTYPE_ALL; + for(unsigned int i = 0; i < std::size(m_aNetClient); i++) + { + int &PortRef = i == CONN_MAIN ? g_Config.m_ClPort : i == CONN_DUMMY ? g_Config.m_ClDummyPort : g_Config.m_ClContactPort; + if(PortRef < 1024) // Reject users setting ports that we don't want to use + { + PortRef = 0; + } + BindAddr.port = PortRef; + unsigned RemainingAttempts = 25; + while(BindAddr.port == 0 || !m_aNetClient[i].Open(BindAddr)) + { + if(BindAddr.port != 0) + { + --RemainingAttempts; + if(RemainingAttempts == 0) + { + if(g_Config.m_Bindaddr[0]) + str_format(pError, ErrorSize, "Could not open the network client, try changing or unsetting the bindaddr '%s'.", g_Config.m_Bindaddr); + else + str_format(pError, ErrorSize, "Could not open the network client."); + return false; + } + } + BindAddr.port = (secure_rand() % 64511) + 1024; + } + } + return true; +} + bool CClient::CtrlShiftKey(int Key, bool &Last) { if(Input()->ModifierIsPressed() && Input()->ShiftIsPressed() && !Last && Input()->KeyIsPressed(Key)) @@ -4530,6 +4540,7 @@ int main(int argc, const char **argv) CWindowsComLifecycle WindowsComLifecycle(true); #endif CCmdlineFix CmdlineFix(&argc, &argv); + bool Silent = false; bool RandInitFailed = false; @@ -4578,6 +4589,12 @@ int main(int argc, const char **argv) pKernel->RegisterInterface(pClient, false); pClient->RegisterInterfaces(); + dbg_assert_set_handler([pClient](const char *pMsg) { + char aMessage[256]; + str_format(aMessage, sizeof(aMessage), "An assertion error occured. Please write down or take a screenshot of the following information and report this error.\n\n%s", pMsg); + pClient->ShowMessageBox("Assertion Error", aMessage); + }); + // create the components IEngine *pEngine = CreateEngine(GAME_NAME, pFutureConsoleLogger, 2); IConsole *pConsole = CreateConsole(CFGFLAG_CLIENT).release(); @@ -4603,7 +4620,9 @@ int main(int argc, const char **argv) if(RandInitFailed) { - dbg_msg("secure", "could not initialize secure RNG"); + const char *pError = "Failed to initialize the secure RNG."; + dbg_msg("secure", "%s", pError); + pClient->ShowMessageBox("Secure RNG Error", pError); return -1; } @@ -4635,6 +4654,9 @@ int main(int argc, const char **argv) if(RegisterFail) { + const char *pError = "Failed to register an interface."; + dbg_msg("client", "%s", pError); + pClient->ShowMessageBox("Kernel Error", pError); delete pKernel; pClient->~CClient(); free(pClient); @@ -4718,7 +4740,10 @@ int main(int argc, const char **argv) // init SDL if(SDL_Init(0) < 0) { - dbg_msg("client", "unable to init SDL base: %s", SDL_GetError()); + char aError[256]; + str_format(aError, sizeof(aError), "Unable to initialize SDL base: %s", SDL_GetError()); + dbg_msg("client", "%s", aError); + pClient->ShowMessageBox("SDL Error", aError); return -1; } @@ -4956,3 +4981,23 @@ void CClient::ShellUnregister() shell_update(); } #endif + +static Uint32 GetSdlMessageBoxFlags(IClient::EMessageBoxType Type) +{ + switch(Type) + { + case IClient::MESSAGE_BOX_TYPE_ERROR: + return SDL_MESSAGEBOX_ERROR; + case IClient::MESSAGE_BOX_TYPE_WARNING: + return SDL_MESSAGEBOX_WARNING; + case IClient::MESSAGE_BOX_TYPE_INFO: + return SDL_MESSAGEBOX_INFORMATION; + } + dbg_assert(false, "Type invalid"); + return 0; +} + +void CClient::ShowMessageBox(const char *pTitle, const char *pMessage, EMessageBoxType Type) +{ + SDL_ShowSimpleMessageBox(GetSdlMessageBoxFlags(Type), pTitle, pMessage, nullptr); +} diff --git a/src/engine/client/client.h b/src/engine/client/client.h index df2f034b6..70201d8c9 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -425,6 +425,7 @@ public: void Run(); + bool InitNetworkClient(char *pError, size_t ErrorSize); bool CtrlShiftKey(int Key, bool &Last); static void Con_Connect(IConsole::IResult *pResult, void *pUserData); @@ -549,6 +550,8 @@ public: void ShellRegister() override; void ShellUnregister() override; #endif + + void ShowMessageBox(const char *pTitle, const char *pMessage, EMessageBoxType Type = MESSAGE_BOX_TYPE_ERROR) override; }; #endif