home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 113 / EnigmaAmiga113CD.iso / software / sviluppo / glquake_src / host.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-02-28  |  20.5 KB  |  965 lines

  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 = {"host_framerate","0"};  // set for slow motion
  58. cvar_t  host_speeds = {"host_speeds","0"};      // set for running times
  59.  
  60. cvar_t  sys_ticrate = {"sys_ticrate","0.05"};
  61. cvar_t  serverprofile = {"serverprofile","0"};
  62.  
  63. cvar_t  fraglimit = {"fraglimit","0",false,true};
  64. cvar_t  timelimit = {"timelimit","0",false,true};
  65. cvar_t  teamplay = {"teamplay","0",false,true};
  66.  
  67. cvar_t  samelevel = {"samelevel","0"};
  68. cvar_t  noexit = {"noexit","0",false,true};
  69.  
  70. #ifdef QUAKE2
  71. cvar_t  developer = {"developer","1"};  // should be 0 for release!
  72. #else
  73. cvar_t  developer = {"developer","0"};
  74. #endif
  75.  
  76. cvar_t  skill = {"skill","1"};            // 0 - 3
  77. cvar_t  deathmatch = {"deathmatch","0"};      // 0, 1, or 2
  78. cvar_t  coop = {"coop","0"};      // 0 or 1
  79.  
  80. cvar_t  pausable = {"pausable","1"};
  81.  
  82. cvar_t  temp1 = {"temp1","0"};
  83.  
  84.  
  85. /*
  86. ================
  87. Host_EndGame
  88. ================
  89. */
  90. void Host_EndGame (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 (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 = 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 (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 (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 (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 = 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 = (time1 - time3)*1000;
  719.     time3 = Sys_FloatTime ();
  720.     pass2 = (time2 - time1)*1000;
  721.     pass3 = (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 = 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 = 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 = 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. #ifdef AMIGA
  886.     VID_Init(host_basepal);
  887.     // IN_Init needs mode_screen initialized !
  888.     IN_Init();
  889. #else
  890. #ifndef _WIN32 // on non win32, mouse comes before video for security reasons
  891.     IN_Init ();
  892. #endif
  893.     VID_Init (host_basepal);
  894. #endif
  895.  
  896.     Draw_Init ();
  897.     SCR_Init ();
  898.     R_Init ();
  899. #ifndef _WIN32
  900.   // on Win32, sound initialization has to come before video initialization, so we
  901.   // can put up a popup if the sound hardware is in use
  902.     S_Init ();
  903. #else
  904.  
  905. #ifdef  GLQUAKE
  906.   // FIXME: doesn't use the new one-window approach yet
  907.     S_Init ();
  908. #endif
  909.  
  910. #endif  // _WIN32
  911.     CDAudio_Init ();
  912.     Sbar_Init ();
  913.     CL_Init ();
  914. #ifdef _WIN32 // on non win32, mouse comes before video for security reasons
  915.     IN_Init ();
  916. #endif
  917.   }
  918.  
  919.   Cbuf_InsertText ("exec quake.rc\n");
  920.  
  921.   Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
  922.   host_hunklevel = Hunk_LowMark ();
  923.  
  924.   host_initialized = true;
  925.   
  926.   Sys_Printf ("========Quake Initialized=========\n");  
  927. }
  928.  
  929.  
  930. /*
  931. ===============
  932. Host_Shutdown
  933.  
  934. FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
  935. to run quit through here before the final handoff to the sys code.
  936. ===============
  937. */
  938. void Host_Shutdown(void)
  939. {
  940.   static qboolean isdown = false;
  941.   
  942.   if (isdown)
  943.   {
  944.     printf ("recursive shutdown\n");
  945.     return;
  946.   }
  947.   isdown = true;
  948.  
  949. // keep Con_Printf from trying to update the screen
  950.   scr_disabled_for_loading = true;
  951.  
  952.   Host_WriteConfiguration (); 
  953.  
  954.   CDAudio_Shutdown ();
  955.   NET_Shutdown ();
  956.   S_Shutdown();
  957.   IN_Shutdown ();
  958.  
  959.   if (cls.state != ca_dedicated)
  960.   {
  961.     VID_Shutdown();
  962.   }
  963. }
  964.  
  965.