Home | History | Annotate | Download | only in jni
      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 */
     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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 
    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 
    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 
    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 */
    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 
    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 
    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 */
    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 */
    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