Quake II RTX doxygen
1.0 dev
|
|
Go to the documentation of this file.
24 #include "server/mvd/protocol.h"
26 #define FOR_EACH_GTV(client) \
27 LIST_FOR_EACH(gtv_client_t, client, >v_client_list, entry)
29 #define FOR_EACH_ACTIVE_GTV(client) \
30 LIST_FOR_EACH(gtv_client_t, client, >v_active_list, active)
47 byte buffer[MAX_GTC_MSGLEN + 4];
50 char name[MAX_CLIENT_NAME];
51 char version[MAX_QPATH];
111 static void mvd_error(
const char *reason);
116 static void flush_stream(
gtv_client_t *client,
int flush);
121 static void rec_start(qhandle_t demofile);
168 char buffer[MAX_OSPATH];
176 Com_Printf(
"Usage: %s <filename>\n",
Cmd_Argv(0));
195 Com_Printf(
"Auto-recording local MVD to %s\n", buffer);
207 Com_Printf(
"Not recording a local MVD.\n");
211 Com_Printf(
"Stopped local MVD auto-recording.\n");
220 {
"stopsound", NULL },
222 {
"screenshot", NULL },
245 if ((u = Com_Find(
dummy_cmds, cmd)) != NULL) {
255 Com_WPrintf(
"%s: runaway alias loop\n", __func__);
268 Com_DPrintf(
"dummy forward: %s\n", line);
273 size_t length, qboolean reliable)
286 text = (
char *)(data + 1);
287 Com_DPrintf(
"dummy stufftext: %s\n", text);
326 for (i = 0; i < j; i++) {
336 #define MVD_USERINFO1 \
337 "\\name\\[MVDSPEC]\\skin\\male/grunt"
339 #define MVD_USERINFO2 \
340 "\\mvdspec\\" STRINGIFY(PROTOCOL_VERSION_MVD_CURRENT) "\\ip\\loopback"
345 char userinfo[MAX_INFO_STRING * 2];
355 Com_DPrintf(
"Dummy MVD client disabled\n");
360 Com_DPrintf(
"Dummy MVD client not supported by game\n");
367 Com_EPrintf(
"No slot for dummy MVD client\n");
371 memset(newcl, 0,
sizeof(*newcl));
379 newcl->
netchan->remote_address.type = NA_LOOPBACK;
381 List_Init(&newcl->
entry);
383 if (
g_features->integer & GMF_EXTRA_USERINFO) {
389 userinfo[strlen(userinfo) + 1] = 0;
397 allow =
ge->ClientConnect(newcl->
edict, userinfo);
403 s =
"Connection refused";
405 Com_EPrintf(
"Dummy MVD client rejected by game: %s\n", s);
431 memset(&cmd, 0,
sizeof(cmd));
432 cmd.msec = BASE_FRAMETIME;
483 if ((
g_features->integer & GMF_PROPERINUSE) && !ent->inuse) {
506 if (!ent->client->ps.fov) {
516 if (ent->client->ps.pmove.pm_type == PM_SPECTATOR) {
521 if ((ent->svflags & SVF_NOCLIENT) || !
ES_INUSE(&ent->s)) {
534 if (ent->client->ps.pmove.pm_type == PM_FREEZE) {
539 if (ent->client->ps.pmove.pm_flags & PMF_NO_PREDICTION) {
548 if ((
g_features->integer & GMF_PROPERINUSE) && !ent->inuse) {
552 if (ent->svflags & SVF_NOCLIENT) {
566 memset(
mvd.
entities, 0,
sizeof(entity_packed_t) * MAX_EDICTS);
581 for (i = 1; i <
ge->num_edicts; i++) {
602 int flags, extra, portalbytes;
603 byte portalbits[MAX_MAP_PORTAL_BYTES];
613 extra |= MVF_NOMSGS << SVCMD_BITS;
628 for (i = 0; i < MAX_CONFIGSTRINGS; i++) {
633 length = strlen(
string);
634 if (length > MAX_QPATH) {
639 MSG_WriteData(
string, length);
647 MSG_WriteData(portalbits, portalbytes);
652 flags |= MSG_PS_IGNORE_BLEND;
655 flags |= MSG_PS_IGNORE_GUNINDEX | MSG_PS_IGNORE_GUNFRAMES;
657 for (i = 0, ps =
mvd.
players; i < sv_maxclients->integer; i++, ps++) {
659 if (!PPS_INUSE(ps)) {
660 extra |= MSG_PS_REMOVE;
662 MSG_WriteDeltaPlayerstate_Packet(NULL, ps, i, flags | extra);
667 for (i = 1, es =
mvd.
entities + 1; i < ge->num_edicts; i++, es++) {
668 flags = MSG_ES_UMASK;
669 if ((j = es->number) != 0) {
670 if (i <= sv_maxclients->integer) {
672 if (PPS_INUSE(ps) && ps->pmove.pm_type == PM_NORMAL) {
673 flags |= MSG_ES_FIRSTPERSON;
677 flags |= MSG_ES_REMOVE;
688 if (!(flags & MSG_ES_FIRSTPERSON)) {
689 VectorCopy(src->origin, dst->origin);
690 VectorCopy(src->angles, dst->angles);
691 VectorCopy(src->old_origin, dst->old_origin);
693 dst->modelindex = src->modelindex;
694 dst->modelindex2 = src->modelindex2;
695 dst->modelindex3 = src->modelindex3;
696 dst->modelindex4 = src->modelindex4;
697 dst->frame = src->frame;
698 dst->skinnum = src->skinnum;
699 dst->effects = src->effects;
700 dst->renderfx = src->renderfx;
701 dst->solid = src->solid;
702 dst->sound = src->sound;
713 player_packed_t *oldps, newps;
714 entity_packed_t *oldes, newes;
716 int flags, portalbytes;
717 byte portalbits[MAX_MAP_PORTAL_BYTES];
725 MSG_WriteData(portalbits, portalbytes);
727 flags = MSG_PS_IGNORE_PREDICTION | MSG_PS_IGNORE_DELTAANGLES;
729 flags |= MSG_PS_IGNORE_BLEND;
732 flags |= MSG_PS_IGNORE_GUNINDEX | MSG_PS_IGNORE_GUNFRAMES;
741 if (PPS_INUSE(oldps)) {
743 MSG_WriteDeltaPlayerstate_Packet(NULL, NULL, i, flags);
744 PPS_INUSE(oldps) = qfalse;
752 if (PPS_INUSE(oldps)) {
756 MSG_WriteDeltaPlayerstate_Packet(oldps, &newps, i, flags);
759 MSG_WriteDeltaPlayerstate_Packet(oldps, &newps, i,
760 flags | MSG_PS_FORCE);
765 PPS_INUSE(oldps) = qtrue;
771 for (i = 1; i <
ge->num_edicts; i++) {
784 if (ent->s.number != i) {
785 Com_WPrintf(
"%s: fixing ent->s.number: %d to %d\n",
786 __func__, ent->s.number, i);
791 flags = MSG_ES_UMASK;
792 if (i <= sv_maxclients->integer) {
794 if (PPS_INUSE(oldps) && oldps->pmove.pm_type == PM_NORMAL) {
797 flags |= MSG_ES_FIRSTPERSON;
801 if (!oldes->number) {
803 flags |= MSG_ES_FORCE | MSG_ES_NEWENTITY;
827 flush_stream(client, Z_SYNC_FLUSH);
832 Com_DPrintf(
"Suspending MVD streams.\n");
848 flush_stream(client, Z_SYNC_FLUSH);
864 Com_DPrintf(
"Resuming MVD streams.\n");
889 if (!delta ||
mvd.
recording || !LIST_EMPTY(>v_active_list)) {
918 Com_DPrintf(
"Enabling server MVD recorder.\n");
943 Com_DPrintf(
"Disabling server MVD recorder.\n");
969 msglen = LittleShort(total);
985 Com_Printf(
"Stopping MVD recording, maximum size reached.\n");
992 Com_Printf(
"Stopping MVD recording, maximum duration reached.\n");
1000 Com_EPrintf(
"Couldn't write local MVD: %s\n",
Q_ErrorString(ret));
1046 mvd_error(
"reliable message overflowed");
1051 Com_WPrintf(
"Unreliable MVD datagram overflowed.\n");
1067 Com_WPrintf(
"Dumping unreliable MVD datagram.\n");
1073 header[0] = total & 255;
1074 header[1] = (total >> 8) & 255;
1075 header[2] = GTS_STREAM_DATA;
1085 flush_stream(client, Z_SYNC_FLUSH);
1133 op = mvd_multicast_all + to;
1140 if (op != mvd_multicast_all && op != mvd_multicast_all_r) {
1155 return !memcmp(
msg_write.data + 1,
"play ", 5);
1169 }
else if (cmd == svc_print) {
1258 int soundindex,
int volume,
1259 int attenuation,
int timeofs)
1261 int extrabits, sendchan;
1269 if (channel & CHAN_NO_PHS_ADD) {
1270 extrabits |= 1 << SVCMD_BITS;
1272 if (channel & CHAN_RELIABLE) {
1274 extrabits |= 2 << SVCMD_BITS;
1281 if (flags & SND_VOLUME)
1283 if (flags & SND_ATTENUATION)
1285 if (flags & SND_OFFSET)
1288 sendchan = (entnum << 3) | (channel & 7);
1305 List_Remove(&client->
entry);
1308 client->
data = NULL;
1314 static void flush_stream(
gtv_client_t *client,
int flush)
1316 fifo_t *fifo = &client->
stream.send;
1317 z_streamp z = &client->z;
1333 data = FIFO_Reserve(fifo, &len);
1340 z->avail_out = (uInt)len;
1342 ret = deflate(z, flush);
1344 len -= z->avail_out;
1346 FIFO_Commit(fifo, len);
1349 }
while (ret == Z_OK);
1361 Com_Printf(
"TCP client %s[%s] dropped: %s\n", client->
name,
1366 if (client->z.
state) {
1368 flush_stream(client, Z_FINISH);
1369 deflateEnd(&client->z);
1373 List_Remove(&client->
active);
1381 fifo_t *fifo = &client->
stream.send;
1392 if (client->z.
state) {
1393 z_streamp z = &client->z;
1396 z->avail_in = (uInt)len;
1399 data = FIFO_Reserve(fifo, &len);
1406 z->avail_out = (uInt)len;
1408 if (deflate(z, Z_NO_FLUSH) != Z_OK) {
1413 len -= z->avail_out;
1415 FIFO_Commit(fifo, len);
1418 }
while (z->avail_in);
1432 header[0] = len & 255;
1433 header[1] = (len >> 8) & 255;
1458 int protocol, flags;
1468 if (FIFO_Usage(&client->
stream.send)) {
1474 if (protocol != GTV_PROTOCOL_VERSION) {
1494 flags &= ~GTF_STRINGCMDS;
1498 flags &= ~GTF_DEFLATE;
1506 client->
stream.send.data = data;
1507 client->
stream.send.size = size;
1508 client->
data = data;
1509 client->
flags = flags;
1519 if (flags & GTF_DEFLATE) {
1520 client->z.zalloc = SV_zalloc;
1521 client->z.zfree = SV_zfree;
1522 if (deflateInit(&client->z, Z_DEFAULT_COMPRESSION) != Z_OK) {
1529 Com_Printf(
"Accepted MVD client %s[%s]\n", client->
name,
1543 flush_stream(client, Z_SYNC_FLUSH);
1552 drop_client(client,
"unexpected stream start message");
1570 List_Append(>v_active_list, &client->
active);
1586 flush_stream(client, Z_SYNC_FLUSH);
1593 drop_client(client,
"unexpected stream stop message");
1599 List_Delete(&client->
active);
1604 flush_stream(client, Z_SYNC_FLUSH);
1610 char string[MAX_GTC_MSGLEN];
1613 drop_client(client,
"unexpected stringcmd message");
1618 Com_DPrintf(
"ignored stringcmd from %s[%s]\n", client->
name,
1627 Com_DPrintf(
"dummy stringcmd from %s[%s]: %s\n", client->
name,
1644 if (!FIFO_TryRead(&client->
stream.recv, &magic, 4)) {
1647 if (magic != MVD_MAGIC) {
1660 if (!FIFO_TryRead(&client->
stream.recv, &msglen, 2)) {
1663 msglen = LittleShort(msglen);
1668 if (msglen > MAX_GTC_MSGLEN) {
1690 case GTC_STREAM_START:
1693 case GTC_STREAM_STOP:
1720 if (!client->
state) {
1738 if (stream->address.type != client->
stream.address.type)
1740 if (stream->address.type == NA_IP && stream->address.ip.u32[0] != client->
stream.address.ip.u32[0])
1742 if (stream->address.type == NA_IP6 && memcmp(stream->address.ip.u8, client->
stream.address.ip.u8, 48 / CHAR_BIT))
1747 Com_Printf(
"TCP client [%s] rejected: too many connections\n",
1757 Com_Printf(
"TCP client [%s] rejected: no free slots\n",
1763 memset(client, 0,
sizeof(*client));
1766 s->recv.data = client->
buffer;
1767 s->recv.size = MAX_GTC_MSGLEN;
1768 s->send.data = client->
buffer + MAX_GTC_MSGLEN;
1770 s->socket = stream->socket;
1771 s->address = stream->address;
1772 s->state = stream->state;
1776 List_SeqAdd(>v_client_list, &client->
entry);
1777 List_Init(&client->
active);
1779 Com_DPrintf(
"TCP client [%s] accepted\n",
1789 unsigned drop_time = 1000 *
sv_timeout->value;
1799 if (ret == NET_ERROR) {
1802 }
else if (ret == NET_OK) {
1810 switch (client->
state) {
1812 if (delta > zombie_time || !FIFO_Usage(&client->
stream.send)) {
1819 if (delta > ghost_time || delta > drop_time) {
1826 if (delta > drop_time) {
1863 "num name buf lastmsg address state\n"
1864 "--- ---------------- --- ------- --------------------- -----\n");
1867 Com_Printf(
"%3d %-16.16s %3"PRIz
" %7u %-21s ",
1868 count, client->
name, FIFO_Usage(&client->
stream.send),
1872 switch (client->
state) {
1874 Com_Printf(
"ZMBI ");
1877 Com_Printf(
"ASGN ");
1880 Com_Printf(
"CNCT ");
1883 Com_Printf(
"PRIM ");
1886 Com_Printf(
"SEND ");
1901 "num name version\n"
1902 "--- ---------------- -----------------------------------------\n");
1906 Com_Printf(
"%3i %-16.16s %-40.40s\n",
1914 if (LIST_EMPTY(>v_client_list)) {
1915 Com_Printf(
"No TCP clients.\n");
1932 switch (client->
state) {
1956 List_Init(>v_client_list);
1957 List_Init(>v_active_list);
1963 Com_EPrintf(
"Fatal MVD error: %s\n", reason);
2006 Com_DPrintf(
"Spawning MVD dummy for auto-recording\n");
2007 Cvar_Set(
"sv_mvd_suspend_time",
"0");
2030 Com_Printf(
"Stopping MVD recording, "
2031 "maximum number of level changes reached.\n");
2076 sizeof(entity_packed_t) * MAX_EDICTS + MAX_MSGLEN * 2, TAG_SERVER);
2084 Cvar_Set(
"sv_reserved_slots",
"1");
2094 if (ret == NET_OK) {
2097 if (ret == NET_ERROR)
2098 Com_EPrintf(
"%s while opening server TCP port.\n",
NET_ErrorString());
2100 Com_EPrintf(
"Server TCP port already in use.\n");
2132 mvd_drop(type == ERR_RECONNECT ? GTS_RECONNECT : GTS_DISCONNECT);
2141 memset(&
mvd, 0,
sizeof(
mvd));
2161 msglen = LittleShort(
msg_write.cursize);
2170 Com_EPrintf(
"Couldn't write local MVD: %s\n",
Q_ErrorString(ret));
2194 Com_Printf(
"MVD recording is disabled on this server.\n");
2199 Com_Printf(
"Already recording a local MVD.\n");
2235 char buffer[MAX_OSPATH];
2237 unsigned mode = FS_MODE_WRITE;
2241 Com_Printf(
"No server running.\n");
2249 Com_Printf(
"Begin local MVD recording.\n");
2253 mode |= FS_FLAG_GZIP;
2261 Com_Printf(
"Missing filename argument.\n");
2284 Com_Printf(
"Recording local MVD to %s\n", buffer);
2300 Com_Printf(
"Not recording a local MVD.\n");
2304 Com_Printf(
"Stopped local MVD recording.\n");
2322 Com_Printf(
"Can't '%s', dummy MVD client is not active\n",
Cmd_Argv(0));
2377 "wait 50; putaway; wait 10; help;", 0);
2379 "putaway; wait 10; help;", 0);
static void parse_hello(gtv_client_t *client)
static void dummy_add_message(client_t *client, byte *data, size_t length, qboolean reliable)
void * Z_ReservedAlloc(size_t size)
static cvar_t * sv_mvd_disconnect_time
void SV_DelMatch_f(list_t *list)
cvar_t * Cvar_Set(const char *var_name, const char *value)
qhandle_t FS_EasyOpenFile(char *buf, size_t size, unsigned mode, const char *dir, const char *name, const char *ext)
void SV_MvdRunClients(void)
static void rec_write(void)
void SV_MvdStatus_f(void)
player_packed_t * players
void SV_MvdStartSound(int entnum, int channel, int flags, int soundindex, int volume, int attenuation, int timeofs)
static void parse_stringcmd(gtv_client_t *client)
static void mvd_disable(void)
char * NET_AdrToString(const netadr_t *a)
static qboolean players_active(void)
static void mvd_drop(gtv_serverop_t op)
static void remove_client(gtv_client_t *client)
static void dump_clients(void)
static void dummy_spawn(void)
qboolean FIFO_ReadMessage(fifo_t *fifo, size_t msglen)
static void SV_AddGtvHost_f(void)
static void dummy_run(void)
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags)
const char * Q_ErrorString(qerror_t error)
static void dummy_exec_string(cmdbuf_t *buf, const char *line)
void SV_MvdShutdown(error_type_t type)
void SV_MvdRegister(void)
static cvar_t * sv_mvd_begincmd
static cvar_t * sv_mvd_maxtime
static void emit_gamestate(void)
void NET_UpdateStream(netstream_t *s)
void SV_MvdUnicast(edict_t *ent, int clientNum, qboolean reliable)
void SV_MvdEndFrame(void)
void SZ_WriteByte(sizebuf_t *sb, int c)
static void rec_start(qhandle_t demofile)
static cvar_t * sv_mvd_password
void SZ_WriteShort(sizebuf_t *sb, int c)
ssize_t FS_Tell(qhandle_t f)
void Cmd_PrintUsage(const cmd_option_t *opt, const char *suffix)
void NET_CloseStream(netstream_t *s)
void SV_ListMatches_f(list_t *list)
#define FOR_EACH_ACTIVE_GTV(client)
static void SV_DelGtvBan_f(void)
void Cbuf_InsertText(cmdbuf_t *buf, const char *text)
void Cmd_TokenizeString(const char *text, qboolean macroExpand)
static cvar_t * sv_mvd_allow_stufftext
static void dummy_record_f(void)
void MSG_WriteByte(int c)
static void dummy_command(void)
static cvar_t * sv_mvd_suspend_time
entity_packed_t * entities
void SZ_Init(sizebuf_t *buf, void *data, size_t size)
void SV_UserinfoChanged(client_t *cl)
void SV_MvdMapChanged(void)
static void mvd_error(const char *reason)
static void write_message(gtv_client_t *client, gtv_serverop_t op)
cvar_t * sv_reserved_slots
neterr_t NET_Accept(netstream_t *s)
static void parse_stream_start(gtv_client_t *client)
static void SV_MvdStuff_f(void)
#define FOR_EACH_GTV(client)
void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, qboolean short_angles)
neterr_t NET_Listen(qboolean arg)
static const ucmd_t dummy_cmds[]
static void resume_streams(void)
static cmdbuf_t dummy_buffer
void MSG_PackPlayer(player_packed_t *out, const player_state_t *in)
static const cmdreg_t c_svmvd[]
int Cmd_ParseOptions(const cmd_option_t *opt)
const char * NET_ErrorString(void)
void SV_MvdMulticast(int leafnum, multicast_t to)
static void suspend_streams(void)
void Cbuf_AddText(cmdbuf_t *buf, const char *text)
static qboolean mvd_enable(void)
char * Info_ValueForKey(const char *s, const char *key)
static void dump_versions(void)
static gtv_client_t * find_slot(void)
int CM_WritePortalBits(cm_t *cm, byte *buffer)
static void rec_stop(void)
void Cmd_Register(const cmdreg_t *reg)
static cvar_t * sv_mvd_maxclients
void MSG_WriteDeltaEntity(const entity_packed_t *from, const entity_packed_t *to, msgEsFlags_t flags)
static cvar_t * sv_mvd_noblend
static void dummy_stop_f(void)
static void rec_frame(size_t total)
void SV_MvdClientDropped(client_t *client)
static void copy_entity_state(entity_packed_t *dst, const entity_packed_t *src, int flags)
void SV_MvdBeginFrame(void)
size_t Q_strlcpy(char *dst, const char *src, size_t size)
static cvar_t * sv_mvd_bufsize
static void check_clients_activity(void)
static cvar_t * sv_mvd_nogun
void MSG_WriteShort(int c)
char * Cmd_AliasCommand(const char *name)
static void write_stream(gtv_client_t *client, void *data, size_t len)
void SV_DropClient(client_t *client, const char *reason)
static cvar_t * sv_mvd_maxmaps
static cvar_t * sv_mvd_scorecmd
static const cmd_option_t o_record[]
static void dummy_wait_f(void)
static void drop_client(gtv_client_t *client, const char *error)
static qboolean parse_message(gtv_client_t *client)
static void parse_ping(gtv_client_t *client)
static void check_players_activity(void)
void Z_TagReserve(size_t size, memtag_t tag)
ssize_t FS_Write(const void *buf, size_t len, qhandle_t f)
static void SV_DelGtvHost_f(void)
static cvar_t * sv_mvd_capture_flags
static void SV_ListGtvHosts_f(void)
void MSG_WriteString(const char *string)
static LIST_DECL(gtv_client_list)
void MSG_WriteLong(int c)
void Cvar_Command(cvar_t *v)
static void accept_client(netstream_t *stream)
int Cvar_ClampInteger(cvar_t *var, int min, int max)
size_t MSG_ReadString(char *dest, size_t size)
static char dummy_buffer_text[MAX_STRING_CHARS]
static cvar_t * sv_mvd_autorecord
void(* AddMessage)(struct client_s *, byte *, size_t, qboolean)
void Cbuf_Execute(cmdbuf_t *buf)
void Cmd_PrintHelp(const cmd_option_t *opt)
byte buffer[MAX_GTC_MSGLEN+4]
void SV_MvdConfigstring(int index, const char *string, size_t len)
static qboolean player_is_active(const edict_t *ent)
cvar_t * Cvar_FindVar(const char *var_name)
addrmatch_t * SV_MatchAddress(list_t *list, netadr_t *addr)
neterr_t NET_RunStream(netstream_t *s)
static void dummy_forward_f(void)
static void emit_frame(void)
void FS_FCloseFile(qhandle_t f)
static qboolean rec_allowed(void)
static cvar_t * sv_mvd_enable
static void parse_stream_stop(gtv_client_t *client)
char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]
size_t FIFO_Write(fifo_t *fifo, const void *buffer, size_t len)
static qboolean filter_unicast_data(edict_t *ent)
void SV_RemoveClient(client_t *client)
static void build_gamestate(void)
static qboolean auth_client(gtv_client_t *client, const char *password)
void SV_MvdRecord_f(void)
static void SV_AddGtvBan_f(void)
static client_t * dummy_find_slot(void)
static cvar_t * sv_mvd_spawn_dummy
char userinfo[MAX_INFO_STRING]
void SV_AddMatch_f(list_t *list)
static int dummy_create(void)
static qboolean entity_is_active(const edict_t *ent)
void SZ_Clear(sizebuf_t *buf)
static cvar_t * sv_mvd_maxsize
char name[MAX_CLIENT_NAME]
static cvar_t * sv_mvd_nomsgs
static void SV_ListGtvBans_f(void)
void SV_MvdBroadcastPrint(int level, const char *string)