sv_autodemo_perclient - record client-like demos on the server (same format, lagged...
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 9 Sep 2007 08:54:14 +0000 (08:54 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 9 Sep 2007 08:54:14 +0000 (08:54 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7564 d7cf8633-e32d-0410-b094-e92efae38249

host.c
host_cmd.c
makefile.inc
server.h
sv_demo.c [new file with mode: 0644]
sv_demo.h [new file with mode: 0644]
sv_main.c

diff --git a/host.c b/host.c
index 521114b..7888bff 100644 (file)
--- a/host.c
+++ b/host.c
@@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "cl_video.h"
 #include "progsvm.h"
 #include "csprogs.h"
+#include "sv_demo.h"
 
 /*
 
@@ -373,6 +374,8 @@ void SV_DropClient(qboolean crash)
        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);
 
index 463fb72..dbd917d 100644 (file)
@@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
 #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"};
@@ -808,6 +809,7 @@ void Host_Name_f (void)
                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);
        }
 }
 
index 8dc3288..41035eb 100644 (file)
@@ -128,7 +128,8 @@ OBJ_COMMON= \
        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
index d7df388..16ddb66 100644 (file)
--- a/server.h
+++ b/server.h
@@ -242,6 +242,9 @@ typedef struct client_s
        // fixangle data
        qboolean fixangle_angles_set;
        vec3_t fixangle_angles;
+
+       // demo recording
+       qfile_t *sv_demo_file;
 } client_t;
 
 
diff --git a/sv_demo.c b/sv_demo.c
new file mode 100644 (file)
index 0000000..3ba4349
--- /dev/null
+++ b/sv_demo.c
@@ -0,0 +1,82 @@
+#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);
+}
diff --git a/sv_demo.h b/sv_demo.h
new file mode 100644 (file)
index 0000000..ad26d9d
--- /dev/null
+++ b/sv_demo.h
@@ -0,0 +1,9 @@
+#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
index 17dbf2c..0db7e30 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // sv_main.c -- server main program
 
 #include "quakedef.h"
+#include "sv_demo.h"
 #include "libcurl.h"
 
 static void SV_SaveEntFile_f(void);
@@ -147,6 +148,10 @@ cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singlepla
 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;
 
@@ -417,6 +422,9 @@ void SV_Init (void)
        }
        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)
        {
@@ -797,6 +805,28 @@ 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_%s_%d.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), levelname, ipaddress, PRVM_NUM_FOR_EDICT(client->edict));
+
+               SV_StartDemoRecording(client, demofile, -1);
+       }
 }
 
 /*
@@ -1755,6 +1785,12 @@ static void SV_SendClientDatagram (client_t *client)
                        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)
@@ -1797,6 +1833,7 @@ static void SV_UpdateToReliableMessages (void)
                        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