1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // common.c
22 // Functions used in client and server
23 //
24 
25 #include "common.h"
26 #include <setjmp.h>
27 
28 jmp_buf	abortframe;		// an ERR_DROP occured, exit the entire frame
29 
30 cVar_t	nullCvar;
31 
32 cVar_t	*developer = &nullCvar;
33 cVar_t	*cg_developer;
34 cVar_t	*fs_developer = &nullCvar;
35 
36 cVar_t	*timescale;
37 cVar_t	*fixedtime;
38 cVar_t	*logfile;		// 1 = buffer log, 2 = flush after each print
39 cVar_t	*dedicated;
40 
41 static qBool	com_initialized;
42 static FILE		*com_logFile;
43 static uint32	com_numErrors;
44 static uint32	com_numWarnings;
45 
46 struct memPool_s	*com_aliasSysPool;
47 struct memPool_s	*com_cmdSysPool;
48 struct memPool_s	*com_cmodelSysPool;
49 struct memPool_s	*com_cvarSysPool;
50 struct memPool_s	*com_fileSysPool;
51 struct memPool_s	*com_genericPool;
52 
53 /*
54 ============================================================================
55 
56 	HASH OPTIMIZING
57 
58 ============================================================================
59 */
60 
61 /*
62 ================
63 Com_HashFileName
64 
65 Normalizes path slashes, and stops before the extension.
66 hashSize MUST be a power of two!
67 ================
68 */
Com_HashFileName(const char * fileName,const int hashSize)69 uint32 Com_HashFileName (const char *fileName, const int hashSize)
70 {
71 	uint32	hashValue;
72 	int		ch, i;
73 
74 	for (i=0, hashValue=0 ; *fileName ; i++) {
75 		ch = *(fileName++);
76 		if (ch == '\\')
77 			ch = '/';
78 		else if (ch == '.')
79 			break;
80 
81 		hashValue = hashValue * 33 + Q_tolower (ch);
82 	}
83 
84 	return (hashValue + (hashValue >> 5)) & (hashSize-1);
85 }
86 
87 
88 /*
89 ================
90 Com_HashGeneric
91 
92 hashSize MUST be a power of two!
93 ================
94 */
Com_HashGeneric(const char * name,const int hashSize)95 uint32 Com_HashGeneric (const char *name, const int hashSize)
96 {
97 	uint32	hashValue;
98 	int		ch, i;
99 
100 	for (i=0, hashValue=0 ; *name ; i++) {
101 		ch = *(name++);
102 		hashValue = hashValue * 33 + Q_tolower (ch);
103 	}
104 
105 	return (hashValue + (hashValue >> 5)) & (hashSize-1);
106 }
107 
108 
109 /*
110 ================
111 Com_HashGenericFast
112 
113 hashSize MUST be a power of two!
114 Same as above except no Q_tolower.
115 ================
116 */
Com_HashGenericFast(const char * name,const int hashSize)117 uint32 Com_HashGenericFast (const char *name, const int hashSize)
118 {
119 	uint32	hashValue;
120 	int		ch, i;
121 
122 	for (i=0, hashValue=0 ; *name ; i++) {
123 		ch = *(name++);
124 		hashValue = hashValue * 33 + ch;
125 	}
126 
127 	return (hashValue + (hashValue >> 5)) & (hashSize-1);
128 }
129 
130 /*
131 ============================================================================
132 
133 	CLIENT / SERVER INTERACTIONS
134 
135 ============================================================================
136 */
137 
138 static int	com_redirectTarget;
139 static char	*com_redirectBuffer;
140 static int	com_redirectBufferSize;
141 static void	(*com_redirectFlush) (int target, char *buffer);
142 
143 /*
144 ================
145 Com_BeginRedirect
146 ================
147 */
Com_BeginRedirect(int target,char * buffer,int bufferSize,void (* flush)(int target,char * buffer))148 void Com_BeginRedirect (int target, char *buffer, int bufferSize, void (*flush)(int target, char *buffer))
149 {
150 	if (!target || !buffer || !bufferSize || !flush)
151 		return;
152 
153 	com_redirectTarget = target;
154 	com_redirectBuffer = buffer;
155 	com_redirectBufferSize = bufferSize;
156 	com_redirectFlush = flush;
157 
158 	*com_redirectBuffer = 0;
159 }
160 
161 
162 /*
163 ================
164 Com_EndRedirect
165 ================
166 */
Com_EndRedirect(void)167 void Com_EndRedirect (void)
168 {
169 	com_redirectFlush (com_redirectTarget, com_redirectBuffer);
170 
171 	com_redirectTarget = 0;
172 	com_redirectBuffer = NULL;
173 	com_redirectBufferSize = 0;
174 	com_redirectFlush = NULL;
175 }
176 
177 
178 /*
179 =============
180 Com_ConPrint
181 
182 Doesn't evaluate args. Com_Printf and Com_DevPrintf use this to hand off
183 console messages to the appropriate targets.
184 =============
185 */
Com_ConPrint(comPrint_t flags,char * string)186 void Com_ConPrint (comPrint_t flags, char *string)
187 {
188 	// Tallying purposes
189 	if (flags & PRNT_ERROR)
190 		com_numErrors++;
191 	else if (flags & PRNT_WARNING)
192 		com_numWarnings++;
193 
194 	// Message redirecting
195 	if (com_redirectTarget) {
196 		if ((int)(strlen (string) + strlen (com_redirectBuffer)) > com_redirectBufferSize - 1) {
197 			com_redirectFlush (com_redirectTarget, com_redirectBuffer);
198 			*com_redirectBuffer = 0;
199 		}
200 		strcat (com_redirectBuffer, string);
201 		return;
202 	}
203 
204 #ifndef DEDICATED_ONLY
205 	// Print to the client console
206 	if (dedicated && !dedicated->intVal)
207 		CL_ConsolePrintf (flags, string);
208 #endif
209 
210 	// Also echo to debugging console
211 	Sys_Print (string);
212 
213 	// Logfile
214 	if (logfile && logfile->intVal) {
215 		char	name[MAX_QPATH];
216 
217 		if (!com_logFile) {
218 			Q_snprintfz (name, sizeof (name), "%s/console.log", FS_Gamedir ());
219 			if (logfile->intVal > 2)
220 				com_logFile = fopen (name, "a");
221 			else
222 				com_logFile = fopen (name, "w");
223 		}
224 		if (com_logFile)
225 			fprintf (com_logFile, "%s", string);
226 		if (logfile->intVal > 1)
227 			fflush (com_logFile);	// force it to save every time
228 	}
229 }
230 
231 
232 /*
233 =============
234 Com_Printf
235 
236 Both client and server can use this, and it will output to the apropriate place.
237 =============
238 */
Com_Printf(comPrint_t flags,char * fmt,...)239 void Com_Printf (comPrint_t flags, char *fmt, ...)
240 {
241 	va_list		argptr;
242 	char		msg[MAX_COMPRINT];
243 
244 	// Evaluate args
245 	va_start (argptr, fmt);
246 	vsnprintf (msg, sizeof (msg), fmt, argptr);
247 	va_end (argptr);
248 
249 	// Print
250 	Com_ConPrint (flags, msg);
251 }
252 
253 
254 /*
255 =============
256 Com_DevPrintf
257 
258 Hands off to Com_ConPrint if developer is on
259 =============
260 */
Com_DevPrintf(comPrint_t flags,char * fmt,...)261 void Com_DevPrintf (comPrint_t flags, char *fmt, ...)
262 {
263 	va_list		argptr;
264 	char		msg[MAX_COMPRINT];
265 
266 	if (!developer->intVal)
267 		return;
268 
269 	// Evaluate args
270 	va_start (argptr, fmt);
271 	vsnprintf (msg, sizeof (msg), fmt, argptr);
272 	va_end (argptr);
273 
274 	// Print
275 	Com_ConPrint (flags, msg);
276 }
277 
278 
279 /*
280 =============
281 Com_Error
282 
283 Both client and server can use this, and it will
284 do the apropriate things.
285 =============
286 */
Com_Error(comError_t code,char * fmt,...)287 void Com_Error (comError_t code, char *fmt, ...)
288 {
289 	va_list			argptr;
290 	static char		msg[MAX_COMPRINT];
291 	static qBool	recursive = qFalse;
292 
293 	if (recursive)
294 		Sys_Error ("Com_Error: Recursive error after: %s", msg);
295 	recursive = qTrue;
296 
297 	// Evaluate args
298 	va_start (argptr, fmt);
299 	vsnprintf (msg, sizeof (msg), fmt, argptr);
300 	va_end (argptr);
301 
302 	switch (code) {
303 	case ERR_DISCONNECT:
304 #ifndef DEDICATED_ONLY
305 		if (!dedicated->intVal)
306 			CL_Disconnect (qTrue);
307 #endif
308 
309 		recursive = qFalse;
310 		if (!com_initialized)
311 			Sys_Error ("%s", msg);
312 		longjmp (abortframe, -1);
313 		break;
314 
315 	case ERR_DROP:
316 		Com_Printf (0, "********************\nERROR: %s\n********************\n", msg);
317 
318 		SV_ServerShutdown (Q_VarArgs ("Server exited: %s\n", msg), qFalse, qFalse);
319 #ifndef DEDICATED_ONLY
320 		if (!dedicated->intVal)
321 			CL_Disconnect (qTrue);
322 #endif
323 
324 		recursive = qFalse;
325 		if (!com_initialized)
326 			Sys_Error ("%s", msg);
327 		longjmp (abortframe, -1);
328 		break;
329 
330 	default:
331 	case ERR_FATAL:
332 		SV_ServerShutdown (Q_VarArgs ("Server fatal crashed: %s\n", msg), qFalse, qTrue);
333 #ifndef DEDICATED_ONLY
334 		if (!dedicated->intVal)
335 			CL_ClientShutdown (qTrue);
336 #endif
337 		break;
338 	}
339 
340 	if (com_logFile) {
341 		fclose (com_logFile);
342 		com_logFile = NULL;
343 	}
344 
345 	Sys_Error ("%s", msg);
346 }
347 
348 
349 /*
350 =============
351 Com_Error_f
352 
353 Just throw a fatal error to test error shutdown procedures
354 =============
355 */
Com_Error_f(void)356 void Com_Error_f (void)
357 {
358 	Com_Error (ERR_FATAL, "%s", Cmd_Args ());
359 }
360 
361 
362 /*
363 =============
364 Com_Quit
365 
366 Both client and server can use this, and it will do the apropriate things.
367 =============
368 */
Com_Quit(qBool error)369 void Com_Quit (qBool error)
370 {
371 	if (Cmd_Argc () > 1)
372 		SV_ServerShutdown (Q_VarArgs ("Server has shut down: %s\n", Cmd_Args ()), qFalse, error);
373 	else
374 		SV_ServerShutdown ("Server has shut down\n", qFalse, error);
375 
376 	if (com_logFile) {
377 		fclose (com_logFile);
378 		com_logFile = NULL;
379 	}
380 
381 	Sys_Quit (error);
382 }
383 
384 
385 /*
386 =============
387 Com_Quit_f
388 =============
389 */
Com_Quit_f(void)390 static void Com_Quit_f (void)
391 {
392 	Com_Quit (qFalse);
393 }
394 
395 /*
396 ============================================================================
397 
398 	CLIENT / SERVER STATES
399 
400 	Non-zero state values indicate initialization
401 ============================================================================
402 */
403 
404 static ssState_t	com_svState;
405 static caState_t	com_clState;
406 
407 /*
408 ==================
409 Com_ClientState
410 ==================
411 */
Com_ClientState(void)412 caState_t Com_ClientState (void)
413 {
414 	return com_clState;
415 }
416 
417 
418 /*
419 ==================
420 Com_SetClientState
421 ==================
422 */
Com_SetClientState(caState_t state)423 void Com_SetClientState (caState_t state)
424 {
425 	com_clState = state;
426 }
427 
428 
429 /*
430 ==================
431 Com_ServerState
432 ==================
433 */
Com_ServerState(void)434 ssState_t Com_ServerState (void)
435 {
436 	return com_svState;
437 }
438 
439 
440 /*
441 ==================
442 Com_SetServerState
443 ==================
444 */
Com_SetServerState(ssState_t state)445 void Com_SetServerState (ssState_t state)
446 {
447 	com_svState = state;
448 }
449 
450 /*
451 ============================================================================
452 
453 	PROGRAM ARGUMENTS
454 
455 ============================================================================
456 */
457 
458 #define MAX_NUM_ARGVS	51
459 
460 static int	com_argCount;
461 static char	*com_argValues[MAX_NUM_ARGVS];
462 
463 /*
464 ================
465 Com_InitArgv
466 ================
467 */
Com_InitArgv(int argc,char ** argv)468 static void Com_InitArgv (int argc, char **argv)
469 {
470 	int		i;
471 
472 	if (argc >= MAX_NUM_ARGVS)
473 		Com_Error (ERR_FATAL, "argc >= MAX_NUM_ARGVS");
474 
475 	com_argCount = argc;
476 	for (i=0 ; i<argc ; i++) {
477 		if (!argv[i] || strlen(argv[i])+1 >= MAX_TOKEN_CHARS)
478 			com_argValues[i] = "";
479 		else
480 			com_argValues[i] = argv[i];
481 	}
482 }
483 
484 
485 /*
486 ================
487 Com_ClearArgv
488 ================
489 */
Com_ClearArgv(int arg)490 static void Com_ClearArgv (int arg)
491 {
492 	if (arg < 0 || arg >= com_argCount || !com_argValues[arg])
493 		return;
494 
495 	com_argValues[arg] = "";
496 }
497 
498 
499 /*
500 ================
501 Com_Argc
502 ================
503 */
Com_Argc(void)504 static int Com_Argc (void)
505 {
506 	return com_argCount;
507 }
508 
509 
510 /*
511 ================
512 Com_Argv
513 ================
514 */
Com_Argv(int arg)515 static char *Com_Argv (int arg)
516 {
517 	if (arg < 0 || arg >= com_argCount || !com_argValues[arg])
518 		return "";
519 
520 	return com_argValues[arg];
521 }
522 
523 
524 /*
525 ===============
526 Com_AddEarlyCommands
527 
528 Adds command line parameters as script statements
529 Commands lead with a +, and continue until another +
530 
531 Set commands are added early, so they are guaranteed to be set before
532 the client and server initialize for the first time.
533 
534 Other commands are added late, after all initialization is complete.
535 ===============
536 */
Com_AddEarlyCommands(qBool clear)537 static void Com_AddEarlyCommands (qBool clear)
538 {
539 	int		i;
540 	char	*s;
541 
542 	for (i=0 ; i<Com_Argc () ; i++) {
543 		s = Com_Argv (i);
544 		if (Q_stricmp (s, "+set"))
545 			continue;
546 
547 		Cbuf_AddText (Q_VarArgs ("set %s %s\n", Com_Argv(i+1), Com_Argv(i+2)));
548 		if (clear) {
549 			Com_ClearArgv (i);
550 			Com_ClearArgv (i+1);
551 			Com_ClearArgv (i+2);
552 		}
553 		i+=2;
554 	}
555 }
556 
557 
558 /*
559 =================
560 Com_AddLateCommands
561 
562 Adds command line parameters as script statements
563 Commands lead with a + and continue until another + or -
564 egl +map amlev1 +
565 
566 Returns qTrue if any late commands were added, which
567 will keep the demoloop from immediately starting
568 =================
569 */
Com_AddLateCommands(void)570 static qBool Com_AddLateCommands (void)
571 {
572 	int		i, j;
573 	int		s;
574 	char	*text, *build, c;
575 	int		argc;
576 	qBool	ret;
577 
578 	// Build the combined string to parse from
579 	s = 0;
580 	argc = Com_Argc ();
581 	for (i=1 ; i<argc ; i++)
582 		s += strlen (Com_Argv (i)) + 1;
583 
584 	if (!s)
585 		return qFalse;
586 
587 	text = Mem_Alloc (s+1);
588 	text[0] = 0;
589 	for (i=1 ; i<argc ; i++) {
590 		strcat (text, Com_Argv (i));
591 		if (i != argc-1)
592 			strcat (text, " ");
593 	}
594 
595 	// Pull out the commands
596 	build = Mem_Alloc (s+1);
597 	build[0] = 0;
598 
599 	for (i=0 ; i<s-1 ; i++) {
600 		if (text[i] == '+') {
601 			i++;
602 
603 			// Skip '+'/'-'/'\0'
604 			for (j=i ; text[j] != '+' && text[j] != '-' && text[j] != '\0' ; j++) ;
605 
606 			c = text[j];
607 			text[j] = 0;
608 
609 			strcat (build, text+i);
610 			strcat (build, "\n");
611 			text[j] = c;
612 			i = j-1;
613 		}
614 	}
615 
616 	ret = (build[0] != 0);
617 	if (ret)
618 		Cbuf_AddText (build);
619 
620 	Mem_Free (text);
621 	Mem_Free (build);
622 
623 	return ret;
624 }
625 
626 /*
627 ============================================================================
628 
629 	INITIALIZATION / FRAME PROCESSING
630 
631 ============================================================================
632 */
633 
634 /*
635 =================
636 Com_Init
637 =================
638 */
Com_Init(int argc,char ** argv)639 void Com_Init (int argc, char **argv)
640 {
641 	uint32	initTime;
642 	char	*verString;
643 
644 	Swap_Init ();
645 
646 	initTime = Sys_UMilliseconds ();
647 	if (setjmp (abortframe))
648 		Sys_Error ("Error during initialization");
649 
650 	seedMT ((unsigned long)time(0));
651 
652 	// Memory init
653 	Mem_Init ();
654 	com_aliasSysPool = Mem_CreatePool ("Common: Alias system");
655 	com_cmdSysPool = Mem_CreatePool ("Common: Command system");
656 	com_cmodelSysPool = Mem_CreatePool ("Common: Collision model");
657 	com_cvarSysPool = Mem_CreatePool ("Common: Cvar system");
658 	com_fileSysPool = Mem_CreatePool ("Common: File system");
659 	com_genericPool = Mem_CreatePool ("Generic");
660 
661 	// Prepare enough of the subsystems to handle cvar and command buffer management
662 	Com_InitArgv (argc, argv);
663 
664 	Cmd_Init ();
665 	Swap_Init ();
666 	Cbuf_Init ();
667 	Alias_Init ();
668 	Cvar_Init ();
669 #ifndef DEDICATED_ONLY
670 	Key_Init ();
671 #endif
672 
673 	// Init information variables
674 	verString = Q_VarArgs ("EGL v%s: %s %s-%s", EGL_VERSTR, __DATE__, BUILDSTRING, CPUSTRING);
675 	Cvar_Register ("cl_version", verString, CVAR_READONLY);
676 	Cvar_Register ("version", verString, CVAR_SERVERINFO|CVAR_READONLY);
677 	Cvar_Register ("vid_ref", verString, CVAR_READONLY);
678 
679 	/*
680 	** we need to add the early commands twice, because a basedir or cddir needs to be set before execing
681 	** config files, but we want other parms to override the settings of the config files
682 	*/
683 	Com_AddEarlyCommands (qFalse);
684 	Cbuf_Execute ();
685 
686 #ifdef DEDICATED_ONLY
687 	dedicated		= Cvar_Register ("dedicated",	"1",	CVAR_READONLY);
688 #else
689 	dedicated		= Cvar_Register ("dedicated",	"0",	CVAR_READONLY);
690 #endif
691 
692 	developer		= Cvar_Register ("developer",		"0",	0);
693 	cg_developer	= Cvar_Register ("cg_developer",	"0",	0);
694 	fs_developer	= Cvar_Register ("fs_developer",	"0",	0);
695 
696 	Sys_Init ();
697 
698 #ifndef DEDICATED_ONLY
699 	if (!dedicated->intVal)
700 		CL_ConsoleInit ();
701 #endif
702 
703 	Com_Printf (0, "========= Common Initialization ========\n");
704 	Com_Printf (0, "EGL v%s by Echon\nhttp://egl.quakedev.com/\n", EGL_VERSTR);
705 	Com_Printf (0, "Compiled: "__DATE__" @ "__TIME__"\n");
706 	Com_Printf (0, "----------------------------------------\n");
707 
708 	FS_Init ();
709 
710 	Com_Printf (0, "\n");
711 
712 #ifndef DEDICATED_ONLY
713 	if (!dedicated->intVal) {
714 		Cbuf_AddText ("exec default.cfg\n");
715 		Cbuf_AddText ("exec config.cfg\n");
716 		Cbuf_AddText ("exec eglcfg.cfg\n");
717 	}
718 #endif
719 
720 	Com_AddEarlyCommands (qTrue);
721 	Cbuf_Execute ();
722 
723 	// Init commands and vars
724 	Mem_Register ();
725 
726 #ifdef _DEBUG
727 	Cmd_AddCommand ("error",	Com_Error_f,	"Error out with a message");
728 #endif
729 
730 	timescale		= Cvar_Register ("timescale",		"1",	CVAR_CHEAT);
731 	fixedtime		= Cvar_Register ("fixedtime",		"0",	CVAR_CHEAT);
732 	logfile			= Cvar_Register ("logfile",			"0",	0);
733 
734 #ifndef DEDICATED_ONLY
735 	if (dedicated->intVal) {
736 #endif
737 		Cmd_AddCommand ("quit", Com_Quit_f,		"Exits");
738 		Cmd_AddCommand ("exit", Com_Quit_f,		"Exits");
739 #ifndef DEDICATED_ONLY
740 	}
741 	else {
742 		FS_ExecAutoexec ();
743 		Cbuf_Execute ();
744 	}
745 #endif
746 
747 	// Init the rest of the sub-systems
748 	NET_Init ();
749 	Netchan_Init ();
750 
751 	SV_ServerInit ();
752 
753 #ifndef DEDICATED_ONLY
754 	if (!dedicated->intVal) {
755 		Sys_ShowConsole (0, qFalse);
756 		CL_ClientInit ();
757 	}
758 #endif
759 
760 	// Add + commands from command line
761 	if (!Com_AddLateCommands ()) {
762 #ifndef DEDICATED_ONLY
763 		if (dedicated->intVal)
764 #endif
765 			Cbuf_AddText ("dedicated_start\n");
766 
767 		Cbuf_Execute ();
768 	}
769 
770 #ifndef DEDICATED_ONLY
771 	if (!dedicated->intVal)
772 		SCR_EndLoadingPlaque ();
773 #endif
774 
775 	// Check memory integrity
776 	Mem_CheckGlobalIntegrity ();
777 
778 	// Touch memory
779 	Mem_TouchGlobal ();
780 
781 	com_initialized = qTrue;
782 
783 	Com_Printf (0, "\nCOMMON - %i error(s), %i warning(s)\n", com_numErrors, com_numWarnings);
784 	Com_Printf (0, "====== Common Initialized %6ums =====\n\n", Sys_UMilliseconds()-initTime);
785 }
786 
787 
788 /*
789 =================
790 Com_Frame
791 =================
792 */
Com_Frame(int msec)793 void __fastcall Com_Frame (int msec)
794 {
795 	char	*conInput;
796 
797 	if (setjmp (abortframe))
798 		return;			// an ERR_DROP was thrown
799 
800 	if (fixedtime->floatVal)
801 		msec = fixedtime->floatVal;
802 	else if (timescale->floatVal) {
803 		msec *= timescale->floatVal;
804 		if (msec < 1)
805 			msec = 1;
806 	}
807 
808 	// Print trace statistics if desired
809 	CM_PrintStats ();
810 
811 	// Pump the message loop
812 	Sys_SendKeyEvents ();
813 
814 	// Command console input
815 	conInput = Sys_ConsoleInput ();
816 	if (conInput)
817 		Cbuf_AddText (conInput);
818 	Cbuf_Execute ();
819 
820 	// Update server
821 	SV_Frame (msec);
822 
823 #ifndef DEDICATED_ONLY
824 	// Update client
825 	if (!dedicated->intVal)
826 		CL_Frame (msec);
827 #endif
828 }
829 
830 
831 /*
832 =================
833 Com_Shutdown
834 =================
835 */
Com_Shutdown(void)836 void Com_Shutdown (void)
837 {
838 	NET_Shutdown ();
839 }
840