Home | History | Annotate | Download | only in WinQuake
      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 
     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.demoplayback = false;
     52     cls.demofile = NULL;
     53     cls.state = ca_disconnected;
     54 
     55     if (cls.timedemo)
     56         CL_FinishTimeDemo ();
     57 }
     58 
     59 /*
     60 ====================
     61 CL_WriteDemoMessage
     62 
     63 Dumps the current net message, prefixed by the length and view angles
     64 ====================
     65 */
     66 void CL_WriteDemoMessage (void)
     67 {
     68     int        len;
     69     int        i;
     70     float    f;
     71 
     72     len = LittleLong (net_message.cursize);
     73     fwrite (&len, 4, 1, cls.demofile);
     74     for (i=0 ; i<3 ; i++)
     75     {
     76         f = LittleFloat (cl.viewangles[i]);
     77         fwrite (&f, 4, 1, cls.demofile);
     78     }
     79     fwrite (net_message.data, net_message.cursize, 1, cls.demofile);
     80     fflush (cls.demofile);
     81 }
     82 
     83 /*
     84 ====================
     85 CL_GetMessage
     86 
     87 Handles recording and playback of demos, on top of NET_ code
     88 ====================
     89 */
     90 int CL_GetMessage (void)
     91 {
     92     int        r, i;
     93     float    f;
     94 
     95     if    (cls.demoplayback)
     96     {
     97     // decide if it is time to grab the next message
     98         if (cls.signon == SIGNONS)    // allways grab until fully connected
     99         {
    100             if (cls.timedemo)
    101             {
    102                 if (host_framecount == cls.td_lastframe)
    103                     return 0;        // allready read this frame's message
    104                 cls.td_lastframe = host_framecount;
    105             // if this is the second frame, grab the real td_starttime
    106             // so the bogus time on the first frame doesn't count
    107                 if (host_framecount == cls.td_startframe + 1)
    108                     cls.td_starttime = realtime;
    109             }
    110             else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0])
    111             {
    112                     return 0;        // don't need another message yet
    113             }
    114         }
    115 
    116     // get the next message
    117         fread (&net_message.cursize, 4, 1, cls.demofile);
    118         VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
    119         for (i=0 ; i<3 ; i++)
    120         {
    121             r = fread (&f, 4, 1, cls.demofile);
    122             cl.mviewangles[0][i] = LittleFloat (f);
    123         }
    124 
    125         net_message.cursize = LittleLong (net_message.cursize);
    126         if (net_message.cursize > MAX_MSGLEN)
    127             Sys_Error ("Demo message > MAX_MSGLEN");
    128         r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
    129         if (r != 1)
    130         {
    131             CL_StopPlayback ();
    132             return 0;
    133         }
    134 
    135         return 1;
    136     }
    137 
    138     while (1)
    139     {
    140         r = NET_GetMessage (cls.netcon);
    141 
    142         if (r != 1 && r != 2)
    143             return r;
    144 
    145     // discard nop keepalive message
    146         if (net_message.cursize == 1 && net_message.data[0] == svc_nop)
    147             Con_Printf ("<-- server to client keepalive\n");
    148         else
    149             break;
    150     }
    151 
    152     if (cls.demorecording)
    153         CL_WriteDemoMessage ();
    154 
    155     return r;
    156 }
    157 
    158 
    159 /*
    160 ====================
    161 CL_Stop_f
    162 
    163 stop recording a demo
    164 ====================
    165 */
    166 void CL_Stop_f (void)
    167 {
    168     if (cmd_source != src_command)
    169         return;
    170 
    171     if (!cls.demorecording)
    172     {
    173         Con_Printf ("Not recording a demo.\n");
    174         return;
    175     }
    176 
    177 // write a disconnect message to the demo file
    178     SZ_Clear (&net_message);
    179     MSG_WriteByte (&net_message, svc_disconnect);
    180     CL_WriteDemoMessage ();
    181 
    182 // finish up
    183     fclose (cls.demofile);
    184     cls.demofile = NULL;
    185     cls.demorecording = false;
    186     Con_Printf ("Completed demo\n");
    187 }
    188 
    189 /*
    190 ====================
    191 CL_Record_f
    192 
    193 record <demoname> <map> [cd track]
    194 ====================
    195 */
    196 void CL_Record_f (void)
    197 {
    198     int        c;
    199     char    name[MAX_OSPATH];
    200     int        track;
    201 
    202     if (cmd_source != src_command)
    203         return;
    204 
    205     c = Cmd_Argc();
    206     if (c != 2 && c != 3 && c != 4)
    207     {
    208         Con_Printf ("record <demoname> [<map> [cd track]]\n");
    209         return;
    210     }
    211 
    212     if (strstr(Cmd_Argv(1), ".."))
    213     {
    214         Con_Printf ("Relative pathnames are not allowed.\n");
    215         return;
    216     }
    217 
    218     if (c == 2 && cls.state == ca_connected)
    219     {
    220         Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
    221         return;
    222     }
    223 
    224 // write the forced cd track number, or -1
    225     if (c == 4)
    226     {
    227         track = atoi(Cmd_Argv(3));
    228         Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
    229     }
    230     else
    231         track = -1;
    232 
    233     sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
    234 
    235 //
    236 // start the map up
    237 //
    238     if (c > 2)
    239         Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
    240 
    241 //
    242 // open the demo file
    243 //
    244     COM_DefaultExtension (name, ".dem");
    245 
    246     Con_Printf ("recording to %s.\n", name);
    247     cls.demofile = fopen (name, "wb");
    248     if (!cls.demofile)
    249     {
    250         Con_Printf ("ERROR: couldn't open.\n");
    251         return;
    252     }
    253 
    254     cls.forcetrack = track;
    255     fprintf (cls.demofile, "%i\n", cls.forcetrack);
    256 
    257     cls.demorecording = true;
    258 }
    259 
    260 
    261 /*
    262 ====================
    263 CL_PlayDemo_f
    264 
    265 play [demoname]
    266 ====================
    267 */
    268 void CL_PlayDemo_f (void)
    269 {
    270     char    name[256];
    271     int c;
    272     qboolean neg = false;
    273 
    274     if (cmd_source != src_command)
    275         return;
    276 
    277     if (Cmd_Argc() > 2)
    278     {
    279         Con_Printf ("play <demoname> : plays a demo\n");
    280         return;
    281     }
    282 
    283 //
    284 // disconnect from server
    285 //
    286     CL_Disconnect ();
    287 
    288 //
    289 // open the demo file
    290 //
    291     const char* cmdName = "demo1";
    292     if (Cmd_Argc() == 2) {
    293         cmdName = Cmd_Argv(1);
    294     }
    295     strcpy (name, cmdName);
    296     COM_DefaultExtension (name, ".dem");
    297 
    298     Con_Printf ("Playing demo from %s.\n", name);
    299     COM_FOpenFile (name, &cls.demofile);
    300     if (!cls.demofile)
    301     {
    302         Con_Printf ("ERROR: couldn't open.\n");
    303         cls.demonum = -1;        // stop demo loop
    304         return;
    305     }
    306 
    307     cls.demoplayback = true;
    308     cls.state = ca_connected;
    309     cls.forcetrack = 0;
    310 
    311     while ((c = getc(cls.demofile)) != '\n')
    312         if (c == '-')
    313             neg = true;
    314         else
    315             cls.forcetrack = cls.forcetrack * 10 + (c - '0');
    316 
    317     if (neg)
    318         cls.forcetrack = -cls.forcetrack;
    319 // ZOID, fscanf is evil
    320 //    fscanf (cls.demofile, "%i\n", &cls.forcetrack);
    321 }
    322 
    323 // The timedemo numbers are very important to testing, so log them even if normal console printing is disabled.
    324 
    325 #define LOGANDPRINT(ARGS) Con_Printf ARGS ; PMPLOG(ARGS)
    326 
    327 /*
    328 ====================
    329 CL_FinishTimeDemo
    330 
    331 ====================
    332 */
    333 void CL_FinishTimeDemo (void)
    334 {
    335     int        frames;
    336     float    time;
    337 
    338     cls.timedemo = false;
    339 
    340 // the first frame didn't count
    341     frames = (host_framecount - cls.td_startframe) - 1;
    342     time = realtime - cls.td_starttime;
    343     if (!time)
    344         time = 1;
    345     LOGANDPRINT(("%i frames %5.3f seconds %5.3f fps\n", frames, time, frames/time));
    346     if (frames > 0)
    347     {
    348         LOGANDPRINT(("Fastest: %5.1f ms on frame %d\n", fastestFrame.time * 1000.0, fastestFrame.frame));
    349         LOGANDPRINT(("Average: %5.1f ms\n", (time / frames) * 1000.0));
    350         LOGANDPRINT(("Slowest: %5.1f ms on frame %d\n", slowestFrame.time * 1000.0, slowestFrame.frame));
    351     }
    352 }
    353 
    354 /*
    355 ====================
    356 CL_TimeDemo_f
    357 
    358 timedemo [demoname]
    359 ====================
    360 */
    361 void CL_TimeDemo_f (void)
    362 {
    363     if (cmd_source != src_command)
    364         return;
    365 
    366     if (Cmd_Argc() > 2)
    367     {
    368         Con_Printf ("timedemo <demoname> : gets demo speeds\n");
    369         return;
    370     }
    371 
    372     CL_PlayDemo_f ();
    373 
    374 // cls.td_starttime will be grabbed at the second frame of the demo, so
    375 // all the loading time doesn't get counted
    376 
    377     cls.timedemo = true;
    378     cls.td_startframe = host_framecount;
    379     cls.td_lastframe = -1;        // get a new message this frame
    380 
    381     InitFrameTimes();
    382 }
    383 
    384