diff --git a/src/ending/code_80280000.c b/src/ending/code_80280000.c index 815f1b229..467bf62bd 100644 --- a/src/ending/code_80280000.c +++ b/src/ending/code_80280000.c @@ -137,8 +137,8 @@ void load_credits(void) { CM_CleanCameras(); Vec3f spawn = {0.0f, 0.0f, 0.0f}; Camera* camera = CM_AddCamera(spawn, 0, 0); - if (NULL == camera) { - return; + if (!camera) { + CM_ThrowRuntimeError("[code_80280000] [load_credits] NULL camera while attempting to create camera for player one"); } CM_AttachCamera(camera, PLAYER_ONE); diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 8f50da220..4242815c1 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -906,6 +906,29 @@ void push_frame() { // Graphics_ThreadUpdate();w } +void CM_ThrowRuntimeError(const char* fmt, ...) { + char error_mesg[2048]; + + va_list args; + va_start(args, fmt); + vsnprintf(error_mesg, sizeof(error_mesg), fmt, args); + va_end(args); + + const char* crash_desc = "\nSpaghettiKart has crashed! Please upload the logs to the support channel in Discord."; + strncat(error_mesg, crash_desc, sizeof(error_mesg) - strlen(error_mesg) - 1); + + SPDLOG_ERROR(error_mesg); + + SDL_ShowSimpleMessageBox( + SDL_MESSAGEBOX_ERROR, + "You dropped your plate of Spaghetti!", + error_mesg, + NULL + ); + + exit(EXIT_FAILURE); +} + #ifdef _WIN32 int SDL_main(int argc, char** argv) { #else diff --git a/src/port/Game.h b/src/port/Game.h index dbf2a7cea..2fcda177d 100644 --- a/src/port/Game.h +++ b/src/port/Game.h @@ -228,6 +228,22 @@ void* GetCup(); void CM_RunGarbageCollector(void); void CM_ResetAudio(void); +// Add print formatting check attribute +#if defined(__GNUC__) || defined(__clang__) +__attribute__((format(printf, 1, 2))) +#endif + +// Add noreturn attribute +#if defined(_MSC_VER) + __declspec(noreturn) +#elif defined(__APPLE__) +#elif defined(__cplusplus) || (__STDC_VERSION__ >= 202311) + [[noreturn]] +#elif defined(__GNUC__) + __attribute__((noreturn)) +#endif +void CM_ThrowRuntimeError(const char* fmt, ...); + // NOLINTEND(readability-identifier-naming) #ifdef __cplusplus diff --git a/src/spawn_players.c b/src/spawn_players.c index fd4ef174b..a5e5d739f 100644 --- a/src/spawn_players.c +++ b/src/spawn_players.c @@ -1241,8 +1241,10 @@ void spawn_players_and_cameras(void) { // Add freecam, tourcam, and lookbehind cameras Vec3f spawn = {player->pos[0], player->pos[1], player->pos[2]}; - camera = CM_AddFreeCamera(spawn, player->rotation[1], 1); + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [spawn_players_and_cameras] NULL camera while attempting to create FreeCamera for player one"); + } gScreenContexts[PLAYER_ONE].freeCamera = camera; if (CVarGetInteger("gFreecam", false) == true) { @@ -1252,18 +1254,20 @@ void spawn_players_and_cameras(void) { if ((CM_IsTourEnabled() == true) && (gModeSelection == GRAND_PRIX) && (Editor_IsPaused() == false)) { camera = CM_AddTourCamera(spawn, player->rotation[1], 1); - if (NULL != camera) { - CM_AttachCamera(camera, PLAYER_ONE); - gScreenContexts[PLAYER_ONE].camera = camera; - gScreenContexts[PLAYER_ONE].pendingCamera = NULL; - CM_CameraSetActive(0, false); - CM_ActivateTourCamera(camera); + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [spawn_players_and_cameras] NULL camera while attempting to create TourCamera for player one"); } + CM_AttachCamera(camera, PLAYER_ONE); + gScreenContexts[PLAYER_ONE].camera = camera; + gScreenContexts[PLAYER_ONE].pendingCamera = NULL; + CM_CameraSetActive(0, false); + CM_ActivateTourCamera(camera); } } void spawn_single_player_camera(u32 mode) { Vec3f spawn = {gPlayerOne->pos[0], gPlayerOne->pos[1], gPlayerOne->pos[2]}; + Vec3f spawn2 = {gPlayerTwo->pos[0], gPlayerTwo->pos[1], gPlayerTwo->pos[2]}; // Technically there should be a default case of mode 10 here. Except it never gets used. if (gModeSelection == GRAND_PRIX && !gDemoMode) { @@ -1278,17 +1282,28 @@ void spawn_single_player_camera(u32 mode) { } Camera* camera = CM_AddCamera(spawn, gPlayerOne->rotation[1], mode); - if (camera) { - CM_AttachCamera(camera, PLAYER_ONE); - gScreenContexts[PLAYER_ONE].camera = camera; - gScreenContexts[PLAYER_ONE].raceCamera = camera; + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [spawn_single_player_camera] NULL camera while attempting to create camera for player one"); } + CM_AttachCamera(camera, PLAYER_ONE); + gScreenContexts[PLAYER_ONE].camera = camera; + gScreenContexts[PLAYER_ONE].raceCamera = camera; + + // For end of race scene + camera = CM_AddCamera(spawn2, gPlayerTwo->rotation[1], mode); + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [spawn_single_player_camera] NULL camera while attempting to create camera for player two"); + } + CM_AttachCamera(camera, PLAYER_TWO); + gScreenContexts[PLAYER_TWO].camera = camera; + gScreenContexts[PLAYER_TWO].raceCamera = camera; camera = CM_AddLookBehindCamera(spawn, gPlayerOne->rotation[1], mode); - if (camera) { - CM_AttachCamera(camera, PLAYER_ONE); - gScreenContexts[PLAYER_ONE].lookBehindCamera = camera; + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [spawn_single_player_camera] NULL camera while attempting to create LookBehind camera for player one"); } + CM_AttachCamera(camera, PLAYER_ONE); + gScreenContexts[PLAYER_ONE].lookBehindCamera = camera; } void spawn_multiplayer_cameras(u32 mode) { @@ -1309,21 +1324,22 @@ void spawn_multiplayer_cameras(u32 mode) { for (size_t i = 0; i < screens; i++) { Vec3f spawn = {gPlayers[i].pos[0], gPlayers[i].pos[1], gPlayers[i].pos[2]}; camera = CM_AddCamera(spawn, gPlayers[i].rotation[1], mode); - if (camera) { - CM_AttachCamera(camera, i); - gScreenContexts[i].camera = camera; - gScreenContexts[i].raceCamera = camera; + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [spawn_multiplayer_cameras] NULL camera while attempting to create camera for player %d", i); } - + CM_AttachCamera(camera, i); + gScreenContexts[i].camera = camera; + gScreenContexts[i].raceCamera = camera; } for (size_t i = 0; i < screens; i++) { Vec3f spawn = {gPlayers[i].pos[0], gPlayers[i].pos[1], gPlayers[i].pos[2]}; camera = CM_AddLookBehindCamera(spawn, gPlayers[i].rotation[1], mode); - if (camera) { - CM_AttachCamera(camera, i); - gScreenContexts[i].lookBehindCamera = camera; + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [spawn_multiplayer_cameras] NULL camera while attempting to create LookBehind camera for player %d", i); } + CM_AttachCamera(camera, i); + gScreenContexts[i].lookBehindCamera = camera; } } @@ -1362,13 +1378,15 @@ void func_8003DB5C(void) { Vec3f spawn = {player->pos[0], player->pos[1], player->pos[2]}; camera = CM_AddCamera(spawn, player->rotation[1], 3); - if (camera) { - CM_AttachCamera(camera, PLAYER_ONE); + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [func_8003DB5C] NULL camera while attempting to create camera for player one"); } + CM_AttachCamera(camera, PLAYER_ONE); camera = CM_AddCamera(spawn, player->rotation[1], 3); - if (camera) { - CM_AttachCamera(camera, PLAYER_ONE); + if (!camera) { + CM_ThrowRuntimeError("[spawn_players] [func_8003DB5C] NULL camera while attempting to create camera for player two"); } + CM_AttachCamera(camera, PLAYER_TWO); for (playerId = 0; playerId < NUM_PLAYERS; playerId++, player++) { load_kart_palette(player, playerId, 1, 0);