recorder interface to ease writing server state to mvds and qtv. ones this

is working properly, adding qtv support will be trivial.
This commit is contained in:
Bill Currie 2005-05-01 09:07:20 +00:00
parent f65c25a7c2
commit 73a34b86e8
13 changed files with 826 additions and 625 deletions

View file

@ -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,

View file

@ -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

48
qw/include/sv_recorder.h Normal file
View file

@ -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

View file

@ -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) \

View file

@ -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, "");
}
}
}

View file

@ -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)

View file

@ -46,7 +46,6 @@ static __attribute__ ((unused)) const char rcsid[] =
#include "compat.h"
#include "server.h"
#include "sv_demo.h"
#include "sv_progs.h"

View file

@ -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-");

View file

@ -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);

View file

@ -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 ();
}

576
qw/source/sv_recorder.c Normal file
View file

@ -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 <sys/time.h>
#include <time.h>
#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 ();
}

View file

@ -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);
}
}

View file

@ -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);
}