]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_demo.c
close packs when clearing search path
[xonotic/darkplaces.git] / cl_demo.c
index 2df5b97fd50c88ce12516cea5f4103af0bb9f132..2485afb908de566dd40700ba0af69b276bff5918 100644 (file)
--- a/cl_demo.c
+++ b/cl_demo.c
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
 
 See the GNU General Public License for more details.
 
@@ -20,6 +20,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 
 
 #include "quakedef.h"
 
+extern cvar_t cl_capturevideo;
+int old_vsync = 0;
+
 void CL_FinishTimeDemo (void);
 
 /*
 void CL_FinishTimeDemo (void);
 
 /*
@@ -27,8 +30,8 @@ void CL_FinishTimeDemo (void);
 
 DEMO CODE
 
 
 DEMO CODE
 
-When a demo is playing back, all NET_SendMessages are skipped, and
-NET_GetMessages are read from the demo file.
+When a demo is playing back, all outgoing network messages are skipped, and
+incoming messages are read from the demo file.
 
 Whenever cl.time gets past the last received message, another message is
 read from the demo file.
 
 Whenever cl.time gets past the last received message, another message is
 read from the demo file.
@@ -44,7 +47,7 @@ Called to play the next demo in the demo loop
 */
 void CL_NextDemo (void)
 {
 */
 void CL_NextDemo (void)
 {
-       char    str[1024];
+       char    str[MAX_INPUTLINE];
 
        if (cls.demonum == -1)
                return;         // don't play demos
 
        if (cls.demonum == -1)
                return;         // don't play demos
@@ -54,7 +57,7 @@ void CL_NextDemo (void)
                cls.demonum = 0;
                if (!cls.demos[cls.demonum][0])
                {
                cls.demonum = 0;
                if (!cls.demos[cls.demonum][0])
                {
-                       Con_Print("No demos listed with startdemos\n");
+                       Con_Print("No demos listed with startdemos\n");
                        cls.demonum = -1;
                        return;
                }
                        cls.demonum = -1;
                        return;
                }
@@ -84,6 +87,10 @@ void CL_StopPlayback (void)
 
        if (cls.timedemo)
                CL_FinishTimeDemo ();
 
        if (cls.timedemo)
                CL_FinishTimeDemo ();
+
+       if (COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
+               Host_Quit_f();
+
 }
 
 /*
 }
 
 /*
@@ -93,7 +100,7 @@ CL_WriteDemoMessage
 Dumps the current net message, prefixed by the length and view angles
 ====================
 */
 Dumps the current net message, prefixed by the length and view angles
 ====================
 */
-void CL_WriteDemoMessage (void)
+void CL_WriteDemoMessage (sizebuf_t *message)
 {
        int             len;
        int             i;
 {
        int             len;
        int             i;
@@ -102,93 +109,120 @@ void CL_WriteDemoMessage (void)
        if (cls.demopaused) // LordHavoc: pausedemo
                return;
 
        if (cls.demopaused) // LordHavoc: pausedemo
                return;
 
-       len = LittleLong (net_message.cursize);
+       len = LittleLong (message->cursize);
        FS_Write (cls.demofile, &len, 4);
        for (i=0 ; i<3 ; i++)
        {
                f = LittleFloat (cl.viewangles[i]);
                FS_Write (cls.demofile, &f, 4);
        }
        FS_Write (cls.demofile, &len, 4);
        for (i=0 ; i<3 ; i++)
        {
                f = LittleFloat (cl.viewangles[i]);
                FS_Write (cls.demofile, &f, 4);
        }
-       FS_Write (cls.demofile, net_message.data, net_message.cursize);
-       FS_Flush (cls.demofile);
+       FS_Write (cls.demofile, message->data, message->cursize);
 }
 
 /*
 ====================
 }
 
 /*
 ====================
-CL_GetMessage
+CL_ReadDemoMessage
 
 
-Handles recording and playback of demos, on top of NET_ code
+Handles playback of demos
 ====================
 */
 ====================
 */
-int CL_GetMessage (void)
+void CL_ReadDemoMessage(void)
 {
 {
-       int             r, i;
-       float   f;
-       
-       if      (cls.demoplayback)
-       {
-               if (cls.demopaused) // LordHavoc: pausedemo
-                       return 0;
+       int r, i;
+       float f;
+
+       if (!cls.demoplayback)
+               return;
+
+       // LordHavoc: pausedemo
+       if (cls.demopaused)
+               return;
 
 
-       // decide if it is time to grab the next message                
-               if (cls.signon == SIGNONS)      // always grab until fully connected
+       while (1)
+       {
+               // decide if it is time to grab the next message
+               // always grab until fully connected
+               if (cls.signon == SIGNONS)
                {
                        if (cls.timedemo)
                        {
                                if (host_framecount == cls.td_lastframe)
                {
                        if (cls.timedemo)
                        {
                                if (host_framecount == cls.td_lastframe)
-                                       return 0;               // already read this frame's message
+                               {
+                                       // already read this frame's message
+                                       return;
+                               }
+                               if (cls.td_lastframe == -1)
+                               {
+                                       // render a couple frames before we start counting
+                                       cls.td_startframe = host_framecount + 3;
+                               }
                                cls.td_lastframe = host_framecount;
                                cls.td_lastframe = host_framecount;
-                       // if this is the second frame, grab the real td_starttime
-                       // so the bogus time on the first frame doesn't count
-                               if (host_framecount == cls.td_startframe + 1)
+                               cls.td_onesecondframes++;
+                               // don't read any new messages during the warm-up period
+                               if (host_framecount < cls.td_startframe)
+                                       return;
+                               // if this is the first official frame we can now grab the real
+                               // td_starttime so the bogus time on the first frame doesn't
+                               // count against the final report
+                               if (host_framecount == cls.td_startframe)
+                               {
                                        cls.td_starttime = realtime;
                                        cls.td_starttime = realtime;
+                                       cls.td_onesecondnexttime = realtime + 1;
+                                       cls.td_onesecondframes = 0;
+                                       cls.td_onesecondminframes = 0;
+                                       cls.td_onesecondmaxframes = 0;
+                                       cls.td_onesecondavgframes = 0;
+                                       cls.td_onesecondavgcount = 0;
+                               }
+                               if (realtime >= cls.td_onesecondnexttime)
+                               {
+                                       if (cls.td_onesecondavgcount == 0)
+                                       {
+                                               cls.td_onesecondminframes = cls.td_onesecondframes;
+                                               cls.td_onesecondmaxframes = cls.td_onesecondframes;
+                                       }
+                                       cls.td_onesecondminframes = min(cls.td_onesecondminframes, cls.td_onesecondframes);
+                                       cls.td_onesecondmaxframes = max(cls.td_onesecondmaxframes, cls.td_onesecondframes);
+                                       cls.td_onesecondavgframes += cls.td_onesecondframes;
+                                       cls.td_onesecondavgcount++;
+                                       cls.td_onesecondframes = 0;
+                                       cls.td_onesecondnexttime++;
+                               }
                        }
                        else if (cl.time <= cl.mtime[0])
                        {
                        }
                        else if (cl.time <= cl.mtime[0])
                        {
-                                       return 0;               // don't need another message yet
+                               // don't need another message yet
+                               return;
                        }
                }
                        }
                }
-               
-       // get the next message
-               FS_Read (cls.demofile, &net_message.cursize, 4);
-               VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
-               for (i=0 ; i<3 ; i++)
+
+               // get the next message
+               FS_Read(cls.demofile, &net_message.cursize, 4);
+               net_message.cursize = LittleLong(net_message.cursize);
+               if (net_message.cursize > net_message.maxsize)
+                       Host_Error("Demo message (%i) > net_message.maxsize (%i)", net_message.cursize, net_message.maxsize);
+               VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
+               for (i = 0;i < 3;i++)
                {
                {
-                       r = FS_Read (cls.demofile, &f, 4);
-                       cl.mviewangles[0][i] = LittleFloat (f);
+                       r = (int)FS_Read(cls.demofile, &f, 4);
+                       cl.mviewangles[0][i] = LittleFloat(f);
                }
                }
-               
-               net_message.cursize = LittleLong (net_message.cursize);
-               if (net_message.cursize > MAX_DATAGRAM)
-                       Host_Error ("Demo message > MAX_DATAGRAM");
-               r = FS_Read (cls.demofile, net_message.data, net_message.cursize);
-               if (r != net_message.cursize)
+
+               if (FS_Read(cls.demofile, net_message.data, net_message.cursize) == net_message.cursize)
                {
                {
-                       CL_Disconnect ();
-                       return 0;
-               }
-       
-               return 1;
-       }
+                       MSG_BeginReading();
+                       CL_ParseServerMessage();
 
 
-       while (1)
-       {
-               r = NET_GetMessage (cls.netcon);
-               
-               if (r != 1 && r != 2)
-                       return r;
-       
-       // discard nop keepalive message
-               if (net_message.cursize == 1 && net_message.data[0] == svc_nop)
-                       Con_Printf ("<-- server to client keepalive\n");
+                       // In case the demo contains a "svc_disconnect" message
+                       if (!cls.demoplayback)
+                               return;
+               }
                else
                else
-                       break;
+               {
+                       CL_Disconnect();
+                       return;
+               }
        }
        }
-
-       if (cls.demorecording)
-               CL_WriteDemoMessage ();
-       
-       return r;
 }
 
 
 }
 
 
@@ -201,25 +235,28 @@ stop recording a demo
 */
 void CL_Stop_f (void)
 {
 */
 void CL_Stop_f (void)
 {
-       if (cmd_source != src_command)
-               return;
+       sizebuf_t buf;
+       unsigned char bufdata[64];
 
        if (!cls.demorecording)
        {
 
        if (!cls.demorecording)
        {
-               Con_Print("Not recording a demo.\n");
+               Con_Print("Not recording a demo.\n");
                return;
        }
 
 // write a disconnect message to the demo file
                return;
        }
 
 // write a disconnect message to the demo file
-       SZ_Clear (&net_message);
-       MSG_WriteByte (&net_message, svc_disconnect);
-       CL_WriteDemoMessage ();
+       // LordHavoc: don't replace the net_message when doing this
+       buf.data = bufdata;
+       buf.maxsize = sizeof(bufdata);
+       SZ_Clear(&buf);
+       MSG_WriteByte(&buf, svc_disconnect);
+       CL_WriteDemoMessage(&buf);
 
 // finish up
        FS_Close (cls.demofile);
        cls.demofile = NULL;
        cls.demorecording = false;
 
 // finish up
        FS_Close (cls.demofile);
        cls.demofile = NULL;
        cls.demorecording = false;
-       Con_Print("Completed demo\n");
+       Con_Print("Completed demo\n");
 }
 
 /*
 }
 
 /*
@@ -231,60 +268,59 @@ record <demoname> <map> [cd track]
 */
 void CL_Record_f (void)
 {
 */
 void CL_Record_f (void)
 {
-       int             c;
-       char    name[MAX_OSPATH];
-       int             track;
-
-       if (cmd_source != src_command)
-               return;
+       int c, track;
+       char name[MAX_OSPATH];
 
        c = Cmd_Argc();
        if (c != 2 && c != 3 && c != 4)
        {
 
        c = Cmd_Argc();
        if (c != 2 && c != 3 && c != 4)
        {
-               Con_Print("record <demoname> [<map> [cd track]]\n");
+               Con_Print("record <demoname> [<map> [cd track]]\n");
                return;
        }
 
        if (strstr(Cmd_Argv(1), ".."))
        {
                return;
        }
 
        if (strstr(Cmd_Argv(1), ".."))
        {
-               Con_Print("Relative pathnames are not allowed.\n");
+               Con_Print("Relative pathnames are not allowed.\n");
                return;
        }
 
        if (c == 2 && cls.state == ca_connected)
        {
                return;
        }
 
        if (c == 2 && cls.state == ca_connected)
        {
-               Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
+               Con_Print("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
                return;
        }
 
                return;
        }
 
+       if (cls.state == ca_connected)
+               CL_Disconnect();
+
        // write the forced cd track number, or -1
        if (c == 4)
        {
                track = atoi(Cmd_Argv(3));
        // write the forced cd track number, or -1
        if (c == 4)
        {
                track = atoi(Cmd_Argv(3));
-               Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
+               Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
        }
        else
        }
        else
-               track = -1;     
-       
+               track = -1;
+
+       // get the demo name
+       strlcpy (name, Cmd_Argv(1), sizeof (name));
+       FS_DefaultExtension (name, ".dem", sizeof (name));
+
        // start the map up
        if (c > 2)
                Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
        // start the map up
        if (c > 2)
                Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
-       
-       // open the demo file
-       strncpy (name, Cmd_Argv(1), sizeof (name) - 1);
-       name[sizeof (name) - 1] = '\0';
-       FS_DefaultExtension (name, ".dem");
 
 
-       Con_Printf ("recording to %s.\n", name);
-       cls.demofile = FS_Open (name, "wb", false);
+       // open the demo file
+       Con_Printf("recording to %s.\n", name);
+       cls.demofile = FS_Open (name, "wb", false, false);
        if (!cls.demofile)
        {
        if (!cls.demofile)
        {
-               Con_Print("ERROR: couldn't open.\n");
+               Con_Print("ERROR: couldn't open.\n");
                return;
        }
 
        cls.forcetrack = track;
                return;
        }
 
        cls.forcetrack = track;
-       FS_Printf (cls.demofile, "%i\n", cls.forcetrack);
+       FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
 
        cls.demorecording = true;
 }
 
        cls.demorecording = true;
 }
@@ -299,37 +335,38 @@ play [demoname]
 */
 void CL_PlayDemo_f (void)
 {
 */
 void CL_PlayDemo_f (void)
 {
-       char    name[256];
+       char    name[MAX_QPATH];
        int c;
        qboolean neg = false;
 
        int c;
        qboolean neg = false;
 
-       if (cmd_source != src_command)
-               return;
-
        if (Cmd_Argc() != 2)
        {
        if (Cmd_Argc() != 2)
        {
-               Con_Print("play <demoname> : plays a demo\n");
+               Con_Print("play <demoname> : plays a demo\n");
                return;
        }
 
        // disconnect from server
        CL_Disconnect ();
                return;
        }
 
        // disconnect from server
        CL_Disconnect ();
+       Host_ShutdownServer ();
+
+       // update networking ports (this is mainly just needed at startup)
+       NetConn_UpdateSockets();
 
        // open the demo file
 
        // open the demo file
-       strcpy (name, Cmd_Argv(1));
-       FS_DefaultExtension (name, ".dem");
+       strlcpy (name, Cmd_Argv(1), sizeof (name));
+       FS_DefaultExtension (name, ".dem", sizeof (name));
+       cls.protocol = PROTOCOL_QUAKE;
 
 
-       Con_Printf ("Playing demo from %s.\n", name);
-       cls.demofile = FS_Open (name, "rb", false);
+       Con_Printf("Playing demo from %s.\n", name);
+       cls.demofile = FS_Open (name, "rb", false, false);
        if (!cls.demofile)
        {
        if (!cls.demofile)
        {
-               Con_Print("ERROR: couldn't open.\n");
+               Con_Print("ERROR: couldn't open.\n");
                cls.demonum = -1;               // stop demo loop
                return;
        }
 
                cls.demonum = -1;               // stop demo loop
                return;
        }
 
-       SCR_BeginLoadingPlaque ();
-
+       strlcpy(cls.demoname, name, sizeof(cls.demoname));
        cls.demoplayback = true;
        cls.state = ca_connected;
        cls.forcetrack = 0;
        cls.demoplayback = true;
        cls.state = ca_connected;
        cls.forcetrack = 0;
@@ -352,18 +389,24 @@ CL_FinishTimeDemo
 */
 void CL_FinishTimeDemo (void)
 {
 */
 void CL_FinishTimeDemo (void)
 {
-       int             frames;
-       double  time; // LordHavoc: changed timedemo accuracy to double
-       
+       int frames;
+       double time, totalfpsavg;
+       double fpsmin, fpsavg, fpsmax; // report min/avg/max fps
+
        cls.timedemo = false;
        cls.timedemo = false;
-       
+
 // the first frame didn't count
        frames = (host_framecount - cls.td_startframe) - 1;
        time = realtime - cls.td_starttime;
 // the first frame didn't count
        frames = (host_framecount - cls.td_startframe) - 1;
        time = realtime - cls.td_starttime;
-       if (!time)
-               time = 1;
-       // LordHavoc: timedemo now prints out 7 digits of fraction
-       Con_Printf ("%i frames %5.7f seconds %5.7f fps\n", frames, time, frames/time);
+       totalfpsavg = time > 0 ? frames / time : 0;
+       fpsmin = cls.td_onesecondminframes;
+       fpsavg = cls.td_onesecondavgcount ? cls.td_onesecondavgframes / cls.td_onesecondavgcount : 0;
+       fpsmax = cls.td_onesecondmaxframes;
+       // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
+       Con_Printf("%i frames %5.7f seconds %5.7f fps, one-second min/avg/max: %.0f %.0f %.0f\n", frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax);
+       Log_Printf("benchmark.log", "date %s | enginedate %s | demo %s | commandline %s | result %i frames %5.7f seconds %5.7f fps, one-second min/avg/max: %.0f %.0f %.0f\n", Sys_TimeString("%Y-%m-%d %H:%M:%S"), buildstring, cls.demoname, cmdline.string, frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax);
+       if (COM_CheckParm("-benchmark"))
+               Host_Quit_f();
 }
 
 /*
 }
 
 /*
@@ -375,15 +418,14 @@ timedemo [demoname]
 */
 void CL_TimeDemo_f (void)
 {
 */
 void CL_TimeDemo_f (void)
 {
-       if (cmd_source != src_command)
-               return;
-
        if (Cmd_Argc() != 2)
        {
        if (Cmd_Argc() != 2)
        {
-               Con_Print("timedemo <demoname> : gets demo speeds\n");
+               Con_Print("timedemo <demoname> : gets demo speeds\n");
                return;
        }
 
                return;
        }
 
+       srand(0); // predictable random sequence for benchmarking
+
        CL_PlayDemo_f ();
 
 // cls.td_starttime will be grabbed at the second frame of the demo, so
        CL_PlayDemo_f ();
 
 // cls.td_starttime will be grabbed at the second frame of the demo, so
@@ -392,11 +434,10 @@ void CL_TimeDemo_f (void)
        // instantly hide console and deactivate it
        key_dest = key_game;
        key_consoleactive = 0;
        // instantly hide console and deactivate it
        key_dest = key_game;
        key_consoleactive = 0;
-       scr_conlines = 0;
        scr_con_current = 0;
 
        cls.timedemo = true;
        scr_con_current = 0;
 
        cls.timedemo = true;
-       cls.td_startframe = host_framecount;
-       cls.td_lastframe = -1;          // get a new message this frame
+       // get first message this frame
+       cls.td_lastframe = -1;
 }
 
 }