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