From 73a34b86e8f991712fd9da6ef29917ea3bceb6b3 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sun, 1 May 2005 09:07:20 +0000 Subject: [PATCH] recorder interface to ease writing server state to mvds and qtv. ones this is working properly, adding qtv support will be trivial. --- qw/include/server.h | 4 +- qw/include/sv_demo.h | 70 +---- qw/include/sv_recorder.h | 48 ++++ qw/source/Makefile.am | 2 +- qw/source/sv_ccmds.c | 20 +- qw/source/sv_demo.c | 407 +++++---------------------- qw/source/sv_ents.c | 1 - qw/source/sv_main.c | 6 +- qw/source/sv_pr_cmds.c | 108 ++++---- qw/source/sv_pr_qwe.c | 5 +- qw/source/sv_recorder.c | 576 +++++++++++++++++++++++++++++++++++++++ qw/source/sv_send.c | 187 ++++--------- qw/source/sv_user.c | 17 +- 13 files changed, 826 insertions(+), 625 deletions(-) create mode 100644 qw/include/sv_recorder.h create mode 100644 qw/source/sv_recorder.c diff --git a/qw/include/server.h b/qw/include/server.h index 30d94bfa4..d2021edc0 100644 --- a/qw/include/server.h +++ b/qw/include/server.h @@ -112,7 +112,7 @@ typedef struct { byte signon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM]; // demo stuff - qboolean demorecording; + struct recorder_s *recorders; } server_t; #define NUM_SPAWN_PARMS 16 @@ -524,7 +524,7 @@ void SV_SetMoveVars(void); void SV_Print (const char *fmt, va_list args); void SV_Printf (const char *fmt, ...) __attribute__((format(printf,1,2))); void SV_SendClientMessages (void); -void SV_SendDemoMessage(void); +void SV_GetStats (struct edict_s *ent, int spectator, int stats[]); void SV_Multicast (const vec3_t origin, int to); void SV_StartSound (struct edict_s *entity, int channel, const char *sample, diff --git a/qw/include/sv_demo.h b/qw/include/sv_demo.h index 77902eb85..97a446696 100644 --- a/qw/include/sv_demo.h +++ b/qw/include/sv_demo.h @@ -21,80 +21,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef __sv_demo_h #define __sv_demo_h -#include "QF/quakeio.h" -#include "QF/sizebuf.h" - -#include "server.h" - -typedef struct dbuffer_s { - byte *data; - int start, end, last; - int maxsize; -} dbuffer_t; - -typedef struct header_s { - byte type; - byte full; - int to; - int size; - byte data[1]; -} header_t; - -typedef struct demobuf_s { - sizebuf_t sz; - int bufsize; - header_t *h; -} demobuf_t; - -typedef struct demo_frame_s { - double time; - demobuf_t buf; -} demo_frame_t; - -#define DEMO_FRAMES 64 -#define DEMO_FRAMES_MASK (DEMO_FRAMES - 1) - -typedef struct demo_s { - demobuf_t *dbuf; - dbuffer_t dbuffer; - sizebuf_t datagram; - - int lastto; - int lasttype; - double time, pingtime; - - delta_t delta; - int stats[MAX_CLIENTS][MAX_CL_STATS]; // ouch! - demo_frame_t frames[DEMO_FRAMES]; - int forceFrame; - - int parsecount; - int lastwritten; - - int size; // XXX doesn't belong here -} demo_t; - -extern demo_t demo; -extern struct cvar_s *sv_demoUseCache; -extern struct cvar_s *sv_demoCacheSize; -extern struct cvar_s *sv_demoMaxDirSize; -extern struct cvar_s *sv_demoDir; +extern struct recorder_s demo; extern struct cvar_s *sv_demofps; extern struct cvar_s *sv_demoPings; -extern struct cvar_s *sv_demoNoVis; extern struct cvar_s *sv_demoMaxSize; -extern struct cvar_s *sv_demoPrefix; -extern struct cvar_s *sv_demoSuffix; -extern struct cvar_s *sv_onrecordfinish; -extern struct cvar_s *sv_ondemoremove; -extern struct cvar_s *sv_demotxt; -extern struct cvar_s *serverdemo; -void DemoWrite_Begin (byte type, int to, int size); -void SV_DemoWritePackets (int num); void SV_Stop (int reason); -void DemoSetMsgBuf (demobuf_t *prev, demobuf_t *cur); void Demo_Init (void); -void SV_DemoPings (void); #endif//__sv_demo_h diff --git a/qw/include/sv_recorder.h b/qw/include/sv_recorder.h new file mode 100644 index 000000000..555d36021 --- /dev/null +++ b/qw/include/sv_recorder.h @@ -0,0 +1,48 @@ +/* + sv_recorder.h + + Interface for recording server state (server side demos and qtv) + + Copyright (C) 2005 #AUTHOR# + + Author: Bill Currie + Date: 2005/5/1 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __sv_recorder_h +#define __sv_recorder_h + +struct sizebuf_s; +typedef struct recorder_s recorder_t; + +void SVR_Init (void); +recorder_t *SVR_AddUser (void (*)(struct sizebuf_s *), int (*)(void)); +void SVR_RemoveUser (recorder_t *r); +struct sizebuf_s *SVR_WriteBegin (byte type, int to, int size); +struct sizebuf_s *SVR_Datagram (void); +void SVR_ForceFrame (void); +int SVR_Frame (void); +void SVR_WritePacket (void); +void SV_SendDemoMessage (void); + +#endif//__sv_recorder_h diff --git a/qw/source/Makefile.am b/qw/source/Makefile.am index 448ffbfb5..c6467eea0 100644 --- a/qw/source/Makefile.am +++ b/qw/source/Makefile.am @@ -71,7 +71,7 @@ EXTRA_DIST=sv_sys_win.c sv_sys_unix.c libqw_server_a_SOURCES= \ crudefile.c sv_ccmds.c sv_demo.c sv_ents.c sv_gib.c sv_init.c sv_main.c \ sv_move.c sv_phys.c sv_pr_cmds.c sv_pr_qwe.c sv_progs.c sv_qtv.c \ - sv_send.c sv_user.c world.c $(syssv_SRC) + sv_recorder.c sv_send.c sv_user.c world.c $(syssv_SRC) qw_server_LIBS= \ $(SERVER_PLUGIN_STATIC_LIBS) \ diff --git a/qw/source/sv_ccmds.c b/qw/source/sv_ccmds.c index 1757ab4b6..15a9b1952 100644 --- a/qw/source/sv_ccmds.c +++ b/qw/source/sv_ccmds.c @@ -57,6 +57,7 @@ static __attribute__ ((unused)) const char rcsid[] = #include "server.h" #include "sv_demo.h" #include "sv_progs.h" +#include "sv_recorder.h" qboolean sv_allow_cheats; @@ -421,7 +422,7 @@ SV_Map_f (void) Qclose (f); free (expanded); - if (sv.demorecording) + if (sv.recorders) SV_Stop (0); SV_BroadcastCommand ("changing\n"); @@ -763,14 +764,15 @@ SV_ConSay (const char *prefix, client_t *client) if (*prefix != 'I') // beep, except for Info says SV_ClientPrintf (0, client, PRINT_CHAT, "%s", ""); } - if (sv.demorecording) { - DemoWrite_Begin (dem_all, 0, strlen (text->str) + 7); - MSG_WriteByte (&demo.dbuf->sz, svc_print); - MSG_WriteByte (&demo.dbuf->sz, PRINT_HIGH); - MSG_WriteString (&demo.dbuf->sz, va ("%s\n", text->str)); - MSG_WriteByte (&demo.dbuf->sz, svc_print); - MSG_WriteByte (&demo.dbuf->sz, PRINT_CHAT); - MSG_WriteString (&demo.dbuf->sz, ""); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_all, 0, strlen (text->str) + 7); + MSG_WriteByte (dbuf, svc_print); + MSG_WriteByte (dbuf, PRINT_HIGH); + MSG_WriteString (dbuf, va ("%s\n", text->str)); + MSG_WriteByte (dbuf, svc_print); + MSG_WriteByte (dbuf, PRINT_CHAT); + MSG_WriteString (dbuf, ""); } } } diff --git a/qw/source/sv_demo.c b/qw/source/sv_demo.c index 9504c65de..3ca9d5630 100644 --- a/qw/source/sv_demo.c +++ b/qw/source/sv_demo.c @@ -47,207 +47,49 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/sys.h" #include "QF/va.h" +#include "compat.h" #include "pmove.h" #include "server.h" #include "sv_demo.h" #include "sv_progs.h" +#include "sv_recorder.h" -demo_t demo; -static byte demo_buffer[20 * MAX_MSGLEN]; -static byte demo_datagram_data[MAX_DATAGRAM]; static QFile *demo_file; static byte *demo_mfile; static qboolean demo_disk; static dstring_t *demo_name; // filename of mvd static dstring_t *demo_text; // filename of description file static void *demo_dest; +static double demo_time; + +static recorder_t *recorder; #define MIN_DEMO_MEMORY 0x100000 #define USECACHE (sv_demoUseCache->int_val && svs.demomemsize) #define DWRITE(a,b,d) dwrite((QFile *) d, a, b) -#define MAXSIZE (demo.dbuffer.end < demo.dbuffer.last ? \ - demo.dbuffer.start - demo.dbuffer.end : \ - demo.dbuffer.maxsize - demo.dbuffer.end) static int demo_max_size; static int demo_size; -cvar_t *sv_demoUseCache; -cvar_t *sv_demoCacheSize; -cvar_t *sv_demoMaxDirSize; -cvar_t *sv_demoDir; -cvar_t *sv_demofps; -cvar_t *sv_demoPings; -cvar_t *sv_demoNoVis; -cvar_t *sv_demoMaxSize; -cvar_t *sv_demoPrefix; -cvar_t *sv_demoSuffix; -cvar_t *sv_onrecordfinish; -cvar_t *sv_ondemoremove; -cvar_t *sv_demotxt; -cvar_t *serverdemo; -int (*dwrite) (QFile * file, const void *buf, int count); +cvar_t *sv_demofps; +cvar_t *sv_demoPings; +cvar_t *sv_demoMaxSize; +static cvar_t *sv_demoUseCache; +static cvar_t *sv_demoCacheSize; +static cvar_t *sv_demoMaxDirSize; +static cvar_t *sv_demoDir; +static cvar_t *sv_demoNoVis; +static cvar_t *sv_demoPrefix; +static cvar_t *sv_demoSuffix; +static cvar_t *sv_onrecordfinish; +static cvar_t *sv_ondemoremove; +static cvar_t *sv_demotxt; +static cvar_t *serverdemo; + +static int (*dwrite) (QFile * file, const void *buf, int count); #define HEADER ((int) &((header_t *) 0)->data) -entity_state_t demo_entities[UPDATE_MASK + 1][MAX_DEMO_PACKET_ENTITIES]; -plent_state_t demo_players[UPDATE_MASK + 1][MAX_CLIENTS]; - -/* - SV_WriteDemoMessage - - Dumps the current net message, prefixed by the length and view angles -*/ -static void -SV_WriteDemoMessage (sizebuf_t *msg, int type, int to, float time) -{ - int len, i, msec; - byte c; - static double prevtime; - - msec = (time - prevtime) * 1000; - prevtime += msec * 0.001; - if (msec > 255) - msec = 255; - if (msec < 2) - msec = 0; - - c = msec; - demo.size += DWRITE (&c, sizeof (c), demo_dest); - - if (demo.lasttype != type || demo.lastto != to) { - demo.lasttype = type; - demo.lastto = to; - switch (demo.lasttype) { - case dem_all: - c = dem_all; - demo.size += DWRITE (&c, sizeof (c), demo_dest); - break; - case dem_multiple: - c = dem_multiple; - demo.size += DWRITE (&c, sizeof (c), demo_dest); - - i = LittleLong (demo.lastto); - demo.size += DWRITE (&i, sizeof (i), demo_dest); - break; - case dem_single: - case dem_stats: - c = demo.lasttype + (demo.lastto << 3); - demo.size += DWRITE (&c, sizeof (c), demo_dest); - break; - default: - SV_Stop (0); - Con_Printf ("bad demo message type:%d", type); - return; - } - } else { - c = dem_read; - demo.size += DWRITE (&c, sizeof (c), demo_dest); - } - - - len = LittleLong (msg->cursize); - demo.size += DWRITE (&len, 4, demo_dest); - demo.size += DWRITE (msg->data, msg->cursize, demo_dest); - - if (demo_disk) - Qflush (demo_file); - else if (demo.size - demo_size > demo_max_size) { - demo_size = demo.size; - demo_mfile -= 0x80000; - Qwrite (demo_file, svs.demomem, 0x80000); - Qflush (demo_file); - memmove (svs.demomem, svs.demomem + 0x80000, demo.size - 0x80000); - } -} - -/* - DemoWriteToDisk - - Writes to disk a message meant for specifc client - or all messages if type == 0 - Message is cleared from demobuf after that -*/ - -static void -SV_DemoWriteToDisk (int type, int to, float time) -{ - int pos = 0, oldm, oldd; - header_t *p; - int size; - sizebuf_t msg; - - p = (header_t *) demo.dbuf->sz.data; - demo.dbuf->h = NULL; - - oldm = demo.dbuf->bufsize; - oldd = demo.dbuffer.start; - while (pos < demo.dbuf->bufsize) { - size = p->size; - pos += HEADER + size; - - // no type means we are writing to disk everything - if (!type || (p->type == type && p->to == to)) { - if (size) { - msg.data = p->data; - msg.cursize = size; - - SV_WriteDemoMessage (&msg, p->type, p->to, time); - } - // data is written so it need to be cleard from demobuf - if (demo.dbuf->sz.data != (byte *) p) - memmove (demo.dbuf->sz.data + size + HEADER, - demo.dbuf->sz.data, (byte *) p - demo.dbuf->sz.data); - - demo.dbuf->bufsize -= size + HEADER; - demo.dbuf->sz.data += size + HEADER; - pos -= size + HEADER; - demo.dbuf->sz.maxsize -= size + HEADER; - demo.dbuffer.start += size + HEADER; - } - // move along - p = (header_t *) (p->data + size); - } - - if (demo.dbuffer.start == demo.dbuffer.last) { - if (demo.dbuffer.start == demo.dbuffer.end) { - demo.dbuffer.end = 0; // demo.dbuffer is empty - demo.dbuf->sz.data = demo.dbuffer.data; - } - // go back to begining of the buffer - demo.dbuffer.last = demo.dbuffer.end; - demo.dbuffer.start = 0; - } -} - -void -SV_DemoWritePackets (int num) -{ - demo_frame_t *frame; - double time; - - if (num > demo.parsecount - demo.lastwritten + 1) - num = demo.parsecount - demo.lastwritten + 1; - - // 'num' frames to write - for (; num; num--, demo.lastwritten++) { - frame = &demo.frames[demo.lastwritten & DEMO_FRAMES_MASK]; - time = frame->time; - - demo.dbuf = &frame->buf; - - // this goes first to reduce demo size a bit -// SV_DemoWriteToDisk (demo.lasttype, demo.lastto, time); - SV_DemoWriteToDisk (0, 0, time); // now goes the rest - } - - if (demo.lastwritten > demo.parsecount) - demo.lastwritten = demo.parsecount; - - demo.dbuf = &demo.frames[demo.parsecount & DEMO_FRAMES_MASK].buf; - demo.dbuf->sz.maxsize = MAXSIZE + demo.dbuf->bufsize; -} - static int memwrite (QFile *_mem, const void *buffer, int size) { @@ -259,95 +101,26 @@ memwrite (QFile *_mem, const void *buffer, int size) return size; } -/* - DemoSetBuf - - Sets position in the buf for writing to specific client -*/ - static void -DemoSetBuf (byte type, int to) +demo_write (sizebuf_t *msg) { - header_t *p; - int pos = 0; - - p = (header_t *) demo.dbuf->sz.data; - - while (pos < demo.dbuf->bufsize) { - pos += HEADER + p->size; - - if (type == p->type && to == p->to && !p->full) { - demo.dbuf->sz.cursize = pos; - demo.dbuf->h = p; - return; - } - - p = (header_t *) (p->data + p->size); - } - // type && to not exist in the buf, so add it - - p->type = type; - p->to = to; - p->size = 0; - p->full = 0; - - demo.dbuf->bufsize += HEADER; - demo.dbuf->sz.cursize = demo.dbuf->bufsize; - demo.dbuffer.end += HEADER; - demo.dbuf->h = p; + DWRITE (msg->data, msg->cursize, demo_dest); } -static void -DemoMoveBuf (void) +static int +demo_frame (void) { - // set the last message mark to the previous frame (i/e begining of this - // one) - demo.dbuffer.last = demo.dbuffer.end - demo.dbuf->bufsize; + double min_fps; - // move buffer to the begining of demo buffer - memmove (demo.dbuffer.data, demo.dbuf->sz.data, demo.dbuf->bufsize); - demo.dbuf->sz.data = demo.dbuffer.data; - demo.dbuffer.end = demo.dbuf->bufsize; - demo.dbuf->h = NULL; // it will be setup again - demo.dbuf->sz.maxsize = MAXSIZE + demo.dbuf->bufsize; -} - -void -DemoWrite_Begin (byte type, int to, int size) -{ - byte *p; - qboolean move = false; - - // will it fit? - while (demo.dbuf->bufsize + size + HEADER > demo.dbuf->sz.maxsize) { - // if we reached the end of buffer move msgbuf to the begining - if (!move && demo.dbuffer.end > demo.dbuffer.start) - move = true; - - SV_DemoWritePackets (1); - if (move && demo.dbuffer.start > demo.dbuf->bufsize + HEADER + size) - DemoMoveBuf (); - } - - if (demo.dbuf->h == NULL || demo.dbuf->h->type != type - || demo.dbuf->h->to != to || demo.dbuf->h->full) { - DemoSetBuf (type, to); - } - - if (demo.dbuf->h->size + size > MAX_MSGLEN) { - demo.dbuf->h->full = 1; - DemoSetBuf (type, to); - } - // we have to make room for new data - if (demo.dbuf->sz.cursize != demo.dbuf->bufsize) { - p = demo.dbuf->sz.data + demo.dbuf->sz.cursize; - memmove (p + size, p, demo.dbuf->bufsize - demo.dbuf->sz.cursize); - } - - demo.dbuf->bufsize += size; - demo.dbuf->h->size += size; - if ((demo.dbuffer.end += size) > demo.dbuffer.last) - demo.dbuffer.last = demo.dbuffer.end; + if (!sv_demofps->value) + min_fps = 20.0; + else + min_fps = sv_demofps->value; + min_fps = max (4, min_fps); + if (sv.time - demo_time < 1.0 / min_fps) + return 0; + demo_time = sv.time; + return 1; } /* @@ -358,7 +131,9 @@ DemoWrite_Begin (byte type, int to, int size) void SV_Stop (int reason) { - if (!sv.demorecording) { + sizebuf_t *dbuf; + + if (!recorder) { Con_Printf ("Not recording a demo.\n"); return; } @@ -372,7 +147,8 @@ SV_Stop (int reason) QFS_Remove (demo_text->str); demo_file = NULL; - sv.demorecording = false; + SVR_RemoveUser (recorder); + recorder = 0; SV_BroadcastPrintf (PRINT_CHAT, "Server recording canceled, demo removed\n"); @@ -384,24 +160,28 @@ SV_Stop (int reason) // write a disconnect message to the demo file // clearup to be sure message will fit + dbuf = SVR_WriteBegin (dem_all, 0, 2 + strlen ("EndOfDemo")); + MSG_WriteByte (dbuf, svc_disconnect); + MSG_WriteString (dbuf, "EndOfDemo"); +/* XXX demo.dbuf->sz.cursize = 0; demo.dbuf->h = NULL; demo.dbuf->bufsize = 0; - DemoWrite_Begin (dem_all, 0, 2 + strlen ("EndOfDemo")); - MSG_WriteByte (&demo.dbuf->sz, svc_disconnect); - MSG_WriteString (&demo.dbuf->sz, "EndOfDemo"); + DemoWrite_Begin ( SV_DemoWritePackets (demo.parsecount - demo.lastwritten + 1); + // finish up if (!demo_disk) { Qwrite (demo_file, svs.demomem, demo.size - demo_size); Qflush (demo_file); } - +*/ Qclose (demo_file); demo_file = NULL; - sv.demorecording = false; + SVR_RemoveUser (recorder); + recorder = 0; if (!reason) SV_BroadcastPrintf (PRINT_CHAT, "Server recording completed\n"); else @@ -453,57 +233,6 @@ SV_Cancel_f (void) SV_Stop (2); } -void -SV_DemoPings (void) -{ - client_t *client; - int j; - - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { - if (client->state != cs_spawned && client->state != cs_server) - continue; - - DemoWrite_Begin (dem_all, 0, 7); - MSG_WriteByte (&demo.dbuf->sz, svc_updateping); - MSG_WriteByte (&demo.dbuf->sz, j); - MSG_WriteShort (&demo.dbuf->sz, SV_CalcPing (client)); - MSG_WriteByte (&demo.dbuf->sz, svc_updatepl); - MSG_WriteByte (&demo.dbuf->sz, j); - MSG_WriteByte (&demo.dbuf->sz, client->lossage); - } -} - -static void -DemoBuffer_Init (dbuffer_t *dbuffer, byte *buf, size_t size) -{ - dbuffer->data = buf; - dbuffer->maxsize = size; - dbuffer->start = 0; - dbuffer->end = 0; - dbuffer->last = 0; -} - -/* - Demo_SetMsgBuf - - Sets the frame message buffer -*/ - -void -DemoSetMsgBuf (demobuf_t *prev, demobuf_t *cur) -{ - // fix the maxsize of previous msg buffer, - // we won't be able to write there anymore - if (prev != NULL) - prev->sz.maxsize = prev->bufsize; - - demo.dbuf = cur; - memset (demo.dbuf, 0, sizeof (*demo.dbuf)); - - demo.dbuf->sz.data = demo.dbuffer.data + demo.dbuffer.end; - demo.dbuf->sz.maxsize = MAXSIZE; -} - static qboolean SV_InitRecord (void) { @@ -535,15 +264,15 @@ SV_WriteRecordDemoMessage (sizebuf_t *msg) byte c; c = 0; - demo.size += DWRITE (&c, sizeof (c), demo_dest); + /*demo.size +=*/ DWRITE (&c, sizeof (c), demo_dest); c = dem_read; - demo.size += DWRITE (&c, sizeof (c), demo_dest); + /*demo.size +=*/ DWRITE (&c, sizeof (c), demo_dest); len = LittleLong (msg->cursize); - demo.size += DWRITE (&len, 4, demo_dest); + /*demo.size +=*/ DWRITE (&len, 4, demo_dest); - demo.size += DWRITE (msg->data, msg->cursize, demo_dest); + /*demo.size +=*/ DWRITE (msg->data, msg->cursize, demo_dest); if (demo_disk) Qflush (demo_file); @@ -556,16 +285,16 @@ SV_WriteSetDemoMessage (void) byte c; c = 0; - demo.size += DWRITE (&c, sizeof (c), demo_dest); + /*demo.size +=*/ DWRITE (&c, sizeof (c), demo_dest); c = dem_set; - demo.size += DWRITE (&c, sizeof (c), demo_dest); + /*demo.size +=*/ DWRITE (&c, sizeof (c), demo_dest); len = LittleLong (0); - demo.size += DWRITE (&len, 4, demo_dest); + /*demo.size +=*/ DWRITE (&len, 4, demo_dest); len = LittleLong (0); - demo.size += DWRITE (&len, 4, demo_dest); + /*demo.size +=*/ DWRITE (&len, 4, demo_dest); if (demo_disk) Qflush (demo_file); @@ -648,20 +377,6 @@ SV_Record (char *name) client_t *player; const char *gamedir, *s; - memset (&demo, 0, sizeof (demo)); - - demo.delta.pvs = dt_pvs_fat; - for (i = 0; i < UPDATE_BACKUP; i++) { - demo.delta.frames[i].entities.entities = demo_entities[i]; - demo.delta.frames[i].players.players = demo_players[i]; - } - - DemoBuffer_Init (&demo.dbuffer, demo_buffer, sizeof (demo_buffer)); - DemoSetMsgBuf (NULL, &demo.frames[0].buf); - - demo.datagram.maxsize = sizeof (demo_datagram_data); - demo.datagram.data = demo_datagram_data; - demo_file = QFS_Open (name, "wb"); if (!demo_file) { Con_Printf ("ERROR: couldn't open %s\n", name); @@ -702,8 +417,8 @@ SV_Record (char *name) } else QFS_Remove (demo_text->str); - sv.demorecording = true; - demo.pingtime = demo.time = sv.time; + recorder = SVR_AddUser (demo_write, demo_frame); + demo_time = sv.time; /*-------------------------------------------------*/ @@ -927,7 +642,7 @@ SV_Record_f (void) return; } - if (sv.demorecording) + if (recorder) SV_Stop (0); dsprintf (name, "%s/%s/%s%s%s", qfs_gamedir->dir.def, sv_demoDir->string, @@ -1023,7 +738,7 @@ SV_EasyRecord_f (void) return; } - if (sv.demorecording) + if (recorder) SV_Stop (0); if (Cmd_Argc () == 2) diff --git a/qw/source/sv_ents.c b/qw/source/sv_ents.c index 9bef89a9f..cfd7e67c6 100644 --- a/qw/source/sv_ents.c +++ b/qw/source/sv_ents.c @@ -46,7 +46,6 @@ static __attribute__ ((unused)) const char rcsid[] = #include "compat.h" #include "server.h" -#include "sv_demo.h" #include "sv_progs.h" diff --git a/qw/source/sv_main.c b/qw/source/sv_main.c index 5d9c51a8c..957b864b6 100644 --- a/qw/source/sv_main.c +++ b/qw/source/sv_main.c @@ -91,6 +91,7 @@ static __attribute__ ((unused)) const char rcsid[] = #include "sv_progs.h" #include "sv_gib.h" #include "sv_qtv.h" +#include "sv_recorder.h" SERVER_PLUGIN_PROTOS static plugin_list_t server_plugin_list[] = { @@ -247,7 +248,7 @@ SV_Shutdown (void) Qclose (sv_fraglogfile); sv_fraglogfile = NULL; } - if (sv.demorecording) + if (sv.recorders) SV_Stop (0); NET_Shutdown (); @@ -1983,7 +1984,7 @@ SV_Frame (float time) SV_SendClientMessages (); demo_start = Sys_DoubleTime (); - if (sv.demorecording) + if (sv.recorders) SV_SendDemoMessage (); demo_end = Sys_DoubleTime (); svs.stats.demo += demo_end - demo_start; @@ -2573,6 +2574,7 @@ SV_Init (void) SV_InitLocal (); Pmove_Init (); + SVR_Init (); Demo_Init (); Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); diff --git a/qw/source/sv_pr_cmds.c b/qw/source/sv_pr_cmds.c index 18b8b2531..3a4dba7ac 100644 --- a/qw/source/sv_pr_cmds.c +++ b/qw/source/sv_pr_cmds.c @@ -52,10 +52,10 @@ static __attribute__ ((unused)) const char rcsid[] = #include "compat.h" #include "crudefile.h" #include "server.h" -#include "sv_demo.h" #include "sv_gib.h" #include "sv_pr_cmds.h" #include "sv_progs.h" +#include "sv_recorder.h" #include "world.h" /* BUILT-IN FUNCTIONS */ @@ -295,10 +295,11 @@ PF_centerprint (progs_t *pr) MSG_ReliableWrite_Begin (&cl->backbuf, svc_centerprint, 2 + strlen (s)); MSG_ReliableWrite_String (&cl->backbuf, s); - if (sv.demorecording) { - DemoWrite_Begin (dem_single, entnum - 1, 2 + strlen (s)); - MSG_WriteByte (&demo.dbuf->sz, svc_centerprint); - MSG_WriteString (&demo.dbuf->sz, s); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, entnum - 1, 2 + strlen (s)); + MSG_WriteByte (dbuf, svc_centerprint); + MSG_WriteString (dbuf, s); } } @@ -604,10 +605,11 @@ PF_stuffcmd (progs_t *pr) p[1] = 0; MSG_ReliableWrite_Begin (&cl->backbuf, svc_stufftext, 2 + p - buf); MSG_ReliableWrite_String (&cl->backbuf, buf); - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 2 + strlen (buf)); - MSG_WriteByte (&demo.dbuf->sz, svc_stufftext); - MSG_WriteString (&demo.dbuf->sz, buf); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 2 + p - buf); + MSG_WriteByte (dbuf, svc_stufftext); + MSG_WriteString (dbuf, buf); } p[1] = t; strcpy (buf, p + 1); // safe because this is a downward, in @@ -898,11 +900,12 @@ PF_lightstyle (progs_t *pr) MSG_ReliableWrite_Char (&cl->backbuf, style); MSG_ReliableWrite_String (&cl->backbuf, val); } - if (sv.demorecording) { - DemoWrite_Begin (dem_all, 0, strlen (val) + 3); - MSG_WriteByte (&demo.dbuf->sz, svc_lightstyle); - MSG_WriteByte (&demo.dbuf->sz, style); - MSG_WriteString (&demo.dbuf->sz, val); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_all, 0, strlen (val) + 3); + MSG_WriteByte (dbuf, svc_lightstyle); + MSG_WriteByte (dbuf, style); + MSG_WriteString (dbuf, val); } } @@ -1137,9 +1140,10 @@ PF_WriteBytes (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, count); MSG_ReliableWrite_SZ (&cl->backbuf, buf, count); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, count); - SZ_Write (&demo.dbuf->sz, buf, count); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, count); + SZ_Write (dbuf, buf, count); } } else { sizebuf_t *msg = WriteDest (pr); @@ -1158,9 +1162,10 @@ PF_WriteByte (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 1); MSG_ReliableWrite_Byte (&cl->backbuf, P_FLOAT (pr, 1)); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 1); - MSG_WriteByte (&demo.dbuf->sz, P_FLOAT (pr, 1)); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 1); + MSG_WriteByte (dbuf, P_FLOAT (pr, 1)); } } else MSG_WriteByte (WriteDest (pr), P_FLOAT (pr, 1)); @@ -1177,9 +1182,10 @@ PF_WriteChar (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 1); MSG_ReliableWrite_Char (&cl->backbuf, P_FLOAT (pr, 1)); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 1); - MSG_WriteByte (&demo.dbuf->sz, P_FLOAT (pr, 1)); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 1); + MSG_WriteByte (dbuf, P_FLOAT (pr, 1)); } } else MSG_WriteByte (WriteDest (pr), P_FLOAT (pr, 1)); @@ -1196,9 +1202,10 @@ PF_WriteShort (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 2); MSG_ReliableWrite_Short (&cl->backbuf, P_FLOAT (pr, 1)); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 2); - MSG_WriteShort (&demo.dbuf->sz, P_FLOAT (pr, 1)); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 2); + MSG_WriteShort (dbuf, P_FLOAT (pr, 1)); } } else MSG_WriteShort (WriteDest (pr), P_FLOAT (pr, 1)); @@ -1215,9 +1222,10 @@ PF_WriteLong (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 4); MSG_ReliableWrite_Long (&cl->backbuf, P_FLOAT (pr, 1)); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 4); - MSG_WriteLong (&demo.dbuf->sz, P_FLOAT (pr, 1)); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 4); + MSG_WriteLong (dbuf, P_FLOAT (pr, 1)); } } else MSG_WriteLong (WriteDest (pr), P_FLOAT (pr, 1)); @@ -1234,9 +1242,10 @@ PF_WriteAngle (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 1); MSG_ReliableWrite_Angle (&cl->backbuf, P_FLOAT (pr, 1)); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 1); - MSG_WriteAngle (&demo.dbuf->sz, P_FLOAT (pr, 1)); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 1); + MSG_WriteAngle (dbuf, P_FLOAT (pr, 1)); } } else MSG_WriteAngle (WriteDest (pr), P_FLOAT (pr, 1)); @@ -1253,9 +1262,10 @@ PF_WriteCoord (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 2); MSG_ReliableWrite_Coord (&cl->backbuf, P_FLOAT (pr, 1)); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 2); - MSG_WriteCoord (&demo.dbuf->sz, P_FLOAT (pr, 1)); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 2); + MSG_WriteCoord (dbuf, P_FLOAT (pr, 1)); } } else MSG_WriteCoord (WriteDest (pr), P_FLOAT (pr, 1)); @@ -1274,9 +1284,10 @@ PF_WriteAngleV (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 1); MSG_ReliableWrite_AngleV (&cl->backbuf, ang); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 1); - MSG_WriteAngleV (&demo.dbuf->sz, ang); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 1); + MSG_WriteAngleV (dbuf, ang); } } else MSG_WriteAngleV (WriteDest (pr), ang); @@ -1295,9 +1306,10 @@ PF_WriteCoordV (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 2); MSG_ReliableWrite_CoordV (&cl->backbuf, coord); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 2); - MSG_WriteCoordV (&demo.dbuf->sz, coord); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 2); + MSG_WriteCoordV (dbuf, coord); } } else MSG_WriteCoordV (WriteDest (pr), coord); @@ -1316,9 +1328,10 @@ PF_WriteString (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 1 + strlen (str)); MSG_ReliableWrite_String (&cl->backbuf, str); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 1 + strlen (str)); - MSG_WriteString (&demo.dbuf->sz, str); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 1 + strlen (str)); + MSG_WriteString (dbuf, str); } } else MSG_WriteString (WriteDest (pr), str); @@ -1337,9 +1350,10 @@ PF_WriteEntity (progs_t *pr) MSG_ReliableCheckBlock (&cl->backbuf, 2); MSG_ReliableWrite_Short (&cl->backbuf, ent); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, 2); - MSG_WriteShort (&demo.dbuf->sz, ent); + if (sv.recorders) { + sizebuf_t *dbuf; + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, 2); + MSG_WriteShort (dbuf, ent); } } else MSG_WriteShort (WriteDest (pr), ent); diff --git a/qw/source/sv_pr_qwe.c b/qw/source/sv_pr_qwe.c index b7ddaadf2..a334560f6 100644 --- a/qw/source/sv_pr_qwe.c +++ b/qw/source/sv_pr_qwe.c @@ -53,9 +53,10 @@ const char rcsid[] = "$Id$"; #include "QF/sys.h" #include "QF/va.h" -#include "sv_demo.h" +#include "server.h" #include "sv_pr_qwe.h" #include "sv_progs.h" +#include "sv_recorder.h" typedef struct { func_t timeofday; @@ -368,7 +369,7 @@ PF_calltimeofday (progs_t * pr) static void PF_forcedemoframe (progs_t * pr) { - demo.forceFrame = 1; + SVR_ForceFrame (); if (P_FLOAT (pr, 0) == 1) SV_SendDemoMessage (); } diff --git a/qw/source/sv_recorder.c b/qw/source/sv_recorder.c new file mode 100644 index 000000000..2adcec427 --- /dev/null +++ b/qw/source/sv_recorder.c @@ -0,0 +1,576 @@ +/* + sv_recorder.c + + Interface for recording server state (server side demos and qtv) + + Copyright (C) 2005 #AUTHOR# + + Author: Bill Currie + Date: 2005/5/1 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static __attribute__ ((unused)) const char rcsid[] = + "$Id$"; + +#ifdef HAVE_STRING_H +# include "string.h" +#endif +#ifdef HAVE_STRINGS_H +# include "strings.h" +#endif +#ifdef HAVE_UNISTD_H +# include "unistd.h" +#endif +#include +#include + +#include "QF/sizebuf.h" + +#include "bothdefs.h" +#include "server.h" +#include "sv_demo.h" +#include "sv_progs.h" +#include "sv_recorder.h" + +typedef struct dbuffer_s { + byte *data; + int start, end, last; + int maxsize; +} dbuffer_t; + +typedef struct header_s { + byte type; + byte full; + int to; + int size; + byte data[1]; +} header_t; + +typedef struct demobuf_s { + sizebuf_t sz; + int bufsize; + header_t *h; +} demobuf_t; + +typedef struct demo_frame_s { + double time; + demobuf_t buf; +} demo_frame_t; + +#define DEMO_FRAMES 64 +#define DEMO_FRAMES_MASK (DEMO_FRAMES - 1) + +typedef struct rec_s { + demobuf_t *dbuf; + dbuffer_t dbuffer; + sizebuf_t datagram; + + int lastto; + int lasttype; + double time, pingtime; + + delta_t delta; + int stats[MAX_CLIENTS][MAX_CL_STATS]; // ouch! + demo_frame_t frames[DEMO_FRAMES]; + int forceFrame; + + int parsecount; + int lastwritten; +} rec_t; + +struct recorder_s { + recorder_t *next; + void (*writer)(sizebuf_t *); + int (*frame)(void); +}; + +static rec_t rec; +static recorder_t *free_recorders; +static recorder_t recorders_list[3]; + +static byte buffer[20 * MAX_MSGLEN]; +static byte datagram_data[MAX_DATAGRAM]; +static byte msg_buffer[MAX_DATAGRAM]; + +#define MIN_DEMO_MEMORY 0x100000 +#define USECACHE (sv_demoUseCache->int_val && svs.demomemsize) +#define MAXSIZE (rec.dbuffer.end < rec.dbuffer.last ? \ + rec.dbuffer.start - rec.dbuffer.end : \ + rec.dbuffer.maxsize - rec.dbuffer.end) + +#define HEADER ((int) &((header_t *) 0)->data) + +static entity_state_t entities[UPDATE_MASK + 1][MAX_DEMO_PACKET_ENTITIES]; +static plent_state_t players[UPDATE_MASK + 1][MAX_CLIENTS]; + +static void +dbuffer_init (dbuffer_t *dbuffer, byte *buf, size_t size) +{ + dbuffer->data = buf; + dbuffer->maxsize = size; + dbuffer->start = 0; + dbuffer->end = 0; + dbuffer->last = 0; +} + +static void +set_msgbuf (demobuf_t *prev, demobuf_t *cur) +{ + // fix the maxsize of previous msg buffer, + // we won't be able to write there anymore + if (prev != NULL) + prev->sz.maxsize = prev->bufsize; + + rec.dbuf = cur; + memset (rec.dbuf, 0, sizeof (*rec.dbuf)); + + rec.dbuf->sz.data = rec.dbuffer.data + rec.dbuffer.end; + rec.dbuf->sz.maxsize = MAXSIZE; +} + +static void +write_msg (sizebuf_t *msg, int type, int to, float time, sizebuf_t *dst) +{ + int msec; + static double prevtime; + + msec = (time - prevtime) * 1000; + prevtime += msec * 0.001; + if (msec > 255) + msec = 255; + if (msec < 2) + msec = 0; + + MSG_WriteByte (dst, msec); + + if (rec.lasttype != type || rec.lastto != to) { + rec.lasttype = type; + rec.lastto = to; + switch (rec.lasttype) { + case dem_all: + MSG_WriteByte (dst, dem_all); + break; + case dem_multiple: + MSG_WriteByte (dst, dem_multiple); + MSG_WriteLong (dst, rec.lastto); + break; + case dem_single: + case dem_stats: + MSG_WriteByte (dst, rec.lasttype | (rec.lastto << 3)); + break; + default: + //XXX SV_Stop (0); + Con_Printf ("bad demo message type:%d", type); + return; + } + } else { + MSG_WriteByte (dst, dem_read); + } + + MSG_WriteLong (dst, msg->cursize); + SZ_Write (dst, msg->data, msg->cursize); +} + +static void +write_to_msg (int type, int to, float time, sizebuf_t *dst) +{ + int pos = 0, oldm, oldd; + header_t *p; + int size; + sizebuf_t msg; + + p = (header_t *) rec.dbuf->sz.data; + rec.dbuf->h = NULL; + + oldm = rec.dbuf->bufsize; + oldd = rec.dbuffer.start; + while (pos < rec.dbuf->bufsize) { + size = p->size; + pos += HEADER + size; + + // no type means we are writing to disk everything + if (!type || (p->type == type && p->to == to)) { + if (size) { + msg.data = p->data; + msg.cursize = size; + + write_msg (&msg, p->type, p->to, time, dst); + } + // data is written so it need to be cleard from demobuf + if (rec.dbuf->sz.data != (byte *) p) + memmove (rec.dbuf->sz.data + size + HEADER, + rec.dbuf->sz.data, (byte *) p - rec.dbuf->sz.data); + + rec.dbuf->bufsize -= size + HEADER; + rec.dbuf->sz.data += size + HEADER; + pos -= size + HEADER; + rec.dbuf->sz.maxsize -= size + HEADER; + rec.dbuffer.start += size + HEADER; + } + // move along + p = (header_t *) (p->data + size); + } + + if (rec.dbuffer.start == rec.dbuffer.last) { + if (rec.dbuffer.start == rec.dbuffer.end) { + rec.dbuffer.end = 0; // rec.dbuffer is empty + rec.dbuf->sz.data = rec.dbuffer.data; + } + // go back to begining of the buffer + rec.dbuffer.last = rec.dbuffer.end; + rec.dbuffer.start = 0; + } +} + +/* + set_buf + + Sets position in the buf for writing to specific client +*/ + +static void +set_buf (byte type, int to) +{ + header_t *p; + int pos = 0; + + p = (header_t *) rec.dbuf->sz.data; + + while (pos < rec.dbuf->bufsize) { + pos += HEADER + p->size; + + if (type == p->type && to == p->to && !p->full) { + rec.dbuf->sz.cursize = pos; + rec.dbuf->h = p; + return; + } + + p = (header_t *) (p->data + p->size); + } + // type && to not exist in the buf, so add it + + p->type = type; + p->to = to; + p->size = 0; + p->full = 0; + + rec.dbuf->bufsize += HEADER; + rec.dbuf->sz.cursize = rec.dbuf->bufsize; + rec.dbuffer.end += HEADER; + rec.dbuf->h = p; +} + +static void +move_buf (void) +{ + // set the last message mark to the previous frame (i/e begining of this + // one) + rec.dbuffer.last = rec.dbuffer.end - rec.dbuf->bufsize; + + // move buffer to the begining of demo buffer + memmove (rec.dbuffer.data, rec.dbuf->sz.data, rec.dbuf->bufsize); + rec.dbuf->sz.data = rec.dbuffer.data; + rec.dbuffer.end = rec.dbuf->bufsize; + rec.dbuf->h = NULL; // it will be setup again + rec.dbuf->sz.maxsize = MAXSIZE + rec.dbuf->bufsize; +} + +static void +clear_rec (void) +{ + int i; + + memset (&rec, 0, sizeof (rec)); + + rec.delta.pvs = dt_pvs_fat; + for (i = 0; i < UPDATE_BACKUP; i++) { + rec.delta.frames[i].entities.entities = entities[i]; + rec.delta.frames[i].players.players = players[i]; + } + dbuffer_init (&rec.dbuffer, buffer, sizeof (buffer)); + set_msgbuf (NULL, &rec.frames[0].buf); + + rec.datagram.maxsize = sizeof (datagram_data); + rec.datagram.data = datagram_data; +} + +void +SVR_Init (void) +{ + recorder_t *r; + unsigned i; + + for (i = 0, r = recorders_list; + i < (sizeof (recorders_list) / sizeof (recorders_list[0])) - 1; + i++, r++) + r->next = r + 1; + r->next = 0; + free_recorders = recorders_list; +} + +recorder_t * +SVR_AddUser (void (*writer)(sizebuf_t *), int (*frame)(void)) +{ + recorder_t *r; + + if (!free_recorders) + return 0; + + if (!sv.recorders) { + clear_rec (); + rec.pingtime = sv.time; + } + + r = free_recorders; + free_recorders = r->next; + + r->next = sv.recorders; + sv.recorders = r; + + r->writer = writer; + r->frame = frame; + + return r; +} + +void +SVR_RemoveUser (recorder_t *r) +{ + recorder_t **_r; + + for (_r = &sv.recorders; *_r; _r = &(*_r)->next) { + if (*_r == r) { + *_r = (*_r)->next; + r->next = free_recorders; + free_recorders = r; + } + } +} + +static void +write_datagram (void) +{ + sizebuf_t *dbuf; + sizebuf_t msg; + + memset (&msg, 0, sizeof (msg)); + msg.data = msg_buffer; + msg.maxsize = sizeof (msg_buffer); + msg.allowoverflow = true; + + if (!rec.delta.delta_sequence) + rec.delta.delta_sequence = -1; + rec.delta.cur_frame = (rec.delta.delta_sequence + 1) & UPDATE_MASK; + rec.delta.out_frame = rec.delta.cur_frame; + SV_WriteEntitiesToClient (&rec.delta, &msg); + dbuf = SVR_WriteBegin (dem_all, 0, msg.cursize); + SZ_Write (dbuf, msg.data, msg.cursize); + // copy the accumulated multicast datagram + // for this client out to the message + if (rec.datagram.cursize) { + dbuf = SVR_WriteBegin (dem_all, 0, rec.datagram.cursize); + SZ_Write (dbuf, rec.datagram.data, rec.datagram.cursize); + SZ_Clear (&rec.datagram); + } + + rec.delta.delta_sequence++; + rec.delta.delta_sequence &= UPDATE_MASK; + rec.frames[rec.parsecount & DEMO_FRAMES_MASK].time = rec.time = sv.time; +} + +void +SVR_WritePacket (void) +{ + demo_frame_t *frame; + double time; + sizebuf_t msg; + recorder_t *r; + + write_datagram (); + + memset (&msg, 0, sizeof (msg)); + msg.data = msg_buffer; + msg.maxsize = sizeof (msg_buffer); + msg.allowoverflow = true; + + frame = &rec.frames[rec.lastwritten & DEMO_FRAMES_MASK]; + time = frame->time; + + rec.dbuf = &frame->buf; + + write_to_msg (0, 0, time, &msg); + + for (r = sv.recorders; r; r = r->next) + r->writer (&msg); + + rec.dbuf = &rec.frames[rec.parsecount & DEMO_FRAMES_MASK].buf; + rec.dbuf->sz.maxsize = MAXSIZE + rec.dbuf->bufsize; + + rec.parsecount++; + set_msgbuf (rec.dbuf, &rec.frames[rec.parsecount & DEMO_FRAMES_MASK].buf); +} + +sizebuf_t * +SVR_WriteBegin (byte type, int to, int size) +{ + byte *p; + qboolean move = false; + + // will it fit? + while (rec.dbuf->bufsize + size + HEADER > rec.dbuf->sz.maxsize) { + // if we reached the end of buffer move msgbuf to the begining + if (!move && rec.dbuffer.end > rec.dbuffer.start) + move = true; + + SVR_WritePacket (); + if (move && rec.dbuffer.start > rec.dbuf->bufsize + HEADER + size) + move_buf (); + } + + if (rec.dbuf->h == NULL || rec.dbuf->h->type != type + || rec.dbuf->h->to != to || rec.dbuf->h->full) { + set_buf (type, to); + } + + if (rec.dbuf->h->size + size > MAX_MSGLEN) { + rec.dbuf->h->full = 1; + set_buf (type, to); + } + // we have to make room for new data + if (rec.dbuf->sz.cursize != rec.dbuf->bufsize) { + p = rec.dbuf->sz.data + rec.dbuf->sz.cursize; + memmove (p + size, p, rec.dbuf->bufsize - rec.dbuf->sz.cursize); + } + + rec.dbuf->bufsize += size; + rec.dbuf->h->size += size; + if ((rec.dbuffer.end += size) > rec.dbuffer.last) + rec.dbuffer.last = rec.dbuffer.end; + return &rec.dbuf->sz; +} + +sizebuf_t * +SVR_Datagram (void) +{ + return &rec.datagram; +} + +void +SVR_ForceFrame (void) +{ + rec.forceFrame = 1; +} + +int +SVR_Frame (void) +{ + recorder_t *r; + + if (rec.forceFrame) { + rec.forceFrame = 0; + return 1; + } + for (r = sv.recorders; r; r = r->next) + if (r->frame ()) + return 1; + return 0; +} + +static void +demo_pings (void) +{ + client_t *client; + int j; + sizebuf_t *dbuf; + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { + if (client->state != cs_spawned && client->state != cs_server) + continue; + + dbuf = SVR_WriteBegin (dem_all, 0, 7); + MSG_WriteByte (dbuf, svc_updateping); + MSG_WriteByte (dbuf, j); + MSG_WriteShort (dbuf, SV_CalcPing (client)); + MSG_WriteByte (dbuf, svc_updatepl); + MSG_WriteByte (dbuf, j); + MSG_WriteByte (dbuf, client->lossage); + } +} + +void +SV_SendDemoMessage (void) +{ + int i, j; + client_t *c; + byte buf[MAX_DATAGRAM]; + sizebuf_t msg, *dbuf; + int stats[MAX_CL_STATS]; + + if (sv_demoPings->value && sv.time - rec.pingtime > sv_demoPings->value) { + demo_pings (); + rec.pingtime = sv.time; + } + + if (!SVR_Frame ()) + return; + + for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++) { + if (c->state != cs_spawned && c->state != cs_server) + continue; + + if (c->spectator) + continue; + + SV_GetStats (c->edict, 0, stats); + + for (j = 0 ; j < MAX_CL_STATS ; j++) + if (stats[j] != rec.stats[i][j]) { + rec.stats[i][j] = stats[j]; + if (stats[j] >=0 && stats[j] <= 255) { + dbuf = SVR_WriteBegin (dem_stats, i, 3); + MSG_WriteByte (dbuf, svc_updatestat); + MSG_WriteByte (dbuf, j); + MSG_WriteByte (dbuf, stats[j]); + } else { + dbuf = SVR_WriteBegin (dem_stats, i, 6); + MSG_WriteByte (dbuf, svc_updatestatlong); + MSG_WriteByte (dbuf, j); + MSG_WriteLong (dbuf, stats[j]); + } + } + } + + // send over all the objects that are in the PVS + // this will include clients, a packetentities, and + // possibly a nails update + msg.data = buf; + msg.maxsize = sizeof (buf); + msg.cursize = 0; + msg.allowoverflow = true; + msg.overflowed = false; + + SVR_WritePacket (); +} diff --git a/qw/source/sv_send.c b/qw/source/sv_send.c index 2ba5832a2..2129d8013 100644 --- a/qw/source/sv_send.c +++ b/qw/source/sv_send.c @@ -52,8 +52,8 @@ static __attribute__ ((unused)) const char rcsid[] = #include "bothdefs.h" #include "compat.h" #include "server.h" -#include "sv_demo.h" #include "sv_progs.h" +#include "sv_recorder.h" #define CHAN_AUTO 0 #define CHAN_WEAPON 1 @@ -356,12 +356,13 @@ SV_Multicast (const vec3_t origin, int to) sv.multicast.cursize); } - if (sv.demorecording) { + if (sv.recorders) { if (reliable) { - DemoWrite_Begin (dem_all, 0, sv.multicast.cursize); - SZ_Write (&demo.dbuf->sz, sv.multicast.data, sv.multicast.cursize); + sizebuf_t *dbuf = SVR_WriteBegin (dem_all, 0, + sv.multicast.cursize); + SZ_Write (dbuf, sv.multicast.data, sv.multicast.cursize); } else - SZ_Write (&demo.datagram, sv.multicast.data, sv.multicast.cursize); + SZ_Write (SVR_Datagram (), sv.multicast.data, sv.multicast.cursize); } SZ_Clear (&sv.multicast); @@ -485,6 +486,7 @@ SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) { edict_t *ent, *other; int i, clnum; + sizebuf_t *dbuf; ent = client->edict; @@ -511,9 +513,9 @@ SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) } // add this to server demo - if (sv.demorecording && msg->cursize) { - DemoWrite_Begin (dem_single, clnum, msg->cursize); - SZ_Write (&demo.dbuf->sz, msg->data, msg->cursize); + if (sv.recorders && msg->cursize) { + dbuf = SVR_WriteBegin (dem_single, clnum, msg->cursize); + SZ_Write (dbuf, msg->data, msg->cursize); } // a fixangle might get lost in a dropped packet. Oh well. @@ -523,16 +525,17 @@ SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) MSG_WriteAngleV (msg, angles); SVfloat (ent, fixangle) = 0; - if (sv.demorecording) { - MSG_WriteByte (&demo.datagram, svc_setangle); - MSG_WriteByte (&demo.datagram, clnum); - MSG_WriteAngleV (&demo.datagram, angles); + if (sv.recorders) { + dbuf = SVR_Datagram (); + MSG_WriteByte (dbuf, svc_setangle); + MSG_WriteByte (dbuf, clnum); + MSG_WriteAngleV (dbuf, angles); } } } -static void -get_stats (edict_t *ent, int spectator, int stats[MAX_CL_STATS]) +void +SV_GetStats (edict_t *ent, int spectator, int stats[]) { memset (stats, 0, sizeof (int) * MAX_CL_STATS); @@ -582,7 +585,7 @@ SV_UpdateClientStats (client_t *client) if (client->spectator && client->spec_track > 0) ent = svs.clients[client->spec_track - 1].edict; - get_stats (ent, client->spectator, stats); + SV_GetStats (ent, client->spectator, stats); for (i = 0; i < MAX_CL_STATS; i++) if (stats[i] != client->stats[i]) { @@ -651,6 +654,7 @@ SV_UpdateToReliableMessages (void) client_t *client; edict_t *ent; int i, j; + sizebuf_t *dbuf; // check for changes to be sent over the reliable streams to all clients for (i = 0, host_client = svs.clients; i < MAX_CLIENTS; i++, host_client++) { @@ -670,12 +674,11 @@ SV_UpdateToReliableMessages (void) SVfloat (host_client->edict, frags)); } - if (sv.demorecording) { - DemoWrite_Begin (dem_all, 0, 4); - MSG_WriteByte (&demo.dbuf->sz, svc_updatefrags); - MSG_WriteByte (&demo.dbuf->sz, i); - MSG_WriteShort (&demo.dbuf->sz, - SVfloat (host_client->edict, frags)); + if (sv.recorders) { + dbuf = SVR_WriteBegin (dem_all, 0, 4); + MSG_WriteByte (dbuf, svc_updatefrags); + MSG_WriteByte (dbuf, i); + MSG_WriteShort (dbuf, SVfloat (host_client->edict, frags)); } host_client->old_frags = SVfloat (host_client->edict, frags); @@ -692,10 +695,10 @@ SV_UpdateToReliableMessages (void) MSG_ReliableWrite_Float (&host_client->backbuf, host_client->entgravity); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, i, 5); - MSG_WriteByte (&demo.dbuf->sz, svc_entgravity); - MSG_WriteFloat (&demo.dbuf->sz, host_client->entgravity); + if (sv.recorders) { + dbuf = SVR_WriteBegin (dem_single, i, 5); + MSG_WriteByte (dbuf, svc_entgravity); + MSG_WriteFloat (dbuf, host_client->entgravity); } } if (sv_fields.maxspeed != -1 @@ -707,10 +710,10 @@ SV_UpdateToReliableMessages (void) MSG_ReliableWrite_Float (&host_client->backbuf, host_client->maxspeed); } - if (sv.demorecording) { - DemoWrite_Begin (dem_single, i, 5); - MSG_WriteByte (&demo.dbuf->sz, svc_maxspeed); - MSG_WriteFloat (&demo.dbuf->sz, host_client->maxspeed); + if (sv.recorders) { + dbuf = SVR_WriteBegin (dem_single, i, 5); + MSG_WriteByte (dbuf, svc_maxspeed); + MSG_WriteFloat (dbuf, host_client->maxspeed); } } } @@ -733,13 +736,13 @@ SV_UpdateToReliableMessages (void) SZ_Write (&client->datagram, sv.datagram.data, sv.datagram.cursize); } - if (sv.demorecording && sv.reliable_datagram.cursize) { - DemoWrite_Begin (dem_all, 0, sv.reliable_datagram.cursize); - SZ_Write (&demo.dbuf->sz, sv.reliable_datagram.data, + if (sv.recorders && sv.reliable_datagram.cursize) { + dbuf = SVR_WriteBegin (dem_all, 0, sv.reliable_datagram.cursize); + SZ_Write (dbuf, sv.reliable_datagram.data, sv.reliable_datagram.cursize); } - if (sv.demorecording) - SZ_Write (&demo.datagram, sv.datagram.data, sv.datagram.cursize); + if (sv.recorders) + SZ_Write (SVR_Datagram (), sv.datagram.data, sv.datagram.cursize); SZ_Clear (&sv.reliable_datagram); SZ_Clear (&sv.datagram); @@ -803,100 +806,6 @@ SV_SendClientMessages (void) } } -void -SV_SendDemoMessage (void) -{ - int i, j; - client_t *c; - byte buf[MAX_DATAGRAM]; - sizebuf_t msg; - int stats[MAX_CL_STATS]; - float min_fps; - - if (sv_demoPings->value && sv.time - demo.pingtime > sv_demoPings->value) { - SV_DemoPings (); - demo.pingtime = sv.time; - } - - if (!sv_demofps->value) - min_fps = 20.0; - else - min_fps = sv_demofps->value; - - min_fps = max (4, min_fps); - if (!demo.forceFrame && sv.time - demo.time < 1.0 / min_fps) - return; - demo.forceFrame = 0; - - for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++) { - if (c->state != cs_spawned && c->state != cs_server) - continue; - - if (c->spectator) - continue; - - get_stats (c->edict, 0, stats); - - for (j = 0 ; j < MAX_CL_STATS ; j++) - if (stats[j] != demo.stats[i][j]) { - demo.stats[i][j] = stats[j]; - if (stats[j] >=0 && stats[j] <= 255) { - DemoWrite_Begin (dem_stats, i, 3); - MSG_WriteByte (&demo.dbuf->sz, svc_updatestat); - MSG_WriteByte (&demo.dbuf->sz, j); - MSG_WriteByte (&demo.dbuf->sz, stats[j]); - } else { - DemoWrite_Begin (dem_stats, i, 6); - MSG_WriteByte (&demo.dbuf->sz, svc_updatestatlong); - MSG_WriteByte (&demo.dbuf->sz, j); - MSG_WriteLong (&demo.dbuf->sz, stats[j]); - } - } - } - - // send over all the objects that are in the PVS - // this will include clients, a packetentities, and - // possibly a nails update - msg.data = buf; - msg.maxsize = sizeof (buf); - msg.cursize = 0; - msg.allowoverflow = true; - msg.overflowed = false; - - if (!demo.delta.delta_sequence) - demo.delta.delta_sequence = -1; - demo.delta.cur_frame = (demo.delta.delta_sequence + 1) & UPDATE_MASK; - demo.delta.out_frame = demo.delta.cur_frame; - SV_WriteEntitiesToClient (&demo.delta, &msg); - DemoWrite_Begin (dem_all, 0, msg.cursize); - SZ_Write (&demo.dbuf->sz, msg.data, msg.cursize); - // copy the accumulated multicast datagram - // for this client out to the message - if (demo.datagram.cursize) { - DemoWrite_Begin (dem_all, 0, demo.datagram.cursize); - SZ_Write (&demo.dbuf->sz, demo.datagram.data, demo.datagram.cursize); - SZ_Clear (&demo.datagram); - } - - demo.delta.delta_sequence++; - demo.delta.delta_sequence &= UPDATE_MASK; - demo.frames[demo.parsecount & DEMO_FRAMES_MASK].time = demo.time = sv.time; - - // that's a backup of 3sec at 20fps, should be enough - // FIXME make this framerate dependent. - // eg. sv_fps->int_val * sv_packetdelay->float_val - if (demo.parsecount - demo.lastwritten > 60) { - SV_DemoWritePackets (1); - } - - demo.parsecount++; - DemoSetMsgBuf (demo.dbuf, - &demo.frames[demo.parsecount & DEMO_FRAMES_MASK].buf); - - if (sv_demoMaxSize->int_val && demo.size > sv_demoMaxSize->int_val * 1024) - SV_Stop (1); -} - #if defined(_WIN32) && !defined(__GNUC__) # pragma optimize( "", on ) #endif @@ -947,11 +856,12 @@ SV_ClientPrintf (int recorder, client_t *cl, int level, const char *fmt, ...) vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); - if (recorder && sv.demorecording) { - DemoWrite_Begin (dem_single, cl - svs.clients, strlen (string) + 3); - MSG_WriteByte (&demo.dbuf->sz, svc_print); - MSG_WriteByte (&demo.dbuf->sz, level); - MSG_WriteString (&demo.dbuf->sz, string); + if (recorder && sv.recorders) { + sizebuf_t *dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, + strlen (string) + 3); + MSG_WriteByte (dbuf, svc_print); + MSG_WriteByte (dbuf, level); + MSG_WriteString (dbuf, string); } SV_PrintToClient (cl, level, string); @@ -985,11 +895,12 @@ SV_BroadcastPrintf (int level, const char *fmt, ...) SV_PrintToClient (cl, level, string); } - if (sv.demorecording) { - DemoWrite_Begin (dem_all, cl - svs.clients, strlen (string) + 3); - MSG_WriteByte (&demo.dbuf->sz, svc_print); - MSG_WriteByte (&demo.dbuf->sz, level); - MSG_WriteString (&demo.dbuf->sz, string); + if (sv.recorders) { + sizebuf_t *dbuf = SVR_WriteBegin (dem_all, cl - svs.clients, + strlen (string) + 3); + MSG_WriteByte (dbuf, svc_print); + MSG_WriteByte (dbuf, level); + MSG_WriteString (dbuf, string); } } diff --git a/qw/source/sv_user.c b/qw/source/sv_user.c index 94dfe7986..6421088a2 100644 --- a/qw/source/sv_user.c +++ b/qw/source/sv_user.c @@ -62,9 +62,9 @@ static __attribute__ ((unused)) const char rcsid[] = #include "compat.h" #include "pmove.h" #include "server.h" -#include "sv_demo.h" -#include "sv_progs.h" #include "sv_gib.h" +#include "sv_progs.h" +#include "sv_recorder.h" #include "world.h" typedef struct ucmd_s { @@ -757,6 +757,7 @@ SV_Say (qboolean team) const char *t1 = 0, *t2, *type, *fmt; client_t *client; int tmp, j, cls = 0; + sizebuf_t *dbuf; if (Cmd_Argc () < 2) return; @@ -877,7 +878,7 @@ SV_Say (qboolean team) SV_ClientPrintf (0, client, PRINT_CHAT, "%s", text->str); } - if (!sv.demorecording || !cls) { + if (!sv.recorders || !cls) { dstring_delete (text); return; } @@ -885,13 +886,13 @@ SV_Say (qboolean team) // player if (!team && ((host_client->spectator && sv_spectalk->value) || !host_client->spectator)) { - DemoWrite_Begin (dem_all, 0, strlen (text->str) + 3); + dbuf = SVR_WriteBegin (dem_all, 0, strlen (text->str) + 3); } else { - DemoWrite_Begin (dem_multiple, cls, strlen (text->str) + 3); + dbuf = SVR_WriteBegin (dem_multiple, cls, strlen (text->str) + 3); } - MSG_WriteByte (&demo.dbuf->sz, svc_print); - MSG_WriteByte (&demo.dbuf->sz, PRINT_CHAT); - MSG_WriteString (&demo.dbuf->sz, text->str); + MSG_WriteByte (dbuf, svc_print); + MSG_WriteByte (dbuf, PRINT_CHAT); + MSG_WriteString (dbuf, text->str); dstring_delete (text); }