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