1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8
9 This file is part of the OpenJK source code.
10
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24
25 // common.c -- misc functions used in client and server
26
27 #include "stringed_ingame.h"
28 #include "qcommon/cm_public.h"
29 #include "qcommon/game_version.h"
30 #include "qcommon/q_version.h"
31 #include "../server/NPCNav/navigator.h"
32 #include "../shared/sys/sys_local.h"
33 #if defined(_WIN32)
34 #define WIN32_LEAN_AND_MEAN
35 #include <windows.h>
36 #endif
37
38 FILE *debuglogfile;
39 fileHandle_t logfile;
40 fileHandle_t com_journalFile; // events are written here
41 fileHandle_t com_journalDataFile; // config files are written here
42
43 cvar_t *com_speeds;
44 cvar_t *com_developer;
45 cvar_t *com_dedicated;
46 cvar_t *com_timescale;
47 cvar_t *com_fixedtime;
48 cvar_t *com_journal;
49 cvar_t *com_timedemo;
50 cvar_t *com_sv_running;
51 cvar_t *com_cl_running;
52 cvar_t *com_logfile; // 1 = buffer log, 2 = flush after each print
53 cvar_t *com_showtrace;
54
55 cvar_t *com_optvehtrace;
56
57 #ifdef G2_PERFORMANCE_ANALYSIS
58 cvar_t *com_G2Report;
59 #endif
60
61 cvar_t *com_version;
62 cvar_t *com_buildScript; // for automated data building scripts
63 cvar_t *com_bootlogo;
64 cvar_t *cl_paused;
65 cvar_t *sv_paused;
66 cvar_t *com_cameraMode;
67 cvar_t *com_homepath;
68 #ifndef _WIN32
69 cvar_t *com_ansiColor = NULL;
70 #endif
71 cvar_t *com_busyWait;
72
73 cvar_t *com_affinity;
74
75 // com_speeds times
76 int time_game;
77 int time_frontend; // renderer frontend time
78 int time_backend; // renderer backend time
79
80 int com_frameTime;
81 int com_frameNumber;
82
83 qboolean com_errorEntered = qfalse;
84 qboolean com_fullyInitialized = qfalse;
85
86 char com_errorMessage[MAXPRINTMSG] = {0};
87
88 void Com_WriteConfig_f( void );
89
90 //============================================================================
91
92 static char *rd_buffer;
93 static int rd_buffersize;
94 static void (*rd_flush)( char *buffer );
95
Com_BeginRedirect(char * buffer,int buffersize,void (* flush)(char *))96 void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)( char *) )
97 {
98 if (!buffer || !buffersize || !flush)
99 return;
100 rd_buffer = buffer;
101 rd_buffersize = buffersize;
102 rd_flush = flush;
103
104 *rd_buffer = 0;
105 }
106
Com_EndRedirect(void)107 void Com_EndRedirect (void)
108 {
109 if ( rd_flush ) {
110 rd_flush(rd_buffer);
111 }
112
113 rd_buffer = NULL;
114 rd_buffersize = 0;
115 rd_flush = NULL;
116 }
117
118 /*
119 =============
120 Com_Printf
121
122 Both client and server can use this, and it will output
123 to the appropriate place.
124
125 A raw string should NEVER be passed as fmt, because of "%f" type crashers.
126 =============
127 */
Com_Printf(const char * fmt,...)128 void QDECL Com_Printf( const char *fmt, ... ) {
129 va_list argptr;
130 char msg[MAXPRINTMSG];
131 static qboolean opening_qconsole = qfalse;
132
133 va_start (argptr,fmt);
134 Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
135 va_end (argptr);
136
137 if ( rd_buffer ) {
138 if ((strlen (msg) + strlen(rd_buffer)) > (size_t)(rd_buffersize - 1)) {
139 rd_flush(rd_buffer);
140 *rd_buffer = 0;
141 }
142 Q_strcat(rd_buffer, rd_buffersize, msg);
143 // TTimo nooo .. that would defeat the purpose
144 //rd_flush(rd_buffer);
145 //*rd_buffer = 0;
146 return;
147 }
148
149 #ifndef DEDICATED
150 CL_ConsolePrint( msg );
151 #endif
152
153 // echo to dedicated console and early console
154 Sys_Print( msg );
155
156 // logfile
157 if ( com_logfile && com_logfile->integer ) {
158 // TTimo: only open the qconsole.log if the filesystem is in an initialized state
159 // also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)
160 if ( !logfile && FS_Initialized() && !opening_qconsole ) {
161 struct tm *newtime;
162 time_t aclock;
163
164 opening_qconsole = qtrue;
165
166 time( &aclock );
167 newtime = localtime( &aclock );
168
169 logfile = FS_FOpenFileWrite( "qconsole.log" );
170
171 if ( logfile ) {
172 Com_Printf( "logfile opened on %s\n", asctime( newtime ) );
173 if ( com_logfile->integer > 1 ) {
174 // force it to not buffer so we get valid
175 // data even if we are crashing
176 FS_ForceFlush(logfile);
177 }
178 }
179 else {
180 Com_Printf( "Opening qconsole.log failed!\n" );
181 Cvar_SetValue( "logfile", 0 );
182 }
183 }
184 opening_qconsole = qfalse;
185 if ( logfile && FS_Initialized()) {
186 FS_Write(msg, strlen(msg), logfile);
187 }
188 }
189
190
191 #if defined(_WIN32) && defined(_DEBUG)
192 if ( *msg )
193 {
194 OutputDebugString ( Q_CleanStr(msg) );
195 OutputDebugString ("\n");
196 }
197 #endif
198 }
199
200
201 /*
202 ================
203 Com_DPrintf
204
205 A Com_Printf that only shows up if the "developer" cvar is set
206 ================
207 */
Com_DPrintf(const char * fmt,...)208 void QDECL Com_DPrintf( const char *fmt, ...) {
209 va_list argptr;
210 char msg[MAXPRINTMSG];
211
212 if ( !com_developer || !com_developer->integer ) {
213 return; // don't confuse non-developers with techie stuff...
214 }
215
216 va_start (argptr,fmt);
217 Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
218 va_end (argptr);
219
220 Com_Printf ("%s", msg);
221 }
222
223 // Outputs to the VC / Windows Debug window (only in debug compile)
Com_OPrintf(const char * fmt,...)224 void QDECL Com_OPrintf( const char *fmt, ...)
225 {
226 va_list argptr;
227 char msg[MAXPRINTMSG];
228
229 va_start (argptr,fmt);
230 Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
231 va_end (argptr);
232 #ifdef _WIN32
233 OutputDebugString(msg);
234 #else
235 printf("%s", msg);
236 #endif
237 }
238
239 /*
240 =============
241 Com_Error
242
243 Both client and server can use this, and it will
244 do the appropriate things.
245 =============
246 */
Com_Error(int code,const char * fmt,...)247 void NORETURN QDECL Com_Error( int code, const char *fmt, ... ) {
248 va_list argptr;
249 static int lastErrorTime;
250 static int errorCount;
251 int currentTime;
252
253 if ( com_errorEntered ) {
254 Sys_Error( "recursive error after: %s", com_errorMessage );
255 }
256 com_errorEntered = qtrue;
257
258 // when we are running automated scripts, make sure we
259 // know if anything failed
260 if ( com_buildScript && com_buildScript->integer ) {
261 code = ERR_FATAL;
262 }
263
264 // ERR_DROPs on dedicated drop to an interactive console
265 // which doesn't make sense for dedicated as it's generally
266 // run unattended
267 if ( com_dedicated && com_dedicated->integer ) {
268 code = ERR_FATAL;
269 }
270
271 // if we are getting a solid stream of ERR_DROP, do an ERR_FATAL
272 currentTime = Sys_Milliseconds();
273 if ( currentTime - lastErrorTime < 100 ) {
274 if ( ++errorCount > 3 ) {
275 code = ERR_FATAL;
276 }
277 } else {
278 errorCount = 0;
279 }
280 lastErrorTime = currentTime;
281
282 va_start (argptr,fmt);
283 Q_vsnprintf (com_errorMessage,sizeof(com_errorMessage), fmt,argptr);
284 va_end (argptr);
285
286 if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) {
287 Cvar_Get("com_errorMessage", "", CVAR_ROM); //give com_errorMessage a default so it won't come back to life after a resetDefaults
288 Cvar_Set("com_errorMessage", com_errorMessage);
289 }
290
291 if ( code == ERR_DISCONNECT || code == ERR_SERVERDISCONNECT || code == ERR_DROP || code == ERR_NEED_CD ) {
292 throw code;
293 } else {
294 CL_Shutdown ();
295 SV_Shutdown (va("Server fatal crashed: %s\n", com_errorMessage));
296 }
297
298 Com_Shutdown ();
299
300 Sys_Error ("%s", com_errorMessage);
301 }
302
303
304 /*
305 =============
306 Com_Quit_f
307
308 Both client and server can use this, and it will
309 do the appropriate things.
310 =============
311 */
Com_Quit_f(void)312 void Com_Quit_f( void ) {
313 // don't try to shutdown if we are in a recursive error
314 if ( !com_errorEntered ) {
315 SV_Shutdown ("Server quit\n");
316 CL_Shutdown ();
317 Com_Shutdown ();
318 FS_Shutdown(qtrue);
319 }
320 Sys_Quit ();
321 }
322
323
324
325 /*
326 ============================================================================
327
328 COMMAND LINE FUNCTIONS
329
330 + characters separate the commandLine string into multiple console
331 command lines.
332
333 All of these are valid:
334
335 quake3 +set test blah +map test
336 quake3 set test blah+map test
337 quake3 set test blah + map test
338
339 ============================================================================
340 */
341
342 #define MAX_CONSOLE_LINES 32
343 int com_numConsoleLines;
344 char *com_consoleLines[MAX_CONSOLE_LINES];
345
346 /*
347 ==================
348 Com_ParseCommandLine
349
350 Break it up into multiple console lines
351 ==================
352 */
Com_ParseCommandLine(char * commandLine)353 void Com_ParseCommandLine( char *commandLine ) {
354 int inq = 0;
355 com_consoleLines[0] = commandLine;
356 com_numConsoleLines = 1;
357
358 while ( *commandLine ) {
359 if (*commandLine == '"') {
360 inq = !inq;
361 }
362 // look for a + seperating character
363 // if commandLine came from a file, we might have real line seperators
364 if ( (*commandLine == '+' && !inq) || *commandLine == '\n' || *commandLine == '\r' ) {
365 if ( com_numConsoleLines == MAX_CONSOLE_LINES ) {
366 return;
367 }
368 com_consoleLines[com_numConsoleLines] = commandLine + 1;
369 com_numConsoleLines++;
370 *commandLine = 0;
371 }
372 commandLine++;
373 }
374 }
375
376
377 /*
378 ===================
379 Com_SafeMode
380
381 Check for "safe" on the command line, which will
382 skip loading of jampconfig.cfg
383 ===================
384 */
Com_SafeMode(void)385 qboolean Com_SafeMode( void ) {
386 int i;
387
388 for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
389 Cmd_TokenizeString( com_consoleLines[i] );
390 if ( !Q_stricmp( Cmd_Argv(0), "safe" )
391 || !Q_stricmp( Cmd_Argv(0), "cvar_restart" ) ) {
392 com_consoleLines[i][0] = 0;
393 return qtrue;
394 }
395 }
396 return qfalse;
397 }
398
399
400 /*
401 ===============
402 Com_StartupVariable
403
404 Searches for command line parameters that are set commands.
405 If match is not NULL, only that cvar will be looked for.
406 That is necessary because cddir and basedir need to be set
407 before the filesystem is started, but all other sets shouls
408 be after execing the config and default.
409 ===============
410 */
Com_StartupVariable(const char * match)411 void Com_StartupVariable( const char *match ) {
412 for (int i=0 ; i < com_numConsoleLines ; i++) {
413 Cmd_TokenizeString( com_consoleLines[i] );
414 if ( strcmp( Cmd_Argv(0), "set" ) ) {
415 continue;
416 }
417
418 char *s = Cmd_Argv(1);
419
420 if ( !match || !strcmp( s, match ) )
421 Cvar_User_Set( s, Cmd_ArgsFrom( 2 ) );
422 }
423 }
424
425
426 /*
427 =================
428 Com_AddStartupCommands
429
430 Adds command line parameters as script statements
431 Commands are seperated by + signs
432
433 Returns qtrue if any late commands were added, which
434 will keep the demoloop from immediately starting
435 =================
436 */
Com_AddStartupCommands(void)437 qboolean Com_AddStartupCommands( void ) {
438 int i;
439 qboolean added;
440
441 added = qfalse;
442 // quote every token, so args with semicolons can work
443 for (i=0 ; i < com_numConsoleLines ; i++) {
444 if ( !com_consoleLines[i] || !com_consoleLines[i][0] ) {
445 continue;
446 }
447
448 // set commands won't override menu startup
449 if ( Q_stricmpn( com_consoleLines[i], "set", 3 ) ) {
450 added = qtrue;
451 }
452 Cbuf_AddText( com_consoleLines[i] );
453 Cbuf_AddText( "\n" );
454 }
455
456 return added;
457 }
458
459
460 //============================================================================
461
Info_Print(const char * s)462 void Info_Print( const char *s ) {
463 char key[BIG_INFO_KEY];
464 char value[BIG_INFO_VALUE];
465 char *o;
466 int l;
467
468 if (*s == '\\')
469 s++;
470 while (*s)
471 {
472 o = key;
473 while (*s && *s != '\\')
474 *o++ = *s++;
475
476 l = o - key;
477 if (l < 20)
478 {
479 Com_Memset (o, ' ', 20-l);
480 key[20] = 0;
481 }
482 else
483 *o = 0;
484 Com_Printf ("%s ", key);
485
486 if (!*s)
487 {
488 Com_Printf ("MISSING VALUE\n");
489 return;
490 }
491
492 o = value;
493 s++;
494 while (*s && *s != '\\')
495 *o++ = *s++;
496 *o = 0;
497
498 if (*s)
499 s++;
500 Com_Printf ("%s\n", value);
501 }
502 }
503
504 /*
505 ============
506 Com_StringContains
507 ============
508 */
Com_StringContains(char * str1,char * str2,int casesensitive)509 char *Com_StringContains(char *str1, char *str2, int casesensitive) {
510 int len, i, j;
511
512 len = strlen(str1) - strlen(str2);
513 for (i = 0; i <= len; i++, str1++) {
514 for (j = 0; str2[j]; j++) {
515 if (casesensitive) {
516 if (str1[j] != str2[j]) {
517 break;
518 }
519 }
520 else {
521 if (toupper(str1[j]) != toupper(str2[j])) {
522 break;
523 }
524 }
525 }
526 if (!str2[j]) {
527 return str1;
528 }
529 }
530 return NULL;
531 }
532
533 /*
534 ============
535 Com_Filter
536 ============
537 */
Com_Filter(char * filter,char * name,int casesensitive)538 int Com_Filter(char *filter, char *name, int casesensitive)
539 {
540 char buf[MAX_TOKEN_CHARS];
541 char *ptr;
542 int i, found;
543
544 while(*filter) {
545 if (*filter == '*') {
546 filter++;
547 for (i = 0; *filter; i++) {
548 if (*filter == '*' || *filter == '?') break;
549 buf[i] = *filter;
550 filter++;
551 }
552 buf[i] = '\0';
553 if (strlen(buf)) {
554 ptr = Com_StringContains(name, buf, casesensitive);
555 if (!ptr) return qfalse;
556 name = ptr + strlen(buf);
557 }
558 }
559 else if (*filter == '?') {
560 filter++;
561 name++;
562 }
563 else if (*filter == '[' && *(filter+1) == '[') {
564 filter++;
565 }
566 else if (*filter == '[') {
567 filter++;
568 found = qfalse;
569 while(*filter && !found) {
570 if (*filter == ']' && *(filter+1) != ']') break;
571 if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
572 if (casesensitive) {
573 if (*name >= *filter && *name <= *(filter+2)) found = qtrue;
574 }
575 else {
576 if (toupper(*name) >= toupper(*filter) &&
577 toupper(*name) <= toupper(*(filter+2))) found = qtrue;
578 }
579 filter += 3;
580 }
581 else {
582 if (casesensitive) {
583 if (*filter == *name) found = qtrue;
584 }
585 else {
586 if (toupper(*filter) == toupper(*name)) found = qtrue;
587 }
588 filter++;
589 }
590 }
591 if (!found) return qfalse;
592 while(*filter) {
593 if (*filter == ']' && *(filter+1) != ']') break;
594 filter++;
595 }
596 filter++;
597 name++;
598 }
599 else {
600 if (casesensitive) {
601 if (*filter != *name) return qfalse;
602 }
603 else {
604 if (toupper(*filter) != toupper(*name)) return qfalse;
605 }
606 filter++;
607 name++;
608 }
609 }
610 return qtrue;
611 }
612
613 /*
614 ============
615 Com_FilterPath
616 ============
617 */
Com_FilterPath(char * filter,char * name,int casesensitive)618 int Com_FilterPath(char *filter, char *name, int casesensitive)
619 {
620 int i;
621 char new_filter[MAX_QPATH];
622 char new_name[MAX_QPATH];
623
624 for (i = 0; i < MAX_QPATH-1 && filter[i]; i++) {
625 if ( filter[i] == '\\' || filter[i] == ':' ) {
626 new_filter[i] = '/';
627 }
628 else {
629 new_filter[i] = filter[i];
630 }
631 }
632 new_filter[i] = '\0';
633 for (i = 0; i < MAX_QPATH-1 && name[i]; i++) {
634 if ( name[i] == '\\' || name[i] == ':' ) {
635 new_name[i] = '/';
636 }
637 else {
638 new_name[i] = name[i];
639 }
640 }
641 new_name[i] = '\0';
642 return Com_Filter(new_filter, new_name, casesensitive);
643 }
644
645 /*
646 ============
647 Com_HashKey
648 ============
649 */
Com_HashKey(char * string,int maxlen)650 int Com_HashKey(char *string, int maxlen) {
651 int hash, i;
652
653 hash = 0;
654 for (i = 0; i < maxlen && string[i] != '\0'; i++) {
655 hash += string[i] * (119 + i);
656 }
657 hash = (hash ^ (hash >> 10) ^ (hash >> 20));
658 return hash;
659 }
660
661 /*
662 ================
663 Com_RealTime
664 ================
665 */
Com_RealTime(qtime_t * qtime)666 int Com_RealTime(qtime_t *qtime) {
667 time_t t;
668 struct tm *tms;
669
670 t = time(NULL);
671 if (!qtime)
672 return t;
673 tms = localtime(&t);
674 if (tms) {
675 qtime->tm_sec = tms->tm_sec;
676 qtime->tm_min = tms->tm_min;
677 qtime->tm_hour = tms->tm_hour;
678 qtime->tm_mday = tms->tm_mday;
679 qtime->tm_mon = tms->tm_mon;
680 qtime->tm_year = tms->tm_year;
681 qtime->tm_wday = tms->tm_wday;
682 qtime->tm_yday = tms->tm_yday;
683 qtime->tm_isdst = tms->tm_isdst;
684 }
685 return t;
686 }
687
688
689 /*
690 ===================================================================
691
692 EVENTS AND JOURNALING
693
694 In addition to these events, .cfg files are also copied to the
695 journaled file
696 ===================================================================
697 */
698
699 #define MAX_PUSHED_EVENTS 1024
700 static int com_pushedEventsHead = 0;
701 static int com_pushedEventsTail = 0;
702 static sysEvent_t com_pushedEvents[MAX_PUSHED_EVENTS];
703
704 /*
705 =================
706 Com_InitJournaling
707 =================
708 */
Com_InitJournaling(void)709 void Com_InitJournaling( void ) {
710 Com_StartupVariable( "journal" );
711 com_journal = Cvar_Get ("journal", "0", CVAR_INIT);
712 if ( !com_journal->integer ) {
713 return;
714 }
715
716 if ( com_journal->integer == 1 ) {
717 Com_Printf( "Journaling events\n");
718 com_journalFile = FS_FOpenFileWrite( "journal.dat" );
719 com_journalDataFile = FS_FOpenFileWrite( "journaldata.dat" );
720 } else if ( com_journal->integer == 2 ) {
721 Com_Printf( "Replaying journaled events\n");
722 FS_FOpenFileRead( "journal.dat", &com_journalFile, qtrue );
723 FS_FOpenFileRead( "journaldata.dat", &com_journalDataFile, qtrue );
724 }
725
726 if ( !com_journalFile || !com_journalDataFile ) {
727 Cvar_Set( "com_journal", "0" );
728 com_journalFile = 0;
729 com_journalDataFile = 0;
730 Com_Printf( "Couldn't open journal files\n" );
731 }
732 }
733
734 /*
735 =================
736 Com_GetRealEvent
737 =================
738 */
Com_GetRealEvent(void)739 sysEvent_t Com_GetRealEvent( void ) {
740 int r;
741 sysEvent_t ev;
742
743 // either get an event from the system or the journal file
744 if ( com_journal->integer == 2 ) {
745 r = FS_Read( &ev, sizeof(ev), com_journalFile );
746 if ( r != sizeof(ev) ) {
747 Com_Error( ERR_FATAL, "Error reading from journal file" );
748 }
749 if ( ev.evPtrLength ) {
750 ev.evPtr = Z_Malloc( ev.evPtrLength, TAG_EVENT, qtrue );
751 r = FS_Read( ev.evPtr, ev.evPtrLength, com_journalFile );
752 if ( r != ev.evPtrLength ) {
753 Com_Error( ERR_FATAL, "Error reading from journal file" );
754 }
755 }
756 } else {
757 ev = Sys_GetEvent();
758
759 // write the journal value out if needed
760 if ( com_journal->integer == 1 ) {
761 r = FS_Write( &ev, sizeof(ev), com_journalFile );
762 if ( r != sizeof(ev) ) {
763 Com_Error( ERR_FATAL, "Error writing to journal file" );
764 }
765 if ( ev.evPtrLength ) {
766 r = FS_Write( ev.evPtr, ev.evPtrLength, com_journalFile );
767 if ( r != ev.evPtrLength ) {
768 Com_Error( ERR_FATAL, "Error writing to journal file" );
769 }
770 }
771 }
772 }
773
774 return ev;
775 }
776
777 /*
778 =================
779 Com_InitPushEvent
780 =================
781 */
Com_InitPushEvent(void)782 void Com_InitPushEvent( void ) {
783 // clear the static buffer array
784 // this requires SE_NONE to be accepted as a valid but NOP event
785 memset( com_pushedEvents, 0, sizeof(com_pushedEvents) );
786 // reset counters while we are at it
787 // beware: GetEvent might still return an SE_NONE from the buffer
788 com_pushedEventsHead = 0;
789 com_pushedEventsTail = 0;
790 }
791
792 /*
793 =================
794 Com_PushEvent
795 =================
796 */
Com_PushEvent(sysEvent_t * event)797 void Com_PushEvent( sysEvent_t *event ) {
798 sysEvent_t *ev;
799 static int printedWarning = 0;
800
801 ev = &com_pushedEvents[ com_pushedEventsHead & (MAX_PUSHED_EVENTS-1) ];
802
803 if ( com_pushedEventsHead - com_pushedEventsTail >= MAX_PUSHED_EVENTS ) {
804
805 // don't print the warning constantly, or it can give time for more...
806 if ( !printedWarning ) {
807 printedWarning = qtrue;
808 Com_Printf( "WARNING: Com_PushEvent overflow\n" );
809 }
810
811 if ( ev->evPtr ) {
812 Z_Free( ev->evPtr );
813 }
814 com_pushedEventsTail++;
815 } else {
816 printedWarning = qfalse;
817 }
818
819 *ev = *event;
820 com_pushedEventsHead++;
821 }
822
823 /*
824 =================
825 Com_GetEvent
826 =================
827 */
Com_GetEvent(void)828 sysEvent_t Com_GetEvent( void ) {
829 if ( com_pushedEventsHead > com_pushedEventsTail ) {
830 com_pushedEventsTail++;
831 return com_pushedEvents[ (com_pushedEventsTail-1) & (MAX_PUSHED_EVENTS-1) ];
832 }
833 return Com_GetRealEvent();
834 }
835
836 /*
837 =================
838 Com_RunAndTimeServerPacket
839 =================
840 */
Com_RunAndTimeServerPacket(netadr_t * evFrom,msg_t * buf)841 void Com_RunAndTimeServerPacket( netadr_t *evFrom, msg_t *buf ) {
842 int t1, t2, msec;
843
844 t1 = 0;
845
846 if ( com_speeds->integer ) {
847 t1 = Sys_Milliseconds ();
848 }
849
850 SV_PacketEvent( *evFrom, buf );
851
852 if ( com_speeds->integer ) {
853 t2 = Sys_Milliseconds ();
854 msec = t2 - t1;
855 if ( com_speeds->integer == 3 ) {
856 Com_Printf( "SV_PacketEvent time: %i\n", msec );
857 }
858 }
859 }
860
861 /*
862 =================
863 Com_EventLoop
864
865 Returns last event time
866 =================
867 */
Com_EventLoop(void)868 int Com_EventLoop( void ) {
869 sysEvent_t ev;
870 netadr_t evFrom;
871 byte bufData[MAX_MSGLEN];
872 msg_t buf;
873
874 MSG_Init( &buf, bufData, sizeof( bufData ) );
875
876 while ( 1 ) {
877 ev = Com_GetEvent();
878
879 // if no more events are available
880 if ( ev.evType == SE_NONE ) {
881 // manually send packet events for the loopback channel
882 while ( NET_GetLoopPacket( NS_CLIENT, &evFrom, &buf ) ) {
883 CL_PacketEvent( evFrom, &buf );
884 }
885
886 while ( NET_GetLoopPacket( NS_SERVER, &evFrom, &buf ) ) {
887 // if the server just shut down, flush the events
888 if ( com_sv_running->integer ) {
889 Com_RunAndTimeServerPacket( &evFrom, &buf );
890 }
891 }
892
893 return ev.evTime;
894 }
895
896
897 switch ( ev.evType ) {
898 default:
899 Com_Error( ERR_FATAL, "Com_EventLoop: bad event type %i", ev.evType );
900 break;
901 case SE_NONE:
902 break;
903 case SE_KEY:
904 CL_KeyEvent( ev.evValue, (qboolean)ev.evValue2, ev.evTime );
905 break;
906 case SE_CHAR:
907 CL_CharEvent( ev.evValue );
908 break;
909 case SE_MOUSE:
910 CL_MouseEvent( ev.evValue, ev.evValue2, ev.evTime );
911 break;
912 case SE_JOYSTICK_AXIS:
913 CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime );
914 break;
915 case SE_CONSOLE:
916 if ( ((char *)ev.evPtr)[0] == '\\' || ((char *)ev.evPtr)[0] == '/' )
917 {
918 Cbuf_AddText( (char *)ev.evPtr+1 );
919 }
920 else
921 {
922 Cbuf_AddText( (char *)ev.evPtr );
923 }
924 Cbuf_AddText( "\n" );
925 break;
926 }
927
928 // free any block data
929 if ( ev.evPtr ) {
930 Z_Free( ev.evPtr );
931 }
932 }
933
934 return 0; // never reached
935 }
936
937 /*
938 ================
939 Com_Milliseconds
940
941 Can be used for profiling, but will be journaled accurately
942 ================
943 */
Com_Milliseconds(void)944 int Com_Milliseconds (void) {
945 sysEvent_t ev;
946
947 // get events and push them until we get a null event with the current time
948 do {
949
950 ev = Com_GetRealEvent();
951 if ( ev.evType != SE_NONE ) {
952 Com_PushEvent( &ev );
953 }
954 } while ( ev.evType != SE_NONE );
955
956 return ev.evTime;
957 }
958
959 //============================================================================
960
961 /*
962 =============
963 Com_Error_f
964
965 Just throw a fatal error to
966 test error shutdown procedures
967 =============
968 */
Com_Error_f(void)969 static void NORETURN Com_Error_f (void) {
970 if ( Cmd_Argc() > 1 ) {
971 Com_Error( ERR_DROP, "Testing drop error" );
972 } else {
973 Com_Error( ERR_FATAL, "Testing fatal error" );
974 }
975 }
976
977
978 /*
979 =============
980 Com_Freeze_f
981
982 Just freeze in place for a given number of seconds to test
983 error recovery
984 =============
985 */
Com_Freeze_f(void)986 static void Com_Freeze_f (void) {
987 float s;
988 int start, now;
989
990 if ( Cmd_Argc() != 2 ) {
991 Com_Printf( "freeze <seconds>\n" );
992 return;
993 }
994 s = atof( Cmd_Argv(1) );
995
996 start = Com_Milliseconds();
997
998 while ( 1 ) {
999 now = Com_Milliseconds();
1000 if ( ( now - start ) * 0.001 > s ) {
1001 break;
1002 }
1003 }
1004 }
1005
1006 /*
1007 =================
1008 Com_Crash_f
1009
1010 A way to force a bus error for development reasons
1011 =================
1012 */
Com_Crash_f(void)1013 static void NORETURN Com_Crash_f( void ) {
1014 * ( volatile int * ) 0 = 0x12345678;
1015 /* that should crash already, but to reassure the compiler: */
1016 abort();
1017 }
1018
1019 /*
1020 ==================
1021 Com_ExecuteCfg
1022 ==================
1023 */
1024
Com_ExecuteCfg(void)1025 void Com_ExecuteCfg(void)
1026 {
1027 Cbuf_ExecuteText(EXEC_NOW, "exec mpdefault.cfg\n");
1028 Cbuf_Execute(); // Always execute after exec to prevent text buffer overflowing
1029
1030 if(!Com_SafeMode())
1031 {
1032 // skip the q3config.cfg and autoexec.cfg if "safe" is on the command line
1033 Cbuf_ExecuteText(EXEC_NOW, "exec " Q3CONFIG_CFG "\n");
1034 Cbuf_Execute();
1035 Cbuf_ExecuteText(EXEC_NOW, "exec autoexec.cfg\n");
1036 Cbuf_Execute();
1037 }
1038 }
1039
1040 /*
1041 =================
1042 Com_InitRand
1043 Seed the random number generator, if possible with an OS supplied random seed.
1044 =================
1045 */
Com_InitRand(void)1046 static void Com_InitRand(void)
1047 {
1048 unsigned int seed;
1049
1050 if(Sys_RandomBytes((byte *) &seed, sizeof(seed)))
1051 srand(seed);
1052 else
1053 srand(time(NULL));
1054 }
1055
1056 /*
1057 =================
1058 Com_ErrorString
1059 Error string for the given error code (from Com_Error).
1060 =================
1061 */
Com_ErrorString(int code)1062 static const char *Com_ErrorString ( int code )
1063 {
1064 switch ( code )
1065 {
1066 case ERR_DISCONNECT:
1067 // fallthrough
1068 case ERR_SERVERDISCONNECT:
1069 return "DISCONNECTED";
1070
1071 case ERR_DROP:
1072 return "DROPPED";
1073
1074 case ERR_NEED_CD:
1075 return "NEED CD";
1076
1077 default:
1078 return "UNKNOWN";
1079 }
1080 }
1081
1082 /*
1083 =================
1084 Com_CatchError
1085 Handles freeing up of resources when Com_Error is called.
1086 =================
1087 */
Com_CatchError(int code)1088 static void Com_CatchError ( int code )
1089 {
1090 if ( code == ERR_DISCONNECT || code == ERR_SERVERDISCONNECT ) {
1091 SV_Shutdown( "Server disconnected" );
1092 CL_Disconnect( qtrue );
1093 CL_FlushMemory( );
1094 // make sure we can get at our local stuff
1095 FS_PureServerSetLoadedPaks( "", "" );
1096 com_errorEntered = qfalse;
1097 } else if ( code == ERR_DROP ) {
1098 Com_Printf ("********************\n"
1099 "ERROR: %s\n"
1100 "********************\n", com_errorMessage);
1101 SV_Shutdown (va("Server crashed: %s\n", com_errorMessage));
1102 CL_Disconnect( qtrue );
1103 CL_FlushMemory( );
1104 // make sure we can get at our local stuff
1105 FS_PureServerSetLoadedPaks( "", "" );
1106 com_errorEntered = qfalse;
1107 } else if ( code == ERR_NEED_CD ) {
1108 SV_Shutdown( "Server didn't have CD" );
1109 if ( com_cl_running && com_cl_running->integer ) {
1110 CL_Disconnect( qtrue );
1111 CL_FlushMemory( );
1112 } else {
1113 Com_Printf("Server didn't have CD\n" );
1114 }
1115 // make sure we can get at our local stuff
1116 FS_PureServerSetLoadedPaks( "", "" );
1117 com_errorEntered = qfalse;
1118 }
1119 }
1120
1121 /*
1122 =================
1123 Com_Init
1124 =================
1125 */
Com_Init(char * commandLine)1126 void Com_Init( char *commandLine ) {
1127 char *s;
1128 int qport;
1129
1130 Com_Printf( "%s %s %s\n", JK_VERSION, PLATFORM_STRING, SOURCE_DATE );
1131
1132 try
1133 {
1134 // initialize the weak pseudo-random number generator for use later.
1135 Com_InitRand();
1136
1137 // do this before anything else decides to push events
1138 Com_InitPushEvent();
1139
1140 Com_InitZoneMemory();
1141 Cvar_Init ();
1142
1143 navigator.Init();
1144
1145 // prepare enough of the subsystems to handle
1146 // cvar and command buffer management
1147 Com_ParseCommandLine( commandLine );
1148
1149 // Swap_Init ();
1150 Cbuf_Init ();
1151
1152 // override anything from the config files with command line args
1153 Com_StartupVariable( NULL );
1154
1155 Com_InitZoneMemoryVars();
1156 Cmd_Init ();
1157
1158 // Seed the random number generator
1159 Rand_Init(Sys_Milliseconds(true));
1160
1161 // get the developer cvar set as early as possible
1162 com_developer = Cvar_Get("developer", "0", CVAR_TEMP, "Developer mode" );
1163
1164 // done early so bind command exists
1165 CL_InitKeyCommands();
1166
1167 com_homepath = Cvar_Get("com_homepath", "", CVAR_INIT);
1168
1169 FS_InitFilesystem ();
1170
1171 Com_InitJournaling();
1172
1173 // Add some commands here already so users can use them from config files
1174 if ( com_developer && com_developer->integer ) {
1175 Cmd_AddCommand ("error", Com_Error_f);
1176 Cmd_AddCommand ("crash", Com_Crash_f );
1177 Cmd_AddCommand ("freeze", Com_Freeze_f);
1178 }
1179 Cmd_AddCommand ("quit", Com_Quit_f, "Quits the game" );
1180 #ifndef FINAL_BUILD
1181 Cmd_AddCommand ("changeVectors", MSG_ReportChangeVectors_f );
1182 #endif
1183 Cmd_AddCommand ("writeconfig", Com_WriteConfig_f, "Write the configuration to file" );
1184 Cmd_SetCommandCompletionFunc( "writeconfig", Cmd_CompleteCfgName );
1185
1186 Com_ExecuteCfg();
1187
1188 // override anything from the config files with command line args
1189 Com_StartupVariable( NULL );
1190
1191 // get dedicated here for proper hunk megs initialization
1192 #ifdef DEDICATED
1193 com_dedicated = Cvar_Get ("dedicated", "2", CVAR_INIT);
1194 Cvar_CheckRange( com_dedicated, 1, 2, qtrue );
1195 #else
1196 //OJKFIXME: Temporarily disabled dedicated server when not using the dedicated server binary.
1197 // Issue is the server not having a renderer when not using ^^^^^
1198 // and crashing in SV_SpawnServer calling re.RegisterMedia_LevelLoadBegin
1199 // Until we fully remove the renderer from the server, the client executable
1200 // will not have dedicated support capabilities.
1201 // Use the dedicated server package.
1202 com_dedicated = Cvar_Get ("_dedicated", "0", CVAR_ROM|CVAR_INIT|CVAR_PROTECTED);
1203 // Cvar_CheckRange( com_dedicated, 0, 2, qtrue );
1204 #endif
1205 // allocate the stack based hunk allocator
1206 Com_InitHunkMemory();
1207
1208 // if any archived cvars are modified after this, we will trigger a writing
1209 // of the config file
1210 cvar_modifiedFlags &= ~CVAR_ARCHIVE;
1211
1212 //
1213 // init commands and vars
1214 //
1215 com_logfile = Cvar_Get ("logfile", "0", CVAR_TEMP );
1216
1217 com_timescale = Cvar_Get ("timescale", "1", CVAR_CHEAT | CVAR_SYSTEMINFO );
1218 com_fixedtime = Cvar_Get ("fixedtime", "0", CVAR_CHEAT);
1219 com_showtrace = Cvar_Get ("com_showtrace", "0", CVAR_CHEAT);
1220
1221 com_speeds = Cvar_Get ("com_speeds", "0", 0);
1222 com_timedemo = Cvar_Get ("timedemo", "0", 0);
1223 com_cameraMode = Cvar_Get ("com_cameraMode", "0", CVAR_CHEAT);
1224
1225 com_optvehtrace = Cvar_Get("com_optvehtrace", "0", 0);
1226
1227 cl_paused = Cvar_Get ("cl_paused", "0", CVAR_ROM);
1228 sv_paused = Cvar_Get ("sv_paused", "0", CVAR_ROM);
1229 com_sv_running = Cvar_Get ("sv_running", "0", CVAR_ROM, "Is a server running?" );
1230 com_cl_running = Cvar_Get ("cl_running", "0", CVAR_ROM, "Is the client running?" );
1231 com_buildScript = Cvar_Get( "com_buildScript", "0", 0 );
1232 #ifndef _WIN32
1233 com_ansiColor = Cvar_Get( "com_ansiColor", "0", CVAR_ARCHIVE_ND );
1234 #endif
1235
1236 #ifdef G2_PERFORMANCE_ANALYSIS
1237 com_G2Report = Cvar_Get("com_G2Report", "0", 0);
1238 #endif
1239
1240 com_affinity = Cvar_Get( "com_affinity", "0", CVAR_ARCHIVE_ND );
1241 com_busyWait = Cvar_Get( "com_busyWait", "0", CVAR_ARCHIVE_ND );
1242
1243 com_bootlogo = Cvar_Get( "com_bootlogo", "1", CVAR_ARCHIVE_ND, "Show intro movies" );
1244
1245 s = va("%s %s %s", JK_VERSION_OLD, PLATFORM_STRING, SOURCE_DATE );
1246 com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO );
1247
1248 SE_Init();
1249
1250 Sys_Init();
1251
1252 Sys_SetProcessorAffinity();
1253
1254 // Pick a random port value
1255 Com_RandomBytes( (byte*)&qport, sizeof(int) );
1256 Netchan_Init( qport & 0xffff ); // pick a port value that should be nice and random
1257
1258 VM_Init();
1259 SV_Init();
1260
1261 com_dedicated->modified = qfalse;
1262 if ( !com_dedicated->integer ) {
1263 CL_Init();
1264 }
1265
1266 // set com_frameTime so that if a map is started on the
1267 // command line it will still be able to count on com_frameTime
1268 // being random enough for a serverid
1269 com_frameTime = Com_Milliseconds();
1270
1271
1272 // add + commands from command line
1273 if ( !Com_AddStartupCommands() )
1274 {
1275 // if the user didn't give any commands, run default action
1276 if ( !com_dedicated->integer )
1277 {
1278 if ( com_bootlogo->integer )
1279 {
1280 Cbuf_AddText ("cinematic openinglogos.roq\n");
1281 }
1282 }
1283 }
1284
1285 // start in full screen ui mode
1286 Cvar_Set("r_uiFullScreen", "1");
1287
1288 CL_StartHunkUsers();
1289
1290 // make sure single player is off by default
1291 Cvar_Set("ui_singlePlayerActive", "0");
1292
1293 com_fullyInitialized = qtrue;
1294 Com_Printf ("--- Common Initialization Complete ---\n");
1295 }
1296 catch ( int code )
1297 {
1298 Com_CatchError (code);
1299 Sys_Error ("Error during initialization: %s", Com_ErrorString (code));
1300 }
1301 }
1302
1303 //==================================================================
1304
Com_WriteConfigToFile(const char * filename)1305 void Com_WriteConfigToFile( const char *filename ) {
1306 fileHandle_t f;
1307
1308 f = FS_FOpenFileWrite( filename );
1309 if ( !f ) {
1310 Com_Printf ("Couldn't write %s.\n", filename );
1311 return;
1312 }
1313
1314 FS_Printf (f, "// generated by OpenJK MP, do not modify\n");
1315 Key_WriteBindings (f);
1316 Cvar_WriteVariables (f);
1317 FS_FCloseFile( f );
1318 }
1319
1320
1321 /*
1322 ===============
1323 Com_WriteConfiguration
1324
1325 Writes key bindings and archived cvars to config file if modified
1326 ===============
1327 */
Com_WriteConfiguration(void)1328 void Com_WriteConfiguration( void ) {
1329 // if we are quiting without fully initializing, make sure
1330 // we don't write out anything
1331 if ( !com_fullyInitialized ) {
1332 return;
1333 }
1334
1335 if ( !(cvar_modifiedFlags & CVAR_ARCHIVE ) ) {
1336 return;
1337 }
1338 cvar_modifiedFlags &= ~CVAR_ARCHIVE;
1339
1340 Com_WriteConfigToFile( Q3CONFIG_CFG );
1341 }
1342
1343
1344 /*
1345 ===============
1346 Com_WriteConfig_f
1347
1348 Write the config file to a specific name
1349 ===============
1350 */
Com_WriteConfig_f(void)1351 void Com_WriteConfig_f( void ) {
1352 char filename[MAX_QPATH];
1353
1354 if ( Cmd_Argc() != 2 ) {
1355 Com_Printf( "Usage: writeconfig <filename>\n" );
1356 return;
1357 }
1358
1359 Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
1360 COM_DefaultExtension( filename, sizeof( filename ), ".cfg" );
1361
1362 if(!COM_CompareExtension(filename, ".cfg"))
1363 {
1364 Com_Printf( "Com_WriteConfig_f: Only the \".cfg\" extension is supported by this command!\n" );
1365 return;
1366 }
1367
1368 if(!FS_FilenameCompare(filename, "mpdefault.cfg") || !FS_FilenameCompare(filename, "default.cfg"))
1369 {
1370 Com_Printf( S_COLOR_YELLOW "Com_WriteConfig_f: The filename \"%s\" is reserved! Please choose another name.\n", filename );
1371 return;
1372 }
1373
1374 Com_Printf( "Writing %s.\n", filename );
1375 Com_WriteConfigToFile( filename );
1376 }
1377
1378 /*
1379 ================
1380 Com_ModifyMsec
1381 ================
1382 */
Com_ModifyMsec(int msec)1383 int Com_ModifyMsec( int msec ) {
1384 int clampTime;
1385
1386 //
1387 // modify time for debugging values
1388 //
1389 if ( com_fixedtime->integer ) {
1390 msec = com_fixedtime->integer;
1391 } else if ( com_timescale->value ) {
1392 msec *= com_timescale->value;
1393 } else if (com_cameraMode->integer) {
1394 msec *= com_timescale->value;
1395 }
1396
1397 // don't let it scale below 1 msec
1398 if ( msec < 1 && com_timescale->value) {
1399 msec = 1;
1400 }
1401
1402 if ( com_dedicated->integer ) {
1403 // dedicated servers don't want to clamp for a much longer
1404 // period, because it would mess up all the client's views
1405 // of time.
1406 if ( com_sv_running->integer && msec > 500 ) {
1407 Com_Printf( "Hitch warning: %i msec frame time\n", msec );
1408 }
1409 clampTime = 5000;
1410 } else
1411 if ( !com_sv_running->integer ) {
1412 // clients of remote servers do not want to clamp time, because
1413 // it would skew their view of the server's time temporarily
1414 clampTime = 5000;
1415 } else {
1416 // for local single player gaming
1417 // we may want to clamp the time to prevent players from
1418 // flying off edges when something hitches.
1419 clampTime = 200;
1420 }
1421
1422 if ( msec > clampTime ) {
1423 msec = clampTime;
1424 }
1425
1426 return msec;
1427 }
1428
1429 #ifdef G2_PERFORMANCE_ANALYSIS
1430 #include "qcommon/timing.h"
1431 void G2Time_ResetTimers(void);
1432 void G2Time_ReportTimers(void);
1433 extern timing_c G2PerformanceTimer_PreciseFrame;
1434 extern int G2Time_PreciseFrame;
1435 #endif
1436
1437 /*
1438 =================
1439 Com_TimeVal
1440 =================
1441 */
1442
Com_TimeVal(int minMsec)1443 int Com_TimeVal(int minMsec)
1444 {
1445 int timeVal;
1446
1447 timeVal = Sys_Milliseconds() - com_frameTime;
1448
1449 if(timeVal >= minMsec)
1450 timeVal = 0;
1451 else
1452 timeVal = minMsec - timeVal;
1453
1454 return timeVal;
1455 }
1456
1457 /*
1458 =================
1459 Com_Frame
1460 =================
1461 */
Com_Frame(void)1462 void Com_Frame( void ) {
1463
1464 try
1465 {
1466 #ifdef G2_PERFORMANCE_ANALYSIS
1467 G2PerformanceTimer_PreciseFrame.Start();
1468 #endif
1469 int msec, minMsec;
1470 int timeVal;
1471 static int lastTime = 0, bias = 0;
1472
1473 int timeBeforeFirstEvents = 0;
1474 int timeBeforeServer = 0;
1475 int timeBeforeEvents = 0;
1476 int timeBeforeClient = 0;
1477 int timeAfter = 0;
1478
1479 // write config file if anything changed
1480 Com_WriteConfiguration();
1481
1482 //
1483 // main event loop
1484 //
1485 if ( com_speeds->integer ) {
1486 timeBeforeFirstEvents = Sys_Milliseconds ();
1487 }
1488
1489 // Figure out how much time we have
1490 if(!com_timedemo->integer)
1491 {
1492 if(com_dedicated->integer)
1493 minMsec = SV_FrameMsec();
1494 else
1495 {
1496 if(com_minimized->integer && com_maxfpsMinimized->integer > 0)
1497 minMsec = 1000 / com_maxfpsMinimized->integer;
1498 else if(com_unfocused->integer && com_maxfpsUnfocused->integer > 0)
1499 minMsec = 1000 / com_maxfpsUnfocused->integer;
1500 else if(com_maxfps->integer > 0)
1501 minMsec = 1000 / com_maxfps->integer;
1502 else
1503 minMsec = 1;
1504
1505 timeVal = com_frameTime - lastTime;
1506 bias += timeVal - minMsec;
1507
1508 if (bias > minMsec)
1509 bias = minMsec;
1510
1511 // Adjust minMsec if previous frame took too long to render so
1512 // that framerate is stable at the requested value.
1513 minMsec -= bias;
1514 }
1515 }
1516 else
1517 minMsec = 1;
1518
1519 timeVal = Com_TimeVal(minMsec);
1520 do {
1521 // Busy sleep the last millisecond for better timeout precision
1522 if(com_busyWait->integer || timeVal < 1)
1523 NET_Sleep(0);
1524 else
1525 NET_Sleep(timeVal - 1);
1526 } while( (timeVal = Com_TimeVal(minMsec)) != 0 );
1527 IN_Frame();
1528
1529 lastTime = com_frameTime;
1530 com_frameTime = Com_EventLoop();
1531
1532 msec = com_frameTime - lastTime;
1533
1534 Cbuf_Execute ();
1535
1536 // mess with msec if needed
1537 msec = Com_ModifyMsec( msec );
1538
1539 //
1540 // server side
1541 //
1542 if ( com_speeds->integer ) {
1543 timeBeforeServer = Sys_Milliseconds ();
1544 }
1545
1546 SV_Frame( msec );
1547
1548 // if "dedicated" has been modified, start up
1549 // or shut down the client system.
1550 // Do this after the server may have started,
1551 // but before the client tries to auto-connect
1552 if ( com_dedicated->modified ) {
1553 // get the latched value
1554 Cvar_Get( "_dedicated", "0", 0 );
1555 com_dedicated->modified = qfalse;
1556 if ( !com_dedicated->integer ) {
1557 CL_Init();
1558 CL_StartHunkUsers(); //fire up the UI!
1559 } else {
1560 CL_Shutdown();
1561 }
1562 }
1563
1564 //
1565 // client system
1566 //
1567 if ( !com_dedicated->integer ) {
1568 //
1569 // run event loop a second time to get server to client packets
1570 // without a frame of latency
1571 //
1572 if ( com_speeds->integer ) {
1573 timeBeforeEvents = Sys_Milliseconds ();
1574 }
1575 Com_EventLoop();
1576 Cbuf_Execute ();
1577
1578
1579 //
1580 // client side
1581 //
1582 if ( com_speeds->integer ) {
1583 timeBeforeClient = Sys_Milliseconds ();
1584 }
1585
1586 CL_Frame( msec );
1587
1588 if ( com_speeds->integer ) {
1589 timeAfter = Sys_Milliseconds ();
1590 }
1591 }
1592 else
1593 {
1594 if ( com_speeds->integer )
1595 {
1596 timeBeforeEvents = timeBeforeClient = timeAfter = Sys_Milliseconds ();
1597 }
1598 }
1599
1600 //
1601 // report timing information
1602 //
1603 if ( com_speeds->integer ) {
1604 int all, sv, ev, cl;
1605
1606 all = timeAfter - timeBeforeServer;
1607 sv = timeBeforeEvents - timeBeforeServer;
1608 ev = timeBeforeServer - timeBeforeFirstEvents + timeBeforeClient - timeBeforeEvents;
1609 cl = timeAfter - timeBeforeClient;
1610 sv -= time_game;
1611 cl -= time_frontend + time_backend;
1612
1613 Com_Printf ("frame:%i all:%3i sv:%3i ev:%3i cl:%3i gm:%3i rf:%3i bk:%3i\n",
1614 com_frameNumber, all, sv, ev, cl, time_game, time_frontend, time_backend );
1615 }
1616
1617 //
1618 // trace optimization tracking
1619 //
1620 if ( com_showtrace->integer ) {
1621
1622 extern int c_traces, c_brush_traces, c_patch_traces;
1623 extern int c_pointcontents;
1624
1625 Com_Printf ("%4i traces (%ib %ip) %4i points\n", c_traces,
1626 c_brush_traces, c_patch_traces, c_pointcontents);
1627 c_traces = 0;
1628 c_brush_traces = 0;
1629 c_patch_traces = 0;
1630 c_pointcontents = 0;
1631 }
1632
1633 if ( com_affinity->modified )
1634 {
1635 com_affinity->modified = qfalse;
1636 Sys_SetProcessorAffinity();
1637 }
1638
1639 com_frameNumber++;
1640 }
1641 catch (int code) {
1642 Com_CatchError (code);
1643 Com_Printf ("%s\n", Com_ErrorString (code));
1644 return;
1645 }
1646
1647 #ifdef G2_PERFORMANCE_ANALYSIS
1648 G2Time_PreciseFrame += G2PerformanceTimer_PreciseFrame.End();
1649
1650 if (com_G2Report && com_G2Report->integer)
1651 {
1652 G2Time_ReportTimers();
1653 }
1654
1655 G2Time_ResetTimers();
1656 #endif
1657 }
1658
1659 /*
1660 =================
1661 Com_Shutdown
1662 =================
1663 */
1664 void MSG_shutdownHuffman();
Com_Shutdown(void)1665 void Com_Shutdown (void)
1666 {
1667 CM_ClearMap();
1668
1669 if (logfile) {
1670 FS_FCloseFile (logfile);
1671 logfile = 0;
1672 com_logfile->integer = 0;//don't open up the log file again!!
1673 }
1674
1675 if ( com_journalFile ) {
1676 FS_FCloseFile( com_journalFile );
1677 com_journalFile = 0;
1678 }
1679
1680 MSG_shutdownHuffman();
1681 /*
1682 // Only used for testing changes to huffman frequency table when tuning.
1683 {
1684 extern float Huff_GetCR(void);
1685 char mess[256];
1686 sprintf(mess,"Eff. CR = %f\n",Huff_GetCR());
1687 OutputDebugString(mess);
1688 }
1689 */
1690 }
1691
1692 /*
1693 ==================
1694 Field_Clear
1695 ==================
1696 */
Field_Clear(field_t * edit)1697 void Field_Clear( field_t *edit ) {
1698 memset(edit->buffer, 0, MAX_EDIT_LINE);
1699 edit->cursor = 0;
1700 edit->scroll = 0;
1701 }
1702
1703 /*
1704 =============================================================================
1705
1706 CONSOLE LINE EDITING
1707
1708 ==============================================================================
1709 */
1710
1711 static const char *completionString;
1712 static char shortestMatch[MAX_TOKEN_CHARS];
1713 static int matchCount;
1714 // field we are working on, passed to Field_AutoComplete(&g_consoleCommand for instance)
1715 static field_t *completionField;
1716
1717 /*
1718 ===============
1719 FindMatches
1720
1721 ===============
1722 */
FindMatches(const char * s)1723 static void FindMatches( const char *s ) {
1724 int i;
1725
1726 if ( Q_stricmpn( s, completionString, strlen( completionString ) ) ) {
1727 return;
1728 }
1729 matchCount++;
1730 if ( matchCount == 1 ) {
1731 Q_strncpyz( shortestMatch, s, sizeof( shortestMatch ) );
1732 return;
1733 }
1734
1735 // cut shortestMatch to the amount common with s
1736 for ( i = 0 ; s[i] ; i++ ) {
1737 if ( tolower(shortestMatch[i]) != tolower(s[i]) ) {
1738 shortestMatch[i] = 0;
1739 break;
1740 }
1741 }
1742 if (!s[i])
1743 {
1744 shortestMatch[i] = 0;
1745 }
1746 }
1747
1748 /*
1749 ===============
1750 PrintMatches
1751
1752 ===============
1753 */
1754 char *Cmd_DescriptionString( const char *cmd_name );
PrintMatches(const char * s)1755 static void PrintMatches( const char *s ) {
1756 if ( !Q_stricmpn( s, shortestMatch, (int)strlen( shortestMatch ) ) ) {
1757 const char *description = Cmd_DescriptionString( s );
1758 Com_Printf( S_COLOR_GREY "Cmd " S_COLOR_WHITE "%s\n", s );
1759 if ( VALIDSTRING( description ) )
1760 Com_Printf( S_COLOR_GREEN " %s" S_COLOR_WHITE "\n", description );
1761 }
1762 }
1763
1764 /*
1765 ===============
1766 PrintArgMatches
1767
1768 ===============
1769 */
1770 #if 0
1771 // This is here for if ever commands with other argument completion
1772 static void PrintArgMatches( const char *s ) {
1773 if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
1774 Com_Printf( S_COLOR_WHITE " %s\n", s );
1775 }
1776 }
1777 #endif
1778
1779 #ifndef DEDICATED
1780 /*
1781 ===============
1782 PrintKeyMatches
1783
1784 ===============
1785 */
PrintKeyMatches(const char * s)1786 static void PrintKeyMatches( const char *s ) {
1787 if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
1788 Com_Printf( S_COLOR_GREY "Key " S_COLOR_WHITE "%s\n", s );
1789 }
1790 }
1791 #endif
1792
1793 /*
1794 ===============
1795 PrintFileMatches
1796
1797 ===============
1798 */
PrintFileMatches(const char * s)1799 static void PrintFileMatches( const char *s ) {
1800 if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
1801 Com_Printf( S_COLOR_GREY "File " S_COLOR_WHITE "%s\n", s );
1802 }
1803 }
1804
1805 /*
1806 ===============
1807 PrintCvarMatches
1808
1809 ===============
1810 */
1811 char *Cvar_DescriptionString( const char *var_name );
PrintCvarMatches(const char * s)1812 static void PrintCvarMatches( const char *s ) {
1813 if ( !Q_stricmpn( s, shortestMatch, (int)strlen( shortestMatch ) ) ) {
1814 char value[TRUNCATE_LENGTH] = {0};
1815 const char *description = Cvar_DescriptionString( s );
1816 Com_TruncateLongString( value, Cvar_VariableString( s ) );
1817 Com_Printf( S_COLOR_GREY "Cvar " S_COLOR_WHITE "%s = " S_COLOR_GREY "\"" S_COLOR_WHITE "%s" S_COLOR_GREY "\"" S_COLOR_WHITE "\n", s, value );
1818 if ( VALIDSTRING( description ) )
1819 Com_Printf( S_COLOR_GREEN " %s" S_COLOR_WHITE "\n", description );
1820 }
1821 }
1822
1823 /*
1824 ===============
1825 Field_FindFirstSeparator
1826 ===============
1827 */
Field_FindFirstSeparator(char * s)1828 static char *Field_FindFirstSeparator( char *s ) {
1829 for ( size_t i=0; i<strlen( s ); i++ ) {
1830 if ( s[i] == ';' )
1831 return &s[ i ];
1832 }
1833
1834 return NULL;
1835 }
1836
1837 /*
1838 ===============
1839 Field_Complete
1840 ===============
1841 */
Field_Complete(void)1842 static qboolean Field_Complete( void ) {
1843 int completionOffset;
1844
1845 if ( matchCount == 0 )
1846 return qtrue;
1847
1848 completionOffset = strlen( completionField->buffer ) - strlen( completionString );
1849
1850 Q_strncpyz( &completionField->buffer[completionOffset], shortestMatch, sizeof( completionField->buffer ) - completionOffset );
1851
1852 completionField->cursor = strlen( completionField->buffer );
1853
1854 if ( matchCount == 1 ) {
1855 Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
1856 completionField->cursor++;
1857 return qtrue;
1858 }
1859
1860 Com_Printf( "%c%s\n", CONSOLE_PROMPT_CHAR, completionField->buffer );
1861
1862 return qfalse;
1863 }
1864
1865 #ifndef DEDICATED
1866 /*
1867 ===============
1868 Field_CompleteKeyname
1869 ===============
1870 */
Field_CompleteKeyname(void)1871 void Field_CompleteKeyname( void )
1872 {
1873 matchCount = 0;
1874 shortestMatch[ 0 ] = 0;
1875
1876 Key_KeynameCompletion( FindMatches );
1877
1878 if( !Field_Complete( ) )
1879 Key_KeynameCompletion( PrintKeyMatches );
1880 }
1881 #endif
1882
1883 /*
1884 ===============
1885 Field_CompleteFilename
1886 ===============
1887 */
Field_CompleteFilename(const char * dir,const char * ext,qboolean stripExt,qboolean allowNonPureFilesOnDisk)1888 void Field_CompleteFilename( const char *dir, const char *ext, qboolean stripExt, qboolean allowNonPureFilesOnDisk )
1889 {
1890 matchCount = 0;
1891 shortestMatch[ 0 ] = 0;
1892
1893 FS_FilenameCompletion( dir, ext, stripExt, FindMatches, allowNonPureFilesOnDisk );
1894
1895 if ( !Field_Complete() )
1896 FS_FilenameCompletion( dir, ext, stripExt, PrintFileMatches, allowNonPureFilesOnDisk );
1897 }
1898
1899 /*
1900 ===============
1901 Field_CompleteCommand
1902 ===============
1903 */
Field_CompleteCommand(char * cmd,qboolean doCommands,qboolean doCvars)1904 void Field_CompleteCommand( char *cmd, qboolean doCommands, qboolean doCvars )
1905 {
1906 int completionArgument = 0;
1907
1908 // Skip leading whitespace and quotes
1909 cmd = Com_SkipCharset( cmd, " \"" );
1910
1911 Cmd_TokenizeStringIgnoreQuotes( cmd );
1912 completionArgument = Cmd_Argc();
1913
1914 // If there is trailing whitespace on the cmd
1915 if ( *(cmd + strlen( cmd )-1) == ' ' ) {
1916 completionString = "";
1917 completionArgument++;
1918 }
1919 else
1920 completionString = Cmd_Argv( completionArgument - 1 );
1921
1922 if ( completionArgument > 1 ) {
1923 const char *baseCmd = Cmd_Argv( 0 );
1924 char *p;
1925
1926 #ifndef DEDICATED
1927 if ( baseCmd[0] == '\\' || baseCmd[0] == '/' )
1928 baseCmd++;
1929 #endif
1930
1931 if( ( p = Field_FindFirstSeparator( cmd ) ) )
1932 Field_CompleteCommand( p + 1, qtrue, qtrue ); // Compound command
1933 else
1934 Cmd_CompleteArgument( baseCmd, cmd, completionArgument );
1935 }
1936 else {
1937 if ( completionString[0] == '\\' || completionString[0] == '/' )
1938 completionString++;
1939
1940 matchCount = 0;
1941 shortestMatch[ 0 ] = 0;
1942
1943 if ( strlen( completionString ) == 0 )
1944 return;
1945
1946 if ( doCommands )
1947 Cmd_CommandCompletion( FindMatches );
1948
1949 if ( doCvars )
1950 Cvar_CommandCompletion( FindMatches );
1951
1952 if ( !Field_Complete() ) {
1953 // run through again, printing matches
1954 if ( doCommands )
1955 Cmd_CommandCompletion( PrintMatches );
1956
1957 if ( doCvars )
1958 Cvar_CommandCompletion( PrintCvarMatches );
1959 }
1960 }
1961 }
1962
1963 /*
1964 ===============
1965 Field_AutoComplete
1966
1967 Perform Tab expansion
1968 ===============
1969 */
Field_AutoComplete(field_t * field)1970 void Field_AutoComplete( field_t *field ) {
1971 if ( !field || !field->buffer[0] )
1972 return;
1973
1974 completionField = field;
1975
1976 Field_CompleteCommand( completionField->buffer, qtrue, qtrue );
1977 }
1978
1979 /*
1980 ==================
1981 Com_RandomBytes
1982
1983 fills string array with len radom bytes, peferably from the OS randomizer
1984 ==================
1985 */
Com_RandomBytes(byte * string,int len)1986 void Com_RandomBytes( byte *string, int len )
1987 {
1988 int i;
1989
1990 if( Sys_RandomBytes( string, len ) )
1991 return;
1992
1993 Com_Printf( "Com_RandomBytes: using weak randomization\n" );
1994 for( i = 0; i < len; i++ )
1995 string[i] = (unsigned char)( rand() % 256 );
1996 }
1997
1998 /*
1999 ===============
2000 Converts a UTF-8 character to UTF-32.
2001 ===============
2002 */
ConvertUTF8ToUTF32(char * utf8CurrentChar,char ** utf8NextChar)2003 uint32_t ConvertUTF8ToUTF32( char *utf8CurrentChar, char **utf8NextChar )
2004 {
2005 uint32_t utf32 = 0;
2006 char *c = utf8CurrentChar;
2007
2008 if( ( *c & 0x80 ) == 0 )
2009 utf32 = *c++;
2010 else if( ( *c & 0xE0 ) == 0xC0 ) // 110x xxxx
2011 {
2012 utf32 |= ( *c++ & 0x1F ) << 6;
2013 utf32 |= ( *c++ & 0x3F );
2014 }
2015 else if( ( *c & 0xF0 ) == 0xE0 ) // 1110 xxxx
2016 {
2017 utf32 |= ( *c++ & 0x0F ) << 12;
2018 utf32 |= ( *c++ & 0x3F ) << 6;
2019 utf32 |= ( *c++ & 0x3F );
2020 }
2021 else if( ( *c & 0xF8 ) == 0xF0 ) // 1111 0xxx
2022 {
2023 utf32 |= ( *c++ & 0x07 ) << 18;
2024 utf32 |= ( *c++ & 0x3F ) << 12;
2025 utf32 |= ( *c++ & 0x3F ) << 6;
2026 utf32 |= ( *c++ & 0x3F );
2027 }
2028 else
2029 {
2030 Com_DPrintf( "Unrecognised UTF-8 lead byte: 0x%x\n", (unsigned int)*c );
2031 c++;
2032 }
2033
2034 *utf8NextChar = c;
2035
2036 return utf32;
2037 }
2038