1 // main.cpp: initialisation & main loop
2 
3 #include "engine.h"
4 
5 #ifdef SDL_VIDEO_DRIVER_X11
6 #include "SDL_syswm.h"
7 #endif
8 
9 extern void cleargamma();
10 
cleanup()11 void cleanup()
12 {
13     recorder::stop();
14     cleanupserver();
15     SDL_ShowCursor(SDL_TRUE);
16     SDL_SetRelativeMouseMode(SDL_FALSE);
17     if(screen) SDL_SetWindowGrab(screen, SDL_FALSE);
18     cleargamma();
19     freeocta(worldroot);
20     extern void clear_command(); clear_command();
21     extern void clear_console(); clear_console();
22     extern void clear_mdls();    clear_mdls();
23     extern void clear_sound();   clear_sound();
24     closelogfile();
25     #ifdef __APPLE__
26         if(screen) SDL_SetWindowFullscreen(screen, 0);
27     #endif
28     SDL_Quit();
29 }
30 
31 extern void writeinitcfg();
32 
quit()33 void quit()                     // normal exit
34 {
35     writeinitcfg();
36     writeservercfg();
37     abortconnect();
38     disconnect();
39     localdisconnect();
40     writecfg();
41     cleanup();
42     exit(EXIT_SUCCESS);
43 }
44 
fatal(const char * s,...)45 void fatal(const char *s, ...)    // failure exit
46 {
47     static int errors = 0;
48     errors++;
49 
50     if(errors <= 2) // print up to one extra recursive error
51     {
52         defvformatstring(msg,s,s);
53         logoutf("%s", msg);
54 
55         if(errors <= 1) // avoid recursion
56         {
57             if(SDL_WasInit(SDL_INIT_VIDEO))
58             {
59                 SDL_ShowCursor(SDL_TRUE);
60                 SDL_SetRelativeMouseMode(SDL_FALSE);
61                 if(screen) SDL_SetWindowGrab(screen, SDL_FALSE);
62                 cleargamma();
63                 #ifdef __APPLE__
64                     if(screen) SDL_SetWindowFullscreen(screen, 0);
65                 #endif
66             }
67             SDL_Quit();
68             SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Cube 2: Sauerbraten fatal error", msg, NULL);
69         }
70     }
71 
72     exit(EXIT_FAILURE);
73 }
74 
75 int curtime = 0, lastmillis = 1, elapsedtime = 0, totalmillis = 1;
76 
77 dynent *player = NULL;
78 
79 int initing = NOT_INITING;
80 
initwarning(const char * desc,int level,int type)81 bool initwarning(const char *desc, int level, int type)
82 {
83     if(initing < level)
84     {
85         addchange(desc, type);
86         return true;
87     }
88     return false;
89 }
90 
91 VAR(desktopw, 1, 0, 0);
92 VAR(desktoph, 1, 0, 0);
93 int screenw = 0, screenh = 0;
94 SDL_Window *screen = NULL;
95 SDL_GLContext glcontext = NULL;
96 
97 #define SCR_MINW 320
98 #define SCR_MINH 200
99 #define SCR_MAXW 10000
100 #define SCR_MAXH 10000
101 #define SCR_DEFAULTW 1024
102 #define SCR_DEFAULTH 768
103 VARF(scr_w, SCR_MINW, -1, SCR_MAXW, initwarning("screen resolution"));
104 VARF(scr_h, SCR_MINH, -1, SCR_MAXH, initwarning("screen resolution"));
105 VARF(depthbits, 0, 0, 32, initwarning("depth-buffer precision"));
106 VARF(fsaa, -1, -1, 16, initwarning("anti-aliasing"));
107 
writeinitcfg()108 void writeinitcfg()
109 {
110     stream *f = openutf8file("init.cfg", "w");
111     if(!f) return;
112     f->printf("// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n");
113     extern int fullscreen, fullscreendesktop;
114     f->printf("fullscreen %d\n", fullscreen);
115     f->printf("fullscreendesktop %d\n", fullscreendesktop);
116     f->printf("scr_w %d\n", scr_w);
117     f->printf("scr_h %d\n", scr_h);
118     f->printf("depthbits %d\n", depthbits);
119     f->printf("fsaa %d\n", fsaa);
120     extern int usesound, soundchans, soundfreq, soundbufferlen;
121     extern char *audiodriver;
122     f->printf("usesound %d\n", usesound);
123     f->printf("soundchans %d\n", soundchans);
124     f->printf("soundfreq %d\n", soundfreq);
125     f->printf("soundbufferlen %d\n", soundbufferlen);
126     if(audiodriver[0]) f->printf("audiodriver %s\n", escapestring(audiodriver));
127     delete f;
128 }
129 
130 COMMAND(quit, "");
131 
getbackgroundres(int & w,int & h)132 static void getbackgroundres(int &w, int &h)
133 {
134     float wk = 1, hk = 1;
135     if(w < 1024) wk = 1024.0f/w;
136     if(h < 768) hk = 768.0f/h;
137     wk = hk = max(wk, hk);
138     w = int(ceil(w*wk));
139     h = int(ceil(h*hk));
140 }
141 
142 string backgroundcaption = "";
143 Texture *backgroundmapshot = NULL;
144 string backgroundmapname = "";
145 char *backgroundmapinfo = NULL;
146 
setbackgroundinfo(const char * caption=NULL,Texture * mapshot=NULL,const char * mapname=NULL,const char * mapinfo=NULL)147 void setbackgroundinfo(const char *caption = NULL, Texture *mapshot = NULL, const char *mapname = NULL, const char *mapinfo = NULL)
148 {
149     renderedframe = false;
150     copystring(backgroundcaption, caption ? caption : "");
151     backgroundmapshot = mapshot;
152     copystring(backgroundmapname, mapname ? mapname : "");
153     if(mapinfo != backgroundmapinfo)
154     {
155         DELETEA(backgroundmapinfo);
156         if(mapinfo) backgroundmapinfo = newstring(mapinfo);
157     }
158 }
159 
restorebackground(bool force=false)160 void restorebackground(bool force = false)
161 {
162     if(renderedframe)
163     {
164         if(!force) return;
165         setbackgroundinfo();
166     }
167     renderbackground(backgroundcaption[0] ? backgroundcaption : NULL, backgroundmapshot, backgroundmapname[0] ? backgroundmapname : NULL, backgroundmapinfo, true);
168 }
169 
bgquad(float x,float y,float w,float h,float tx=0,float ty=0,float tw=1,float th=1)170 void bgquad(float x, float y, float w, float h, float tx = 0, float ty = 0, float tw = 1, float th = 1)
171 {
172     gle::begin(GL_TRIANGLE_STRIP);
173     gle::attribf(x,   y);   gle::attribf(tx,      ty);
174     gle::attribf(x+w, y);   gle::attribf(tx + tw, ty);
175     gle::attribf(x,   y+h); gle::attribf(tx,      ty + th);
176     gle::attribf(x+w, y+h); gle::attribf(tx + tw, ty + th);
177     gle::end();
178 }
179 
renderbackground(const char * caption,Texture * mapshot,const char * mapname,const char * mapinfo,bool restore,bool force)180 void renderbackground(const char *caption, Texture *mapshot, const char *mapname, const char *mapinfo, bool restore, bool force)
181 {
182     if(!inbetweenframes && !force) return;
183 
184     if(!restore || force) stopsounds(); // stop sounds while loading
185 
186     int w = screenw, h = screenh;
187     if(forceaspect) w = int(ceil(h*forceaspect));
188     getbackgroundres(w, h);
189     gettextres(w, h);
190 
191     static int lastupdate = -1, lastw = -1, lasth = -1;
192     static float backgroundu = 0, backgroundv = 0, detailu = 0, detailv = 0;
193     static int numdecals = 0;
194     static struct decal { float x, y, size; int side; } decals[12];
195     if((renderedframe && !mainmenu && lastupdate != lastmillis) || lastw != w || lasth != h)
196     {
197         lastupdate = lastmillis;
198         lastw = w;
199         lasth = h;
200 
201         backgroundu = rndscale(1);
202         backgroundv = rndscale(1);
203         detailu = rndscale(1);
204         detailv = rndscale(1);
205         numdecals = sizeof(decals)/sizeof(decals[0]);
206         numdecals = numdecals/3 + rnd((numdecals*2)/3 + 1);
207         float maxsize = min(w, h)/16.0f;
208         loopi(numdecals)
209         {
210             decal d = { rndscale(w), rndscale(h), maxsize/2 + rndscale(maxsize/2), rnd(2) };
211             decals[i] = d;
212         }
213     }
214     else if(lastupdate != lastmillis) lastupdate = lastmillis;
215 
216     loopi(restore ? 1 : 3)
217     {
218         hudmatrix.ortho(0, w, h, 0, -1, 1);
219         resethudmatrix();
220 
221         hudshader->set();
222         gle::colorf(1, 1, 1);
223 
224         gle::defvertex(2);
225         gle::deftexcoord0();
226 
227         settexture("data/background.png", 0);
228         float bu = w*0.67f/256.0f + backgroundu, bv = h*0.67f/256.0f + backgroundv;
229         bgquad(0, 0, w, h, 0, 0, bu, bv);
230         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
231         glEnable(GL_BLEND);
232         settexture("data/background_detail.png", 0);
233         float du = w*0.8f/512.0f + detailu, dv = h*0.8f/512.0f + detailv;
234         bgquad(0, 0, w, h, 0, 0, du, dv);
235         settexture("data/background_decal.png", 3);
236         gle::begin(GL_QUADS);
237         loopj(numdecals)
238         {
239             float hsz = decals[j].size, hx = clamp(decals[j].x, hsz, w-hsz), hy = clamp(decals[j].y, hsz, h-hsz), side = decals[j].side;
240             gle::attribf(hx-hsz, hy-hsz); gle::attribf(side,   0);
241             gle::attribf(hx+hsz, hy-hsz); gle::attribf(1-side, 0);
242             gle::attribf(hx+hsz, hy+hsz); gle::attribf(1-side, 1);
243             gle::attribf(hx-hsz, hy+hsz); gle::attribf(side,   1);
244         }
245         gle::end();
246         float lh = 0.5f*min(w, h), lw = lh*2,
247               lx = 0.5f*(w - lw), ly = 0.5f*(h*0.5f - lh);
248         settexture((maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize) >= 1024 && (screenw > 1280 || screenh > 800) ? "data/logo_1024.png" : "data/logo.png", 3);
249         bgquad(lx, ly, lw, lh);
250         if(caption)
251         {
252             int tw = text_width(caption);
253             float tsz = 0.04f*min(w, h)/FONTH,
254                   tx = 0.5f*(w - tw*tsz), ty = h - 0.075f*1.5f*min(w, h) - 1.25f*FONTH*tsz;
255             pushhudmatrix();
256             hudmatrix.translate(tx, ty, 0);
257             hudmatrix.scale(tsz, tsz, 1);
258             flushhudmatrix();
259             draw_text(caption, 0, 0);
260             pophudmatrix();
261         }
262         if(mapshot || mapname)
263         {
264             int infowidth = 12*FONTH;
265             float sz = 0.35f*min(w, h), msz = (0.75f*min(w, h) - sz)/(infowidth + FONTH), x = 0.5f*(w-sz), y = ly+lh - sz/15;
266             if(mapinfo)
267             {
268                 int mw, mh;
269                 text_bounds(mapinfo, mw, mh, infowidth);
270                 x -= 0.5f*(mw*msz + FONTH*msz);
271             }
272             if(mapshot && mapshot!=notexture)
273             {
274                 glBindTexture(GL_TEXTURE_2D, mapshot->id);
275                 bgquad(x, y, sz, sz);
276             }
277             else
278             {
279                 int qw, qh;
280                 text_bounds("?", qw, qh);
281                 float qsz = sz*0.5f/max(qw, qh);
282                 pushhudmatrix();
283                 hudmatrix.translate(x + 0.5f*(sz - qw*qsz), y + 0.5f*(sz - qh*qsz), 0);
284                 hudmatrix.scale(qsz, qsz, 1);
285                 flushhudmatrix();
286                 draw_text("?", 0, 0);
287                 pophudmatrix();
288                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
289             }
290             settexture("data/mapshot_frame.png", 3);
291             bgquad(x, y, sz, sz);
292             if(mapname)
293             {
294                 int tw = text_width(mapname);
295                 float tsz = sz/(8*FONTH),
296                       tx = 0.9f*sz - tw*tsz, ty = 0.9f*sz - FONTH*tsz;
297                 if(tx < 0.1f*sz) { tsz = 0.1f*sz/tw; tx = 0.1f; }
298                 pushhudmatrix();
299                 hudmatrix.translate(x+tx, y+ty, 0);
300                 hudmatrix.scale(tsz, tsz, 1);
301                 flushhudmatrix();
302                 draw_text(mapname, 0, 0);
303                 pophudmatrix();
304             }
305             if(mapinfo)
306             {
307                 pushhudmatrix();
308                 hudmatrix.translate(x+sz+FONTH*msz, y, 0);
309                 hudmatrix.scale(msz, msz, 1);
310                 flushhudmatrix();
311                 draw_text(mapinfo, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, -1, infowidth);
312                 pophudmatrix();
313             }
314         }
315         glDisable(GL_BLEND);
316         if(!restore) swapbuffers(false);
317     }
318 
319     if(!restore) setbackgroundinfo(caption, mapshot, mapname, mapinfo);
320 }
321 
322 VAR(progressbackground, 0, 0, 1);
323 
324 float loadprogress = 0;
325 
renderprogress(float bar,const char * text,GLuint tex,bool background)326 void renderprogress(float bar, const char *text, GLuint tex, bool background)   // also used during loading
327 {
328     if(!inbetweenframes || drawtex) return;
329 
330     extern int menufps, maxfps;
331     int fps = menufps ? (maxfps ? min(maxfps, menufps) : menufps) : maxfps;
332     if(fps)
333     {
334         static int lastprogress = 0;
335         int ticks = SDL_GetTicks(), diff = ticks - lastprogress;
336         if(bar > 0 && diff >= 0 && diff < (1000 + fps-1)/fps) return;
337         lastprogress = ticks;
338     }
339 
340     clientkeepalive();      // make sure our connection doesn't time out while loading maps etc.
341 
342     SDL_PumpEvents(); // keep the event queue awake to avoid 'beachball' cursor
343 
344     extern int mesa_swap_bug, curvsync;
345     bool forcebackground = progressbackground || (mesa_swap_bug && (curvsync || totalmillis==1));
346     if(background || forcebackground) restorebackground(forcebackground);
347 
348     int w = screenw, h = screenh;
349     if(forceaspect) w = int(ceil(h*forceaspect));
350     getbackgroundres(w, h);
351     gettextres(w, h);
352 
353     hudmatrix.ortho(0, w, h, 0, -1, 1);
354     resethudmatrix();
355 
356     hudshader->set();
357     gle::colorf(1, 1, 1);
358 
359     gle::defvertex(2);
360     gle::deftexcoord0();
361 
362     float fh = 0.075f*min(w, h), fw = fh*10,
363           fx = renderedframe ? w - fw - fh/4 : 0.5f*(w - fw),
364           fy = renderedframe ? fh/4 : h - fh*1.5f,
365           fu1 = 0/512.0f, fu2 = 511/512.0f,
366           fv1 = 0/64.0f, fv2 = 52/64.0f;
367     settexture("data/loading_frame.png", 3);
368     bgquad(fx, fy, fw, fh, fu1, fv1, fu2-fu1, fv2-fv1);
369 
370     glEnable(GL_BLEND);
371     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
372 
373     float bw = fw*(511 - 2*17)/511.0f, bh = fh*20/52.0f,
374           bx = fx + fw*17/511.0f, by = fy + fh*16/52.0f,
375           bv1 = 0/32.0f, bv2 = 20/32.0f,
376           su1 = 0/32.0f, su2 = 7/32.0f, sw = fw*7/511.0f,
377           eu1 = 23/32.0f, eu2 = 30/32.0f, ew = fw*7/511.0f,
378           mw = bw - sw - ew,
379           ex = bx+sw + max(mw*bar, fw*7/511.0f);
380     if(bar > 0)
381     {
382         settexture("data/loading_bar.png", 3);
383         gle::begin(GL_QUADS);
384         gle::attribf(bx,    by);    gle::attribf(su1, bv1);
385         gle::attribf(bx+sw, by);    gle::attribf(su2, bv1);
386         gle::attribf(bx+sw, by+bh); gle::attribf(su2, bv2);
387         gle::attribf(bx,    by+bh); gle::attribf(su1, bv2);
388 
389         gle::attribf(bx+sw, by);    gle::attribf(su2, bv1);
390         gle::attribf(ex,    by);    gle::attribf(eu1, bv1);
391         gle::attribf(ex,    by+bh); gle::attribf(eu1, bv2);
392         gle::attribf(bx+sw, by+bh); gle::attribf(su2, bv2);
393 
394         gle::attribf(ex,    by);    gle::attribf(eu1, bv1);
395         gle::attribf(ex+ew, by);    gle::attribf(eu2, bv1);
396         gle::attribf(ex+ew, by+bh); gle::attribf(eu2, bv2);
397         gle::attribf(ex,    by+bh); gle::attribf(eu1, bv2);
398         gle::end();
399     }
400 
401     if(text)
402     {
403         int tw = text_width(text);
404         float tsz = bh*0.8f/FONTH;
405         if(tw*tsz > mw) tsz = mw/tw;
406         pushhudmatrix();
407         hudmatrix.translate(bx+sw, by + (bh - FONTH*tsz)/2, 0);
408         hudmatrix.scale(tsz, tsz, 1);
409         flushhudmatrix();
410         draw_text(text, 0, 0);
411         pophudmatrix();
412     }
413 
414     glDisable(GL_BLEND);
415 
416     if(tex)
417     {
418         glBindTexture(GL_TEXTURE_2D, tex);
419         float sz = 0.35f*min(w, h), x = 0.5f*(w-sz), y = 0.5f*min(w, h) - sz/15;
420         bgquad(x, y, sz, sz);
421 
422         glEnable(GL_BLEND);
423         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
424         settexture("data/mapshot_frame.png", 3);
425         bgquad(x, y, sz, sz);
426         glDisable(GL_BLEND);
427     }
428 
429     swapbuffers(false);
430 }
431 
432 int keyrepeatmask = 0, textinputmask = 0;
433 Uint32 textinputtime = 0;
434 VAR(textinputfilter, 0, 5, 1000);
435 
keyrepeat(bool on,int mask)436 void keyrepeat(bool on, int mask)
437 {
438     if(on) keyrepeatmask |= mask;
439     else keyrepeatmask &= ~mask;
440 }
441 
textinput(bool on,int mask)442 void textinput(bool on, int mask)
443 {
444     if(on)
445     {
446         if(!textinputmask)
447         {
448             SDL_StartTextInput();
449             textinputtime = SDL_GetTicks();
450         }
451         textinputmask |= mask;
452     }
453     else if(textinputmask)
454     {
455         textinputmask &= ~mask;
456         if(!textinputmask) SDL_StopTextInput();
457     }
458 }
459 
460 #ifdef WIN32
461 // SDL_WarpMouseInWindow behaves erratically on Windows, so force relative mouse instead.
462 VARN(relativemouse, userelativemouse, 1, 1, 0);
463 #else
464 VARNP(relativemouse, userelativemouse, 0, 1, 1);
465 #endif
466 
467 bool shouldgrab = false, grabinput = false, minimized = false, canrelativemouse = true, relativemouse = false;
468 
469 #ifdef SDL_VIDEO_DRIVER_X11
470 VAR(sdl_xgrab_bug, 0, 0, 1);
471 #endif
472 
inputgrab(bool on,bool delay=false)473 void inputgrab(bool on, bool delay = false)
474 {
475 #ifdef SDL_VIDEO_DRIVER_X11
476     bool wasrelativemouse = relativemouse;
477 #endif
478     if(on)
479     {
480         SDL_ShowCursor(SDL_FALSE);
481         if(canrelativemouse && userelativemouse)
482         {
483             if(SDL_SetRelativeMouseMode(SDL_TRUE) >= 0)
484             {
485                 SDL_SetWindowGrab(screen, SDL_TRUE);
486                 relativemouse = true;
487             }
488             else
489             {
490                 SDL_SetWindowGrab(screen, SDL_FALSE);
491                 canrelativemouse = false;
492                 relativemouse = false;
493             }
494         }
495     }
496     else
497     {
498         SDL_ShowCursor(SDL_TRUE);
499         if(relativemouse)
500         {
501             SDL_SetWindowGrab(screen, SDL_FALSE);
502             SDL_SetRelativeMouseMode(SDL_FALSE);
503             relativemouse = false;
504         }
505     }
506     shouldgrab = delay;
507 
508 #ifdef SDL_VIDEO_DRIVER_X11
509     if((relativemouse || wasrelativemouse) && sdl_xgrab_bug)
510     {
511         // Workaround for buggy SDL X11 pointer grabbing
512         union { SDL_SysWMinfo info; uchar buf[sizeof(SDL_SysWMinfo) + 128]; };
513         SDL_GetVersion(&info.version);
514         if(SDL_GetWindowWMInfo(screen, &info) && info.subsystem == SDL_SYSWM_X11)
515         {
516             if(relativemouse)
517             {
518                 uint mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
519                 XGrabPointer(info.info.x11.display, info.info.x11.window, True, mask, GrabModeAsync, GrabModeAsync, info.info.x11.window, None, CurrentTime);
520             }
521             else XUngrabPointer(info.info.x11.display, CurrentTime);
522         }
523     }
524 #endif
525 }
526 
527 bool initwindowpos = false;
528 
setfullscreen(bool enable)529 void setfullscreen(bool enable)
530 {
531     if(!screen) return;
532     //initwarning(enable ? "fullscreen" : "windowed");
533     extern int fullscreendesktop;
534     SDL_SetWindowFullscreen(screen, enable ? (fullscreendesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN) : 0);
535     if(!enable)
536     {
537         SDL_SetWindowSize(screen, scr_w, scr_h);
538         if(initwindowpos)
539         {
540             int winx = SDL_WINDOWPOS_CENTERED, winy = SDL_WINDOWPOS_CENTERED;
541             SDL_SetWindowPosition(screen, winx, winy);
542             initwindowpos = false;
543         }
544     }
545 }
546 
547 #ifdef _DEBUG
548 VARF(fullscreen, 0, 0, 1, setfullscreen(fullscreen!=0));
549 #else
550 VARF(fullscreen, 0, 1, 1, setfullscreen(fullscreen!=0));
551 #endif
552 
resetfullscreen()553 void resetfullscreen()
554 {
555     setfullscreen(false);
556     setfullscreen(true);
557 }
558 
559 VARF(fullscreendesktop, 0, 0, 1, if(fullscreen) resetfullscreen());
560 
screenres(int w,int h)561 void screenres(int w, int h)
562 {
563     scr_w = clamp(w, SCR_MINW, SCR_MAXW);
564     scr_h = clamp(h, SCR_MINH, SCR_MAXH);
565     if(screen)
566     {
567         if(fullscreendesktop)
568         {
569             scr_w = min(scr_w, desktopw);
570             scr_h = min(scr_h, desktoph);
571         }
572         if(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN)
573         {
574             if(fullscreendesktop) gl_resize();
575             else resetfullscreen();
576             initwindowpos = true;
577         }
578         else
579         {
580             SDL_SetWindowSize(screen, scr_w, scr_h);
581             SDL_SetWindowPosition(screen, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
582             initwindowpos = false;
583         }
584     }
585     else
586     {
587         initwarning("screen resolution");
588     }
589 }
590 
591 ICOMMAND(screenres, "ii", (int *w, int *h), screenres(*w, *h));
592 
setgamma(int val)593 static void setgamma(int val)
594 {
595     if(screen && SDL_SetWindowBrightness(screen, val/100.0f) < 0) conoutf(CON_ERROR, "Could not set gamma: %s", SDL_GetError());
596 }
597 
598 static int curgamma = 100;
599 VARFNP(gamma, reqgamma, 30, 100, 300,
600 {
601     if(initing || reqgamma == curgamma) return;
602     curgamma = reqgamma;
603     setgamma(curgamma);
604 });
605 
restoregamma()606 void restoregamma()
607 {
608     if(initing || reqgamma == 100) return;
609     curgamma = reqgamma;
610     setgamma(curgamma);
611 }
612 
cleargamma()613 void cleargamma()
614 {
615     if(curgamma != 100 && screen) SDL_SetWindowBrightness(screen, 1.0f);
616 }
617 
618 int curvsync = -1;
restorevsync()619 void restorevsync()
620 {
621     if(initing || !glcontext) return;
622     extern int vsync, vsynctear;
623     if(!SDL_GL_SetSwapInterval(vsync ? (vsynctear ? -1 : 1) : 0))
624         curvsync = vsync;
625 }
626 
627 VARFP(vsync, 0, 0, 1, restorevsync());
628 VARFP(vsynctear, 0, 0, 1, { if(vsync) restorevsync(); });
629 
setupscreen()630 void setupscreen()
631 {
632     if(glcontext)
633     {
634         SDL_GL_DeleteContext(glcontext);
635         glcontext = NULL;
636     }
637     if(screen)
638     {
639         SDL_DestroyWindow(screen);
640         screen = NULL;
641     }
642     curvsync = -1;
643 
644     SDL_Rect desktop;
645     if(SDL_GetDisplayBounds(0, &desktop) < 0) fatal("failed querying desktop bounds: %s", SDL_GetError());
646     desktopw = desktop.w;
647     desktoph = desktop.h;
648 
649     if(scr_h < 0) scr_h = fullscreen ? desktoph : SCR_DEFAULTH;
650     if(scr_w < 0) scr_w = (scr_h*desktopw)/desktoph;
651     scr_w = clamp(scr_w, SCR_MINW, SCR_MAXW);
652     scr_h = clamp(scr_h, SCR_MINH, SCR_MAXH);
653     if(fullscreendesktop)
654     {
655         scr_w = min(scr_w, desktopw);
656         scr_h = min(scr_h, desktoph);
657     }
658 
659     int winx = SDL_WINDOWPOS_UNDEFINED, winy = SDL_WINDOWPOS_UNDEFINED, winw = scr_w, winh = scr_h, flags = SDL_WINDOW_RESIZABLE;
660     if(fullscreen)
661     {
662         if(fullscreendesktop)
663         {
664             winw = desktopw;
665             winh = desktoph;
666             flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
667         }
668         else flags |= SDL_WINDOW_FULLSCREEN;
669         initwindowpos = true;
670     }
671 
672     SDL_GL_ResetAttributes();
673     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
674     #if !defined(WIN32) && !defined(__APPLE__)
675     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
676     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
677     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
678     #endif
679     static const int configs[] =
680     {
681         0x3, /* try everything */
682         0x2, 0x1, /* try disabling one at a time */
683         0 /* try disabling everything */
684     };
685     int config = 0;
686     if(!depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
687     if(!fsaa)
688     {
689         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
690         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
691     }
692     loopi(sizeof(configs)/sizeof(configs[0]))
693     {
694         config = configs[i];
695         if(!depthbits && config&1) continue;
696         if(fsaa<=0 && config&2) continue;
697         if(depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config&1 ? depthbits : 24);
698         if(fsaa>0)
699         {
700             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, config&2 ? 1 : 0);
701             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config&2 ? fsaa : 0);
702         }
703         screen = SDL_CreateWindow("Cube 2: Sauerbraten", winx, winy, winw, winh, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS | flags);
704         if(!screen) continue;
705 
706     #ifdef __APPLE__
707         static const int glversions[] = { 32, 20 };
708     #else
709         static const int glversions[] = { 33, 32, 31, 30, 20 };
710     #endif
711         loopj(sizeof(glversions)/sizeof(glversions[0]))
712         {
713             glcompat = glversions[j] <= 30 ? 1 : 0;
714             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glversions[j] / 10);
715             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glversions[j] % 10);
716             SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, glversions[j] >= 32 ? SDL_GL_CONTEXT_PROFILE_CORE : 0);
717             glcontext = SDL_GL_CreateContext(screen);
718             if(glcontext) break;
719         }
720         if(glcontext) break;
721     }
722     if(!screen) fatal("failed to create OpenGL window: %s", SDL_GetError());
723     else if(!glcontext) fatal("failed to create OpenGL context: %s", SDL_GetError());
724     else
725     {
726         if(depthbits && (config&1)==0) conoutf(CON_WARN, "%d bit z-buffer not supported - disabling", depthbits);
727         if(fsaa>0 && (config&2)==0) conoutf(CON_WARN, "%dx anti-aliasing not supported - disabling", fsaa);
728     }
729 
730     SDL_SetWindowMinimumSize(screen, SCR_MINW, SCR_MINH);
731     SDL_SetWindowMaximumSize(screen, SCR_MAXW, SCR_MAXH);
732 
733     SDL_GetWindowSize(screen, &screenw, &screenh);
734 }
735 
resetgl()736 void resetgl()
737 {
738     clearchanges(CHANGE_GFX);
739 
740     renderbackground("resetting OpenGL");
741 
742     extern void cleanupva();
743     extern void cleanupparticles();
744     extern void cleanupdecals();
745     extern void cleanupblobs();
746     extern void cleanupsky();
747     extern void cleanupmodels();
748     extern void cleanupprefabs();
749     extern void cleanuplightmaps();
750     extern void cleanupblendmap();
751     extern void cleanshadowmap();
752     extern void cleanreflections();
753     extern void cleanupglare();
754     extern void cleanupdepthfx();
755     recorder::cleanup();
756     cleanupva();
757     cleanupparticles();
758     cleanupdecals();
759     cleanupblobs();
760     cleanupsky();
761     cleanupmodels();
762     cleanupprefabs();
763     cleanuptextures();
764     cleanuplightmaps();
765     cleanupblendmap();
766     cleanshadowmap();
767     cleanreflections();
768     cleanupglare();
769     cleanupdepthfx();
770     cleanupshaders();
771     cleanupgl();
772 
773     setupscreen();
774     inputgrab(grabinput);
775     gl_init();
776 
777     inbetweenframes = false;
778     if(!reloadtexture(*notexture) ||
779        !reloadtexture("data/logo.png") ||
780        !reloadtexture("data/logo_1024.png") ||
781        !reloadtexture("data/background.png") ||
782        !reloadtexture("data/background_detail.png") ||
783        !reloadtexture("data/background_decal.png") ||
784        !reloadtexture("data/mapshot_frame.png") ||
785        !reloadtexture("data/loading_frame.png") ||
786        !reloadtexture("data/loading_bar.png"))
787         fatal("failed to reload core texture");
788     reloadfonts();
789     inbetweenframes = true;
790     renderbackground("initializing...");
791     restoregamma();
792     restorevsync();
793     reloadshaders();
794     reloadtextures();
795     initlights();
796     allchanged(true);
797 }
798 
799 COMMAND(resetgl, "");
800 
801 static queue<SDL_Event, 32> events;
802 
filterevent(const SDL_Event & event)803 static inline bool filterevent(const SDL_Event &event)
804 {
805     switch(event.type)
806     {
807         case SDL_MOUSEMOTION:
808             if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
809             {
810                 if(event.motion.x == screenw / 2 && event.motion.y == screenh / 2)
811                     return false;  // ignore any motion events generated by SDL_WarpMouse
812                 #ifdef __APPLE__
813                 if(event.motion.y == 0)
814                     return false;  // let mac users drag windows via the title bar
815                 #endif
816             }
817             break;
818     }
819     return true;
820 }
821 
pumpevents(queue<SDL_Event,SIZE> & events)822 template <int SIZE> static inline bool pumpevents(queue<SDL_Event, SIZE> &events)
823 {
824     while(events.empty())
825     {
826         SDL_PumpEvents();
827         databuf<SDL_Event> buf = events.reserve(events.capacity());
828         int n = SDL_PeepEvents(buf.getbuf(), buf.remaining(), SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
829         if(n <= 0) return false;
830         loopi(n) if(filterevent(buf.buf[i])) buf.put(buf.buf[i]);
831         events.addbuf(buf);
832     }
833     return true;
834 }
835 
836 static int interceptkeysym = 0;
837 
interceptevents(void * data,SDL_Event * event)838 static int interceptevents(void *data, SDL_Event *event)
839 {
840     switch(event->type)
841     {
842         case SDL_MOUSEMOTION: return 0;
843         case SDL_KEYDOWN:
844             if(event->key.keysym.sym == interceptkeysym)
845             {
846                 interceptkeysym = -interceptkeysym;
847                 return 0;
848             }
849             break;
850     }
851     return 1;
852 }
853 
clearinterceptkey()854 static void clearinterceptkey()
855 {
856     SDL_DelEventWatch(interceptevents, NULL);
857     interceptkeysym = 0;
858 }
859 
interceptkey(int sym)860 bool interceptkey(int sym)
861 {
862     if(!interceptkeysym)
863     {
864         interceptkeysym = sym;
865         SDL_FilterEvents(interceptevents, NULL);
866         if(interceptkeysym < 0)
867         {
868             interceptkeysym = 0;
869             return true;
870         }
871         SDL_AddEventWatch(interceptevents, NULL);
872     }
873     else if(abs(interceptkeysym) != sym) interceptkeysym = sym;
874     SDL_PumpEvents();
875     if(interceptkeysym < 0)
876     {
877         clearinterceptkey();
878         interceptkeysym = sym;
879         SDL_FilterEvents(interceptevents, NULL);
880         interceptkeysym = 0;
881         return true;
882     }
883     return false;
884 }
885 
ignoremousemotion()886 static void ignoremousemotion()
887 {
888     SDL_PumpEvents();
889     SDL_FlushEvent(SDL_MOUSEMOTION);
890 }
891 
resetmousemotion()892 static void resetmousemotion()
893 {
894     if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
895     {
896         SDL_WarpMouseInWindow(screen, screenw / 2, screenh / 2);
897     }
898 }
899 
checkmousemotion(int & dx,int & dy)900 static void checkmousemotion(int &dx, int &dy)
901 {
902     while(pumpevents(events))
903     {
904         SDL_Event &event = events.removing();
905         if(event.type != SDL_MOUSEMOTION) return;
906         dx += event.motion.xrel;
907         dy += event.motion.yrel;
908         events.remove();
909     }
910 }
911 
checkinput()912 void checkinput()
913 {
914     if(interceptkeysym) clearinterceptkey();
915     //int lasttype = 0, lastbut = 0;
916     bool mousemoved = false;
917     int focused = 0;
918     while(pumpevents(events))
919     {
920         SDL_Event &event = events.remove();
921 
922         if(focused && event.type!=SDL_WINDOWEVENT) { if(grabinput != (focused>0)) inputgrab(grabinput = focused>0, shouldgrab); focused = 0; }
923 
924         switch(event.type)
925         {
926             case SDL_QUIT:
927                 quit();
928                 return;
929 
930             case SDL_TEXTINPUT:
931                 if(textinputmask && int(event.text.timestamp-textinputtime) >= textinputfilter)
932                 {
933                     uchar buf[SDL_TEXTINPUTEVENT_TEXT_SIZE+1];
934                     size_t len = decodeutf8(buf, sizeof(buf)-1, (const uchar *)event.text.text, strlen(event.text.text));
935                     if(len > 0) { buf[len] = '\0'; processtextinput((const char *)buf, len); }
936                 }
937                 break;
938 
939             case SDL_KEYDOWN:
940             case SDL_KEYUP:
941                 if(keyrepeatmask || !event.key.repeat)
942                     processkey(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.mod | SDL_GetModState());
943                 break;
944 
945             case SDL_WINDOWEVENT:
946                 switch(event.window.event)
947                 {
948                     case SDL_WINDOWEVENT_CLOSE:
949                         quit();
950                         break;
951 
952                     case SDL_WINDOWEVENT_FOCUS_GAINED:
953                         shouldgrab = true;
954                         break;
955                     case SDL_WINDOWEVENT_ENTER:
956                         shouldgrab = false;
957                         focused = 1;
958                         break;
959 
960                     case SDL_WINDOWEVENT_LEAVE:
961                     case SDL_WINDOWEVENT_FOCUS_LOST:
962                         shouldgrab = false;
963                         focused = -1;
964                         break;
965 
966                     case SDL_WINDOWEVENT_MINIMIZED:
967                         minimized = true;
968                         break;
969 
970                     case SDL_WINDOWEVENT_MAXIMIZED:
971                     case SDL_WINDOWEVENT_RESTORED:
972                         minimized = false;
973                         break;
974 
975                     case SDL_WINDOWEVENT_RESIZED:
976                         break;
977 
978                     case SDL_WINDOWEVENT_SIZE_CHANGED:
979                     {
980                         SDL_GetWindowSize(screen, &screenw, &screenh);
981                         if(!fullscreendesktop || !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
982                         {
983                             scr_w = clamp(screenw, SCR_MINW, SCR_MAXW);
984                             scr_h = clamp(screenh, SCR_MINH, SCR_MAXH);
985                         }
986                         gl_resize();
987                         break;
988                     }
989                 }
990                 break;
991 
992             case SDL_MOUSEMOTION:
993                 if(grabinput)
994                 {
995                     int dx = event.motion.xrel, dy = event.motion.yrel;
996                     checkmousemotion(dx, dy);
997                     if(!g3d_movecursor(dx, dy)) mousemove(dx, dy);
998                     mousemoved = true;
999                 }
1000                 else if(shouldgrab) inputgrab(grabinput = true);
1001                 break;
1002 
1003             case SDL_MOUSEBUTTONDOWN:
1004             case SDL_MOUSEBUTTONUP:
1005                 //if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it
1006                 switch(event.button.button)
1007                 {
1008                     case SDL_BUTTON_LEFT: processkey(-1, event.button.state==SDL_PRESSED); break;
1009                     case SDL_BUTTON_MIDDLE: processkey(-2, event.button.state==SDL_PRESSED); break;
1010                     case SDL_BUTTON_RIGHT: processkey(-3, event.button.state==SDL_PRESSED); break;
1011                     case SDL_BUTTON_X1: processkey(-6, event.button.state==SDL_PRESSED); break;
1012                     case SDL_BUTTON_X2: processkey(-7, event.button.state==SDL_PRESSED); break;
1013                 }
1014                 //lasttype = event.type;
1015                 //lastbut = event.button.button;
1016                 break;
1017 
1018             case SDL_MOUSEWHEEL:
1019                 if(event.wheel.y > 0) { processkey(-4, true); processkey(-4, false); }
1020                 else if(event.wheel.y < 0) { processkey(-5, true); processkey(-5, false); }
1021                 break;
1022         }
1023     }
1024     if(focused) { if(grabinput != (focused>0)) inputgrab(grabinput = focused>0, shouldgrab); focused = 0; }
1025     if(mousemoved) resetmousemotion();
1026 }
1027 
swapbuffers(bool overlay)1028 void swapbuffers(bool overlay)
1029 {
1030     recorder::capture(overlay);
1031     gle::disable();
1032     SDL_GL_SwapWindow(screen);
1033 }
1034 
1035 VAR(menufps, 0, 60, 1000);
1036 VARP(maxfps, 0, 200, 1000);
1037 
limitfps(int & millis,int curmillis)1038 void limitfps(int &millis, int curmillis)
1039 {
1040     int limit = (mainmenu || minimized) && menufps ? (maxfps ? min(maxfps, menufps) : menufps) : maxfps;
1041     if(!limit) return;
1042     static int fpserror = 0;
1043     int delay = 1000/limit - (millis-curmillis);
1044     if(delay < 0) fpserror = 0;
1045     else
1046     {
1047         fpserror += 1000%limit;
1048         if(fpserror >= limit)
1049         {
1050             ++delay;
1051             fpserror -= limit;
1052         }
1053         if(delay > 0)
1054         {
1055             SDL_Delay(delay);
1056             millis += delay;
1057         }
1058     }
1059 }
1060 
1061 #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
stackdumper(unsigned int type,EXCEPTION_POINTERS * ep)1062 void stackdumper(unsigned int type, EXCEPTION_POINTERS *ep)
1063 {
1064     if(!ep) fatal("unknown type");
1065     EXCEPTION_RECORD *er = ep->ExceptionRecord;
1066     CONTEXT *context = ep->ContextRecord;
1067     char out[512];
1068     formatstring(out, "Cube 2: Sauerbraten Win32 Exception: 0x%x [0x%x]\n\n", er->ExceptionCode, er->ExceptionCode==EXCEPTION_ACCESS_VIOLATION ? er->ExceptionInformation[1] : -1);
1069     SymInitialize(GetCurrentProcess(), NULL, TRUE);
1070 #ifdef _AMD64_
1071 	STACKFRAME64 sf = {{context->Rip, 0, AddrModeFlat}, {}, {context->Rbp, 0, AddrModeFlat}, {context->Rsp, 0, AddrModeFlat}, 0};
1072     while(::StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &sf, context, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL))
1073 	{
1074 		union { IMAGEHLP_SYMBOL64 sym; char symext[sizeof(IMAGEHLP_SYMBOL64) + sizeof(string)]; };
1075 		sym.SizeOfStruct = sizeof(sym);
1076 		sym.MaxNameLength = sizeof(symext) - sizeof(sym);
1077 		IMAGEHLP_LINE64 line;
1078 		line.SizeOfStruct = sizeof(line);
1079         DWORD64 symoff;
1080 		DWORD lineoff;
1081         if(SymGetSymFromAddr64(GetCurrentProcess(), sf.AddrPC.Offset, &symoff, &sym) && SymGetLineFromAddr64(GetCurrentProcess(), sf.AddrPC.Offset, &lineoff, &line))
1082 #else
1083     STACKFRAME sf = {{context->Eip, 0, AddrModeFlat}, {}, {context->Ebp, 0, AddrModeFlat}, {context->Esp, 0, AddrModeFlat}, 0};
1084     while(::StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &sf, context, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL))
1085 	{
1086 		union { IMAGEHLP_SYMBOL sym; char symext[sizeof(IMAGEHLP_SYMBOL) + sizeof(string)]; };
1087 		sym.SizeOfStruct = sizeof(sym);
1088 		sym.MaxNameLength = sizeof(symext) - sizeof(sym);
1089 		IMAGEHLP_LINE line;
1090 		line.SizeOfStruct = sizeof(line);
1091         DWORD symoff, lineoff;
1092         if(SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &symoff, &sym) && SymGetLineFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &lineoff, &line))
1093 #endif
1094         {
1095             char *del = strrchr(line.FileName, '\\');
1096             concformatstring(out, "%s - %s [%d]\n", sym.Name, del ? del + 1 : line.FileName, line.LineNumber);
1097         }
1098     }
1099     fatal(out);
1100 }
1101 #endif
1102 
1103 #define MAXFPSHISTORY 60
1104 
1105 int fpspos = 0, fpshistory[MAXFPSHISTORY];
1106 
1107 void resetfpshistory()
1108 {
1109     loopi(MAXFPSHISTORY) fpshistory[i] = 1;
1110     fpspos = 0;
1111 }
1112 
1113 void updatefpshistory(int millis)
1114 {
1115     fpshistory[fpspos++] = max(1, min(1000, millis));
1116     if(fpspos>=MAXFPSHISTORY) fpspos = 0;
1117 }
1118 
1119 void getfps(int &fps, int &bestdiff, int &worstdiff)
1120 {
1121     int total = fpshistory[MAXFPSHISTORY-1], best = total, worst = total;
1122     loopi(MAXFPSHISTORY-1)
1123     {
1124         int millis = fpshistory[i];
1125         total += millis;
1126         if(millis < best) best = millis;
1127         if(millis > worst) worst = millis;
1128     }
1129 
1130     fps = (1000*MAXFPSHISTORY)/total;
1131     bestdiff = 1000/best-fps;
1132     worstdiff = fps-1000/worst;
1133 }
1134 
1135 void getfps_(int *raw)
1136 {
1137     int fps, bestdiff, worstdiff;
1138     if(*raw) fps = 1000/fpshistory[(fpspos+MAXFPSHISTORY-1)%MAXFPSHISTORY];
1139     else getfps(fps, bestdiff, worstdiff);
1140     intret(fps);
1141 }
1142 
1143 COMMANDN(getfps, getfps_, "i");
1144 
1145 bool inbetweenframes = false, renderedframe = true;
1146 
1147 static bool findarg(int argc, char **argv, const char *str)
1148 {
1149     for(int i = 1; i<argc; i++) if(strstr(argv[i], str)==argv[i]) return true;
1150     return false;
1151 }
1152 
1153 static int clockrealbase = 0, clockvirtbase = 0;
1154 static void clockreset() { clockrealbase = SDL_GetTicks(); clockvirtbase = totalmillis; }
1155 VARFP(clockerror, 990000, 1000000, 1010000, clockreset());
1156 VARFP(clockfix, 0, 0, 1, clockreset());
1157 
1158 int getclockmillis()
1159 {
1160     int millis = SDL_GetTicks() - clockrealbase;
1161     if(clockfix) millis = int(millis*(double(clockerror)/1000000));
1162     millis += clockvirtbase;
1163     return max(millis, totalmillis);
1164 }
1165 
1166 VAR(numcpus, 1, 1, 16);
1167 
1168 int main(int argc, char **argv)
1169 {
1170     #ifdef WIN32
1171     //atexit((void (__cdecl *)(void))_CrtDumpMemoryLeaks);
1172     #ifndef _DEBUG
1173     #ifndef __GNUC__
1174     __try {
1175     #endif
1176     #endif
1177     #endif
1178 
1179     setlogfile(NULL);
1180 
1181     int dedicated = 0;
1182     char *load = NULL, *initscript = NULL;
1183 
1184     initing = INIT_RESET;
1185     // set home dir first
1186     for(int i = 1; i<argc; i++) if(argv[i][0]=='-' && argv[i][1] == 'q') { sethomedir(&argv[i][2]); break; }
1187     // set log after home dir, but before anything else
1188     for(int i = 1; i<argc; i++) if(argv[i][0]=='-' && argv[i][1] == 'g')
1189     {
1190         const char *file = argv[i][2] ? &argv[i][2] : "log.txt";
1191         setlogfile(file);
1192         logoutf("Setting log file: %s", file);
1193         break;
1194     }
1195     execfile("init.cfg", false);
1196     for(int i = 1; i<argc; i++)
1197     {
1198         if(argv[i][0]=='-') switch(argv[i][1])
1199         {
1200             case 'q': if(homedir[0]) logoutf("Using home directory: %s", homedir); break;
1201             case 'r': /* compat, ignore */ break;
1202             case 'k':
1203             {
1204                 const char *dir = addpackagedir(&argv[i][2]);
1205                 if(dir) logoutf("Adding package directory: %s", dir);
1206                 break;
1207             }
1208             case 'g': break;
1209             case 'd': dedicated = atoi(&argv[i][2]); if(dedicated<=0) dedicated = 2; break;
1210             case 'w': scr_w = clamp(atoi(&argv[i][2]), SCR_MINW, SCR_MAXW); if(!findarg(argc, argv, "-h")) scr_h = -1; break;
1211             case 'h': scr_h = clamp(atoi(&argv[i][2]), SCR_MINH, SCR_MAXH); if(!findarg(argc, argv, "-w")) scr_w = -1; break;
1212             case 'z': depthbits = atoi(&argv[i][2]); break;
1213             case 'b': /* compat, ignore */ break;
1214             case 'a': fsaa = atoi(&argv[i][2]); break;
1215             case 'v': /* compat, ignore */ break;
1216             case 't': fullscreen = atoi(&argv[i][2]); break;
1217             case 's': /* compat, ignore */ break;
1218             case 'f': /* compat, ignore */ break;
1219             case 'l':
1220             {
1221                 char pkgdir[] = "packages/";
1222                 load = strstr(path(&argv[i][2]), path(pkgdir));
1223                 if(load) load += sizeof(pkgdir)-1;
1224                 else load = &argv[i][2];
1225                 break;
1226             }
1227             case 'x': initscript = &argv[i][2]; break;
1228             default: if(!serveroption(argv[i])) gameargs.add(argv[i]); break;
1229         }
1230         else gameargs.add(argv[i]);
1231     }
1232     initing = NOT_INITING;
1233 
1234     numcpus = clamp(SDL_GetCPUCount(), 1, 16);
1235 
1236     if(dedicated <= 1)
1237     {
1238         logoutf("init: sdl");
1239 
1240         if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0) fatal("Unable to initialize SDL: %s", SDL_GetError());
1241 
1242 #ifdef SDL_VIDEO_DRIVER_X11
1243         SDL_version version;
1244         SDL_GetVersion(&version);
1245         if (SDL_VERSIONNUM(version.major, version.minor, version.patch) <= SDL_VERSIONNUM(2, 0, 12))
1246             sdl_xgrab_bug = 1;
1247 #endif
1248     }
1249 
1250     logoutf("init: net");
1251     if(enet_initialize()<0) fatal("Unable to initialise network module");
1252     atexit(enet_deinitialize);
1253     enet_time_set(0);
1254 
1255     logoutf("init: game");
1256     game::parseoptions(gameargs);
1257     initserver(dedicated>0, dedicated>1);  // never returns if dedicated
1258     ASSERT(dedicated <= 1);
1259     game::initclient();
1260 
1261     logoutf("init: video");
1262     SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "0");
1263     #if !defined(WIN32) && !defined(__APPLE__)
1264     SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
1265     #endif
1266     setupscreen();
1267     SDL_ShowCursor(SDL_FALSE);
1268     SDL_StopTextInput(); // workaround for spurious text-input events getting sent on first text input toggle?
1269 
1270     logoutf("init: gl");
1271     gl_checkextensions();
1272     gl_init();
1273     notexture = textureload("packages/textures/notexture.png");
1274     if(!notexture) fatal("could not find core textures");
1275 
1276     logoutf("init: console");
1277     if(!execfile("data/stdlib.cfg", false)) 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.
1278     if(!execfile("data/font.cfg", false)) fatal("cannot find font definitions");
1279     if(!setfont("default")) fatal("no default font specified");
1280 
1281     inbetweenframes = true;
1282     renderbackground("initializing...");
1283 
1284     logoutf("init: world");
1285     camera1 = player = game::iterdynents(0);
1286     emptymap(0, true, NULL, false);
1287 
1288     logoutf("init: sound");
1289     initsound();
1290 
1291     logoutf("init: cfg");
1292     initing = INIT_LOAD;
1293     execfile("data/keymap.cfg");
1294     execfile("data/stdedit.cfg");
1295     execfile("data/sounds.cfg");
1296     execfile("data/menus.cfg");
1297     execfile("data/heightmap.cfg");
1298     execfile("data/blendbrush.cfg");
1299     defformatstring(gamecfgname, "data/game_%s.cfg", game::gameident());
1300     execfile(gamecfgname);
1301     if(game::savedservers()) execfile(game::savedservers(), false);
1302 
1303     identflags |= IDF_PERSIST;
1304 
1305     if(!execfile(game::savedconfig(), false))
1306     {
1307         execfile(game::defaultconfig());
1308         writecfg(game::restoreconfig());
1309     }
1310     execfile(game::autoexec(), false);
1311 
1312     identflags &= ~IDF_PERSIST;
1313 
1314     initing = INIT_GAME;
1315     game::loadconfigs();
1316 
1317     initing = NOT_INITING;
1318 
1319     logoutf("init: render");
1320     restoregamma();
1321     restorevsync();
1322     loadshaders();
1323     initparticles();
1324     initdecals();
1325 
1326     identflags |= IDF_PERSIST;
1327 
1328     logoutf("init: mainloop");
1329 
1330     if(execfile("once.cfg", false)) remove(findfile("once.cfg", "rb"));
1331 
1332     if(load)
1333     {
1334         logoutf("init: localconnect");
1335         //localconnect();
1336         game::changemap(load);
1337     }
1338 
1339     if(initscript) execute(initscript);
1340 
1341     initmumble();
1342     resetfpshistory();
1343 
1344     inputgrab(grabinput = true);
1345     ignoremousemotion();
1346 
1347     for(;;)
1348     {
1349         static int frames = 0;
1350         int millis = getclockmillis();
1351         limitfps(millis, totalmillis);
1352         elapsedtime = millis - totalmillis;
1353         static int timeerr = 0;
1354         int scaledtime = game::scaletime(elapsedtime) + timeerr;
1355         curtime = scaledtime/100;
1356         timeerr = scaledtime%100;
1357         if(!multiplayer(false) && curtime>200) curtime = 200;
1358         if(game::ispaused()) curtime = 0;
1359 		lastmillis += curtime;
1360         totalmillis = millis;
1361         updatetime();
1362 
1363         checkinput();
1364         menuprocess();
1365         tryedit();
1366 
1367         if(lastmillis) game::updateworld();
1368 
1369         checksleep(lastmillis);
1370 
1371         serverslice(false, 0);
1372 
1373         if(frames) updatefpshistory(elapsedtime);
1374         frames++;
1375 
1376         // miscellaneous general game effects
1377         recomputecamera();
1378         updateparticles();
1379         updatesounds();
1380 
1381         if(minimized) continue;
1382 
1383         inbetweenframes = false;
1384         if(mainmenu) gl_drawmainmenu();
1385         else gl_drawframe();
1386         swapbuffers();
1387         renderedframe = inbetweenframes = true;
1388     }
1389 
1390     ASSERT(0);
1391     return EXIT_FAILURE;
1392 
1393     #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
1394     } __except(stackdumper(0, GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) { return 0; }
1395     #endif
1396 }
1397