// LadyHavoc: if crashing very early, or currently shutting down, do
// Sys_Error instead
- if (host.framecount < 3 || host.state == host_shutdown)
- Sys_Error ("Host_Error during %s: %s", host.framecount < 3 ? "startup" : "shutdown", hosterrorstring1);
+ if (host.framecount < 1 || host.state != host_active)
+ Sys_Error ("Host_Error during %s: %s", host_state_str[host.state], hosterrorstring1);
if (hosterror)
Sys_Error ("Host_Error: recursively entered (original error was: %s new error is: %s)", hosterrorstring2, hosterrorstring1);
// restore configured outfd
sys.outfd = outfd;
+ // can't abort a frame if we didn't start one yet, won't get here in that case (see above)
Host_AbortCurrentFrame();
}
host.realtime = 0;
host.dirtytime = Sys_DirtyTime();
- if (setjmp(host.abortframe)) // Huh?!
- Sys_Error("Engine initialization failed. Check the console (if available) for additional information.\n");
-
if (Sys_CheckParm("-profilegameonly"))
Sys_AllowProfiling(false);
// NOTE: menu commands are freed by Cmd_RestoreInitState
Cmd_SaveInitState();
- // FIXME: put this into some neat design, but the menu should be allowed to crash
- // without crashing the whole game, so this should just be a short-time solution
-
// here comes the not so critical stuff
Host_AddConfigText(cmd_local);
Cbuf_Execute(cmd_local->cbuf); // cannot be in Host_AddConfigText as that would cause Host_LoadConfig_f to loop!
- host.state = host_active;
-
CL_StartVideo();
Log_Start();
}
Con_DPrint("========Initialized=========\n");
+ host.state = host_active;
if (cls.state != ca_dedicated)
SV_StartThread();
{
double cl_wait, sv_wait;
+ ++host.framecount;
+
TaskQueue_Frame(false);
// keep the random time dependent, but not when playing demos/benchmarking
host_init,
host_loading,
host_active,
- // states >= host_shutdown cause graceful shutdown, see Sys_HandleCrash() comments
+ /// states >= host_shutdown cause graceful shutdown, see Sys_HandleCrash() comments
host_shutdown,
- host_failing, ///< crashing
- host_failed ///< crashed or aborted, SDL dialog open
+ host_failing, ///< crashing (inside crash handler)
+ host_failed ///< crashed or aborted, SDL dialog open
} host_state_t;
+static const char * const host_state_str[] =
+{
+ [host_init] = "init",
+ [host_loading] = "loading",
+ [host_active] = "normal operation",
+ [host_shutdown] = "shutdown",
+ [host_failing] = "crashing",
+ [host_failed] = "crashed",
+};
typedef struct host_static_s
{
jmp_buf abortframe;
int state;
- unsigned int framecount; // incremented every frame, never reset (checked by Host_Error and Host_SaveConfig_f)
- double realtime; // the accumulated mainloop time since application started (with filtering), without any slowmo or clamping
- double dirtytime; // the main loop wall time for this frame, equal to Sys_DirtyTime() at the start of this host frame
- double sleeptime; // time spent sleeping after the last frame
- qbool restless; // don't sleep
- qbool paused; // global paused state, pauses both client and server
+ unsigned int framecount; ///< incremented every frame, never reset, >0 means Host_AbortCurrentFrame() is possible
+ double realtime; ///< the accumulated mainloop time since application started (with filtering), without any slowmo or clamping
+ double dirtytime; ///< the main loop wall time for this frame, equal to Sys_DirtyTime() at the start of this host frame
+ double sleeptime; ///< time spent sleeping after the last frame
+ qbool restless; ///< don't sleep
+ qbool paused; ///< global paused state, pauses both client and server
cmd_buf_t *cbuf;
struct
if (complain)
{
- Con_Printf("Couldn't load %s using ", filename);
+ Con_Printf(CON_ERROR "Couldn't load %s using ", filename);
for (format = firstformat;format->formatstring;format++)
{
dpsnprintf (name, sizeof(name), format->formatstring, basename);
}
+static bool mp_failed;
static void M_Main_Draw (void)
{
int f;
const char *s;
M_Background(640, 480); //fall back is always to 640x480, this makes it most readable at that.
y = 480/3-16;
- s = "You have reached this menu due to missing or unlocatable content/data";M_PrintRed ((640-strlen(s)*8)*0.5, (480/3)-16, s);y+=8;
- y+=8;
- s = "You may consider adding";M_Print ((640-strlen(s)*8)*0.5, y, s);y+=8;
- s = "-basedir /path/to/game";M_Print ((640-strlen(s)*8)*0.5, y, s);y+=8;
- s = "to your launch commandline";M_Print ((640-strlen(s)*8)*0.5, y, s);y+=8;
+ if (mp_failed)
+ {
+ s = "The menu QC program has failed.";M_PrintRed ((640-strlen(s)*8)*0.5, y, s);y+=8;
+ y+=8;
+ s = "You should find the specific error(s) in the console.";M_Print ((640-strlen(s)*8)*0.5, y, s);y+=8;
+ }
+ else
+ {
+ s = "The required files were not found.";M_PrintRed ((640-strlen(s)*8)*0.5, y, s);y+=8;
+ y+=8;
+ s = "You may consider adding";M_Print ((640-strlen(s)*8)*0.5, y, s);y+=8;
+ s = "-basedir /path/to/game";M_Print ((640-strlen(s)*8)*0.5, y, s);y+=8;
+ s = "to your launch commandline.";M_Print ((640-strlen(s)*8)*0.5, y, s);y+=8;
+ }
M_Print (640/2 - 48, 480/2, "Open Console"); //The console usually better shows errors (failures)
M_Print (640/2 - 48, 480/2 + 8, "Quit");
M_DrawCharacter(640/2 - 56, 480/2 + (8 * m_main_cursor), 12+((int)(host.realtime*4)&1));
void MR_SetRouting (qbool forceold);
-void MVM_error_cmd(const char *format, ...) DP_FUNC_PRINTF(1);
-void MVM_error_cmd(const char *format, ...)
+jmp_buf mp_abort;
+static void MVM_error_cmd(const char *format, ...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN;
+static void MVM_error_cmd(const char *format, ...)
{
static qbool processingError = false;
char errorstring[MAX_INPUTLINE];
dpvsnprintf (errorstring, sizeof(errorstring), format, argptr);
va_end (argptr);
- if (host.framecount < 3)
- Sys_Error("Menu_Error: %s", errorstring);
-
Con_Printf(CON_ERROR "Menu_Error: %s\n", errorstring);
if(!processingError)
Con_Print("Falling back to engine menu\n");
key_dest = key_game;
MR_SetRouting (true);
+ mp_failed = true;
+ if (cls.state != ca_connected || key_dest != key_game) // if not disrupting a game
+ MR_ToggleMenu(1); // ensure error screen appears, eg for: menu_progs ""; menu_restart
// reset the active scene, too (to be on the safe side ;))
R_SelectScene( RST_CLIENT );
// restore configured outfd
sys.outfd = outfd;
- // Let video start at least
- Host_AbortCurrentFrame();
+ // no frame abort: menu failure shouldn't interfere with more important VMs
+ longjmp(mp_abort, 1);
}
static void MVM_begin_increase_edicts(prvm_prog_t *prog)
{
prvm_prog_t *prog = MVM_prog;
+ if (setjmp(mp_abort))
+ return;
+
// pass key
prog->globals.fp[OFS_PARM0] = (prvm_vec_t) key;
prog->globals.fp[OFS_PARM1] = (prvm_vec_t) ascii;
if (!prog->loaded)
return;
+ if (setjmp(mp_abort))
+ return;
+
R_SelectScene( RST_MENU );
// reset the temp entities each frame
{
prvm_prog_t *prog = MVM_prog;
+ if (setjmp(mp_abort))
+ return;
+
prog->globals.fp[OFS_PARM0] = (prvm_vec_t) mode;
prog->ExecuteProgram(prog, PRVM_menufunction(m_toggle),"m_toggle(float mode) required");
}
static void MP_NewMap(void)
{
prvm_prog_t *prog = MVM_prog;
+
+ if (setjmp(mp_abort))
+ return;
+
if (PRVM_menufunction(m_newmap))
prog->ExecuteProgram(prog, PRVM_menufunction(m_newmap),"m_newmap() required");
}
static int MP_GetServerListEntryCategory(const serverlist_entry_t *entry)
{
prvm_prog_t *prog = MVM_prog;
+
+ if (setjmp(mp_abort))
+ return 0;
+
serverlist_callbackentry = entry;
if (PRVM_menufunction(m_gethostcachecategory))
{
static void MP_Shutdown (void)
{
prvm_prog_t *prog = MVM_prog;
+
+ if (setjmp(mp_abort))
+ return;
+
if (prog->loaded)
prog->ExecuteProgram(prog, PRVM_menufunction(m_shutdown),"m_shutdown() required");
static void MP_Init (void)
{
prvm_prog_t *prog = MVM_prog;
+
+ if (setjmp(mp_abort))
+ return;
+
PRVM_Prog_Init(prog, cmd_local);
prog->edictprivate_size = 0; // no private struct used
extern void (*MR_NewMap) (void);
extern int (*MR_GetServerListEntryCategory) (const struct serverlist_entry_s *entry);
+// menu QC error handling
+extern jmp_buf mp_abort;
+
typedef struct video_resolution_s
{
const char *type;
qbool MP_ConsoleCommand(const char *text, size_t textlen)
{
prvm_prog_t *prog = MVM_prog;
+ if (setjmp(mp_abort))
+ return false;
return PRVM_ConsoleCommand(prog, text, textlen, &prog->funcoffsets.GameCommand, false, -1, 0, "QC function GameCommand is missing");
}
memcpy(palette_rgb, palfile, 768);
else
{
- Con_DPrint("Couldn't load gfx/palette.lmp, falling back on internal palette\n");
+ Con_DPrint(CON_WARN "Couldn't load gfx/palette.lmp, falling back on internal palette\n");
memcpy(palette_rgb, host_quakepal, 768);
}
if (palfile)
void (*init_cmd)(struct prvm_prog_s *prog); ///< [INIT] used by PRVM_InitProg
void (*reset_cmd)(struct prvm_prog_s *prog); ///< [INIT] used by PRVM_ResetProg
- void (*error_cmd)(const char *format, ...) DP_FUNC_PRINTF(1); ///< [INIT]
+ void (*error_cmd)(const char *format, ...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN; ///< [INIT]
void (*ExecuteProgram)(struct prvm_prog_s *prog, func_t fnum, const char *errormessage); ///< pointer to one of the *VM_ExecuteProgram functions
} prvm_prog_t;
}
// dump the stack so host_error can shutdown functions
- prog->depth = 0;
- prog->localstack_used = 0;
+ // and free memory, unset prog->loaded, etc
+ PRVM_Prog_Reset(prog);
}
/*
host.dirtytime = newtime;
sleeptime = Host_Frame(time);
- ++host.framecount;
sleeptime -= Sys_DirtyTime() - host.dirtytime; // execution time
#ifdef __EMSCRIPTEN__