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