CL_WriteDemoMessage(&buf);
// finish up
+ if (cl_autodemo.integer && ((cl_autodemo_delete.integer & 1) ^ ((cl_autodemo_delete.integer >> 1) & 1))) // bit 0 XOR bit 1
+ {
+ FS_RemoveOnClose(cls.demofile);
+ Con_Print("Completed and deleted demo\n");
+ }
+ else
+ Con_Print("Completed demo\n");
FS_Close (cls.demofile);
cls.demofile = NULL;
cls.demorecording = false;
- Con_Print("Completed demo\n");
}
/*
cvar_t cl_autodemo = {CVAR_SAVE, "cl_autodemo", "0", "records every game played, using the date/time and map name to name the demo file" };
cvar_t cl_autodemo_nameformat = {CVAR_SAVE, "cl_autodemo_nameformat", "autodemos/%Y-%m-%d_%H-%M", "The format of the cl_autodemo filename, followed by the map name (the date is encoded using strftime escapes)" };
+cvar_t cl_autodemo_delete = {0, "cl_autodemo_delete", "0", "Delete demos after recording. This is a bitmask, bit 1 gives the default, bit 0 inverts the meaning of bit 1 for the current demo. Thus, the values are: 0 = disabled; 1 = delete current demo only; 2 = delete all demos from this point on; 3 = delete all demos except the current demo" };
cvar_t r_draweffects = {0, "r_draweffects", "1","renders temporary sprite effects"};
Cvar_RegisterVariable (&cl_autodemo);
Cvar_RegisterVariable (&cl_autodemo_nameformat);
+ Cvar_RegisterVariable (&cl_autodemo_delete);
Cmd_AddCommand ("fog", CL_Fog_f, "set global fog parameters (density red green blue [alpha [mindist [maxdist [top [fadedepth]]]]])");
Cmd_AddCommand ("fog_heighttexture", CL_Fog_HeightTexture_f, "set global fog parameters (density red green blue alpha mindist maxdist top depth textures/mapname/fogheight.tga)");
Con_Printf ("Auto-recording to %s.\n", demofile);
+ // Clear the invert flag for every new demo
+ Cvar_SetValueQuick(&cl_autodemo_delete, cl_autodemo_delete.integer & ~0x1);
+
cls.demofile = FS_OpenRealFile(demofile, "wb", false);
if (cls.demofile)
{
extern cvar_t cl_autodemo;
extern cvar_t cl_autodemo_nameformat;
+extern cvar_t cl_autodemo_delete;
extern cvar_t r_draweffects;
#define QFILE_FLAG_DEFLATED (1 << 1)
/// file is actually already loaded data
#define QFILE_FLAG_DATA (1 << 2)
+/// real file will be removed on close
+#define QFILE_FLAG_REMOVE (1 << 3)
#define FILE_BUFF_SIZE 2048
typedef struct
ztoolkit_t* ztk; ///< For zipped files.
const unsigned char *data; ///< For data files.
+
+ const char *filename; ///< Kept around for QFILE_FLAG_REMOVE, unused otherwise
};
return NULL;
}
+ file->filename = Mem_strdup(fs_mempool, filepath);
+
file->real_length = lseek (file->handle, 0, SEEK_END);
// For files opened in append mode, we start at the end of the file
if (close (file->handle))
return EOF;
+ if (file->filename)
+ {
+ if (file->flags & QFILE_FLAG_REMOVE)
+ remove(file->filename);
+
+ Mem_Free((void *) file->filename);
+ }
+
if (file->ztk)
{
qz_inflateEnd (&file->ztk->zstream);
return 0;
}
+void FS_RemoveOnClose(qfile_t* file)
+{
+ file->flags |= QFILE_FLAG_REMOVE;
+}
/*
====================
qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet);
qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet);
int FS_Close (qfile_t* file);
+void FS_RemoveOnClose(qfile_t* file);
fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize);
fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize);
int FS_Print(qfile_t* file, const char *msg);
int dimension_hit; // ssqc / csqc
int dimension_solid; // ssqc / csqc
int disableclientprediction; // ssqc
+ int discardabledemo; // ssqc
int dphitcontentsmask; // ssqc / csqc
int drawonlytoclient; // ssqc
int effects; // ssqc / csqc
prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
+ prog->fieldoffsets.discardabledemo = PRVM_ED_FindFieldOffset("discardabledemo");
prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
#include "quakedef.h"
#include "sv_demo.h"
+extern cvar_t sv_autodemo_perclient_discardable;
+
void SV_StartDemoRecording(client_t *client, const char *filename, int forcetrack)
{
char name[MAX_QPATH];
+ prvm_eval_t *val;
if(client->sv_demo_file != NULL)
return; // we already have a demo
Con_Printf("Recording demo for # %d (%s) to %s\n", PRVM_NUM_FOR_EDICT(client->edict), client->netaddress, name);
+ // Reset discardable flag for every new demo.
+ if ((val = PRVM_EDICTFIELDVALUE(client->edict, prog->fieldoffsets.discardabledemo)))
+ val->_float = 0;
+
client->sv_demo_file = FS_OpenRealFile(name, "wb", false);
if(!client->sv_demo_file)
{
{
sizebuf_t buf;
unsigned char bufdata[64];
+ prvm_eval_t *val;
if(client->sv_demo_file == NULL)
return;
MSG_WriteByte(&buf, svc_disconnect);
SV_WriteDemoMessage(client, &buf, false);
+ if (sv_autodemo_perclient_discardable.integer && (val = PRVM_EDICTFIELDVALUE(client->edict, prog->fieldoffsets.discardabledemo)) && val->_float)
+ {
+ FS_RemoveOnClose(client->sv_demo_file);
+ Con_Printf("Stopped recording discardable demo for # %d (%s)\n", PRVM_NUM_FOR_EDICT(client->edict), client->netaddress);
+ }
+ else
+ Con_Printf("Stopped recording demo for # %d (%s)\n", PRVM_NUM_FOR_EDICT(client->edict), client->netaddress);
+
FS_Close(client->sv_demo_file);
client->sv_demo_file = NULL;
- Con_Printf("Stopped recording demo for # %d (%s)\n", PRVM_NUM_FOR_EDICT(client->edict), client->netaddress);
}
void SV_WriteNetnameIntoDemo(client_t *client)
cvar_t sv_autodemo_perclient = {CVAR_SAVE, "sv_autodemo_perclient", "0", "set to 1 to enable autorecorded per-client demos (they'll start to record at the beginning of a match); set it to 2 to also record client->server packets (for debugging)"};
cvar_t sv_autodemo_perclient_nameformat = {CVAR_SAVE, "sv_autodemo_perclient_nameformat", "sv_autodemos/%Y-%m-%d_%H-%M", "The format of the sv_autodemo_perclient filename, followed by the map name, the client number and the IP address + port number, separated by underscores (the date is encoded using strftime escapes)" };
+cvar_t sv_autodemo_perclient_discardable = {CVAR_SAVE, "sv_autodemo_perclient_discardable", "0", "Allow game code to decide whether a demo should be kept or discarded."};
cvar_t halflifebsp = {0, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
Cvar_RegisterVariable (&sv_autodemo_perclient);
Cvar_RegisterVariable (&sv_autodemo_perclient_nameformat);
+ Cvar_RegisterVariable (&sv_autodemo_perclient_discardable);
Cvar_RegisterVariable (&halflifebsp);