home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 113 / EnigmaAmiga113CD.iso / software / sviluppo / quakeworld_src / client / cl_demo.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-17  |  17.9 KB  |  816 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 included (GNU.txt) 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.  
  21. #include "quakedef.h"
  22.  
  23. void CL_FinishTimeDemo (void);
  24.  
  25. /*
  26. ==============================================================================
  27.  
  28. DEMO CODE
  29.  
  30. When a demo is playing back, all NET_SendMessages are skipped, and
  31. NET_GetMessages are read from the demo file.
  32.  
  33. Whenever cl.time gets past the last received message, another message is
  34. read from the demo file.
  35. ==============================================================================
  36. */
  37.  
  38. /*
  39. ==============
  40. CL_StopPlayback
  41.  
  42. Called when a demo file runs out, or the user starts a game
  43. ==============
  44. */
  45. void CL_StopPlayback (void)
  46. {
  47.   if (!cls.demoplayback)
  48.     return;
  49.  
  50. #ifdef AMIGA
  51.   if (cls.demofile) fclose(cls.demofile);
  52.     cls.demofile=0;
  53. #else
  54.   fclose (cls.demofile);
  55. #endif
  56.   cls.demofile = NULL;
  57.   cls.state = ca_disconnected;
  58.   cls.demoplayback = 0;
  59.  
  60.   if (cls.timedemo)
  61.     CL_FinishTimeDemo ();
  62. }
  63.  
  64. #define dem_cmd   0
  65. #define dem_read  1
  66. #define dem_set   2
  67.  
  68. /*
  69. ====================
  70. CL_WriteDemoCmd
  71.  
  72. Writes the current user cmd
  73. ====================
  74. */
  75. void CL_WriteDemoCmd (usercmd_t *pcmd)
  76. {
  77.   int   i;
  78.   float fl;
  79.   byte  c;
  80.   usercmd_t cmd;
  81.  
  82. //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
  83.  
  84.   fl = LittleFloat((float)realtime);
  85.   fwrite (&fl, sizeof(fl), 1, cls.demofile);
  86.  
  87.   c = dem_cmd;
  88.   fwrite (&c, sizeof(c), 1, cls.demofile);
  89.  
  90.   // correct for byte order, bytes don't matter
  91.   cmd = *pcmd;
  92.  
  93.   for (i = 0; i < 3; i++)
  94.     cmd.angles[i] = LittleFloat(cmd.angles[i]);
  95.   cmd.forwardmove = LittleShort(cmd.forwardmove);
  96.   cmd.sidemove    = LittleShort(cmd.sidemove);
  97.   cmd.upmove      = LittleShort(cmd.upmove);
  98.  
  99.   fwrite(&cmd, sizeof(cmd), 1, cls.demofile);
  100.  
  101.   for (i=0 ; i<3 ; i++)
  102.   {
  103.     fl = LittleFloat (cl.viewangles[i]);
  104.     fwrite (&fl, 4, 1, cls.demofile);
  105.   }
  106.  
  107.   fflush (cls.demofile);
  108. }
  109.  
  110. /*
  111. ====================
  112. CL_WriteDemoMessage
  113.  
  114. Dumps the current net message, prefixed by the length and view angles
  115. ====================
  116. */
  117. void CL_WriteDemoMessage (sizebuf_t *msg)
  118. {
  119.   int   len;
  120.   float fl;
  121.   byte  c;
  122.  
  123. //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
  124.  
  125.   if (!cls.demorecording)
  126.     return;
  127.  
  128.   fl = LittleFloat((float)realtime);
  129.   fwrite (&fl, sizeof(fl), 1, cls.demofile);
  130.  
  131.   c = dem_read;
  132.   fwrite (&c, sizeof(c), 1, cls.demofile);
  133.  
  134.   len = LittleLong (msg->cursize);
  135.   fwrite (&len, 4, 1, cls.demofile);
  136.   fwrite (msg->data, msg->cursize, 1, cls.demofile);
  137.  
  138.   fflush (cls.demofile);
  139. }
  140.  
  141. /*
  142. ====================
  143. CL_GetDemoMessage
  144.  
  145.   FIXME...
  146. ====================
  147. */
  148. qboolean CL_GetDemoMessage (void)
  149. {
  150.   int   r, i, j;
  151.   float f;
  152.   float demotime;
  153.   byte  c;
  154.   usercmd_t *pcmd;
  155.  
  156.   // read the time from the packet
  157.   fread(&demotime, sizeof(demotime), 1, cls.demofile);
  158.   demotime = LittleFloat(demotime);
  159.  
  160. // decide if it is time to grab the next message    
  161.   if (cls.timedemo) {
  162.     if (cls.td_lastframe < 0)
  163.       cls.td_lastframe = demotime;
  164.     else if (demotime > cls.td_lastframe) {
  165.       cls.td_lastframe = demotime;
  166.       // rewind back to time
  167.       fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
  168.           SEEK_SET);
  169.       return 0;   // allready read this frame's message
  170.     }
  171.     if (!cls.td_starttime && cls.state == ca_active) {
  172.       cls.td_starttime = Sys_DoubleTime();
  173.       cls.td_startframe = host_framecount;
  174.     }
  175.     realtime = demotime; // warp
  176.   } else if (!cl.paused && cls.state >= ca_onserver) {  // allways grab until fully connected
  177.     if (realtime + 1.0 < demotime) {
  178.       // too far back
  179.       realtime = demotime - 1.0;
  180.       // rewind back to time
  181.       fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
  182.           SEEK_SET);
  183.       return 0;
  184.     } else if (realtime < demotime) {
  185.       // rewind back to time
  186.       fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
  187.           SEEK_SET);
  188.       return 0;   // don't need another message yet
  189.     }
  190.   } else
  191.     realtime = demotime; // we're warping
  192.  
  193.   if (cls.state < ca_demostart)
  194.     Host_Error ("CL_GetDemoMessage: cls.state != ca_active");
  195.   
  196.   // get the msg type
  197.   fread (&c, sizeof(c), 1, cls.demofile);
  198.   
  199.   switch (c) {
  200.   case dem_cmd :
  201.     // user sent input
  202.     i = cls.netchan.outgoing_sequence & UPDATE_MASK;
  203.     pcmd = &cl.frames[i].cmd;
  204.     r = fread (pcmd, sizeof(*pcmd), 1, cls.demofile);
  205.     if (r != 1)
  206.     {
  207.       CL_StopPlayback ();
  208.       return 0;
  209.     }
  210.     // byte order stuff
  211.     for (j = 0; j < 3; j++)
  212.       pcmd->angles[j] = LittleFloat(pcmd->angles[j]);
  213.     pcmd->forwardmove = LittleShort(pcmd->forwardmove);
  214.     pcmd->sidemove    = LittleShort(pcmd->sidemove);
  215.     pcmd->upmove      = LittleShort(pcmd->upmove);
  216.     cl.frames[i].senttime = demotime;
  217.     cl.frames[i].receivedtime = -1;   // we haven't gotten a reply yet
  218.     cls.netchan.outgoing_sequence++;
  219.     for (i=0 ; i<3 ; i++)
  220.     {
  221.       r = fread (&f, 4, 1, cls.demofile);
  222.       cl.viewangles[i] = LittleFloat (f);
  223.     }
  224.     break;
  225.  
  226.   case dem_read:
  227.     // get the next message
  228.     fread (&net_message.cursize, 4, 1, cls.demofile);
  229.     net_message.cursize = LittleLong (net_message.cursize);
  230.   //Con_Printf("read: %ld bytes\n", net_message.cursize);
  231.     if (net_message.cursize > MAX_MSGLEN)
  232.       Sys_Error ("Demo message > MAX_MSGLEN");
  233.     r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
  234.     if (r != 1)
  235.     {
  236.       CL_StopPlayback ();
  237.       return 0;
  238.     }
  239.     break;
  240.  
  241.   case dem_set :
  242.     fread (&i, 4, 1, cls.demofile);
  243.     cls.netchan.outgoing_sequence = LittleLong(i);
  244.     fread (&i, 4, 1, cls.demofile);
  245.     cls.netchan.incoming_sequence = LittleLong(i);
  246.     break;
  247.  
  248.   default :
  249.     Con_Printf("Corrupted demo.\n");
  250.     CL_StopPlayback ();
  251.     return 0;
  252.   }
  253.  
  254.   return 1;
  255. }
  256.  
  257. /*
  258. ====================
  259. CL_GetMessage
  260.  
  261. Handles recording and playback of demos, on top of NET_ code
  262. ====================
  263. */
  264. qboolean CL_GetMessage (void)
  265. {
  266.   if  (cls.demoplayback)
  267.     return CL_GetDemoMessage ();
  268.  
  269.   if (!NET_GetPacket ())
  270.     return false;
  271.  
  272.   CL_WriteDemoMessage (&net_message);
  273.   
  274.   return true;
  275. }
  276.  
  277.  
  278. /*
  279. ====================
  280. CL_Stop_f
  281.  
  282. stop recording a demo
  283. ====================
  284. */
  285. void CL_Stop_f (void)
  286. {
  287.   if (!cls.demorecording)
  288.   {
  289.     Con_Printf ("Not recording a demo.\n");
  290.     return;
  291.   }
  292.  
  293. // write a disconnect message to the demo file
  294.   SZ_Clear (&net_message);
  295.   MSG_WriteLong (&net_message, -1); // -1 sequence means out of band
  296.   MSG_WriteByte (&net_message, svc_disconnect);
  297.   MSG_WriteString (&net_message, "EndOfDemo");
  298.   CL_WriteDemoMessage (&net_message);
  299.  
  300. // finish up
  301. #ifdef AMIGA
  302.   if (cls.demofile) fclose(cls.demofile);
  303.     cls.demofile=0;
  304. #else
  305.   fclose (cls.demofile);
  306. #endif
  307.   cls.demofile = NULL;
  308.   cls.demorecording = false;
  309.   Con_Printf ("Completed demo\n");
  310. }
  311.  
  312.  
  313. /*
  314. ====================
  315. CL_WriteDemoMessage
  316.  
  317. Dumps the current net message, prefixed by the length and view angles
  318. ====================
  319. */
  320. void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq)
  321. {
  322.   int   len;
  323.   int   i;
  324.   float fl;
  325.   byte  c;
  326.  
  327. //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
  328.  
  329.   if (!cls.demorecording)
  330.     return;
  331.  
  332.   fl = LittleFloat((float)realtime);
  333.   fwrite (&fl, sizeof(fl), 1, cls.demofile);
  334.  
  335.   c = dem_read;
  336.   fwrite (&c, sizeof(c), 1, cls.demofile);
  337.  
  338.   len = LittleLong (msg->cursize + 8);
  339.   fwrite (&len, 4, 1, cls.demofile);
  340.  
  341.   i = LittleLong(seq);
  342.   fwrite (&i, 4, 1, cls.demofile);
  343.   fwrite (&i, 4, 1, cls.demofile);
  344.  
  345.   fwrite (msg->data, msg->cursize, 1, cls.demofile);
  346.  
  347.   fflush (cls.demofile);
  348. }
  349.  
  350.  
  351. void CL_WriteSetDemoMessage (void)
  352. {
  353.   int   len;
  354.   float fl;
  355.   byte  c;
  356.  
  357. //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
  358.  
  359.   if (!cls.demorecording)
  360.     return;
  361.  
  362.   fl = LittleFloat((float)realtime);
  363.   fwrite (&fl, sizeof(fl), 1, cls.demofile);
  364.  
  365.   c = dem_set;
  366.   fwrite (&c, sizeof(c), 1, cls.demofile);
  367.  
  368.   len = LittleLong(cls.netchan.outgoing_sequence);
  369.   fwrite (&len, 4, 1, cls.demofile);
  370.   len = LittleLong(cls.netchan.incoming_sequence);
  371.   fwrite (&len, 4, 1, cls.demofile);
  372.  
  373.   fflush (cls.demofile);
  374. }
  375.  
  376.  
  377.  
  378.  
  379. /*
  380. ====================
  381. CL_Record_f
  382.  
  383. record <demoname> <server>
  384. ====================
  385. */
  386. void CL_Record_f (void)
  387. {
  388.   int   c;
  389.   char  name[MAX_OSPATH];
  390.   sizebuf_t buf;
  391.   char  buf_data[MAX_MSGLEN];
  392.   int n, i, j;
  393.   char *s;
  394.   entity_t *ent;
  395.   entity_state_t *es, blankes;
  396.   player_info_t *player;
  397.   extern  char gamedirfile[];
  398.   int seq = 1;
  399.  
  400.   c = Cmd_Argc();
  401.   if (c != 2)
  402.   {
  403.     Con_Printf ("record <demoname>\n");
  404.     return;
  405.   }
  406.  
  407.   if (cls.state != ca_active) {
  408.     Con_Printf ("You must be connected to record.\n");
  409.     return;
  410.   }
  411.  
  412.   if (cls.demorecording)
  413.     CL_Stop_f();
  414.   
  415.   sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
  416.  
  417. //
  418. // open the demo file
  419. //
  420.   COM_DefaultExtension (name, ".qwd");
  421.  
  422.   cls.demofile = fopen (name, "wb");
  423.   if (!cls.demofile)
  424.   {
  425.     Con_Printf ("ERROR: couldn't open.\n");
  426.     return;
  427.   }
  428.  
  429.   Con_Printf ("recording to %s.\n", name);
  430.   cls.demorecording = true;
  431.  
  432. /*-------------------------------------------------*/
  433.  
  434. // serverdata
  435.   // send the info about the new client to all connected clients
  436.   memset(&buf, 0, sizeof(buf));
  437.   buf.data = buf_data;
  438.   buf.maxsize = sizeof(buf_data);
  439.  
  440. // send the serverdata
  441.   MSG_WriteByte (&buf, svc_serverdata);
  442.   MSG_WriteLong (&buf, PROTOCOL_VERSION);
  443.   MSG_WriteLong (&buf, cl.servercount);
  444.   MSG_WriteString (&buf, gamedirfile);
  445.  
  446.   if (cl.spectator)
  447.     MSG_WriteByte (&buf, cl.playernum | 128);
  448.   else
  449.     MSG_WriteByte (&buf, cl.playernum);
  450.  
  451.   // send full levelname
  452.   MSG_WriteString (&buf, cl.levelname);
  453.  
  454.   // send the movevars
  455.   MSG_WriteFloat(&buf, movevars.gravity);
  456.   MSG_WriteFloat(&buf, movevars.stopspeed);
  457.   MSG_WriteFloat(&buf, movevars.maxspeed);
  458.   MSG_WriteFloat(&buf, movevars.spectatormaxspeed);
  459.   MSG_WriteFloat(&buf, movevars.accelerate);
  460.   MSG_WriteFloat(&buf, movevars.airaccelerate);
  461.   MSG_WriteFloat(&buf, movevars.wateraccelerate);
  462.   MSG_WriteFloat(&buf, movevars.friction);
  463.   MSG_WriteFloat(&buf, movevars.waterfriction);
  464.   MSG_WriteFloat(&buf, movevars.entgravity);
  465.  
  466.   // send music
  467.   MSG_WriteByte (&buf, svc_cdtrack);
  468.   MSG_WriteByte (&buf, 0); // none in demos
  469.  
  470.   // send server info string
  471.   MSG_WriteByte (&buf, svc_stufftext);
  472.   MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) );
  473.  
  474.   // flush packet
  475.   CL_WriteRecordDemoMessage (&buf, seq++);
  476.   SZ_Clear (&buf); 
  477.  
  478. // soundlist
  479.   MSG_WriteByte (&buf, svc_soundlist);
  480.   MSG_WriteByte (&buf, 0);
  481.  
  482.   n = 0;
  483.   s = cl.sound_name[n+1];
  484.   while (*s) {
  485.     MSG_WriteString (&buf, s);
  486.     if (buf.cursize > MAX_MSGLEN/2) {
  487.       MSG_WriteByte (&buf, 0);
  488.       MSG_WriteByte (&buf, n);
  489.       CL_WriteRecordDemoMessage (&buf, seq++);
  490.       SZ_Clear (&buf); 
  491.       MSG_WriteByte (&buf, svc_soundlist);
  492.       MSG_WriteByte (&buf, n + 1);
  493.     }
  494.     n++;
  495.     s = cl.sound_name[n+1];
  496.   }
  497.   if (buf.cursize) {
  498.     MSG_WriteByte (&buf, 0);
  499.     MSG_WriteByte (&buf, 0);
  500.     CL_WriteRecordDemoMessage (&buf, seq++);
  501.     SZ_Clear (&buf); 
  502.   }
  503.  
  504. // modellist
  505.   MSG_WriteByte (&buf, svc_modellist);
  506.   MSG_WriteByte (&buf, 0);
  507.  
  508.   n = 0;
  509.   s = cl.model_name[n+1];
  510.   while (*s) {
  511.     MSG_WriteString (&buf, s);
  512.     if (buf.cursize > MAX_MSGLEN/2) {
  513.       MSG_WriteByte (&buf, 0);
  514.       MSG_WriteByte (&buf, n);
  515.       CL_WriteRecordDemoMessage (&buf, seq++);
  516.       SZ_Clear (&buf); 
  517.       MSG_WriteByte (&buf, svc_modellist);
  518.       MSG_WriteByte (&buf, n + 1);
  519.     }
  520.     n++;
  521.     s = cl.model_name[n+1];
  522.   }
  523.   if (buf.cursize) {
  524.     MSG_WriteByte (&buf, 0);
  525.     MSG_WriteByte (&buf, 0);
  526.     CL_WriteRecordDemoMessage (&buf, seq++);
  527.     SZ_Clear (&buf); 
  528.   }
  529.  
  530. // spawnstatic
  531.  
  532.   for (i = 0; i < cl.num_statics; i++) {
  533.     ent = cl_static_entities + i;
  534.  
  535.     MSG_WriteByte (&buf, svc_spawnstatic);
  536.  
  537.     for (j = 1; j < MAX_MODELS; j++)
  538.       if (ent->model == cl.model_precache[j])
  539.         break;
  540.     if (j == MAX_MODELS)
  541.       MSG_WriteByte (&buf, 0);
  542.     else
  543.       MSG_WriteByte (&buf, j);
  544.  
  545.     MSG_WriteByte (&buf, ent->frame);
  546.     MSG_WriteByte (&buf, 0);
  547.     MSG_WriteByte (&buf, ent->skinnum);
  548.     for (j=0 ; j<3 ; j++)
  549.     {
  550.       MSG_WriteCoord (&buf, ent->origin[j]);
  551.       MSG_WriteAngle (&buf, ent->angles[j]);
  552.     }
  553.  
  554.     if (buf.cursize > MAX_MSGLEN/2) {
  555.       CL_WriteRecordDemoMessage (&buf, seq++);
  556.       SZ_Clear (&buf); 
  557.     }
  558.   }
  559.  
  560. // spawnstaticsound
  561.   // static sounds are skipped in demos, life is hard
  562.  
  563. // baselines
  564.  
  565.   memset(&blankes, 0, sizeof(blankes));
  566.   for (i = 0; i < MAX_EDICTS; i++) {
  567.     es = cl_baselines + i;
  568.  
  569.     if (memcmp(es, &blankes, sizeof(blankes))) {
  570.       MSG_WriteByte (&buf,svc_spawnbaseline);   
  571.       MSG_WriteShort (&buf, i);
  572.  
  573.       MSG_WriteByte (&buf, es->modelindex);
  574.       MSG_WriteByte (&buf, es->frame);
  575.       MSG_WriteByte (&buf, es->colormap);
  576.       MSG_WriteByte (&buf, es->skinnum);
  577.       for (j=0 ; j<3 ; j++)
  578.       {
  579.         MSG_WriteCoord(&buf, es->origin[j]);
  580.         MSG_WriteAngle(&buf, es->angles[j]);
  581.       }
  582.  
  583.       if (buf.cursize > MAX_MSGLEN/2) {
  584.         CL_WriteRecordDemoMessage (&buf, seq++);
  585.         SZ_Clear (&buf); 
  586.       }
  587.     }
  588.   }
  589.  
  590.   MSG_WriteByte (&buf, svc_stufftext);
  591.   MSG_WriteString (&buf, va("cmd spawn %i 0\n", cl.servercount) );
  592.  
  593.   if (buf.cursize) {
  594.     CL_WriteRecordDemoMessage (&buf, seq++);
  595.     SZ_Clear (&buf); 
  596.   }
  597.  
  598. // send current status of all other players
  599.  
  600.   for (i = 0; i < MAX_CLIENTS; i++) {
  601.     player = cl.players + i;
  602.  
  603.     MSG_WriteByte (&buf, svc_updatefrags);
  604.     MSG_WriteByte (&buf, i);
  605.     MSG_WriteShort (&buf, player->frags);
  606.     
  607.     MSG_WriteByte (&buf, svc_updateping);
  608.     MSG_WriteByte (&buf, i);
  609.     MSG_WriteShort (&buf, player->ping);
  610.     
  611.     MSG_WriteByte (&buf, svc_updatepl);
  612.     MSG_WriteByte (&buf, i);
  613.     MSG_WriteByte (&buf, player->pl);
  614.     
  615.     MSG_WriteByte (&buf, svc_updateentertime);
  616.     MSG_WriteByte (&buf, i);
  617.     MSG_WriteFloat (&buf, player->entertime);
  618.  
  619.     MSG_WriteByte (&buf, svc_updateuserinfo);
  620.     MSG_WriteByte (&buf, i);
  621.     MSG_WriteLong (&buf, player->userid);
  622.     MSG_WriteString (&buf, player->userinfo);
  623.  
  624.     if (buf.cursize > MAX_MSGLEN/2) {
  625.       CL_WriteRecordDemoMessage (&buf, seq++);
  626.       SZ_Clear (&buf); 
  627.     }
  628.   }
  629.   
  630. // send all current light styles
  631.   for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
  632.   {
  633.     MSG_WriteByte (&buf, svc_lightstyle);
  634.     MSG_WriteByte (&buf, (char)i);
  635.     MSG_WriteString (&buf, cl_lightstyle[i].map);
  636.   }
  637.  
  638.   for (i = 0; i < MAX_CL_STATS; i++) {
  639.     MSG_WriteByte (&buf, svc_updatestatlong);
  640.     MSG_WriteByte (&buf, i);
  641.     MSG_WriteLong (&buf, cl.stats[i]);
  642.     if (buf.cursize > MAX_MSGLEN/2) {
  643.       CL_WriteRecordDemoMessage (&buf, seq++);
  644.       SZ_Clear (&buf); 
  645.     }
  646.   }
  647.  
  648. #if 0
  649.   MSG_WriteByte (&buf, svc_updatestatlong);
  650.   MSG_WriteByte (&buf, STAT_TOTALMONSTERS);
  651.   MSG_WriteLong (&buf, cl.stats[STAT_TOTALMONSTERS]);
  652.  
  653.   MSG_WriteByte (&buf, svc_updatestatlong);
  654.   MSG_WriteByte (&buf, STAT_SECRETS);
  655.   MSG_WriteLong (&buf, cl.stats[STAT_SECRETS]);
  656.  
  657.   MSG_WriteByte (&buf, svc_updatestatlong);
  658.   MSG_WriteByte (&buf, STAT_MONSTERS);
  659.   MSG_WriteLong (&buf, cl.stats[STAT_MONSTERS]);
  660. #endif
  661.  
  662.   // get the client to check and download skins
  663.   // when that is completed, a begin command will be issued
  664.   MSG_WriteByte (&buf, svc_stufftext);
  665.   MSG_WriteString (&buf, va("skins\n") );
  666.  
  667.   CL_WriteRecordDemoMessage (&buf, seq++);
  668.  
  669.   CL_WriteSetDemoMessage();
  670.  
  671.   // done
  672. }
  673.  
  674. /*
  675. ====================
  676. CL_ReRecord_f
  677.  
  678. record <demoname>
  679. ====================
  680. */
  681. void CL_ReRecord_f (void)
  682. {
  683.   int   c;
  684.   char  name[MAX_OSPATH];
  685.  
  686.   c = Cmd_Argc();
  687.   if (c != 2)
  688.   {
  689.     Con_Printf ("rerecord <demoname>\n");
  690.     return;
  691.   }
  692.  
  693.   if (!*cls.servername) {
  694.     Con_Printf("No server to reconnect to...\n");
  695.     return;
  696.   }
  697.  
  698.   if (cls.demorecording)
  699.     CL_Stop_f();
  700.   
  701.   sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
  702.  
  703. //
  704. // open the demo file
  705. //
  706.   COM_DefaultExtension (name, ".qwd");
  707.  
  708.   cls.demofile = fopen (name, "wb");
  709.   if (!cls.demofile)
  710.   {
  711.     Con_Printf ("ERROR: couldn't open.\n");
  712.     return;
  713.   }
  714.  
  715.   Con_Printf ("recording to %s.\n", name);
  716.   cls.demorecording = true;
  717.  
  718.   CL_Disconnect();
  719.   CL_BeginServerConnect();
  720. }
  721.  
  722.  
  723. /*
  724. ====================
  725. CL_PlayDemo_f
  726.  
  727. play [demoname]
  728. ====================
  729. */
  730. void CL_PlayDemo_f (void)
  731. {
  732.   char  name[256];
  733.  
  734.   if (Cmd_Argc() != 2)
  735.   {
  736.     Con_Printf ("play <demoname> : plays a demo\n");
  737.     return;
  738.   }
  739.  
  740. //
  741. // disconnect from server
  742. //
  743.   CL_Disconnect ();
  744.   
  745. //
  746. // open the demo file
  747. //
  748.   strcpy (name, Cmd_Argv(1));
  749.   COM_DefaultExtension (name, ".qwd");
  750.  
  751.   Con_Printf ("Playing demo from %s.\n", name);
  752.   COM_FOpenFile (name, &cls.demofile);
  753.   if (!cls.demofile)
  754.   {
  755.     Con_Printf ("ERROR: couldn't open.\n");
  756.     cls.demonum = -1;   // stop demo loop
  757.     return;
  758.   }
  759.  
  760.   cls.demoplayback = true;
  761.   cls.state = ca_demostart;
  762.   Netchan_Setup (&cls.netchan, net_from, 0);
  763.   realtime = 0;
  764. }
  765.  
  766. /*
  767. ====================
  768. CL_FinishTimeDemo
  769.  
  770. ====================
  771. */
  772. void CL_FinishTimeDemo (void)
  773. {
  774.   int   frames;
  775.   float time;
  776.   
  777.   cls.timedemo = false;
  778.   
  779. // the first frame didn't count
  780.   frames = (host_framecount - cls.td_startframe) - 1;
  781.   time = Sys_DoubleTime() - cls.td_starttime;
  782.   if (!time)
  783.     time = 1;
  784.   Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time);
  785. }
  786.  
  787. /*
  788. ====================
  789. CL_TimeDemo_f
  790.  
  791. timedemo [demoname]
  792. ====================
  793. */
  794. void CL_TimeDemo_f (void)
  795. {
  796.   if (Cmd_Argc() != 2)
  797.   {
  798.     Con_Printf ("timedemo <demoname> : gets demo speeds\n");
  799.     return;
  800.   }
  801.  
  802.   CL_PlayDemo_f ();
  803.   
  804.   if (cls.state != ca_demostart)
  805.     return;
  806.  
  807. // cls.td_starttime will be grabbed at the second frame of the demo, so
  808. // all the loading time doesn't get counted
  809.   
  810.   cls.timedemo = true;
  811.   cls.td_starttime = 0;
  812.   cls.td_startframe = host_framecount;
  813.   cls.td_lastframe = -1;    // get a new message this frame
  814. }
  815.  
  816.