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