• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 Copyright (C) 1996-1997 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 // host.c -- coordinates spawning and killing of local servers
21 
22 #include "quakedef.h"
23 #include "r_local.h"
24 
25 #include <android/log.h>
26 #define LOG_TAG "Quake host"
27 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
28 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
29 
30 /*
31 
32 A server can allways be started, even if the system started out as a client
33 to a remote system.
34 
35 A client can NOT be started if the system started as a dedicated server.
36 
37 Memory is cleared / released when a server or client begins, not when they end.
38 
39 */
40 
41 quakeparms_t host_parms;
42 
43 qboolean	host_initialized;		// true if into command execution
44 
45 double		host_frametime;
46 double		host_time;
47 double		realtime;				// without any filtering or bounding
48 double		oldrealtime;			// last frame run
49 int			host_framecount;
50 qboolean    host_framethrottled; // Running too fast
51 
52 int			host_hunklevel;
53 
54 int			minimum_memory;
55 
56 client_t	*host_client;			// current client
57 
58 jmp_buf 	host_abortserver;
59 
60 byte		*host_basepal;
61 byte		*host_colormap;
62 
63 cvar_t	host_framerate = CVAR2("host_framerate","0");	// set for slow motion
64 cvar_t	host_speeds = CVAR2("host_speeds","0");			// set for running times
65 
66 cvar_t	sys_ticrate = CVAR2("sys_ticrate","0.05");
67 cvar_t	serverprofile = CVAR2("serverprofile","1");
68 
69 cvar_t	fraglimit = CVAR4("fraglimit","0",false,true);
70 cvar_t	timelimit = CVAR4("timelimit","0",false,true);
71 cvar_t	teamplay = CVAR4("teamplay","0",false,true);
72 
73 cvar_t	samelevel = CVAR2("samelevel","0");
74 cvar_t	noexit = CVAR4("noexit","0",false,true);
75 
76 #ifdef QUAKE2
77 cvar_t	developer = CVAR2("developer","1");	// should be 0 for release!
78 #else
79 cvar_t	developer = CVAR2("developer","0");
80 #endif
81 
82 cvar_t	skill = CVAR2("skill","1");						// 0 - 3
83 cvar_t	deathmatch = CVAR2("deathmatch","0");			// 0, 1, or 2
84 cvar_t	coop = CVAR2("coop","0");			// 0 or 1
85 
86 cvar_t	pausable = CVAR2("pausable","1");
87 
88 cvar_t	temp1 = CVAR2("temp1","0");
89 
90 
91 /*
92 ================
93 Host_EndGame
94 ================
95 */
Host_EndGame(const char * message,...)96 void Host_EndGame (const char *message, ...)
97 {
98 	va_list		argptr;
99 	char		string[1024];
100 
101 	va_start (argptr,message);
102 	vsprintf (string,message,argptr);
103 	va_end (argptr);
104 	Con_DPrintf ("Host_EndGame: %s\n",string);
105 
106 	if (sv.active)
107 		Host_ShutdownServer (false);
108 
109 	if (cls.state == ca_dedicated)
110 		Sys_Error ("Host_EndGame: %s\n",string);	// dedicated servers exit
111 
112 	if (cls.demonum != -1)
113 		CL_NextDemo ();
114 	else
115 		CL_Disconnect ();
116 
117 	longjmp (host_abortserver, 1);
118 }
119 
120 /*
121 ================
122 Host_Error
123 
124 This shuts down both the client and server
125 ================
126 */
Host_Error(const char * error,...)127 void Host_Error (const char *error, ...)
128 {
129 	va_list		argptr;
130 	char		string[1024];
131 	static	qboolean inerror = false;
132 
133 	if (inerror)
134 		Sys_Error ("Host_Error: recursively entered");
135 	inerror = true;
136 
137 	SCR_EndLoadingPlaque ();		// reenable screen updates
138 
139 	va_start (argptr,error);
140 	vsprintf (string,error,argptr);
141 	va_end (argptr);
142 	Con_Printf ("Host_Error: %s\n",string);
143 
144 	if (sv.active)
145 		Host_ShutdownServer (false);
146 
147 	if (cls.state == ca_dedicated)
148 		Sys_Error ("Host_Error: %s\n",string);	// dedicated servers exit
149 
150 	CL_Disconnect ();
151 	cls.demonum = -1;
152 
153 	inerror = false;
154 
155 	longjmp (host_abortserver, 1);
156 }
157 
158 /*
159 ================
160 Host_FindMaxClients
161 ================
162 */
Host_FindMaxClients(void)163 void	Host_FindMaxClients (void)
164 {
165 	int		i;
166 
167 	svs.maxclients = 1;
168 
169 	i = COM_CheckParm ("-dedicated");
170 	if (i)
171 	{
172 		cls.state = ca_dedicated;
173 		if (i != (com_argc - 1))
174 		{
175 			svs.maxclients = Q_atoi (com_argv[i+1]);
176 		}
177 		else
178 			svs.maxclients = 8;
179 	}
180 	else
181 		cls.state = ca_disconnected;
182 
183 	i = COM_CheckParm ("-listen");
184 	if (i)
185 	{
186 		if (cls.state == ca_dedicated)
187 			Sys_Error ("Only one of -dedicated or -listen can be specified");
188 		if (i != (com_argc - 1))
189 			svs.maxclients = Q_atoi (com_argv[i+1]);
190 		else
191 			svs.maxclients = 8;
192 	}
193 	if (svs.maxclients < 1)
194 		svs.maxclients = 8;
195 	else if (svs.maxclients > MAX_SCOREBOARD)
196 		svs.maxclients = MAX_SCOREBOARD;
197 
198 	svs.maxclientslimit = svs.maxclients;
199 	if (svs.maxclientslimit < 4)
200 		svs.maxclientslimit = 4;
201 	svs.clients = (client_s*) Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients");
202 
203 	if (svs.maxclients > 1)
204 		Cvar_SetValue ("deathmatch", 1.0);
205 	else
206 		Cvar_SetValue ("deathmatch", 0.0);
207 }
208 
209 
210 /*
211 =======================
212 Host_InitLocal
213 ======================
214 */
Host_InitLocal(void)215 void Host_InitLocal (void)
216 {
217 	Host_InitCommands ();
218 
219 	Cvar_RegisterVariable (&host_framerate);
220 	Cvar_RegisterVariable (&host_speeds);
221 
222 	Cvar_RegisterVariable (&sys_ticrate);
223 	Cvar_RegisterVariable (&serverprofile);
224 
225 	Cvar_RegisterVariable (&fraglimit);
226 	Cvar_RegisterVariable (&timelimit);
227 	Cvar_RegisterVariable (&teamplay);
228 	Cvar_RegisterVariable (&samelevel);
229 	Cvar_RegisterVariable (&noexit);
230 	Cvar_RegisterVariable (&skill);
231 	Cvar_RegisterVariable (&developer);
232 	Cvar_RegisterVariable (&deathmatch);
233 	Cvar_RegisterVariable (&coop);
234 
235 	Cvar_RegisterVariable (&pausable);
236 
237 	Cvar_RegisterVariable (&temp1);
238 
239 	Host_FindMaxClients ();
240 
241 	host_time = 1.0;		// so a think at time 0 won't get called
242 }
243 
244 
245 /*
246 ===============
247 Host_WriteConfiguration
248 
249 Writes key bindings and archived cvars to config.cfg
250 ===============
251 */
Host_WriteConfiguration(void)252 void Host_WriteConfiguration (void)
253 {
254 	FILE	*f;
255 
256 // dedicated servers initialize the host but don't parse and set the
257 // config.cfg cvars
258 	if (host_initialized & !isDedicated)
259 	{
260 		f = fopen (va("%s/config.cfg",com_gamedir), "w");
261 		if (!f)
262 		{
263 			Con_Printf ("Couldn't write config.cfg.\n");
264 			return;
265 		}
266 
267 		Key_WriteBindings (f);
268 		Cvar_WriteVariables (f);
269 
270 		fclose (f);
271 	}
272 }
273 
274 
275 /*
276 =================
277 SV_ClientPrintf
278 
279 Sends text across to be displayed
280 FIXME: make this just a stuffed echo?
281 =================
282 */
SV_ClientPrintf(const char * fmt,...)283 void SV_ClientPrintf (const char *fmt, ...)
284 {
285 	va_list		argptr;
286 	char		string[1024];
287 
288 	va_start (argptr,fmt);
289 	vsprintf (string, fmt,argptr);
290 	va_end (argptr);
291 
292 	MSG_WriteByte (&host_client->message, svc_print);
293 	MSG_WriteString (&host_client->message, string);
294 }
295 
296 /*
297 =================
298 SV_BroadcastPrintf
299 
300 Sends text to all active clients
301 =================
302 */
SV_BroadcastPrintf(const char * fmt,...)303 void SV_BroadcastPrintf (const char *fmt, ...)
304 {
305 	va_list		argptr;
306 	char		string[1024];
307 	int			i;
308 
309 	va_start (argptr,fmt);
310 	vsprintf (string, fmt,argptr);
311 	va_end (argptr);
312 
313 	for (i=0 ; i<svs.maxclients ; i++)
314 		if (svs.clients[i].active && svs.clients[i].spawned)
315 		{
316 			MSG_WriteByte (&svs.clients[i].message, svc_print);
317 			MSG_WriteString (&svs.clients[i].message, string);
318 		}
319 }
320 
321 /*
322 =================
323 Host_ClientCommands
324 
325 Send text over to the client to be executed
326 =================
327 */
Host_ClientCommands(const char * fmt,...)328 void Host_ClientCommands (const char *fmt, ...)
329 {
330 	va_list		argptr;
331 	char		string[1024];
332 
333 	va_start (argptr,fmt);
334 	vsprintf (string, fmt,argptr);
335 	va_end (argptr);
336 
337 	MSG_WriteByte (&host_client->message, svc_stufftext);
338 	MSG_WriteString (&host_client->message, string);
339 }
340 
341 /*
342 =====================
343 SV_DropClient
344 
345 Called when the player is getting totally kicked off the host
346 if (crash = true), don't bother sending signofs
347 =====================
348 */
SV_DropClient(qboolean crash)349 void SV_DropClient (qboolean crash)
350 {
351 	int		saveSelf;
352 	int		i;
353 	client_t *client;
354 
355 	if (!crash)
356 	{
357 		// send any final messages (don't check for errors)
358 		if (NET_CanSendMessage (host_client->netconnection))
359 		{
360 			MSG_WriteByte (&host_client->message, svc_disconnect);
361 			NET_SendMessage (host_client->netconnection, &host_client->message);
362 		}
363 
364 		if (host_client->edict && host_client->spawned)
365 		{
366 		// call the prog function for removing a client
367 		// this will set the body to a dead frame, among other things
368 			saveSelf = pr_global_struct->self;
369 			pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
370 			PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
371 			pr_global_struct->self = saveSelf;
372 		}
373 
374 		Sys_Printf ("Client %s removed\n",host_client->name);
375 	}
376 
377 // break the net connection
378 	NET_Close (host_client->netconnection);
379 	host_client->netconnection = NULL;
380 
381 // free the client (the body stays around)
382 	host_client->active = false;
383 	host_client->name[0] = 0;
384 	host_client->old_frags = -999999;
385 	net_activeconnections--;
386 
387 // send notification to all clients
388 	for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
389 	{
390 		if (!client->active)
391 			continue;
392 		MSG_WriteByte (&client->message, svc_updatename);
393 		MSG_WriteByte (&client->message, host_client - svs.clients);
394 		MSG_WriteString (&client->message, "");
395 		MSG_WriteByte (&client->message, svc_updatefrags);
396 		MSG_WriteByte (&client->message, host_client - svs.clients);
397 		MSG_WriteShort (&client->message, 0);
398 		MSG_WriteByte (&client->message, svc_updatecolors);
399 		MSG_WriteByte (&client->message, host_client - svs.clients);
400 		MSG_WriteByte (&client->message, 0);
401 	}
402 }
403 
404 /*
405 ==================
406 Host_ShutdownServer
407 
408 This only happens at the end of a game, not between levels
409 ==================
410 */
Host_ShutdownServer(qboolean crash)411 void Host_ShutdownServer(qboolean crash)
412 {
413 	int		i;
414 	int		count;
415 	sizebuf_t	buf;
416 	char		message[4];
417 	double	start;
418 
419 	if (!sv.active)
420 		return;
421 
422 	sv.active = false;
423 
424 // stop all client sounds immediately
425 	if (cls.state == ca_connected)
426 		CL_Disconnect ();
427 
428 // flush any pending messages - like the score!!!
429 	start = Sys_FloatTime();
430 	do
431 	{
432 		count = 0;
433 		for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
434 		{
435 			if (host_client->active && host_client->message.cursize)
436 			{
437 				if (NET_CanSendMessage (host_client->netconnection))
438 				{
439 					NET_SendMessage(host_client->netconnection, &host_client->message);
440 					SZ_Clear (&host_client->message);
441 				}
442 				else
443 				{
444 					NET_GetMessage(host_client->netconnection);
445 					count++;
446 				}
447 			}
448 		}
449 		if ((Sys_FloatTime() - start) > 3.0)
450 			break;
451 	}
452 	while (count);
453 
454 // make sure all the clients know we're disconnecting
455 	buf.data = (byte*) message;
456 	buf.maxsize = 4;
457 	buf.cursize = 0;
458 	MSG_WriteByte(&buf, svc_disconnect);
459 	count = NET_SendToAll(&buf, 5);
460 	if (count)
461 		Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
462 
463 	for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
464 		if (host_client->active)
465 			SV_DropClient(crash);
466 
467 //
468 // clear structures
469 //
470 	memset (&sv, 0, sizeof(sv));
471 	memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t));
472 }
473 
474 
475 /*
476 ================
477 Host_ClearMemory
478 
479 This clears all the memory used by both the client and server, but does
480 not reinitialize anything.
481 ================
482 */
Host_ClearMemory(void)483 void Host_ClearMemory (void)
484 {
485 	Con_DPrintf ("Clearing memory\n");
486 	D_FlushCaches ();
487 	Mod_ClearAll ();
488 	if (host_hunklevel)
489 		Hunk_FreeToLowMark (host_hunklevel);
490 
491 	cls.signon = 0;
492 	memset (&sv, 0, sizeof(sv));
493 	memset (&cl, 0, sizeof(cl));
494 }
495 
496 
497 //============================================================================
498 
499 
500 /*
501 ===================
502 Host_FilterTime
503 
504 Returns false if the time is too short to run a frame
505 ===================
506 */
Host_FilterTime(float time)507 qboolean Host_FilterTime (float time)
508 {
509 	realtime += time;
510 
511 	if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0)
512 		return false;		// framerate is too high
513 
514 	host_frametime = realtime - oldrealtime;
515 	oldrealtime = realtime;
516 
517 	if (host_framerate.value > 0)
518 		host_frametime = host_framerate.value;
519 	else
520 	{	// don't allow really long or short frames
521 		if (host_frametime > 0.1)
522 			host_frametime = 0.1;
523 		if (host_frametime < 0.001)
524 			host_frametime = 0.001;
525 	}
526 
527 	return true;
528 }
529 
530 
531 /*
532 ===================
533 Host_GetConsoleCommands
534 
535 Add them exactly as if they had been typed at the console
536 ===================
537 */
Host_GetConsoleCommands(void)538 void Host_GetConsoleCommands (void)
539 {
540 	char	*cmd;
541 
542 	while (1)
543 	{
544 		cmd = Sys_ConsoleInput ();
545 		if (!cmd)
546 			break;
547 		Cbuf_AddText (cmd);
548 	}
549 }
550 
551 
552 /*
553 ==================
554 Host_ServerFrame
555 
556 ==================
557 */
558 #ifdef FPS_20
559 
_Host_ServerFrame(void)560 void _Host_ServerFrame (void)
561 {
562 // run the world state
563 	pr_global_struct->frametime = host_frametime;
564 
565 // read client messages
566 	SV_RunClients ();
567 
568 // move things around and think
569 // always pause in single player if in console or menus
570 	if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
571 		SV_Physics ();
572 }
573 
Host_ServerFrame(void)574 void Host_ServerFrame (void)
575 {
576 	float	save_host_frametime;
577 	float	temp_host_frametime;
578 
579 // run the world state
580 	pr_global_struct->frametime = host_frametime;
581 
582 // set the time and clear the general datagram
583 	SV_ClearDatagram ();
584 
585 // check for new clients
586 	SV_CheckForNewClients ();
587 
588 	temp_host_frametime = save_host_frametime = host_frametime;
589 	while(temp_host_frametime > (1.0/72.0))
590 	{
591 		if (temp_host_frametime > 0.05)
592 			host_frametime = 0.05;
593 		else
594 			host_frametime = temp_host_frametime;
595 		temp_host_frametime -= host_frametime;
596 		_Host_ServerFrame ();
597 	}
598 	host_frametime = save_host_frametime;
599 
600 // send all messages to the clients
601 	SV_SendClientMessages ();
602 }
603 
604 #else
605 
Host_ServerFrame(void)606 void Host_ServerFrame (void)
607 {
608 // run the world state
609 	pr_global_struct->frametime = host_frametime;
610 
611 // set the time and clear the general datagram
612 	SV_ClearDatagram ();
613 
614 // check for new clients
615 	SV_CheckForNewClients ();
616 
617 // read client messages
618 	SV_RunClients ();
619 
620 // move things around and think
621 // always pause in single player if in console or menus
622 	if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
623 		SV_Physics ();
624 
625 // send all messages to the clients
626 	SV_SendClientMessages ();
627 }
628 
629 #endif
630 
631 
632 /*
633 ==================
634 Host_Frame
635 
636 Runs all active servers
637 ==================
638 */
_Host_Frame(float time)639 void _Host_Frame (float time)
640 {
641 	static double		time1 = 0;
642 	static double		time2 = 0;
643 	static double		time3 = 0;
644 	int			pass1, pass2, pass3;
645 
646 	if (setjmp (host_abortserver) )
647 		return;			// something bad happened, or the server disconnected
648 
649 // keep the random time dependent
650 	rand ();
651 
652 // decide the simulation time
653     host_framethrottled = !Host_FilterTime (time);
654 	if (host_framethrottled)
655 		return;			// don't run too fast, or packets will flood out
656 
657 // get new key events
658 	Sys_SendKeyEvents ();
659 
660 // allow mice or other external controllers to add commands
661 	IN_Commands ();
662 
663 // process console commands
664 	Cbuf_Execute ();
665 
666 	NET_Poll();
667 
668 // if running the server locally, make intentions now
669 	if (sv.active)
670 		CL_SendCmd ();
671 
672 //-------------------
673 //
674 // server operations
675 //
676 //-------------------
677 
678 // check for commands typed to the host
679 	Host_GetConsoleCommands ();
680 
681 	if (sv.active)
682 		Host_ServerFrame ();
683 
684 //-------------------
685 //
686 // client operations
687 //
688 //-------------------
689 
690 // if running the server remotely, send intentions now after
691 // the incoming messages have been read
692 	if (!sv.active)
693 		CL_SendCmd ();
694 
695 	host_time += host_frametime;
696 
697 // fetch results from server
698 	if (cls.state == ca_connected)
699 	{
700 		CL_ReadFromServer ();
701 	}
702 
703 // update video
704 	if (host_speeds.value)
705 		time1 = Sys_FloatTime ();
706 
707 	SCR_UpdateScreen ();
708 
709 	if (host_speeds.value)
710 		time2 = Sys_FloatTime ();
711 
712 // update audio
713 	if (cls.signon == SIGNONS)
714 	{
715 		S_Update (r_origin, vpn, vright, vup);
716 		CL_DecayLights ();
717 	}
718 	else
719 		S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
720 
721 	CDAudio_Update();
722 
723 	if (host_speeds.value)
724 	{
725 		pass1 = (int) ((time1 - time3)*1000);
726 		time3 = Sys_FloatTime ();
727 		pass2 = (int) ((time2 - time1)*1000);
728 		pass3 = (int) ((time3 - time2)*1000);
729 		Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
730 					pass1+pass2+pass3, pass1, pass2, pass3);
731 	}
732 
733 	host_framecount++;
734 }
735 
Host_Frame(float time)736 void Host_Frame (float time)
737 {
738 	double	time1, time2;
739 	static double	timetotal, timetotal_acc;
740 	static int		timecount, timecount_acc;
741 	int		i, c, m, m_acc;
742 
743 	if (!serverprofile.value)
744 	{
745 		_Host_Frame (time);
746 		return;
747 	}
748 
749 	time1 = Sys_FloatTime ();
750 	_Host_Frame (time);
751 	time2 = Sys_FloatTime ();
752 
753 	timetotal += time2 - time1;
754 	timecount++;
755 
756 	if (timecount < 1000)
757 		return;
758 
759         timetotal_acc += timetotal;
760         timecount_acc += timecount;
761         m = (int) (timetotal*10000/timecount);
762 	m_acc = (int) (timetotal_acc*10000/timecount_acc);
763 	timecount = 0;
764 	timetotal = 0;
765 	c = 0;
766 	for (i=0 ; i<svs.maxclients ; i++)
767 	{
768 		if (svs.clients[i].active)
769 			c++;
770 	}
771 
772 	Con_Printf ("serverprofile: %2i clients %2i msec\n",  c,  m/10);
773    LOGI("serverprofile: %2i clients %2i.%1i msec, acc = %2i.%1i msec\n",  c,  m/10, m%10, m_acc/10, m_acc%10);
774 }
775 
776 //============================================================================
777 
778 
779 extern int vcrFile;
780 #define	VCR_SIGNATURE	0x56435231
781 // "VCR1"
782 
Host_InitVCR(quakeparms_t * parms)783 void Host_InitVCR (quakeparms_t *parms)
784 {
785 	int		i, len, n;
786 	char	*p;
787 
788 	if (COM_CheckParm("-playback"))
789 	{
790 		if (com_argc != 2)
791 			Sys_Error("No other parameters allowed with -playback\n");
792 
793 		Sys_FileOpenRead("quake.vcr", &vcrFile);
794 		if (vcrFile == -1)
795 			Sys_Error("playback file not found\n");
796 
797 		Sys_FileRead (vcrFile, &i, sizeof(int));
798 		if (i != VCR_SIGNATURE)
799 			Sys_Error("Invalid signature in vcr file\n");
800 
801 		Sys_FileRead (vcrFile, &com_argc, sizeof(int));
802 		com_argv = (const char**) malloc(com_argc * sizeof(char *));
803 		com_argv[0] = parms->argv[0];
804 		for (i = 0; i < com_argc; i++)
805 		{
806 			Sys_FileRead (vcrFile, &len, sizeof(int));
807 			p = (char*) malloc(len);
808 			Sys_FileRead (vcrFile, p, len);
809 			com_argv[i+1] = p;
810 		}
811 		com_argc++; /* add one for arg[0] */
812 		parms->argc = com_argc;
813 		parms->argv = com_argv;
814 	}
815 
816 	if ( (n = COM_CheckParm("-record")) != 0)
817 	{
818 		vcrFile = Sys_FileOpenWrite("quake.vcr");
819 
820 		i = VCR_SIGNATURE;
821 		Sys_FileWrite(vcrFile, &i, sizeof(int));
822 		i = com_argc - 1;
823 		Sys_FileWrite(vcrFile, &i, sizeof(int));
824 		for (i = 1; i < com_argc; i++)
825 		{
826 			if (i == n)
827 			{
828 				len = 10;
829 				Sys_FileWrite(vcrFile, &len, sizeof(int));
830 				Sys_FileWrite(vcrFile, "-playback", len);
831 				continue;
832 			}
833 			len = Q_strlen(com_argv[i]) + 1;
834 			Sys_FileWrite(vcrFile, &len, sizeof(int));
835 			Sys_FileWrite(vcrFile, com_argv[i], len);
836 		}
837 	}
838 
839 }
840 
841 /*
842 ====================
843 Host_Init
844 ====================
845 */
Host_Init(quakeparms_t * parms)846 void Host_Init (quakeparms_t *parms)
847 {
848 
849 	if (standard_quake)
850 		minimum_memory = MINIMUM_MEMORY;
851 	else
852 		minimum_memory = MINIMUM_MEMORY_LEVELPAK;
853 
854 	if (COM_CheckParm ("-minmemory"))
855 		parms->memsize = minimum_memory;
856 
857 	host_parms = *parms;
858 
859 	if (parms->memsize < minimum_memory)
860 		Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms->memsize / (float)0x100000);
861 
862 	com_argc = parms->argc;
863 	com_argv = parms->argv;
864 
865 	Memory_Init (parms->membase, parms->memsize);
866 	Cbuf_Init ();
867 	Cmd_Init ();
868 	V_Init ();
869 	Chase_Init ();
870 	Host_InitVCR (parms);
871 	COM_Init (parms->basedir);
872 	Host_InitLocal ();
873 	W_LoadWadFile ("gfx.wad");
874 	Key_Init ();
875 	Con_Init ();
876 	M_Init ();
877 	PR_Init ();
878 	Mod_Init ();
879 	NET_Init ();
880 	SV_Init ();
881 
882 	Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
883 	Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
884 
885 	R_InitTextures ();		// needed even for dedicated servers
886 
887 	if (cls.state != ca_dedicated)
888 	{
889 		host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp");
890 		if (!host_basepal)
891 			Sys_Error ("Couldn't load gfx/palette.lmp");
892 		host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp");
893 		if (!host_colormap)
894 			Sys_Error ("Couldn't load gfx/colormap.lmp");
895 
896 #ifndef _WIN32 // on non win32, mouse comes before video for security reasons
897 		IN_Init ();
898 #endif
899 		VID_Init (host_basepal);
900 
901 		Draw_Init ();
902 		SCR_Init ();
903 		R_Init ();
904 
905 #ifndef _WIN32
906 	// on Win32, sound initialization has to come before video initialization, so we
907 	// can put up a popup if the sound hardware is in use
908 
909 	// Actually S_Init is called from inside VID_Init. So don't call here.
910 	// 	S_Init ();
911 #else
912 
913 #ifdef	GLQUAKE
914 	// FIXME: doesn't use the new one-window approach yet
915 		S_Init ();
916 #endif
917 
918 #endif	// _WIN32
919 		CDAudio_Init ();
920 		Sbar_Init ();
921 		CL_Init ();
922 #ifdef _WIN32 // on non win32, mouse comes before video for security reasons
923 		IN_Init ();
924 #endif
925 	}
926 
927 	Cbuf_InsertText ("exec quake.rc\n");
928 
929 	Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
930 	host_hunklevel = Hunk_LowMark ();
931 
932 	host_initialized = true;
933 
934 	Sys_Printf ("========Quake Initialized=========\n");
935 }
936 
937 
938 /*
939 ===============
940 Host_Shutdown
941 
942 FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
943 to run quit through here before the final handoff to the sys code.
944 ===============
945 */
Host_Shutdown(void)946 void Host_Shutdown(void)
947 {
948 	static qboolean isdown = false;
949 
950 	if (isdown)
951 	{
952 		printf ("recursive shutdown\n");
953 		return;
954 	}
955 	isdown = true;
956 
957 // keep Con_Printf from trying to update the screen
958 	scr_disabled_for_loading = true;
959 
960 	Host_WriteConfiguration ();
961 
962 	CDAudio_Shutdown ();
963 	NET_Shutdown ();
964 	S_Shutdown();
965 	IN_Shutdown ();
966 
967 	if (cls.state != ca_dedicated)
968 	{
969 		VID_Shutdown();
970 	}
971 }
972 
973