Initial Comit: First commit.
[SauerEngine.git] / src / engine / main.cpp
blob9475e1ad54865c55b2573efe26e0287959e3be09
1 // main.cpp: initialisation & main loop
3 #include "pch.h"
4 #include "engine.h"
6 void cleanup()
8 cleanupserver();
9 SDL_ShowCursor(1);
10 freeocta(worldroot);
11 extern void clear_command(); clear_command();
12 extern void clear_console(); clear_console();
13 extern void clear_mdls(); clear_mdls();
14 extern void clear_sound(); clear_sound();
15 SDL_Quit();
18 void quit() // normal exit
20 extern void writeinitcfg();
21 writeinitcfg();
22 writeservercfg();
23 abortconnect();
24 disconnect(1);
25 writecfg();
26 cleanup();
27 exit(EXIT_SUCCESS);
30 void fatal(const char *s, ...) // failure exit
32 static int errors = 0;
33 errors++;
35 if(errors <= 2) // print up to one extra recursive error
37 s_sprintfdlv(msg,s,s);
38 puts(msg);
40 if(errors <= 1) // avoid recursion
42 SDL_ShowCursor(1);
43 #ifdef WIN32
44 MessageBox(NULL, msg, "sauerbraten fatal error", MB_OK|MB_SYSTEMMODAL);
45 #endif
46 SDL_Quit();
50 exit(EXIT_FAILURE);
53 SDL_Surface *screen = NULL;
55 int curtime;
56 int totalmillis = 0, lastmillis = 0;
58 dynent *player = NULL;
60 static int initing = NOT_INITING;
61 static bool restoredinits = false;
63 bool initwarning(const char *desc, int level, int type)
65 if(initing < level)
67 addchange(desc, type);
68 return true;
70 return false;
73 VARF(scr_w, 320, 1024, 10000, initwarning("screen resolution"));
74 VARF(scr_h, 200, 768, 10000, initwarning("screen resolution"));
75 VARF(colorbits, 0, 0, 32, initwarning("color depth"));
76 VARF(depthbits, 0, 0, 32, initwarning("depth-buffer precision"));
77 VARF(stencilbits, 0, 1, 32, initwarning("stencil-buffer precision"));
78 VARF(fsaa, -1, -1, 16, initwarning("anti-aliasing"));
79 VARF(vsync, -1, -1, 1, initwarning("vertical sync"));
81 void writeinitcfg()
83 if(!restoredinits) return;
84 FILE *f = openfile("init.cfg", "w");
85 if(!f) return;
86 fprintf(f, "// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n");
87 extern int fullscreen;
88 fprintf(f, "fullscreen %d\n", fullscreen);
89 fprintf(f, "scr_w %d\n", scr_w);
90 fprintf(f, "scr_h %d\n", scr_h);
91 fprintf(f, "colorbits %d\n", colorbits);
92 fprintf(f, "depthbits %d\n", depthbits);
93 fprintf(f, "stencilbits %d\n", stencilbits);
94 fprintf(f, "fsaa %d\n", fsaa);
95 fprintf(f, "vsync %d\n", vsync);
96 extern int useshaders, shaderprecision;
97 fprintf(f, "shaders %d\n", useshaders);
98 fprintf(f, "shaderprecision %d\n", shaderprecision);
99 extern int soundchans, soundfreq, soundbufferlen;
100 fprintf(f, "soundchans %d\n", soundchans);
101 fprintf(f, "soundfreq %d\n", soundfreq);
102 fprintf(f, "soundbufferlen %d\n", soundbufferlen);
103 fclose(f);
106 void screenshot(char *filename)
108 SDL_Surface *image = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0);
109 if(!image) return;
110 uchar *tmp = new uchar[screen->w*screen->h*3];
111 glPixelStorei(GL_PACK_ALIGNMENT, 1);
112 glReadPixels(0, 0, screen->w, screen->h, GL_RGB, GL_UNSIGNED_BYTE, tmp);
113 uchar *dst = (uchar *)image->pixels;
114 loopi(screen->h)
116 memcpy(dst, &tmp[3*screen->w*(screen->h-i-1)], 3*screen->w);
117 endianswap(dst, 3, screen->w);
118 dst += image->pitch;
120 delete[] tmp;
121 if(!filename[0])
123 static string buf;
124 s_sprintf(buf)("screenshot_%d.bmp", lastmillis);
125 filename = buf;
127 else path(filename);
128 SDL_SaveBMP(image, findfile(filename, "wb"));
129 SDL_FreeSurface(image);
132 COMMAND(screenshot, "s");
133 COMMAND(quit, "");
135 static void getcomputescreenres(int &w, int &h)
137 float wk = 1, hk = 1;
138 if(w < 1024) wk = 1024.0f/w;
139 if(h < 768) hk = 768.0f/h;
140 wk = hk = max(wk, hk);
141 w = int(ceil(w*wk));
142 h = int(ceil(h*hk));
145 void computescreen(const char *text, Texture *t, const char *overlaytext)
147 int w = screen->w, h = screen->h;
148 getcomputescreenres(w, h);
149 gettextres(w, h);
150 glEnable(GL_BLEND);
151 glEnable(GL_TEXTURE_2D);
152 glDisable(GL_DEPTH_TEST);
153 glDisable(GL_CULL_FACE);
154 glClearColor(0.15f, 0.15f, 0.15f, 1);
155 glColor3f(1, 1, 1);
156 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
157 glMatrixMode(GL_PROJECTION);
158 glLoadIdentity();
159 glOrtho(0, w, h, 0, -1, 1);
160 glMatrixMode(GL_MODELVIEW);
161 glLoadIdentity();
162 defaultshader->set();
163 loopi(2)
165 glClear(GL_COLOR_BUFFER_BIT);
166 if(text)
168 glPushMatrix();
169 glScalef(1/3.0f, 1/3.0f, 1);
170 draw_text(text, 70, 2*FONTH + FONTH/2);
171 glPopMatrix();
173 if(t)
175 glDisable(GL_BLEND);
176 glBindTexture(GL_TEXTURE_2D, t->id);
177 #if 0
178 int x = (w-640)/2, y = (h-320)/2;
179 glBegin(GL_TRIANGLE_FAN);
180 glTexCoord2f(0.5f, 0.5f); glVertex2f(x+640/2.0f, y+320/2.0f);
181 loopj(64+1)
183 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);
184 glTexCoord2f(c, 320.0f/640.0f*(s-0.5f)+0.5f);
185 glVertex2f(x+640*c, y+320*s);
187 #else
188 int sz = 256, x = (w-sz)/2, y = min(384, h-256);
189 glBegin(GL_QUADS);
190 glTexCoord2f(0, 0); glVertex2f(x, y);
191 glTexCoord2f(1, 0); glVertex2f(x+sz, y);
192 glTexCoord2f(1, 1); glVertex2f(x+sz, y+sz);
193 glTexCoord2f(0, 1); glVertex2f(x, y+sz);
194 #endif
195 glEnd();
196 glEnable(GL_BLEND);
198 if(overlaytext)
200 int sz = 256, x = (w-sz)/2, y = min(384, h-256), tw = text_width(overlaytext);
201 int tx = t && tw < sz*2 - FONTH/3 ?
202 2*(x + sz) - tw - FONTH/3 :
203 2*(x + sz/2) - tw/2,
204 ty = t ?
205 2*(y + sz) - FONTH*4/3 :
206 2*(y + sz/2) - FONTH/2;
207 glPushMatrix();
208 glScalef(1/2.0f, 1/2.0f, 1);
209 draw_text(overlaytext, tx, ty);
210 glPopMatrix();
212 int x = (w-512)/2, y = 128;
213 settexture("data/sauer_logo_512_256a.png");
214 glBegin(GL_QUADS);
215 glTexCoord2f(0, 0); glVertex2f(x, y);
216 glTexCoord2f(1, 0); glVertex2f(x+512, y);
217 glTexCoord2f(1, 1); glVertex2f(x+512, y+256);
218 glTexCoord2f(0, 1); glVertex2f(x, y+256);
219 glEnd();
220 SDL_GL_SwapBuffers();
222 glDisable(GL_BLEND);
223 glDisable(GL_TEXTURE_2D);
224 glEnable(GL_DEPTH_TEST);
225 glEnable(GL_CULL_FACE);
228 static void bar(float bar, int w, int o, float r, float g, float b)
230 int side = 2*FONTH;
231 float x1 = side, x2 = min(bar, 1.0f)*(w*3-2*side)+side;
232 float y1 = o*FONTH;
233 glColor3f(r, g, b);
234 glBegin(GL_TRIANGLE_STRIP);
235 loopk(10)
237 float c = cosf(M_PI/2 + k/9.0f*M_PI), s = 1 + sinf(M_PI/2 + k/9.0f*M_PI);
238 glVertex2f(x2 - c*FONTH, y1 + s*FONTH);
239 glVertex2f(x1 + c*FONTH, y1 + s*FONTH);
241 glEnd();
243 #if 0
244 glColor3f(0.3f, 0.3f, 0.3f);
245 glBegin(GL_LINE_LOOP);
246 loopk(10)
248 float c = cosf(M_PI/2 + k/9.0f*M_PI), s = 1 + sinf(M_PI/2 + k/9.0f*M_PI);
249 glVertex2f(x1 + c*FONTH, y1 + s*FONTH);
251 loopk(10)
253 float c = cosf(M_PI/2 + k/9.0f*M_PI), s = 1 - sinf(M_PI/2 + k/9.0f*M_PI);
254 glVertex2f(x2 - c*FONTH, y1 + s*FONTH);
256 glEnd();
257 #endif
260 void show_out_of_renderloop_progress(float bar1, const char *text1, float bar2, const char *text2, GLuint tex) // also used during loading
262 if(!inbetweenframes) return;
264 clientkeepalive(); // make sure our connection doesn't time out while loading maps etc.
266 #ifdef __APPLE__
267 interceptkey(SDLK_UNKNOWN); // keep the event queue awake to avoid 'beachball' cursor
268 #endif
270 int w = screen->w, h = screen->h;
271 getcomputescreenres(w, h);
272 gettextres(w, h);
274 glDisable(GL_DEPTH_TEST);
275 glMatrixMode(GL_MODELVIEW);
276 glPushMatrix();
277 glLoadIdentity();
278 glMatrixMode(GL_PROJECTION);
279 glPushMatrix();
280 glLoadIdentity();
281 glOrtho(0, w*3, h*3, 0, -1, 1);
282 notextureshader->set();
284 glLineWidth(3);
286 if(text1)
288 bar(1, w, 4, 0, 0, 0.8f);
289 if(bar1>0) bar(bar1, w, 4, 0, 0.5f, 1);
292 if(bar2>0)
294 bar(1, w, 6, 0.5f, 0, 0);
295 bar(bar2, w, 6, 0.75f, 0, 0);
298 glLineWidth(1);
300 glEnable(GL_BLEND);
301 glEnable(GL_TEXTURE_2D);
302 defaultshader->set();
304 if(text1) draw_text(text1, 2*FONTH, 4*FONTH + FONTH/2);
305 if(bar2>0) draw_text(text2, 2*FONTH, 6*FONTH + FONTH/2);
307 glDisable(GL_BLEND);
309 if(tex)
311 glBindTexture(GL_TEXTURE_2D, tex);
312 int sz = 256, x = (w-sz)/2, y = min(384, h-256);
313 sz *= 3;
314 x *= 3;
315 y *= 3;
316 glBegin(GL_QUADS);
317 glTexCoord2f(0, 0); glVertex2f(x, y);
318 glTexCoord2f(1, 0); glVertex2f(x+sz, y);
319 glTexCoord2f(1, 1); glVertex2f(x+sz, y+sz);
320 glTexCoord2f(0, 1); glVertex2f(x, y+sz);
321 glEnd();
324 glDisable(GL_TEXTURE_2D);
326 glPopMatrix();
327 glMatrixMode(GL_MODELVIEW);
328 glPopMatrix();
329 glEnable(GL_DEPTH_TEST);
330 SDL_GL_SwapBuffers();
333 void setfullscreen(bool enable)
335 if(!screen) return;
336 #if defined(WIN32) || defined(__APPLE__)
337 initwarning(enable ? "fullscreen" : "windowed");
338 #else
339 if(enable == !(screen->flags&SDL_FULLSCREEN))
341 SDL_WM_ToggleFullScreen(screen);
342 SDL_WM_GrabInput((screen->flags&SDL_FULLSCREEN) ? SDL_GRAB_ON : SDL_GRAB_OFF);
344 #endif
347 #ifdef _DEBUG
348 VARF(fullscreen, 0, 0, 1, setfullscreen(fullscreen!=0));
349 #else
350 VARF(fullscreen, 0, 1, 1, setfullscreen(fullscreen!=0));
351 #endif
353 void screenres(int *w, int *h)
355 #if !defined(WIN32) && !defined(__APPLE__)
356 if(initing >= INIT_RESET)
358 #endif
359 scr_w = *w;
360 scr_h = *h;
361 #if defined(WIN32) || defined(__APPLE__)
362 initwarning("screen resolution");
363 #else
364 return;
366 SDL_Surface *surf = SDL_SetVideoMode(*w, *h, 0, SDL_OPENGL|SDL_RESIZABLE|(screen->flags&SDL_FULLSCREEN));
367 if(!surf) return;
368 screen = surf;
369 scr_w = screen->w;
370 scr_h = screen->h;
371 glViewport(0, 0, scr_w, scr_h);
372 #endif
375 COMMAND(screenres, "ii");
377 VARFP(gamma, 30, 100, 300,
379 float f = gamma/100.0f;
380 if(SDL_SetGamma(f,f,f)==-1)
382 conoutf(CON_ERROR, "Could not set gamma (card/driver doesn't support it?)");
383 conoutf(CON_ERROR, "sdl: %s", SDL_GetError());
387 void resetgamma()
389 float f = gamma/100.0f;
390 if(f==1) return;
391 SDL_SetGamma(1, 1, 1);
392 SDL_SetGamma(f, f, f);
395 void setupscreen(int &usedcolorbits, int &useddepthbits, int &usedfsaa)
397 int flags = SDL_RESIZABLE;
398 #if defined(WIN32) || defined(__APPLE__)
399 flags = 0;
400 #endif
401 if(fullscreen) flags |= SDL_FULLSCREEN;
402 SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL|flags);
403 if(modes && modes!=(SDL_Rect **)-1)
405 bool hasmode = false;
406 for(int i = 0; modes[i]; i++)
408 if(scr_w <= modes[i]->w && scr_h <= modes[i]->h) { hasmode = true; break; }
410 if(!hasmode) { scr_w = modes[0]->w; scr_h = modes[0]->h; }
412 bool hasbpp = true;
413 if(colorbits && modes)
414 hasbpp = SDL_VideoModeOK(modes!=(SDL_Rect **)-1 ? modes[0]->w : scr_w, modes!=(SDL_Rect **)-1 ? modes[0]->h : scr_h, colorbits, SDL_OPENGL|flags)==colorbits;
416 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
417 #if SDL_VERSION_ATLEAST(1, 2, 11)
418 if(vsync>=0) SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, vsync);
419 #endif
420 static int configs[] =
422 0x7, /* try everything */
423 0x6, 0x5, 0x3, /* try disabling one at a time */
424 0x4, 0x2, 0x1, /* try disabling two at a time */
425 0 /* try disabling everything */
427 int config = 0;
428 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
429 if(!depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
430 if(!fsaa)
432 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
433 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
435 loopi(sizeof(configs)/sizeof(configs[0]))
437 config = configs[i];
438 if(!depthbits && config&1) continue;
439 if(!stencilbits && config&2) continue;
440 if(fsaa<=0 && config&4) continue;
441 if(depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config&1 ? depthbits : 16);
442 if(stencilbits)
444 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config&2 ? 1 : 0);
445 hasstencil = (config&2)!=0;
447 if(fsaa>0)
449 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, config&4 ? 1 : 0);
450 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config&4 ? fsaa : 0);
452 screen = SDL_SetVideoMode(scr_w, scr_h, hasbpp ? colorbits : 0, SDL_OPENGL|flags);
453 if(screen) break;
455 if(!screen) fatal("Unable to create OpenGL screen: %s", SDL_GetError());
456 else
458 if(!hasbpp) conoutf(CON_WARN, "%d bit color buffer not supported - disabling", colorbits);
459 if(depthbits && (config&1)==0) conoutf(CON_WARN, "%d bit z-buffer not supported - disabling", depthbits);
460 if(stencilbits && (config&2)==0) conoutf(CON_WARN, "Stencil buffer not supported - disabling");
461 if(fsaa>0 && (config&4)==0) conoutf(CON_WARN, "%dx anti-aliasing not supported - disabling", fsaa);
464 scr_w = screen->w;
465 scr_h = screen->h;
467 #ifdef WIN32
468 SDL_WM_GrabInput(SDL_GRAB_ON);
469 #else
470 SDL_WM_GrabInput(fullscreen ? SDL_GRAB_ON : SDL_GRAB_OFF);
471 #endif
473 usedcolorbits = hasbpp ? colorbits : 0;
474 useddepthbits = config&1 ? depthbits : 0;
475 usedfsaa = config&4 ? fsaa : 0;
478 void resetgl()
480 clearchanges(CHANGE_GFX);
482 computescreen("resetting OpenGL");
484 extern void cleanupva();
485 extern void cleanupparticles();
486 extern void cleanupmodels();
487 extern void cleanuptextures();
488 extern void cleanuplightmaps();
489 extern void cleanshadowmap();
490 extern void cleanreflections();
491 extern void cleanupglare();
492 extern void cleanupdepthfx();
493 extern void cleanupshaders();
494 extern void cleanupgl();
495 cleanupva();
496 cleanupparticles();
497 cleanupmodels();
498 cleanuptextures();
499 cleanuplightmaps();
500 cleanshadowmap();
501 cleanreflections();
502 cleanupglare();
503 cleanupdepthfx();
504 cleanupshaders();
505 cleanupgl();
507 SDL_SetVideoMode(0, 0, 0, 0);
509 int usedcolorbits = 0, useddepthbits = 0, usedfsaa = 0;
510 setupscreen(usedcolorbits, useddepthbits, usedfsaa);
511 gl_init(scr_w, scr_h, usedcolorbits, useddepthbits, usedfsaa);
513 extern void reloadfonts();
514 extern void reloadtextures();
515 extern void reloadshaders();
516 inbetweenframes = false;
517 if(!reloadtexture(*notexture) ||
518 !reloadtexture("data/sauer_logo_512_256a.png"))
519 fatal("failed to reload core texture");
520 reloadfonts();
521 inbetweenframes = true;
522 computescreen("initializing...");
523 resetgamma();
524 reloadshaders();
525 reloadtextures();
526 initlights();
527 allchanged(true);
530 COMMAND(resetgl, "");
532 void keyrepeat(bool on)
534 SDL_EnableKeyRepeat(on ? SDL_DEFAULT_REPEAT_DELAY : 0,
535 SDL_DEFAULT_REPEAT_INTERVAL);
538 static int ignoremouse = 5, grabmouse = 0;
540 vector<SDL_Event> events;
542 void pushevent(const SDL_Event &e)
544 events.add(e);
547 bool interceptkey(int sym)
549 SDL_Event event;
550 while(SDL_PollEvent(&event))
552 switch(event.type)
554 case SDL_KEYDOWN:
555 if(event.key.keysym.sym == sym)
556 return true;
558 default:
559 pushevent(event);
560 break;
563 return false;
566 void checkinput()
568 SDL_Event event;
569 int lasttype = 0, lastbut = 0;
570 while(events.length() || SDL_PollEvent(&event))
572 if(events.length()) event = events.remove(0);
574 switch(event.type)
576 case SDL_QUIT:
577 quit();
578 break;
580 #if !defined(WIN32) && !defined(__APPLE__)
581 case SDL_VIDEORESIZE:
582 screenres(&event.resize.w, &event.resize.h);
583 break;
584 #endif
586 case SDL_KEYDOWN:
587 case SDL_KEYUP:
588 keypress(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.unicode);
589 break;
591 case SDL_ACTIVEEVENT:
592 if(event.active.state & SDL_APPINPUTFOCUS)
593 grabmouse = event.active.gain;
594 else
595 if(event.active.gain)
596 grabmouse = 1;
597 break;
599 case SDL_MOUSEMOTION:
600 if(ignoremouse) { ignoremouse--; break; }
601 #ifndef WIN32
602 if(!(screen->flags&SDL_FULLSCREEN) && grabmouse)
604 #ifdef __APPLE__
605 if(event.motion.y == 0) break; //let mac users drag windows via the title bar
606 #endif
607 if(event.motion.x == screen->w / 2 && event.motion.y == screen->h / 2) break;
608 SDL_WarpMouse(screen->w / 2, screen->h / 2);
610 if((screen->flags&SDL_FULLSCREEN) || grabmouse)
611 #endif
612 if(!g3d_movecursor(event.motion.xrel, event.motion.yrel))
613 mousemove(event.motion.xrel, event.motion.yrel);
614 break;
616 case SDL_MOUSEBUTTONDOWN:
617 case SDL_MOUSEBUTTONUP:
618 if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it
619 keypress(-event.button.button, event.button.state!=0, 0);
620 lasttype = event.type;
621 lastbut = event.button.button;
622 break;
627 VARF(gamespeed, 10, 100, 1000, if(multiplayer()) gamespeed = 100);
629 VARF(paused, 0, 0, 1, if(multiplayer()) paused = 0);
631 VARP(maxfps, 0, 200, 1000);
633 void limitfps(int &millis, int curmillis)
635 if(!maxfps) return;
636 static int fpserror = 0;
637 int delay = 1000/maxfps - (millis-curmillis);
638 if(delay < 0) fpserror = 0;
639 else
641 fpserror += 1000%maxfps;
642 if(fpserror >= maxfps)
644 ++delay;
645 fpserror -= maxfps;
647 if(delay > 0)
649 SDL_Delay(delay);
650 millis += delay;
655 #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
656 void stackdumper(unsigned int type, EXCEPTION_POINTERS *ep)
658 if(!ep) fatal("unknown type");
659 EXCEPTION_RECORD *er = ep->ExceptionRecord;
660 CONTEXT *context = ep->ContextRecord;
661 string out, t;
662 s_sprintf(out)("Sauerbraten Win32 Exception: 0x%x [0x%x]\n\n", er->ExceptionCode, er->ExceptionCode==EXCEPTION_ACCESS_VIOLATION ? er->ExceptionInformation[1] : -1);
663 STACKFRAME sf = {{context->Eip, 0, AddrModeFlat}, {}, {context->Ebp, 0, AddrModeFlat}, {context->Esp, 0, AddrModeFlat}, 0};
664 SymInitialize(GetCurrentProcess(), NULL, TRUE);
666 while(::StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &sf, context, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL))
668 struct { IMAGEHLP_SYMBOL sym; string n; } si = { { sizeof( IMAGEHLP_SYMBOL ), 0, 0, 0, sizeof(string) } };
669 IMAGEHLP_LINE li = { sizeof( IMAGEHLP_LINE ) };
670 DWORD off;
671 if(SymGetSymFromAddr(GetCurrentProcess(), (DWORD)sf.AddrPC.Offset, &off, &si.sym) && SymGetLineFromAddr(GetCurrentProcess(), (DWORD)sf.AddrPC.Offset, &off, &li))
673 char *del = strrchr(li.FileName, '\\');
674 s_sprintf(t)("%s - %s [%d]\n", si.sym.Name, del ? del + 1 : li.FileName, li.LineNumber);
675 s_strcat(out, t);
678 fatal(out);
680 #endif
682 #define MAXFPSHISTORY 60
684 int fpspos = 0, fpshistory[MAXFPSHISTORY];
686 void resetfpshistory()
688 loopi(MAXFPSHISTORY) fpshistory[i] = 1;
689 fpspos = 0;
692 void updatefpshistory(int millis)
694 fpshistory[fpspos++] = max(1, min(1000, millis));
695 if(fpspos>=MAXFPSHISTORY) fpspos = 0;
698 void getfps(int &fps, int &bestdiff, int &worstdiff)
700 int total = fpshistory[MAXFPSHISTORY-1], best = total, worst = total;
701 loopi(MAXFPSHISTORY-1)
703 int millis = fpshistory[i];
704 total += millis;
705 if(millis < best) best = millis;
706 if(millis > worst) worst = millis;
709 fps = (1000*MAXFPSHISTORY)/total;
710 bestdiff = 1000/best-fps;
711 worstdiff = fps-1000/worst;
714 void getfps_(int *raw)
716 int fps, bestdiff, worstdiff;
717 if(*raw) fps = 1000/fpshistory[(fpspos+MAXFPSHISTORY-1)%MAXFPSHISTORY];
718 else getfps(fps, bestdiff, worstdiff);
719 intret(fps);
722 COMMANDN(getfps, getfps_, "i");
724 bool inbetweenframes = false;
726 static bool findarg(int argc, char **argv, const char *str)
728 for(int i = 1; i<argc; i++) if(strstr(argv[i], str)==argv[i]) return true;
729 return false;
732 static int clockrealbase = 0, clockvirtbase = 0;
733 static void clockreset() { clockrealbase = SDL_GetTicks(); clockvirtbase = totalmillis; }
734 VARFP(clockerror, 990000, 1000000, 1010000, clockreset());
735 VARFP(clockfix, 0, 0, 1, clockreset());
737 int main(int argc, char **argv)
739 #ifdef WIN32
740 //atexit((void (__cdecl *)(void))_CrtDumpMemoryLeaks);
741 #ifndef _DEBUG
742 #ifndef __GNUC__
743 __try {
744 #endif
745 #endif
746 #endif
748 bool dedicated = false;
749 char *load = NULL, *initscript = NULL;
751 #define log(s) puts("init: " s)
753 initing = INIT_RESET;
754 for(int i = 1; i<argc; i++)
756 if(argv[i][0]=='-') switch(argv[i][1])
758 case 'q': printf("Using home directory: %s\n", &argv[i][2]); sethomedir(&argv[i][2]); break;
759 case 'k': printf("Adding package directory: %s\n", &argv[i][2]); addpackagedir(&argv[i][2]); break;
760 case 'r': execfile(argv[i][2] ? &argv[i][2] : "init.cfg"); restoredinits = true; break;
761 case 'd': dedicated = true; break;
762 case 'w': scr_w = atoi(&argv[i][2]); if(scr_w<320) scr_w = 320; if(!findarg(argc, argv, "-h")) scr_h = (scr_w*3)/4; break;
763 case 'h': scr_h = atoi(&argv[i][2]); if(scr_h<200) scr_h = 200; if(!findarg(argc, argv, "-w")) scr_w = (scr_h*4)/3; break;
764 case 'z': depthbits = atoi(&argv[i][2]); break;
765 case 'b': colorbits = atoi(&argv[i][2]); break;
766 case 'a': fsaa = atoi(&argv[i][2]); break;
767 case 'v': vsync = atoi(&argv[i][2]); break;
768 case 't': fullscreen = atoi(&argv[i][2]); break;
769 case 's': stencilbits = atoi(&argv[i][2]); break;
770 case 'f':
772 extern int useshaders, shaderprecision;
773 int n = atoi(&argv[i][2]);
774 useshaders = n ? 1 : 0;
775 shaderprecision = min(max(n - 1, 0), 3);
776 break;
778 case 'l':
780 char pkgdir[] = "packages/";
781 load = strstr(path(&argv[i][2]), path(pkgdir));
782 if(load) load += sizeof(pkgdir)-1;
783 else load = &argv[i][2];
784 break;
786 case 'x': initscript = &argv[i][2]; break;
787 default: if(!serveroption(argv[i])) gameargs.add(argv[i]); break;
789 else gameargs.add(argv[i]);
791 initing = NOT_INITING;
793 log("sdl");
795 int par = 0;
796 #ifdef _DEBUG
797 par = SDL_INIT_NOPARACHUTE;
798 #ifdef WIN32
799 SetEnvironmentVariable("SDL_DEBUG", "1");
800 #endif
801 #endif
803 //#ifdef WIN32
804 //SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
805 //#endif
807 if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_AUDIO|par)<0) fatal("Unable to initialize SDL: %s", SDL_GetError());
809 log("enet");
810 if(enet_initialize()<0) fatal("Unable to initialise network module");
812 initserver(dedicated); // never returns if dedicated
814 log("video: mode");
815 int usedcolorbits = 0, useddepthbits = 0, usedfsaa = 0;
816 setupscreen(usedcolorbits, useddepthbits, usedfsaa);
818 log("video: misc");
819 SDL_WM_SetCaption("sauerbraten engine", NULL);
820 keyrepeat(false);
821 SDL_ShowCursor(0);
823 log("gl");
824 gl_checkextensions();
825 gl_init(scr_w, scr_h, usedcolorbits, useddepthbits, usedfsaa);
826 notexture = textureload("data/notexture.png");
827 if(!notexture) fatal("could not find core textures");
829 log("console");
830 persistidents = false;
831 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.
832 if(!execfile("data/font.cfg")) fatal("cannot find font definitions");
833 if(!setfont("default")) fatal("no default font specified");
835 computescreen("initializing...");
836 inbetweenframes = true;
838 log("gl: effects");
839 loadshaders();
840 particleinit();
841 initdecals();
843 log("world");
844 camera1 = player = cl->iterdynents(0);
845 emptymap(0, true);
847 log("sound");
848 initsound();
850 log("cfg");
851 exec("data/keymap.cfg");
852 exec("data/stdedit.cfg");
853 exec("data/menus.cfg");
854 exec("data/sounds.cfg");
855 exec("data/brush.cfg");
856 execfile("mybrushes.cfg");
857 if(cl->savedservers()) execfile(cl->savedservers());
859 persistidents = true;
861 initing = INIT_LOAD;
862 if(!execfile(cl->savedconfig())) exec(cl->defaultconfig());
863 execfile(cl->autoexec());
864 initing = NOT_INITING;
866 persistidents = false;
868 string gamecfgname;
869 s_strcpy(gamecfgname, "data/game_");
870 s_strcat(gamecfgname, cl->gameident());
871 s_strcat(gamecfgname, ".cfg");
872 exec(gamecfgname);
874 persistidents = true;
876 log("localconnect");
877 localconnect();
878 cc->gameconnect(false);
879 //cc->changemap(load ? load : cl->defaultmap());
881 if(initscript) execute(initscript);
883 log("mainloop");
885 initmumble();
886 resetfpshistory();
888 for(;;)
890 static int frames = 0;
891 int millis = SDL_GetTicks() - clockrealbase;
892 if(clockfix) millis = int(millis*(double(clockerror)/1000000));
893 millis += clockvirtbase;
894 if(millis<totalmillis) millis = totalmillis;
895 limitfps(millis, totalmillis);
896 int elapsed = millis-totalmillis;
897 if(multiplayer(false)) curtime = elapsed;
898 else
900 static int timeerr = 0;
901 int scaledtime = elapsed*gamespeed + timeerr;
902 curtime = scaledtime/100;
903 timeerr = scaledtime%100;
904 if(curtime>200) curtime = 200;
905 if(paused) curtime = 0;
908 checkinput();
910 if(lastmillis) cl->updateworld(worldpos, curtime, lastmillis);
912 menuprocess();
914 lastmillis += curtime;
915 totalmillis = millis;
917 checksleep(lastmillis);
919 serverslice(0);
921 if(frames) updatefpshistory(elapsed);
922 frames++;
924 // miscellaneous general game effects
925 findorientation();
926 entity_particles();
927 updatevol();
928 checkmapsounds();
930 inbetweenframes = false;
931 if(frames>2) gl_drawframe(screen->w, screen->h);
932 SDL_GL_SwapBuffers();
933 inbetweenframes = true;
936 ASSERT(0);
937 return EXIT_FAILURE;
939 #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
940 } __except(stackdumper(0, GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) { return 0; }
941 #endif