Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  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. #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. #ifndef _WIN32
  894.         // on Win32, sound initialization has to come before video initialization, so we
  895.         // can put up a popup if the sound hardware is in use
  896.                 S_Init ();
  897. #else
  898.  
  899. #ifdef  GLQUAKE
  900.         // FIXME: doesn't use the new one-window approach yet
  901.                 S_Init ();
  902. #endif
  903.  
  904. #endif  // _WIN32
  905.                 CDAudio_Init ();
  906.                 Sbar_Init ();
  907.                 CL_Init ();
  908. #ifdef _WIN32 // on non win32, mouse comes before video for security reasons
  909.                 IN_Init ();
  910. #endif
  911.         }
  912.  
  913.         Cbuf_InsertText ("exec quake.rc\n");
  914.  
  915.         Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
  916.         host_hunklevel = Hunk_LowMark ();
  917.  
  918.         host_initialized = true;
  919.        
  920.         Sys_Printf ("========Quake Initialized=========\n");   
  921. }
  922.  
  923.  
  924. /*
  925. ===============
  926. Host_Shutdown
  927.  
  928. FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
  929. to run quit through here before the final handoff to the sys code.
  930. ===============
  931. */
  932. void Host_Shutdown(void)
  933. {
  934.         static qboolean isdown = false;
  935.        
  936.         if (isdown)
  937.         {
  938.                 printf ("recursive shutdown\n");
  939.                 return;
  940.         }
  941.         isdown = true;
  942.  
  943. // keep Con_Printf from trying to update the screen
  944.         scr_disabled_for_loading = true;
  945.  
  946.         Host_WriteConfiguration ();
  947.  
  948.         CDAudio_Shutdown ();
  949.         NET_Shutdown ();
  950.         S_Shutdown();
  951.         IN_Shutdown ();
  952.  
  953.         if (cls.state != ca_dedicated)
  954.         {
  955.                 VID_Shutdown();
  956.         }
  957. }
  958.  
  959.