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