split up main() into chunks to make it more manageable.

added top-level exception handler and Die()

This was SVN commit r730.
This commit is contained in:
janwas 2004-07-12 16:50:46 +00:00
parent 89d47437b4
commit f723248d8f

View File

@ -4,10 +4,8 @@
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
#include <cmath> #include <cmath>
#include <stdarg.h>
// Alan: For some reason if this gets included after anything else some
// compile time errors get thrown up todo with javascript internal typedefs
#include "scripting/ScriptingHost.h"
#include "sdl.h" #include "sdl.h"
#include "ogl.h" #include "ogl.h"
@ -16,9 +14,8 @@
#include "input.h" #include "input.h"
#include "lib.h" #include "lib.h"
#include "res/res.h" #include "res/res.h"
#include "res/file.h"
#ifdef _M_IX86 #ifdef _M_IX86
#include "sysdep/ia32.h" #include "sysdep/ia32.h" // _control87
#endif #endif
#include "ps/CConsole.h" #include "ps/CConsole.h"
@ -33,12 +30,14 @@
#include "Model.h" #include "Model.h"
#include "UnitManager.h" #include "UnitManager.h"
#include "BaseEntityCollection.h" #include "BaseEntityCollection.h"
#include "Entity.h" #include "Entity.h"
#include "EntityHandles.h" #include "EntityHandles.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "PathfindEngine.h" #include "PathfindEngine.h"
#include "scripting/ScriptingHost.h"
#include "scripting/JSInterface_Entity.h" #include "scripting/JSInterface_Entity.h"
#include "scripting/JSInterface_BaseEntity.h" #include "scripting/JSInterface_BaseEntity.h"
#include "scripting/JSInterface_Vector3D.h" #include "scripting/JSInterface_Vector3D.h"
@ -69,6 +68,7 @@ bool mouseButtons[5];
int g_xres, g_yres; int g_xres, g_yres;
int g_bpp; int g_bpp;
int g_freq; int g_freq;
bool g_active = true;
// flag to disable extended GL extensions until fix found - specifically, crashes // flag to disable extended GL extensions until fix found - specifically, crashes
@ -106,6 +106,91 @@ extern int dir_add_watch(const char* const dir, bool watch_subdirs);
extern void sle(int);
static size_t frameCount=0;
static bool quit = false; // break out of main loop
const wchar_t* HardcodedErrorString(int err)
{
#define E(sym) case sym: return L#sym;
switch(err)
{
E(ERR_NO_MEM)
E(ERR_FILE_NOT_FOUND)
E(ERR_INVALID_HANDLE)
E(ERR_INVALID_PARAM)
E(ERR_EOF)
E(ERR_PATH_NOT_FOUND)
E(ERR_VFS_PATH_LENGTH)
default:
return 0;
}
}
const wchar_t* ErrorString(int err)
{
// language file not available (yet)
if(1)
return HardcodedErrorString(err);
// TODO: load from language file
}
static int write_sys_info();
// TODO: load from language file
// these will need to be variables; better to make an index into string table
// (as with errors)? if it's a string, what happens if lang file load failed?
#define STR_UNHANDLED_EXCEPTION L"unhandled exception"
#define STR_SDL_INIT_FAILED L"SDL library initialization failed: %hs\n"
#define STR_SET_VMODE_FAILED L"could not set %dx%d graphics mode: %hs\n"
#define STR_OGL_EXT_MISSING L"required ARB_multitexture or ARB_texture_env_combine extension not available"
#define STR_MAP_LOAD_FAILED L"Failed to load map %hs\n"
static void Die(int err, const wchar_t* fmt, ...)
{
va_list args;
va_start(args, fmt);
wchar_t buf[1000];
vswprintf(buf, 1000, fmt, args);
va_end(args);
const wchar_t* err_string = ErrorString(err);
if(err_string)
{
wcscat(buf, L" ("); wcscat(buf, err_string); wcscat(buf, L")\n");
}
wdisplay_msg(L"0ad", buf);
write_sys_info();
exit(EXIT_FAILURE);
}
void Testing (void) void Testing (void)
{ {
g_Console->InsertMessage(L"Testing Function Registration"); g_Console->InsertMessage(L"Testing Function Registration");
@ -159,7 +244,9 @@ static int write_sys_info()
fprintf(f, "%s\n", gfx_drv_ver); fprintf(f, "%s\n", gfx_drv_ver);
fprintf(f, "%dx%d:%d@%d\n", g_xres, g_yres, g_bpp, g_freq); fprintf(f, "%dx%d:%d@%d\n", g_xres, g_yres, g_bpp, g_freq);
// .. network name / ips // .. network name / ips
char hostname[100]; // possibly nodename != hostname // note: can't use un.nodename because it is for an
// "implementation-defined communications network".
char hostname[128];
if (gethostname(hostname, sizeof(hostname)) == 0) // make sure it succeeded if (gethostname(hostname, sizeof(hostname)) == 0) // make sure it succeeded
{ {
fprintf(f, "%s\n", hostname); fprintf(f, "%s\n", hostname);
@ -178,27 +265,6 @@ static int write_sys_info()
} }
// error before GUI is initialized: display message, and quit
// TODO: localization
static void display_startup_error(const wchar_t* msg)
{
const wchar_t* caption = L"0ad startup problem";
write_sys_info();
wdisplay_msg(caption, msg);
exit(1);
}
// error before GUI is initialized: display message, and quit
// TODO: localization
static void display_startup_error(const char* msg)
{
const char* caption = "0ad startup problem";
write_sys_info();
display_msg(caption, msg);
exit(1);
}
static int set_vmode(int w, int h, int bpp, bool fullscreen) static int set_vmode(int w, int h, int bpp, bool fullscreen)
@ -217,6 +283,9 @@ static int set_vmode(int w, int h, int bpp, bool fullscreen)
oglInit(); // required after each mode change oglInit(); // required after each mode change
if(SDL_SetGamma(g_Gamma, g_Gamma, g_Gamma) < 0)
debug_warn("SDL_SetGamma failed");
return 0; return 0;
} }
@ -253,8 +322,6 @@ static void WriteScreenshot()
} }
bool active = true;
static bool quit = false; // break out of main loop
// HACK: Let code from other files (i.e. the scripting system) quit // HACK: Let code from other files (i.e. the scripting system) quit
void kill_mainloop() void kill_mainloop()
@ -269,7 +336,7 @@ static int handler(const SDL_Event* ev)
switch(ev->type) switch(ev->type)
{ {
case SDL_ACTIVEEVENT: case SDL_ACTIVEEVENT:
active = ev->active.gain != 0; g_active = ev->active.gain != 0;
break; break;
case SDL_KEYDOWN: case SDL_KEYDOWN:
@ -577,6 +644,40 @@ void ParseArgs(int argc, char* argv[])
static void InitScripting()
{
// Create the scripting host. This needs to be done before the GUI is created.
new ScriptingHost;
// Register the JavaScript interfaces with the runtime
JSI_Entity::init();
JSI_BaseEntity::init();
JSI_IGUIObject::init();
JSI_GUITypes::init();
JSI_Vector3D::init();
}
static void InitVfs(char* argv0)
{
// set current directory to "$game_dir/data".
// this is necessary because it is otherwise unknown,
// especially if run from a shortcut / symlink.
//
// "../data" is relative to the executable (in "$game_dir/system").
//
// rationale for data/ being root: untrusted scripts must not be
// allowed to overwrite critical game (or worse, OS) files.
// the VFS prevents any accesses to files above this directory.
int err = file_rel_chdir(argv0, "../data");
if(err < 0)
throw err;
// display_startup_error(L"error setting current directory.\n"\
// L"argv[0] is probably incorrect. please start the game via command-line.");
vfs_mount("", "mods/official", 0);
vfs_mount("screenshots", "screenshots", 0);
}
static void psInit() static void psInit()
{ {
g_Font_Console = unifont_load("fonts/console"); g_Font_Console = unifont_load("fonts/console");
@ -610,9 +711,42 @@ static void psShutdown()
CXeromyces::Terminate(); CXeromyces::Terminate();
} }
extern u64 PREVTSC; extern u64 PREVTSC;
int main(int argc, char* argv[])
static void Shutdown()
{ {
psShutdown(); // Must delete g_GUI before g_ScriptingHost
delete &g_ScriptingHost;
delete &g_Pathfinder;
delete &g_EntityManager;
delete &g_EntityTemplateCollection;
// destroy actor related stuff
delete &g_UnitMan;
delete &g_ObjMan;
delete &g_SkelAnimMan;
// destroy terrain related stuff
delete &g_TexMan;
// destroy renderer
delete &g_Renderer;
delete &g_ConfigDB;
// Not particularly consistent with all the other singletons,
// but the GUI currently uses it and so it needs to be unloaded
NPFontManager::release();
}
static void Init(int argc, char* argv[])
{
sle(1134);
// If you ever want to catch a particular allocation: // If you ever want to catch a particular allocation:
//_CrtSetBreakAlloc(4128); //_CrtSetBreakAlloc(4128);
@ -627,9 +761,6 @@ PREVTSC=TSC;
#endif #endif
const int ERR_MSG_SIZE = 1000;
wchar_t err_msg[ERR_MSG_SIZE];
lib_init(); lib_init();
// set 24 bit (float) FPU precision for faster divides / sqrts // set 24 bit (float) FPU precision for faster divides / sqrts
@ -642,24 +773,13 @@ PREVTSC=TSC;
// and fonts are set later in psInit()) // and fonts are set later in psInit())
g_Console = new CConsole(); g_Console = new CConsole();
// Create the scripting host. This needs to be done before the GUI is created.
new ScriptingHost;
// Register the JavaScript interfaces with the runtime
JSI_Entity::init();
JSI_BaseEntity::init();
JSI_IGUIObject::init();
JSI_GUITypes::init();
JSI_Vector3D::init();
detect(); detect();
InitVfs(argv[0]);
// init SDL // init SDL
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0) if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0)
{ Die(0, STR_SDL_INIT_FAILED, SDL_GetError());
swprintf(err_msg, ERR_MSG_SIZE, L"SDL library initialization failed: %hs\n", SDL_GetError());
display_startup_error(err_msg);
}
atexit(SDL_Quit); atexit(SDL_Quit);
SDL_EnableUNICODE(1); SDL_EnableUNICODE(1);
@ -667,22 +787,8 @@ PREVTSC=TSC;
// (command line params may override these) // (command line params may override these)
get_cur_vmode(&g_xres, &g_yres, &g_bpp, &g_freq); get_cur_vmode(&g_xres, &g_yres, &g_bpp, &g_freq);
// set current directory to "$game_dir/data".
// this is necessary because it is otherwise unknown, InitScripting(); // before GUI
// especially if run from a shortcut / symlink.
//
// "../data" is relative to the executable (in "$game_dir/system").
//
// rationale for data/ being root: untrusted scripts must not be
// allowed to overwrite critical game (or worse, OS) files.
// the VFS prevents any accesses to files above this directory.
int err = file_rel_chdir(argv[0], "../data");
if(err < 0)
{
swprintf(err_msg, ERR_MSG_SIZE, L"error setting current directory.\n"\
L"argv[0] is probably incorrect. please start the game via command-line.");
display_startup_error(err_msg);
}
new CConfigDB; new CConfigDB;
g_ConfigDB.SetConfigFile(CFG_SYSTEM, false, "config/system.cfg"); g_ConfigDB.SetConfigFile(CFG_SYSTEM, false, "config/system.cfg");
@ -710,17 +816,16 @@ PREVTSC=TSC;
bool windowed=false; bool windowed=false;
if (val) val->GetBool(windowed); if (val) val->GetBool(windowed);
if(set_vmode(g_xres, g_yres, 32, !windowed) < 0)
{
swprintf(err_msg, ERR_MSG_SIZE, L"could not set %dx%d graphics mode: %hs\n", g_xres, g_yres, SDL_GetError());
display_startup_error(err_msg);
}
write_sys_info(); write_sys_info();
sle(11340106);
if(set_vmode(g_xres, g_yres, 32, !windowed) < 0)
Die(0, STR_SET_VMODE_FAILED, g_xres, g_yres, SDL_GetError());
if(!oglExtAvail("GL_ARB_multitexture") || !oglExtAvail("GL_ARB_texture_env_combine")) if(!oglExtAvail("GL_ARB_multitexture") || !oglExtAvail("GL_ARB_texture_env_combine"))
display_startup_error(L"required ARB_multitexture or ARB_texture_env_combine extension not available"); Die(0, STR_OGL_EXT_MISSING);
// enable/disable VSync // enable/disable VSync
// note: "GL_EXT_SWAP_CONTROL" is "historical" according to dox. // note: "GL_EXT_SWAP_CONTROL" is "historical" according to dox.
@ -728,20 +833,13 @@ PREVTSC=TSC;
wglSwapIntervalEXT(g_VSync? 1 : 0); wglSwapIntervalEXT(g_VSync? 1 : 0);
if(SDL_SetGamma(g_Gamma, g_Gamma, g_Gamma) < 0)
{
debug_warn("SDL_SetGamma failed");
}
vfs_mount("", "mods/official", 0);
vfs_mount("screenshots", "screenshots", 0);
#ifdef _MSC_VER #ifdef _MSC_VER
u64 CURTSC=rdtsc(); u64 CURTSC=rdtsc();
debug_out( debug_out(
"----------------------------------------\n"\ "----------------------------------------\n"\
"VFS ready (elapsed = %f ms)\n"\ "low-level ready (elapsed = %f ms)\n"\
"----------------------------------------\n", (CURTSC-PREVTSC)/2e9*1e3); "----------------------------------------\n", (CURTSC-PREVTSC)/2e9*1e3);
PREVTSC=CURTSC; PREVTSC=CURTSC;
#endif #endif
@ -793,9 +891,7 @@ if(!g_MapFile)
CMapReader reader; CMapReader reader;
reader.LoadMap(mapfilename); reader.LoadMap(mapfilename);
} catch (...) { } catch (...) {
char errmsg[256]; Die(0, STR_MAP_LOAD_FAILED, mapfilename.c_str());
sprintf(errmsg, "Failed to load map %s\n", mapfilename.c_str());
display_startup_error(errmsg);
} }
} }
@ -815,7 +911,6 @@ if(!g_MapFile)
// render everything to a blank frame to force renderer to load everything // render everything to a blank frame to force renderer to load everything
RenderNoCull(); RenderNoCull();
size_t frameCount=0;
if (g_FixedFrameTiming) { if (g_FixedFrameTiming) {
#if 0 // TOPDOWN #if 0 // TOPDOWN
g_Camera.SetProjection(1.0f,10000.0f,DEGTORAD(90)); g_Camera.SetProjection(1.0f,10000.0f,DEGTORAD(90));
@ -849,17 +944,26 @@ PREVTSC=CURTSC;
} }
#endif #endif
}
// fixed timestep main loop
const double TICK_TIME = 30e-3; // [s]
double time0 = get_time();
while(!quit) static void Frame()
{ {
static double last_time;
const double time = get_time();
const float TimeSinceLastFrame = (float)(time-last_time);
last_time = time;
ONCE(return);
// first call: set last_time and return
assert(TimeSinceLastFrame >= 0.0f);
res_reload_changed_files(); res_reload_changed_files();
// TODO: limiter in case simulation can't keep up? // TODO: limiter in case simulation can't keep up?
const double TICK_TIME = 30e-3; // [s]
#if 0 #if 0
double time1 = get_time(); double time1 = get_time();
while((time1-time0) > TICK_TIME) while((time1-time0) > TICK_TIME)
@ -872,15 +976,11 @@ PREVTSC=CURTSC;
} }
#endif #endif
double time1 = get_time();
// ugly, but necessary. these are one-shot events, have to be reset. // ugly, but necessary. these are one-shot events, have to be reset.
mouseButtons[SDL_BUTTON_WHEELUP] = false; mouseButtons[SDL_BUTTON_WHEELUP] = false;
mouseButtons[SDL_BUTTON_WHEELDOWN] = false; mouseButtons[SDL_BUTTON_WHEELDOWN] = false;
in_get_events(); in_get_events();
float TimeSinceLastFrame = (float)(time1-time0);
assert(TimeSinceLastFrame >= 0.0f);
if(TimeSinceLastFrame > 0.0f) if(TimeSinceLastFrame > 0.0f)
{ {
UpdateWorld(TimeSinceLastFrame); UpdateWorld(TimeSinceLastFrame);
@ -889,7 +989,7 @@ PREVTSC=CURTSC;
g_Console->Update(TimeSinceLastFrame); g_Console->Update(TimeSinceLastFrame);
} }
if(active) if(g_active)
{ {
Render(); Render();
SDL_GL_SwapBuffers(); SDL_GL_SwapBuffers();
@ -900,34 +1000,31 @@ PREVTSC=CURTSC;
SDL_Delay(10); SDL_Delay(10);
calc_fps(); calc_fps();
time0=time1;
frameCount++; frameCount++;
if (g_FixedFrameTiming && frameCount==100) quit=true; if (g_FixedFrameTiming && frameCount==100) quit=true;
} // main loop, while(!quit) }
psShutdown(); // Must delete g_GUI before g_ScriptingHost
delete &g_ScriptingHost;
delete &g_Pathfinder;
delete &g_EntityManager;
delete &g_EntityTemplateCollection;
// destroy actor related stuff int main(int argc, char* argv[])
delete &g_UnitMan; {
delete &g_ObjMan; try
delete &g_SkelAnimMan; {
Init(argc, argv);
// destroy terrain related stuff while(!quit)
delete &g_TexMan; Frame();
// destroy renderer Shutdown();
delete &g_Renderer; }
catch(...)
delete &g_ConfigDB; {
// debug build: let debugger display it (more convenient)
// Not particularly consistent with all the other singletons, #ifndef NDEBUG
// but the GUI currently uses it and so it needs to be unloaded throw;
NPFontManager::release(); #endif
Die(0, STR_UNHANDLED_EXCEPTION);
}
exit(0); exit(0);
return 0; return 0;