// main.cpp: initialisation & main loop #include "pch.h" #include "engine.h" void quit() // normal exit { writeservercfg(); abortconnect(); disconnect(1); writecfg(); cleangl(); cleanupserver(); SDL_ShowCursor(1); freeocta(worldroot); extern void clear_command(); clear_command(); extern void clear_console(); clear_console(); extern void clear_mdls(); clear_mdls(); extern void clear_sound(); clear_sound(); SDL_Quit(); exit(EXIT_SUCCESS); } void fatal(char *s, char *o) // failure exit { s_sprintfd(msg)("%s%s\n", s, o); printf(msg); #ifdef WIN32 MessageBox(NULL, msg, "sauerbraten fatal error", MB_OK|MB_SYSTEMMODAL); #endif exit(EXIT_FAILURE); } SDL_Surface *screen = NULL; int curtime; int totalmillis = 0, lastmillis = 0; dynent *player = NULL; int scr_w = 1024, scr_h = 768; void screenshot(char *filename) { SDL_Surface *image = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0); if(!image) return; uchar *tmp = new uchar[scr_w*scr_h*3]; glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, scr_w, scr_h, GL_RGB, GL_UNSIGNED_BYTE, tmp); uchar *dst = (uchar *)image->pixels; loopi(scr_h) { memcpy(dst, &tmp[3*scr_w*(scr_h-i-1)], 3*scr_w); endianswap(dst, 3, scr_w); dst += image->pitch; } delete[] tmp; if(!filename[0]) { static string buf; s_sprintf(buf)("screenshot_%d.bmp", lastmillis); filename = buf; } SDL_SaveBMP(image, filename); SDL_FreeSurface(image); } COMMAND(screenshot, "s"); COMMAND(quit, ""); void computescreen(const char *text, Texture *t) { int w = scr_w, h = scr_h; gettextres(w, h); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glClearColor(0.15f, 0.15f, 0.15f, 1); glColor3f(1, 1, 1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glMatrixMode(GL_PROJECTION); loopi(2) { glLoadIdentity(); glOrtho(0, w*3, h*3, 0, -1, 1); glClear(GL_COLOR_BUFFER_BIT); draw_text(text, 70, 2*FONTH + FONTH/2); glLoadIdentity(); glOrtho(0, w, h, 0, -1, 1); if(t) { glDisable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, t->gl); #if 0 int x = (w-640)/2, y = (h-320)/2; glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0.5f, 0.5f); glVertex2f(x+640/2.0f, y+320/2.0f); loopj(64+1) { float c = 0.5f+0.5f*cosf(2*M_PI*j/64.0f), s = 0.5f+0.5f*sinf(2*M_PI*j/64.0f); glTexCoord2f(c, 320.0f/640.0f*(s-0.5f)+0.5f); glVertex2f(x+640*c, y+320*s); } #else int sz = 256, x = (w-sz)/2, y = min(384, h-256); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(x, y); glTexCoord2f(1, 0); glVertex2i(x+sz, y); glTexCoord2f(1, 1); glVertex2i(x+sz, y+sz); glTexCoord2f(0, 1); glVertex2i(x, y+sz); #endif glEnd(); glEnable(GL_BLEND); } int x = (w-512)/2, y = 128; settexture("data/sauer_logo_512_256a.png"); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(x, y); glTexCoord2f(1, 0); glVertex2i(x+512, y); glTexCoord2f(1, 1); glVertex2i(x+512, y+256); glTexCoord2f(0, 1); glVertex2i(x, y+256); glEnd(); SDL_GL_SwapBuffers(); } glMatrixMode(GL_MODELVIEW); glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } static void bar(float bar, int w, int o, float r, float g, float b) { int side = 50; glColor3f(r, g, b); glVertex2f(side, o*FONTH); glVertex2f(bar*(w*3-2*side)+side, o*FONTH); glVertex2f(bar*(w*3-2*side)+side, (o+2)*FONTH); glVertex2f(side, (o+2)*FONTH); } void show_out_of_renderloop_progress(float bar1, const char *text1, float bar2, const char *text2) // also used during loading { if(!inbetweenframes) return; clientkeepalive(); // make sure our connection doesn't time out while loading maps etc. int w = scr_w, h = scr_h; gettextres(w, h); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, w*3, h*3, 0, -1, 1); notextureshader->set(); glBegin(GL_QUADS); if(text1) { bar(1, w, 4, 0, 0, 0.8f); bar(bar1, w, 4, 0, 0.5f, 1); } if(bar2>0) { bar(1, w, 6, 0.5f, 0, 0); bar(bar2, w, 6, 0.75f, 0, 0); } glEnd(); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); defaultshader->set(); if(text1) draw_text(text1, 70, 4*FONTH + FONTH/2); if(bar2>0) draw_text(text2, 70, 6*FONTH + FONTH/2); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glPopMatrix(); glEnable(GL_DEPTH_TEST); SDL_GL_SwapBuffers(); } void setfullscreen(bool enable) { if(enable == !(screen->flags&SDL_FULLSCREEN)) { #if defined(WIN32) || defined(__APPLE__) conoutf("\"fullscreen\" variable not supported on this platform. Use the -t command-line option."); extern int fullscreen; fullscreen = !enable; #else SDL_WM_ToggleFullScreen(screen); SDL_WM_GrabInput((screen->flags&SDL_FULLSCREEN) ? SDL_GRAB_ON : SDL_GRAB_OFF); #endif } } void screenres(int *w, int *h, int *bpp = 0) { #if defined(WIN32) || defined(__APPLE__) conoutf("\"screenres\" command not supported on this platform. Use the -w and -h command-line options."); #else SDL_Surface *surf = SDL_SetVideoMode(*w, *h, bpp ? *bpp : 0, SDL_OPENGL|SDL_RESIZABLE|(screen->flags&SDL_FULLSCREEN)); if(!surf) return; screen = surf; scr_w = screen->w; scr_h = screen->h; glViewport(0, 0, scr_w, scr_h); #endif } VARF(fullscreen, 0, 0, 1, setfullscreen(fullscreen!=0)); COMMAND(screenres, "iii"); VARFP(gamma, 30, 100, 300, { float f = gamma/100.0f; if(SDL_SetGamma(f,f,f)==-1) { conoutf("Could not set gamma (card/driver doesn't support it?)"); conoutf("sdl: %s", SDL_GetError()); } }); void keyrepeat(bool on) { SDL_EnableKeyRepeat(on ? SDL_DEFAULT_REPEAT_DELAY : 0, SDL_DEFAULT_REPEAT_INTERVAL); } VARF(gamespeed, 10, 100, 1000, if(multiplayer()) gamespeed = 100); VARF(paused, 0, 0, 1, if(multiplayer()) paused = 0); VAR(maxfps, 5, 200, 500); void limitfps(int &millis, int curmillis) { static int fpserror = 0; int delay = 1000/maxfps - (millis-curmillis); if(delay < 0) fpserror = 0; else { fpserror += 1000%maxfps; if(fpserror >= maxfps) { ++delay; fpserror -= maxfps; } if(delay > 0) { SDL_Delay(delay); millis += delay; } } } #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__) void stackdumper(unsigned int type, EXCEPTION_POINTERS *ep) { if(!ep) fatal("unknown type"); EXCEPTION_RECORD *er = ep->ExceptionRecord; CONTEXT *context = ep->ContextRecord; string out, t; s_sprintf(out)("Sauerbraten Win32 Exception: 0x%x [0x%x]\n\n", er->ExceptionCode, er->ExceptionCode==EXCEPTION_ACCESS_VIOLATION ? er->ExceptionInformation[1] : -1); STACKFRAME sf = {{context->Eip, 0, AddrModeFlat}, {}, {context->Ebp, 0, AddrModeFlat}, {context->Esp, 0, AddrModeFlat}, 0}; SymInitialize(GetCurrentProcess(), NULL, TRUE); while(::StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &sf, context, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL)) { struct { IMAGEHLP_SYMBOL sym; string n; } si = { { sizeof( IMAGEHLP_SYMBOL ), 0, 0, 0, sizeof(string) } }; IMAGEHLP_LINE li = { sizeof( IMAGEHLP_LINE ) }; DWORD off; if(SymGetSymFromAddr(GetCurrentProcess(), (DWORD)sf.AddrPC.Offset, &off, &si.sym) && SymGetLineFromAddr(GetCurrentProcess(), (DWORD)sf.AddrPC.Offset, &off, &li)) { char *del = strrchr(li.FileName, '\\'); s_sprintf(t)("%s - %s [%d]\n", si.sym.Name, del ? del + 1 : li.FileName, li.LineNumber); s_strcat(out, t); } } fatal(out); } #endif #define MAXFPSHISTORY 60 int fpspos = 0, fpshistory[MAXFPSHISTORY]; void resetfpshistory() { loopi(MAXFPSHISTORY) fpshistory[i] = 1; fpspos = 0; } void updatefpshistory(int millis) { fpshistory[fpspos++] = max(1, min(1000, millis)); if(fpspos>=MAXFPSHISTORY) fpspos = 0; } void getfps(int &fps, int &bestdiff, int &worstdiff) { int total = fpshistory[MAXFPSHISTORY-1], best = total, worst = total; loopi(MAXFPSHISTORY-1) { int millis = fpshistory[i]; total += millis; if(millis < best) best = millis; if(millis > worst) worst = millis; } fps = (1000*MAXFPSHISTORY)/total; bestdiff = 1000/best-fps; worstdiff = fps-1000/worst; } bool inbetweenframes = false; int main(int argc, char **argv) { #ifdef WIN32 //atexit((void (__cdecl *)(void))_CrtDumpMemoryLeaks); #ifndef _DEBUG #ifndef __GNUC__ __try { #endif #endif #endif bool dedicated = false; int fs = SDL_FULLSCREEN, par = 0, depth = 0, bpp = 0, fsaa = 0, stencil = 1, vsync = -1; char *load = NULL, *initscript = NULL; #define log(s) puts("init: " s) log("sdl"); for(int i = 1; i=0) SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, vsync); #endif int resize = SDL_RESIZABLE; #if defined(WIN32) || defined(__APPLE__) resize = 0; #endif SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL|resize|fs); if(modes && modes!=(SDL_Rect **)-1) { bool hasmode = false; for(int i = 0; modes[i]; i++) { if(scr_w <= modes[i]->w && scr_h <= modes[i]->h) { hasmode = true; break; } } if(!hasmode) { scr_w = modes[0]->w; scr_h = modes[0]->h; } } screen = SDL_SetVideoMode(scr_w, scr_h, bpp, SDL_OPENGL|resize|fs); if(!screen) { if(stencil) { SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); hasstencil = false; } screen = SDL_SetVideoMode(scr_w, scr_h, bpp, SDL_OPENGL|resize|fs); if(!screen) fatal("Unable to create OpenGL screen: ", SDL_GetError()); } scr_w = screen->w; scr_h = screen->h; fullscreen = fs!=0; log("video: misc"); SDL_WM_SetCaption("sauerbraten engine", NULL); #ifndef WIN32 if(fs) #endif SDL_WM_GrabInput(SDL_GRAB_ON); keyrepeat(false); SDL_ShowCursor(0); log("console"); persistidents = false; if(!execfile("data/stdlib.cfg")) fatal("cannot find data files (you are running from the wrong folder, try .bat file in the main folder)"); // this is the first file we load. if(!execfile("data/font.cfg")) fatal("cannot find font definitions"); if(!setfont("default")) fatal("no default font specified"); log("gl"); gl_init(scr_w, scr_h, bpp, depth, fsaa); crosshair = textureload("data/crosshair.png", 3, false); if(!crosshair) fatal("could not find core textures"); computescreen("initializing..."); inbetweenframes = true; particleinit(); log("world"); camera1 = player = cl->iterdynents(0); emptymap(0, true); log("sound"); initsound(); log("cfg"); exec("data/keymap.cfg"); exec("data/stdedit.cfg"); exec("data/menus.cfg"); exec("data/sounds.cfg"); exec("data/brush.cfg"); execfile("mybrushes.cfg"); execfile(cl->savedservers()); persistidents = true; if(!execfile(cl->savedconfig())) exec(cl->defaultconfig()); execfile(cl->autoexec()); persistidents = false; string gamecfgname; s_strcpy(gamecfgname, "data/game_"); s_strcat(gamecfgname, cl->gameident()); s_strcat(gamecfgname, ".cfg"); exec(gamecfgname); persistidents = true; log("localconnect"); localconnect(); cc->gameconnect(false); cc->changemap(load ? load : cl->defaultmap()); if(initscript) execute(initscript); log("mainloop"); resetfpshistory(); int ignore = 5, grabmouse = 0; for(;;) { static int frames = 0; int millis = SDL_GetTicks(); limitfps(millis, totalmillis); int elapsed = millis-totalmillis; curtime = elapsed*gamespeed/100; if(curtime>200) curtime = 200; else if(curtime<1) curtime = 1; if(paused) curtime = 0; if(lastmillis) cl->updateworld(worldpos, curtime, lastmillis); menuprocess(); lastmillis += curtime; totalmillis = millis; checksleep(lastmillis); serverslice(time(NULL), 0); if(frames) updatefpshistory(elapsed); frames++; // miscellaneous general game effects findorientation(); entity_particles(); updatevol(); checkmapsounds(); inbetweenframes = false; SDL_GL_SwapBuffers(); if(frames>2) gl_drawframe(scr_w, scr_h); //SDL_Delay(10); inbetweenframes = true; SDL_Event event; int lasttype = 0, lastbut = 0; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: quit(); break; #if !defined(WIN32) && !defined(__APPLE__) case SDL_VIDEORESIZE: screenres(&event.resize.w, &event.resize.h); break; #endif case SDL_KEYDOWN: case SDL_KEYUP: keypress(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.unicode); break; case SDL_ACTIVEEVENT: if(event.active.state & SDL_APPINPUTFOCUS) grabmouse = event.active.gain; else if(event.active.gain) grabmouse = 1; break; case SDL_MOUSEMOTION: if(ignore) { ignore--; break; } if(!(screen->flags&SDL_FULLSCREEN) && grabmouse) { #ifdef __APPLE__ if(event.motion.y == 0) break; //let mac users drag windows via the title bar #endif if(event.motion.x == scr_w / 2 && event.motion.y == scr_h / 2) break; SDL_WarpMouse(scr_w / 2, scr_h / 2); } #ifndef WIN32 if((screen->flags&SDL_FULLSCREEN) || grabmouse) #endif mousemove(event.motion.xrel, event.motion.yrel); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it keypress(-event.button.button, event.button.state!=0, 0); lasttype = event.type; lastbut = event.button.button; break; } } } ASSERT(0); return EXIT_FAILURE; #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__) } __except(stackdumper(0, GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) { return 0; } #endif }