#include "cl_video.h"
#include "progsvm.h"
#include "csprogs.h"
+#include "sv_demo.h"
/*
int i;
Con_Printf("Client \"%s\" dropped\n", host_client->name);
+ SV_StopDemoRecording(host_client);
+
// make sure edict is not corrupt (from a level change for example)
host_client->edict = PRVM_EDICT_NUM(host_client - svs.clients + 1);
*/
#include "quakedef.h"
+#include "sv_demo.h"
int current_skill;
cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
MSG_WriteString (&sv.reliable_datagram, host_client->name);
+ SV_WriteNetnameIntoDemo(host_client);
}
}
view.o \
wad.o \
world.o \
- zone.o
+ zone.o \
+ sv_demo.c
# note that builddate.c is very intentionally not compiled to a .o before
# being linked, because it should be recompiled every time an executable is
// fixangle data
qboolean fixangle_angles_set;
vec3_t fixangle_angles;
+
+ // demo recording
+ qfile_t *sv_demo_file;
} client_t;
--- /dev/null
+#include "quakedef.h"
+#include "sv_demo.h"
+
+void SV_StartDemoRecording(client_t *client, const char *filename, int forcetrack)
+{
+ char name[MAX_QPATH];
+
+ if(client->sv_demo_file != NULL)
+ return; // we already have a demo
+
+ strlcpy(name, filename, sizeof(name));
+ FS_DefaultExtension(name, ".dem", sizeof(name));
+
+ Con_Printf("Recording demo for # %d (%s) to %s\n", PRVM_NUM_FOR_EDICT(client->edict), client->netaddress, name);
+
+ client->sv_demo_file = FS_Open(name, "wb", false, false);
+ if(!client->sv_demo_file)
+ {
+ Con_Print("ERROR: couldn't open.\n");
+ return;
+ }
+
+ FS_Printf(client->sv_demo_file, "%i\n", forcetrack);
+}
+
+void SV_WriteDemoMessage(client_t *client, sizebuf_t *sendbuffer)
+{
+ int len, i;
+ float f;
+
+ if(client->sv_demo_file == NULL)
+ return;
+ if(sendbuffer->cursize == 0)
+ return;
+
+ len = LittleLong(sendbuffer->cursize);
+ FS_Write(client->sv_demo_file, &len, 4);
+ for(i = 0; i < 3; ++i)
+ {
+ f = LittleFloat(client->edict->fields.server->v_angle[i]);
+ FS_Write(client->sv_demo_file, &f, 4);
+ }
+ FS_Write(client->sv_demo_file, sendbuffer->data, sendbuffer->cursize);
+}
+
+void SV_StopDemoRecording(client_t *client)
+{
+ sizebuf_t buf;
+ unsigned char bufdata[64];
+
+ if(client->sv_demo_file == NULL)
+ return;
+
+ buf.data = bufdata;
+ buf.maxsize = sizeof(bufdata);
+ SZ_Clear(&buf);
+ MSG_WriteByte(&buf, svc_disconnect);
+ SV_WriteDemoMessage(client, &buf);
+
+ 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)
+{
+ // This "pseudo packet" is written so a program can easily find out whose demo this is
+ sizebuf_t buf;
+ unsigned char bufdata[128];
+
+ if(client->sv_demo_file == NULL)
+ return;
+
+ buf.data = bufdata;
+ buf.maxsize = sizeof(bufdata);
+ SZ_Clear(&buf);
+ MSG_WriteByte(&buf, svc_stufftext);
+ MSG_WriteUnterminatedString(&buf, "\n// this demo contains the point of view of: ");
+ MSG_WriteUnterminatedString(&buf, client->name);
+ MSG_WriteString(&buf, "\n");
+ SV_WriteDemoMessage(client, &buf);
+}
--- /dev/null
+#ifndef SV_DEMO_H
+#define SV_DEMO_H
+
+void SV_StartDemoRecording(client_t *client, const char *filename, int forcetrack);
+void SV_WriteDemoMessage(client_t *client, sizebuf_t *sendbuffer);
+void SV_StopDemoRecording(client_t *client);
+void SV_WriteNetnameIntoDemo(client_t *client);
+
+#endif
// sv_main.c -- server main program
#include "quakedef.h"
+#include "sv_demo.h"
#include "libcurl.h"
static void SV_SaveEntFile_f(void);
cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
+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)"};
+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 IP address + port number, and the client number, separated by underscores" };
+
+
server_t sv;
server_static_t svs;
}
Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
+ Cvar_RegisterVariable (&sv_autodemo_perclient);
+ Cvar_RegisterVariable (&sv_autodemo_perclient_nameformat);
+
// any special defaults for gamemodes go here
if (gamemode == GAME_HIPNOTIC)
{
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_%s_%d.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), levelname, ipaddress, PRVM_NUM_FOR_EDICT(client->edict));
+
+ SV_StartDemoRecording(client, demofile, -1);
+ }
}
/*
SZ_Write (&msg, data, downloadsize);
}
+ // reliable only if none is in progress
+ if(client->sendsignon != 2 && !client->netconnection->sendMessageLength)
+ SV_WriteDemoMessage(client, &(client->netconnection->message));
+ // unreliable
+ SV_WriteDemoMessage(client, &msg);
+
// send the datagram
NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate, client->sendsignon == 2);
if (client->sendsignon == 1 && !client->netconnection->message.cursize)
MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
MSG_WriteByte (&sv.reliable_datagram, i);
MSG_WriteString (&sv.reliable_datagram, host_client->name);
+ SV_WriteNetnameIntoDemo(host_client);
}
// DP_SV_CLIENTCOLORS