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
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;
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);
cls.demonum = -1; // stop demo loop
return;
}
-
strlcpy(cls.demoname, name, sizeof(cls.demoname));
+
cls.demoplayback = true;
cls.state = ca_connected;
cls.forcetrack = 0;
if (cl.loadcsqc)
{
cl.loadcsqc = false;
+
CL_VM_Init();
}
}
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");
// 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)
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);
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;
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;
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;
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
#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);
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])
{
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)
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);
- }
}
/*