demo recording: stuff csprogs.dat files into .dem files so demos can always be played...
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 11 Aug 2008 18:12:51 +0000 (18:12 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 11 Aug 2008 18:12:51 +0000 (18:12 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8443 d7cf8633-e32d-0410-b094-e92efae38249

cl_demo.c
cl_parse.c
client.h
csprogs.c
csprogs.h
sv_main.c

index 580125b..27959bf 100644 (file)
--- a/cl_demo.c
+++ b/cl_demo.c
@@ -119,6 +119,57 @@ void CL_WriteDemoMessage (sizebuf_t *message)
        FS_Write (cls.demofile, message->data, message->cursize);
 }
 
+/*
+====================
+CL_CutDemo
+
+Dumps the current demo to a buffer, and resets the demo to its starting point.
+Used to insert csprogs.dat files as a download to the beginning of a demo file.
+====================
+*/
+void CL_CutDemo (void **buf, fs_offset_t *filesize)
+{
+       *buf = NULL;
+       *filesize = 0;
+
+       FS_Close(cls.demofile);
+       *buf = FS_LoadFile(cls.demoname, tempmempool, false, filesize);
+
+       // restart the demo recording
+       cls.demofile = FS_Open(cls.demoname, "wb", false, false);
+       if(!cls.demofile)
+               Host_Error("failed to reopen the demo file");
+       FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
+}
+
+/*
+====================
+CL_PasteDemo
+
+Adds the cut stuff back to the demo. Also frees the buffer.
+Used to insert csprogs.dat files as a download to the beginning of a demo file.
+====================
+*/
+void CL_PasteDemo (void **buf, fs_offset_t *filesize)
+{
+       fs_offset_t startoffset = 0;
+
+       if(!*buf)
+               return;
+
+       // skip cdtrack
+       while(startoffset < *filesize && ((char *)(*buf))[startoffset] != '\n')
+               ++startoffset;
+       if(startoffset < *filesize)
+               ++startoffset;
+
+       FS_Write(cls.demofile, *buf + startoffset, *filesize - startoffset);
+
+       Mem_Free(*buf);
+       *buf = NULL;
+       *filesize = 0;
+}
+
 /*
 ====================
 CL_ReadDemoMessage
@@ -209,6 +260,9 @@ void CL_ReadDemoMessage(void)
                        MSG_BeginReading();
                        CL_ParseServerMessage();
 
+                       if (cls.signon != SIGNONS)
+                               Cbuf_Execute(); // immediately execute svc_stufftext if in the demo before connect!
+
                        // In case the demo contains a "svc_disconnect" message
                        if (!cls.demoplayback)
                                return;
@@ -317,6 +371,7 @@ void CL_Record_f (void)
                Con_Print("ERROR: couldn't open.\n");
                return;
        }
+       strlcpy(cls.demoname, name, sizeof(cls.demoname));
 
        cls.forcetrack = track;
        FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
@@ -364,8 +419,8 @@ void CL_PlayDemo_f (void)
                cls.demonum = -1;               // stop demo loop
                return;
        }
-
        strlcpy(cls.demoname, name, sizeof(cls.demoname));
+
        cls.demoplayback = true;
        cls.state = ca_connected;
        cls.forcetrack = 0;
index 6b8e372..d6423d1 100644 (file)
@@ -424,6 +424,7 @@ static void CL_SetupWorldModel(void)
        if (cl.loadcsqc)
        {
                cl.loadcsqc = false;
+
                CL_VM_Init();
        }
 }
@@ -1644,6 +1645,7 @@ void CL_ParseServerInfo (void)
                        cls.forcetrack = -1;
                        FS_Printf (cls.demofile, "%i\n", cls.forcetrack);
                        cls.demorecording = true;
+                       strlcpy(cls.demoname, demofile, sizeof(cls.demoname));
                }
                else
                        Con_Print ("ERROR: couldn't open.\n");
index 4129806..6d032f3 100644 (file)
--- a/client.h
+++ b/client.h
@@ -518,7 +518,7 @@ typedef struct client_static_s
        // list of demos in loop
        char demos[MAX_DEMOS][MAX_DEMONAME];
        // the actively playing demo (set by CL_PlayDemo_f)
-       char demoname[64];
+       char demoname[MAX_QPATH];
 
 // demo recording info must be here, because record is started before
 // entering a map (and clearing client_state_t)
@@ -1214,6 +1214,9 @@ void CL_StopPlayback(void);
 void CL_ReadDemoMessage(void);
 void CL_WriteDemoMessage(sizebuf_t *mesage);
 
+void CL_CutDemo(void **buf, fs_offset_t *filesize);
+void CL_PasteDemo(void **buf, fs_offset_t *filesize);
+
 void CL_NextDemo(void);
 void CL_Stop_f(void);
 void CL_Record_f(void);
index 69b9734..92408e4 100644 (file)
--- a/csprogs.c
+++ b/csprogs.c
@@ -664,6 +664,46 @@ qboolean CL_VM_CB_LoadEdict(prvm_edict_t *ent)
 
 void Cmd_ClearCsqcFuncs (void);
 
+// returns true if the packet is valid, false if end of file is reached
+// used for dumping the CSQC download into demo files
+qboolean MakeDownloadPacket(const char *filename, void *data, unsigned long len, int crc, int cnt, sizebuf_t *buf, int protocol)
+{
+       int packetsize = buf->maxsize - 7; // byte short long
+       int npackets = (len + packetsize - 1) / (packetsize);
+
+       if(protocol == PROTOCOL_QUAKEWORLD)
+               return false; // CSQC can't run in QW anyway
+
+       SZ_Clear(buf);
+       if(cnt == 0)
+       {
+               MSG_WriteByte(buf, svc_stufftext);
+               MSG_WriteString(buf, va("\ncl_downloadbegin %lu %s\n", len, filename));
+               return true;
+       }
+       else if(cnt >= 1 && cnt <= npackets)
+       {
+               unsigned long thispacketoffset = (cnt - 1) * packetsize;
+               int thispacketsize = len - thispacketoffset;
+               if(thispacketsize > packetsize)
+                       thispacketsize = packetsize;
+
+               MSG_WriteByte(buf, svc_downloaddata);
+               MSG_WriteLong(buf, thispacketoffset);
+               MSG_WriteShort(buf, thispacketsize);
+               SZ_Write(buf, data + thispacketoffset, thispacketsize);
+
+               return true;
+       }
+       else if(cnt == npackets + 1)
+       {
+               MSG_WriteByte(buf, svc_stufftext);
+               MSG_WriteString(buf, va("\ncl_downloadfinished %lu %d\n", len, crc));
+               return true;
+       }
+       return false;
+}
+
 void CL_VM_Init (void)
 {
        const char* csprogsfn;
@@ -696,16 +736,19 @@ void CL_VM_Init (void)
        if (csprogsdata)
        {
                csprogsdatacrc = CRC_Block(csprogsdata, csprogsdatasize);
-               Mem_Free(csprogsdata);
                if (csprogsdatacrc != requiredcrc || csprogsdatasize != requiredsize)
                {
                        if (cls.demoplayback)
                        {
                                Con_Printf("^1Warning: Your %s is not the same version as the demo was recorded with (CRC/size are %i/%i but should be %i/%i)\n", csqc_progname.string, csprogsdatacrc, (int)csprogsdatasize, requiredcrc, requiredsize);
-                               return;
+                               // Mem_Free(csprogsdata);
+                               // return;
+                               // We WANT to continue here, and play the demo with different csprogs!
+                               // After all, this is just a warning. Sure things may go wrong from here.
                        }
                        else
                        {
+                               Mem_Free(csprogsdata);
                                Con_Printf("^1Your %s is not the same version as the server (CRC is %i/%i but should be %i/%i)\n", csqc_progname.string, csprogsdatacrc, (int)csprogsdatasize, requiredcrc, requiredsize);
                                CL_Disconnect();
                                return;
@@ -759,11 +802,30 @@ void CL_VM_Init (void)
                CL_VM_Error("CSQC %s ^2failed to load\n", csprogsfn);
                if(!sv.active)
                        CL_Disconnect();
+               Mem_Free(csprogsdata);
                return;
        }
 
        Con_Printf("CSQC %s ^5loaded (crc=%i, size=%i)\n", csprogsfn, csprogsdatacrc, (int)csprogsdatasize);
 
+       if(cls.demorecording)
+       {
+               int i;
+               char buf[NET_MAXMESSAGE];
+               sizebuf_t sb;
+               void *demobuf; fs_offset_t demofilesize;
+
+               sb.data = (void *) buf;
+               sb.maxsize = sizeof(buf);
+               i = 0;
+
+               CL_CutDemo(&demobuf, &demofilesize);
+               while(MakeDownloadPacket(csprogsfn, csprogsdata, csprogsdatasize, csprogsdatacrc, i++, &sb, cls.protocol))
+                       CL_WriteDemoMessage(&sb);
+               CL_PasteDemo(&demobuf, &demofilesize);
+       }
+       Mem_Free(csprogsdata);
+
        // check if OP_STATE animation is possible in this dat file
        if (prog->fieldoffsets.nextthink >= 0 && prog->fieldoffsets.frame >= 0 && prog->fieldoffsets.think >= 0 && prog->globaloffsets.self >= 0)
                prog->flag |= PRVM_OP_STATE;
index 8d3278e..25fabd9 100644 (file)
--- a/csprogs.h
+++ b/csprogs.h
@@ -53,4 +53,6 @@ extern cvar_t csqc_progname;  //[515]: csqc crc check and right csprogs name acco
 extern cvar_t csqc_progcrc;
 extern cvar_t csqc_progsize;
 
+qboolean MakeDownloadPacket(const char *filename, void *data, unsigned long len, int crc, int cnt, sizebuf_t *buf, int protocol);
+
 #endif
index 937826d..0ac5ac5 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "sv_demo.h"
 #include "libcurl.h"
+#include "csprogs.h"
 
 static void SV_SaveEntFile_f(void);
 static void SV_StartDownload_f(void);
@@ -739,6 +740,28 @@ void SV_SendServerinfo (client_t *client)
        dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
        MSG_WriteString (&client->netconnection->message,message);
 
+       SV_StopDemoRecording(client); // to split up demos into different files
+       if(sv_autodemo_perclient.integer && client->netconnection)
+       {
+               char demofile[MAX_OSPATH];
+               char levelname[MAX_QPATH];
+               char ipaddress[MAX_QPATH];
+               size_t i;
+
+               // start a new demo file
+               strlcpy(levelname, FS_FileWithoutPath(sv.worldmodel->name), sizeof(levelname));
+               if (strrchr(levelname, '.'))
+                       *(strrchr(levelname, '.')) = 0;
+
+               LHNETADDRESS_ToString(&(client->netconnection->peeraddress), ipaddress, sizeof(ipaddress), true);
+               for(i = 0; ipaddress[i]; ++i)
+                       if(!isalnum(ipaddress[i]))
+                               ipaddress[i] = '-';
+               dpsnprintf (demofile, sizeof(demofile), "%s_%s_%d_%s.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), levelname, PRVM_NUM_FOR_EDICT(client->edict), ipaddress);
+
+               SV_StartDemoRecording(client, demofile, -1);
+       }
+
        //[515]: init csprogs according to version of svprogs, check the crc, etc.
        if (sv.csqc_progname[0])
        {
@@ -750,6 +773,29 @@ void SV_SendServerinfo (client_t *client)
                MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize));
                MSG_WriteByte (&client->netconnection->message, svc_stufftext);
                MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc));
+
+               if(client->sv_demo_file != NULL)
+               {
+                       void *csqcbuf;
+                       fs_offset_t csqclen;
+                       int csqccrc;
+                       int i;
+                       char buf[NET_MAXMESSAGE];
+                       sizebuf_t sb;
+
+                       csqcbuf = FS_LoadFile(sv.csqc_progname, tempmempool, true, &csqclen);
+                       if(csqcbuf)
+                       {
+                               csqccrc = CRC_Block(csqcbuf, csqclen);
+                               sb.data = (void *) buf;
+                               sb.maxsize = sizeof(buf);
+                               i = 0;
+                               while(MakeDownloadPacket(sv.csqc_progname, csqcbuf, csqclen, csqccrc, i++, &sb, sv.protocol))
+                                       SV_WriteDemoMessage(client, &sb, false);
+                               Mem_Free(csqcbuf);
+                       }
+               }
+
                //[515]: init stufftext string (it is sent before svc_serverinfo)
                val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.SV_InitCmd);
                if (val)
@@ -819,28 +865,6 @@ void SV_SendServerinfo (client_t *client)
        client->num_pings = 0;
 #endif
        client->ping = 0;
-
-       SV_StopDemoRecording(client); // to split up demos into different files
-       if(sv_autodemo_perclient.integer && client->netconnection)
-       {
-               char demofile[MAX_OSPATH];
-               char levelname[MAX_QPATH];
-               char ipaddress[MAX_QPATH];
-               size_t i;
-
-               // start a new demo file
-               strlcpy(levelname, FS_FileWithoutPath(sv.worldmodel->name), sizeof(levelname));
-               if (strrchr(levelname, '.'))
-                       *(strrchr(levelname, '.')) = 0;
-
-               LHNETADDRESS_ToString(&(client->netconnection->peeraddress), ipaddress, sizeof(ipaddress), true);
-               for(i = 0; ipaddress[i]; ++i)
-                       if(!isalnum(ipaddress[i]))
-                               ipaddress[i] = '-';
-               dpsnprintf (demofile, sizeof(demofile), "%s_%s_%d_%s.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), levelname, PRVM_NUM_FOR_EDICT(client->edict), ipaddress);
-
-               SV_StartDemoRecording(client, demofile, -1);
-       }
 }
 
 /*