Quake II RTX doxygen  1.0 dev
mvd.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 2003-2008 Andrey Nazarov
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 
19 //
20 // sv_mvd.c - GTV server and local MVD recorder
21 //
22 
23 #include "server.h"
24 #include "server/mvd/protocol.h"
25 
26 #define FOR_EACH_GTV(client) \
27  LIST_FOR_EACH(gtv_client_t, client, &gtv_client_list, entry)
28 
29 #define FOR_EACH_ACTIVE_GTV(client) \
30  LIST_FOR_EACH(gtv_client_t, client, &gtv_active_list, active)
31 
32 typedef struct {
33  list_t entry;
34  list_t active;
36  netstream_t stream;
37 #if USE_ZLIB
38  z_stream z;
39 #endif
40  unsigned msglen;
41  unsigned lastmessage;
42 
43  unsigned flags;
44  unsigned maxbuf;
45  unsigned bufcount;
46 
47  byte buffer[MAX_GTC_MSGLEN + 4]; // recv buffer
48  byte *data; // send buffer
49 
50  char name[MAX_CLIENT_NAME];
51  char version[MAX_QPATH];
52 } gtv_client_t;
53 
54 typedef struct {
55  qboolean enabled;
56  qboolean active;
58  unsigned layout_time;
59  unsigned clients_active;
60  unsigned players_active;
61 
62  // reliable data, may not be discarded
63  sizebuf_t message;
64 
65  // unreliable data, may be discarded
66  sizebuf_t datagram;
67 
68  // delta compressor buffers
69  player_packed_t *players; // [maxclients]
70  entity_packed_t *entities; // [MAX_EDICTS]
71 
72  // local recorder
73  qhandle_t recording;
74  int numlevels; // stop after that many levels
75  int numframes; // stop after that many frames
76 
77  // TCP client pool
78  gtv_client_t *clients; // [sv_mvd_maxclients]
79 } mvd_server_t;
80 
82 
83 // TCP client lists
84 static LIST_DECL(gtv_client_list);
85 static LIST_DECL(gtv_active_list);
86 
87 static LIST_DECL(gtv_white_list);
88 static LIST_DECL(gtv_black_list);
89 
90 static cvar_t *sv_mvd_enable;
91 static cvar_t *sv_mvd_maxclients;
92 static cvar_t *sv_mvd_bufsize;
93 static cvar_t *sv_mvd_password;
94 static cvar_t *sv_mvd_noblend;
95 static cvar_t *sv_mvd_nogun;
96 static cvar_t *sv_mvd_nomsgs;
97 static cvar_t *sv_mvd_maxsize;
98 static cvar_t *sv_mvd_maxtime;
99 static cvar_t *sv_mvd_maxmaps;
100 static cvar_t *sv_mvd_begincmd;
101 static cvar_t *sv_mvd_scorecmd;
102 static cvar_t *sv_mvd_autorecord;
103 static cvar_t *sv_mvd_capture_flags;
104 static cvar_t *sv_mvd_disconnect_time;
105 static cvar_t *sv_mvd_suspend_time;
106 static cvar_t *sv_mvd_allow_stufftext;
107 static cvar_t *sv_mvd_spawn_dummy;
108 
109 static qboolean mvd_enable(void);
110 static void mvd_disable(void);
111 static void mvd_error(const char *reason);
112 
113 static void write_stream(gtv_client_t *client, void *data, size_t len);
114 static void write_message(gtv_client_t *client, gtv_serverop_t op);
115 #if USE_ZLIB
116 static void flush_stream(gtv_client_t *client, int flush);
117 #endif
118 
119 static void rec_stop(void);
120 static qboolean rec_allowed(void);
121 static void rec_start(qhandle_t demofile);
122 static void rec_write(void);
123 
124 
125 /*
126 ==============================================================================
127 
128 DUMMY MVD CLIENT
129 
130 MVD dummy is a fake client maintained entirely server side.
131 Representing MVD observers, this client is used to obtain base playerstate
132 for freefloat observers, receive scoreboard updates and text messages, etc.
133 
134 ==============================================================================
135 */
136 
137 static cmdbuf_t dummy_buffer;
138 static char dummy_buffer_text[MAX_STRING_CHARS];
139 
140 static void dummy_wait_f(void)
141 {
142  int count = atoi(Cmd_Argv(1));
143 
144  if (count < 1) {
145  count = 1;
146  }
147  dummy_buffer.waitCount = count;
148 }
149 
150 static void dummy_command(void)
151 {
152  sv_client = mvd.dummy;
154  ge->ClientCommand(sv_player);
155  sv_client = NULL;
156  sv_player = NULL;
157 }
158 
159 static void dummy_forward_f(void)
160 {
161  Cmd_Shift();
162  Com_DPrintf("dummy cmd: %s %s\n", Cmd_Argv(0), Cmd_Args());
163  dummy_command();
164 }
165 
166 static void dummy_record_f(void)
167 {
168  char buffer[MAX_OSPATH];
169  qhandle_t f;
170 
171  if (!sv_mvd_autorecord->integer) {
172  return;
173  }
174 
175  if (Cmd_Argc() < 2) {
176  Com_Printf("Usage: %s <filename>\n", Cmd_Argv(0));
177  return;
178  }
179 
180  if (!rec_allowed()) {
181  return;
182  }
183 
184  f = FS_EasyOpenFile(buffer, sizeof(buffer), FS_MODE_WRITE,
185  "demos/", Cmd_Argv(1), ".mvd2");
186  if (!f) {
187  return;
188  }
189 
190  if (!mvd_enable()) {
191  FS_FCloseFile(f);
192  return;
193  }
194 
195  Com_Printf("Auto-recording local MVD to %s\n", buffer);
196 
197  rec_start(f);
198 }
199 
200 static void dummy_stop_f(void)
201 {
202  if (!sv_mvd_autorecord->integer) {
203  return;
204  }
205 
206  if (!mvd.recording) {
207  Com_Printf("Not recording a local MVD.\n");
208  return;
209  }
210 
211  Com_Printf("Stopped local MVD auto-recording.\n");
212  rec_stop();
213 }
214 
215 static const ucmd_t dummy_cmds[] = {
216  { "cmd", dummy_forward_f },
217  { "set", Cvar_Set_f },
218  { "alias", Cmd_Alias_f },
219  { "play", NULL },
220  { "stopsound", NULL },
221  { "exec", NULL },
222  { "screenshot", NULL },
223  { "wait", dummy_wait_f },
224  { "record", dummy_record_f },
225  { "stop", dummy_stop_f },
226  { NULL, NULL }
227 };
228 
229 static void dummy_exec_string(cmdbuf_t *buf, const char *line)
230 {
231  char *cmd, *alias;
232  const ucmd_t *u;
233  cvar_t *v;
234 
235  if (!line[0]) {
236  return;
237  }
238 
239  Cmd_TokenizeString(line, qtrue);
240 
241  cmd = Cmd_Argv(0);
242  if (!cmd[0]) {
243  return;
244  }
245  if ((u = Com_Find(dummy_cmds, cmd)) != NULL) {
246  if (u->func) {
247  u->func();
248  }
249  return;
250  }
251 
252  alias = Cmd_AliasCommand(cmd);
253  if (alias) {
254  if (++dummy_buffer.aliasCount == ALIAS_LOOP_COUNT) {
255  Com_WPrintf("%s: runaway alias loop\n", __func__);
256  return;
257  }
258  Cbuf_InsertText(&dummy_buffer, alias);
259  return;
260  }
261 
262  v = Cvar_FindVar(cmd);
263  if (v) {
264  Cvar_Command(v);
265  return;
266  }
267 
268  Com_DPrintf("dummy forward: %s\n", line);
269  dummy_command();
270 }
271 
272 static void dummy_add_message(client_t *client, byte *data,
273  size_t length, qboolean reliable)
274 {
275  char *text;
276 
277  if (!length || !reliable || data[0] != svc_stufftext) {
278  return; // not interesting
279  }
280 
281  if (sv_mvd_allow_stufftext->integer <= 0) {
282  return; // not allowed
283  }
284 
285  data[length] = 0;
286  text = (char *)(data + 1);
287  Com_DPrintf("dummy stufftext: %s\n", text);
288  Cbuf_AddText(&dummy_buffer, text);
289 }
290 
291 static void dummy_spawn(void)
292 {
293  if (!mvd.dummy)
294  return;
295 
296  sv_client = mvd.dummy;
298  ge->ClientBegin(sv_player);
299  sv_client = NULL;
300  sv_player = NULL;
301 
302  if (sv_mvd_begincmd->string[0]) {
304  }
305 
307 
309 }
310 
312 {
313  client_t *c;
314  int i, j;
315 
316  // first check if there is a free reserved slot
317  j = sv_maxclients->integer - sv_reserved_slots->integer;
318  for (i = j; i < sv_maxclients->integer; i++) {
319  c = &svs.client_pool[i];
320  if (!c->state) {
321  return c;
322  }
323  }
324 
325  // then check regular slots
326  for (i = 0; i < j; i++) {
327  c = &svs.client_pool[i];
328  if (!c->state) {
329  return c;
330  }
331  }
332 
333  return NULL;
334 }
335 
336 #define MVD_USERINFO1 \
337  "\\name\\[MVDSPEC]\\skin\\male/grunt"
338 
339 #define MVD_USERINFO2 \
340  "\\mvdspec\\" STRINGIFY(PROTOCOL_VERSION_MVD_CURRENT) "\\ip\\loopback"
341 
342 static int dummy_create(void)
343 {
344  client_t *newcl;
345  char userinfo[MAX_INFO_STRING * 2];
346  char *s;
347  qboolean allow;
348  int number;
349 
350  // do nothing if already created
351  if (mvd.dummy)
352  return 0;
353 
354  if (sv_mvd_spawn_dummy->integer <= 0) {
355  Com_DPrintf("Dummy MVD client disabled\n");
356  return 0;
357  }
358 
359  if (sv_mvd_spawn_dummy->integer == 1 && !(g_features->integer & GMF_MVDSPEC)) {
360  Com_DPrintf("Dummy MVD client not supported by game\n");
361  return 0;
362  }
363 
364  // find a free client slot
365  newcl = dummy_find_slot();
366  if (!newcl) {
367  Com_EPrintf("No slot for dummy MVD client\n");
368  return -1;
369  }
370 
371  memset(newcl, 0, sizeof(*newcl));
372  number = newcl - svs.client_pool;
373  newcl->number = newcl->slot = number;
374  newcl->protocol = -1;
375  newcl->state = cs_connected;
376  newcl->AddMessage = dummy_add_message;
377  newcl->edict = EDICT_NUM(number + 1);
378  newcl->netchan = SV_Mallocz(sizeof(netchan_t));
379  newcl->netchan->remote_address.type = NA_LOOPBACK;
380 
381  List_Init(&newcl->entry);
382 
383  if (g_features->integer & GMF_EXTRA_USERINFO) {
384  strcpy(userinfo, MVD_USERINFO1);
385  strcpy(userinfo + strlen(userinfo) + 1, MVD_USERINFO2);
386  } else {
387  strcpy(userinfo, MVD_USERINFO1);
388  strcat(userinfo, MVD_USERINFO2);
389  userinfo[strlen(userinfo) + 1] = 0;
390  }
391 
392  mvd.dummy = newcl;
393 
394  // get the game a chance to reject this connection or modify the userinfo
395  sv_client = newcl;
396  sv_player = newcl->edict;
397  allow = ge->ClientConnect(newcl->edict, userinfo);
398  sv_client = NULL;
399  sv_player = NULL;
400  if (!allow) {
401  s = Info_ValueForKey(userinfo, "rejmsg");
402  if (!*s) {
403  s = "Connection refused";
404  }
405  Com_EPrintf("Dummy MVD client rejected by game: %s\n", s);
406  Z_Free(newcl->netchan);
407  mvd.dummy = NULL;
408  return -1;
409  }
410 
411  // parse some info from the info strings
412  Q_strlcpy(newcl->userinfo, userinfo, sizeof(newcl->userinfo));
413  SV_UserinfoChanged(newcl);
414 
415  return 1;
416 }
417 
418 static void dummy_run(void)
419 {
420  usercmd_t cmd;
421 
422  if (!mvd.dummy)
423  return;
424 
426  if (dummy_buffer.waitCount > 0) {
427  dummy_buffer.waitCount--;
428  }
429 
430  // run ClientThink to prevent timeouts, etc
431  memset(&cmd, 0, sizeof(cmd));
432  cmd.msec = BASE_FRAMETIME;
433  sv_client = mvd.dummy;
435  ge->ClientThink(sv_player, &cmd);
436  sv_client = NULL;
437  sv_player = NULL;
438 
439  // check if the layout is constantly updated. if not,
440  // game mod has probably closed the scoreboard, open it again
441  if (mvd.active && sv_mvd_scorecmd->string[0]) {
442  if (svs.realtime - mvd.layout_time > 9000) {
445  }
446  }
447 }
448 
449 /*
450 ==============================================================================
451 
452 FRAME UPDATES
453 
454 As MVD stream operates over reliable transport, there is no concept of
455 "baselines" and delta compression is always performed from the last
456 state seen on the map. There is also no support for "nodelta" frames
457 (except the very first frame sent as part of the gamestate).
458 
459 This allows building only one update per frame and multicasting it to
460 several destinations at once.
461 
462 Additional bandwidth savings are performed by filtering out origin and
463 angles updates on player entities, as MVD client can easily recover them
464 from corresponding player states, assuming those are kept in sync by the
465 game mod. This assumption should be generally true for moving players,
466 as vanilla Q2 server performs PVS/PHS culling for them using origin from
467 entity states, but not player states.
468 
469 ==============================================================================
470 */
471 
472 /*
473 Attempts to determine if the given player entity is active,
474 and the given player state should be captured into MVD stream.
475 
476 Entire function is a nasty hack. Ideally a compatible game DLL
477 should do it for us by providing some SVF_* flag or something.
478 */
479 static qboolean player_is_active(const edict_t *ent)
480 {
481  int num;
482 
483  if ((g_features->integer & GMF_PROPERINUSE) && !ent->inuse) {
484  return qfalse;
485  }
486 
487  // not a client at all?
488  if (!ent->client) {
489  return qfalse;
490  }
491 
492  num = NUM_FOR_EDICT(ent) - 1;
493  if (num < 0 || num >= sv_maxclients->integer) {
494  return qfalse;
495  }
496 
497  // by default, check if client is actually connected
498  // it may not be the case for bots!
499  if (sv_mvd_capture_flags->integer & 1) {
500  if (svs.client_pool[num].state != cs_spawned) {
501  return qfalse;
502  }
503  }
504 
505  // first of all, make sure player_state_t is valid
506  if (!ent->client->ps.fov) {
507  return qfalse;
508  }
509 
510  // always capture dummy MVD client
511  if (mvd.dummy && ent == mvd.dummy->edict) {
512  return qtrue;
513  }
514 
515  // never capture spectators
516  if (ent->client->ps.pmove.pm_type == PM_SPECTATOR) {
517  return qfalse;
518  }
519 
520  // check entity visibility
521  if ((ent->svflags & SVF_NOCLIENT) || !ES_INUSE(&ent->s)) {
522  // never capture invisible entities
523  if (sv_mvd_capture_flags->integer & 2) {
524  return qfalse;
525  }
526  } else {
527  // always capture visible entities (default)
528  if (sv_mvd_capture_flags->integer & 4) {
529  return qtrue;
530  }
531  }
532 
533  // they are likely following someone in case of PM_FREEZE
534  if (ent->client->ps.pmove.pm_type == PM_FREEZE) {
535  return qfalse;
536  }
537 
538  // they are likely following someone if PMF_NO_PREDICTION is set
539  if (ent->client->ps.pmove.pm_flags & PMF_NO_PREDICTION) {
540  return qfalse;
541  }
542 
543  return qtrue;
544 }
545 
546 static qboolean entity_is_active(const edict_t *ent)
547 {
548  if ((g_features->integer & GMF_PROPERINUSE) && !ent->inuse) {
549  return qfalse;
550  }
551 
552  if (ent->svflags & SVF_NOCLIENT) {
553  return qfalse;
554  }
555 
556  return ES_INUSE(&ent->s);
557 }
558 
559 // Initializes MVD delta compressor for the first time on this map.
560 static void build_gamestate(void)
561 {
562  edict_t *ent;
563  int i;
564 
565  memset(mvd.players, 0, sizeof(player_packed_t) * sv_maxclients->integer);
566  memset(mvd.entities, 0, sizeof(entity_packed_t) * MAX_EDICTS);
567 
568  // set base player states
569  for (i = 0; i < sv_maxclients->integer; i++) {
570  ent = EDICT_NUM(i + 1);
571 
572  if (!player_is_active(ent)) {
573  continue;
574  }
575 
576  MSG_PackPlayer(&mvd.players[i], &ent->client->ps);
577  PPS_INUSE(&mvd.players[i]) = qtrue;
578  }
579 
580  // set base entity states
581  for (i = 1; i < ge->num_edicts; i++) {
582  ent = EDICT_NUM(i);
583 
584  if (!entity_is_active(ent)) {
585  continue;
586  }
587 
588  MSG_PackEntity(&mvd.entities[i], &ent->s, qfalse);
589  mvd.entities[i].number = i;
590  }
591 }
592 
593 // Writes a single giant message with all the startup info,
594 // followed by an uncompressed (baseline) frame.
595 static void emit_gamestate(void)
596 {
597  char *string;
598  int i, j;
599  player_packed_t *ps;
600  entity_packed_t *es;
601  size_t length;
602  int flags, extra, portalbytes;
603  byte portalbits[MAX_MAP_PORTAL_BYTES];
604 
605  // don't bother writing if there are no active MVD clients
606  if (!mvd.recording && LIST_EMPTY(&gtv_active_list)) {
607  return;
608  }
609 
610  // pack MVD stream flags into extra bits
611  extra = 0;
612  if (sv_mvd_nomsgs->integer && mvd.dummy) {
613  extra |= MVF_NOMSGS << SVCMD_BITS;
614  }
615 
616  // send the serverdata
617  MSG_WriteByte(mvd_serverdata | extra);
618  MSG_WriteLong(PROTOCOL_VERSION_MVD);
619  MSG_WriteShort(PROTOCOL_VERSION_MVD_CURRENT);
621  MSG_WriteString(fs_game->string);
622  if (mvd.dummy)
624  else
625  MSG_WriteShort(-1);
626 
627  // send configstrings
628  for (i = 0; i < MAX_CONFIGSTRINGS; i++) {
629  string = sv.configstrings[i];
630  if (!string[0]) {
631  continue;
632  }
633  length = strlen(string);
634  if (length > MAX_QPATH) {
635  length = MAX_QPATH;
636  }
637 
638  MSG_WriteShort(i);
639  MSG_WriteData(string, length);
640  MSG_WriteByte(0);
641  }
642  MSG_WriteShort(MAX_CONFIGSTRINGS);
643 
644  // send baseline frame
645  portalbytes = CM_WritePortalBits(&sv.cm, portalbits);
646  MSG_WriteByte(portalbytes);
647  MSG_WriteData(portalbits, portalbytes);
648 
649  // send player states
650  flags = 0;
651  if (sv_mvd_noblend->integer) {
652  flags |= MSG_PS_IGNORE_BLEND;
653  }
654  if (sv_mvd_nogun->integer) {
655  flags |= MSG_PS_IGNORE_GUNINDEX | MSG_PS_IGNORE_GUNFRAMES;
656  }
657  for (i = 0, ps = mvd.players; i < sv_maxclients->integer; i++, ps++) {
658  extra = 0;
659  if (!PPS_INUSE(ps)) {
660  extra |= MSG_PS_REMOVE;
661  }
662  MSG_WriteDeltaPlayerstate_Packet(NULL, ps, i, flags | extra);
663  }
664  MSG_WriteByte(CLIENTNUM_NONE);
665 
666  // send entity states
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) {
671  ps = &mvd.players[i - 1];
672  if (PPS_INUSE(ps) && ps->pmove.pm_type == PM_NORMAL) {
673  flags |= MSG_ES_FIRSTPERSON;
674  }
675  }
676  } else {
677  flags |= MSG_ES_REMOVE;
678  }
679  es->number = i;
680  MSG_WriteDeltaEntity(NULL, es, flags);
681  es->number = j;
682  }
683  MSG_WriteShort(0);
684 }
685 
686 static void copy_entity_state(entity_packed_t *dst, const entity_packed_t *src, int flags)
687 {
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);
692  }
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;
703  dst->event = 0;
704 }
705 
706 /*
707 Builds a new delta compressed MVD frame by capturing all entity and player
708 states and calculating portalbits. The same frame is used for all MVD clients,
709 as well as local recorder.
710 */
711 static void emit_frame(void)
712 {
713  player_packed_t *oldps, newps;
714  entity_packed_t *oldes, newes;
715  edict_t *ent;
716  int flags, portalbytes;
717  byte portalbits[MAX_MAP_PORTAL_BYTES];
718  int i;
719 
720  MSG_WriteByte(mvd_frame);
721 
722  // send portal bits
723  portalbytes = CM_WritePortalBits(&sv.cm, portalbits);
724  MSG_WriteByte(portalbytes);
725  MSG_WriteData(portalbits, portalbytes);
726 
727  flags = MSG_PS_IGNORE_PREDICTION | MSG_PS_IGNORE_DELTAANGLES;
728  if (sv_mvd_noblend->integer) {
729  flags |= MSG_PS_IGNORE_BLEND;
730  }
731  if (sv_mvd_nogun->integer) {
732  flags |= MSG_PS_IGNORE_GUNINDEX | MSG_PS_IGNORE_GUNFRAMES;
733  }
734 
735  // send player states
736  for (i = 0; i < sv_maxclients->integer; i++) {
737  oldps = &mvd.players[i];
738  ent = EDICT_NUM(i + 1);
739 
740  if (!player_is_active(ent)) {
741  if (PPS_INUSE(oldps)) {
742  // the old player isn't present in the new message
743  MSG_WriteDeltaPlayerstate_Packet(NULL, NULL, i, flags);
744  PPS_INUSE(oldps) = qfalse;
745  }
746  continue;
747  }
748 
749  // quantize
750  MSG_PackPlayer(&newps, &ent->client->ps);
751 
752  if (PPS_INUSE(oldps)) {
753  // delta update from old position
754  // because the force parm is false, this will not result
755  // in any bytes being emited if the player has not changed at all
756  MSG_WriteDeltaPlayerstate_Packet(oldps, &newps, i, flags);
757  } else {
758  // this is a new player, send it from the last state
759  MSG_WriteDeltaPlayerstate_Packet(oldps, &newps, i,
760  flags | MSG_PS_FORCE);
761  }
762 
763  // shuffle current state to previous
764  *oldps = newps;
765  PPS_INUSE(oldps) = qtrue;
766  }
767 
768  MSG_WriteByte(CLIENTNUM_NONE); // end of packetplayers
769 
770  // send entity states
771  for (i = 1; i < ge->num_edicts; i++) {
772  oldes = &mvd.entities[i];
773  ent = EDICT_NUM(i);
774 
775  if (!entity_is_active(ent)) {
776  if (oldes->number) {
777  // the old entity isn't present in the new message
778  MSG_WriteDeltaEntity(oldes, NULL, MSG_ES_FORCE);
779  oldes->number = 0;
780  }
781  continue;
782  }
783 
784  if (ent->s.number != i) {
785  Com_WPrintf("%s: fixing ent->s.number: %d to %d\n",
786  __func__, ent->s.number, i);
787  ent->s.number = i;
788  }
789 
790  // calculate flags
791  flags = MSG_ES_UMASK;
792  if (i <= sv_maxclients->integer) {
793  oldps = &mvd.players[i - 1];
794  if (PPS_INUSE(oldps) && oldps->pmove.pm_type == PM_NORMAL) {
795  // do not waste bandwidth on origin/angle updates,
796  // client will recover them from player state
797  flags |= MSG_ES_FIRSTPERSON;
798  }
799  }
800 
801  if (!oldes->number) {
802  // this is a new entity, send it from the last state
803  flags |= MSG_ES_FORCE | MSG_ES_NEWENTITY;
804  }
805 
806  // quantize
807  MSG_PackEntity(&newes, &ent->s, qfalse);
808 
809  MSG_WriteDeltaEntity(oldes, &newes, flags);
810 
811  // shuffle current state to previous
812  copy_entity_state(oldes, &newes, flags);
813  oldes->number = i;
814  }
815 
816  MSG_WriteShort(0); // end of packetentities
817 }
818 
819 static void suspend_streams(void)
820 {
821  gtv_client_t *client;
822 
823  FOR_EACH_ACTIVE_GTV(client) {
824  // send stream suspend marker
825  write_message(client, GTS_STREAM_DATA);
826 #if USE_ZLIB
827  flush_stream(client, Z_SYNC_FLUSH);
828 #endif
829  NET_UpdateStream(&client->stream);
830  }
831 
832  Com_DPrintf("Suspending MVD streams.\n");
833  mvd.active = qfalse;
834 }
835 
836 static void resume_streams(void)
837 {
838  gtv_client_t *client;
839 
840  // build and emit gamestate
841  build_gamestate();
842  emit_gamestate();
843 
844  FOR_EACH_ACTIVE_GTV(client) {
845  // send gamestate
846  write_message(client, GTS_STREAM_DATA);
847 #if USE_ZLIB
848  flush_stream(client, Z_SYNC_FLUSH);
849 #endif
850  NET_UpdateStream(&client->stream);
851  }
852 
853  // write it to demofile
854  if (mvd.recording) {
855  rec_write();
856  }
857 
858  // clear gamestate
860 
862  SZ_Clear(&mvd.message);
863 
864  Com_DPrintf("Resuming MVD streams.\n");
865  mvd.active = qtrue;
866 }
867 
868 static qboolean players_active(void)
869 {
870  int i;
871  edict_t *ent;
872 
873  for (i = 0; i < sv_maxclients->integer; i++) {
874  ent = EDICT_NUM(i + 1);
875  if (mvd.dummy && ent == mvd.dummy->edict)
876  continue;
877  if (player_is_active(ent))
878  return qtrue;
879  }
880 
881  return qfalse;
882 }
883 
884 // disconnects MVD dummy if no MVD clients are active for some time
885 static void check_clients_activity(void)
886 {
887  unsigned delta = sv_mvd_disconnect_time->value * 60 * 1000;
888 
889  if (!delta || mvd.recording || !LIST_EMPTY(&gtv_active_list)) {
891  } else if (svs.realtime - mvd.clients_active > delta) {
892  mvd_disable();
893  }
894 }
895 
896 // suspends or resumes MVD streams depending on players activity
897 static void check_players_activity(void)
898 {
899  unsigned delta = sv_mvd_suspend_time->value * 60 * 1000;
900 
901  if (!delta || players_active()) {
903  if (!mvd.active) {
904  resume_streams();
905  }
906  } else if (mvd.active) {
907  if (svs.realtime - mvd.players_active > delta) {
908  suspend_streams();
909  }
910  }
911 }
912 
913 static qboolean mvd_enable(void)
914 {
915  int ret;
916 
917  if (!mvd.enabled)
918  Com_DPrintf("Enabling server MVD recorder.\n");
919 
920  // create and spawn MVD dummy
921  ret = dummy_create();
922  if (ret < 0)
923  return qfalse;
924 
925  if (ret > 0)
926  dummy_spawn();
927 
928  // we are enabled now
929  mvd.enabled = qtrue;
930 
931  // don't timeout
933 
934  // check for activation
936 
937  return qtrue;
938 }
939 
940 static void mvd_disable(void)
941 {
942  if (mvd.enabled)
943  Com_DPrintf("Disabling server MVD recorder.\n");
944 
945  // drop (no-op if already dropped) and remove MVD dummy. NULL out pointer
946  // before calling SV_DropClient to prevent spurious error message.
947  if (mvd.dummy) {
948  client_t *tmp = mvd.dummy;
949  mvd.dummy = NULL;
950  SV_DropClient(tmp, NULL);
951  SV_RemoveClient(tmp);
952  }
953 
955  SZ_Clear(&mvd.message);
956 
957  mvd.enabled = qfalse;
958  mvd.active = qfalse;
959 }
960 
961 static void rec_frame(size_t total)
962 {
963  uint16_t msglen;
964  ssize_t ret;
965 
966  if (!total)
967  return;
968 
969  msglen = LittleShort(total);
970  ret = FS_Write(&msglen, 2, mvd.recording);
971  if (ret != 2)
972  goto fail;
973  ret = FS_Write(mvd.message.data, mvd.message.cursize, mvd.recording);
974  if (ret != mvd.message.cursize)
975  goto fail;
976  ret = FS_Write(msg_write.data, msg_write.cursize, mvd.recording);
977  if (ret != msg_write.cursize)
978  goto fail;
979  ret = FS_Write(mvd.datagram.data, mvd.datagram.cursize, mvd.recording);
980  if (ret != mvd.datagram.cursize)
981  goto fail;
982 
983  if (sv_mvd_maxsize->value > 0 &&
984  FS_Tell(mvd.recording) > sv_mvd_maxsize->value * 1000) {
985  Com_Printf("Stopping MVD recording, maximum size reached.\n");
986  rec_stop();
987  return;
988  }
989 
990  if (sv_mvd_maxtime->value > 0 &&
991  ++mvd.numframes > sv_mvd_maxtime->value * 600) {
992  Com_Printf("Stopping MVD recording, maximum duration reached.\n");
993  rec_stop();
994  return;
995  }
996 
997  return;
998 
999 fail:
1000  Com_EPrintf("Couldn't write local MVD: %s\n", Q_ErrorString(ret));
1001  rec_stop();
1002 }
1003 
1004 /*
1005 ==================
1006 SV_MvdBeginFrame
1007 ==================
1008 */
1010 {
1011  if (mvd.enabled)
1013 
1014  if (mvd.enabled)
1016 }
1017 
1018 /*
1019 ==================
1020 SV_MvdEndFrame
1021 ==================
1022 */
1023 void SV_MvdEndFrame(void)
1024 {
1025  gtv_client_t *client;
1026  size_t total;
1027  byte header[3];
1028 
1029  if (!SV_FRAMESYNC)
1030  return;
1031 
1032  // do nothing if not enabled
1033  if (!mvd.enabled) {
1034  return;
1035  }
1036 
1037  dummy_run();
1038 
1039  // do nothing if not active
1040  if (!mvd.active) {
1041  return;
1042  }
1043 
1044  // if reliable message overflowed, kick all clients
1045  if (mvd.message.overflowed) {
1046  mvd_error("reliable message overflowed");
1047  return;
1048  }
1049 
1050  if (mvd.datagram.overflowed) {
1051  Com_WPrintf("Unreliable MVD datagram overflowed.\n");
1052  SZ_Clear(&mvd.datagram);
1053  }
1054 
1055  // emit a delta update common to all clients
1056  emit_frame();
1057 
1058  // if reliable message and frame update don't fit, kick all clients
1059  if (mvd.message.cursize + msg_write.cursize >= MAX_MSGLEN) {
1060  SZ_Clear(&msg_write);
1061  mvd_error("frame overflowed");
1062  return;
1063  }
1064 
1065  // check if unreliable datagram fits
1066  if (mvd.message.cursize + msg_write.cursize + mvd.datagram.cursize >= MAX_MSGLEN) {
1067  Com_WPrintf("Dumping unreliable MVD datagram.\n");
1068  SZ_Clear(&mvd.datagram);
1069  }
1070 
1071  // build message header
1072  total = mvd.message.cursize + msg_write.cursize + mvd.datagram.cursize + 1;
1073  header[0] = total & 255;
1074  header[1] = (total >> 8) & 255;
1075  header[2] = GTS_STREAM_DATA;
1076 
1077  // send frame to clients
1078  FOR_EACH_ACTIVE_GTV(client) {
1079  write_stream(client, header, sizeof(header));
1080  write_stream(client, mvd.message.data, mvd.message.cursize);
1081  write_stream(client, msg_write.data, msg_write.cursize);
1082  write_stream(client, mvd.datagram.data, mvd.datagram.cursize);
1083 #if USE_ZLIB
1084  if (++client->bufcount > client->maxbuf) {
1085  flush_stream(client, Z_SYNC_FLUSH);
1086  }
1087 #endif
1088  NET_UpdateStream(&client->stream);
1089  }
1090 
1091  // write frame to demofile
1092  if (mvd.recording) {
1093  rec_frame(total - 1);
1094  }
1095 
1096  // clear frame
1097  SZ_Clear(&msg_write);
1098 
1099  // clear datagrams
1100  SZ_Clear(&mvd.datagram);
1101  SZ_Clear(&mvd.message);
1102 }
1103 
1104 
1105 
1106 /*
1107 ==============================================================================
1108 
1109 GAME API HOOKS
1110 
1111 These hooks are called from PF_* functions to add additional
1112 out-of-band data into the MVD stream.
1113 
1114 ==============================================================================
1115 */
1116 
1117 /*
1118 ==============
1119 SV_MvdMulticast
1120 ==============
1121 */
1122 void SV_MvdMulticast(int leafnum, multicast_t to)
1123 {
1124  mvd_ops_t op;
1125  sizebuf_t *buf;
1126  int bits;
1127 
1128  // do nothing if not active
1129  if (!mvd.active) {
1130  return;
1131  }
1132 
1133  op = mvd_multicast_all + to;
1134  buf = to < MULTICAST_ALL_R ? &mvd.datagram : &mvd.message;
1135  bits = (msg_write.cursize >> 8) & 7;
1136 
1137  SZ_WriteByte(buf, op | (bits << SVCMD_BITS));
1138  SZ_WriteByte(buf, msg_write.cursize & 255);
1139 
1140  if (op != mvd_multicast_all && op != mvd_multicast_all_r) {
1141  SZ_WriteShort(buf, leafnum);
1142  }
1143 
1144  SZ_Write(buf, msg_write.data, msg_write.cursize);
1145 }
1146 
1147 // Performs some basic filtering of the unicast data that would be
1148 // otherwise discarded by the MVD client.
1149 static qboolean filter_unicast_data(edict_t *ent)
1150 {
1151  int cmd = msg_write.data[0];
1152 
1153  // discard any stufftexts, except of play sound hacks
1154  if (cmd == svc_stufftext) {
1155  return !memcmp(msg_write.data + 1, "play ", 5);
1156  }
1157 
1158  // if there is no dummy client, don't discard anything
1159  if (!mvd.dummy) {
1160  return qtrue;
1161  }
1162 
1163  if (cmd == svc_layout) {
1164  if (ent != mvd.dummy->edict) {
1165  // discard any layout updates to players
1166  return qfalse;
1167  }
1169  } else if (cmd == svc_print) {
1170  if (ent != mvd.dummy->edict && sv_mvd_nomsgs->integer) {
1171  // optionally discard text messages to players
1172  return qfalse;
1173  }
1174  }
1175 
1176  return qtrue;
1177 }
1178 
1179 /*
1180 ==============
1181 SV_MvdUnicast
1182 ==============
1183 */
1184 void SV_MvdUnicast(edict_t *ent, int clientNum, qboolean reliable)
1185 {
1186  mvd_ops_t op;
1187  sizebuf_t *buf;
1188  int bits;
1189 
1190  // do nothing if not active
1191  if (!mvd.active) {
1192  return;
1193  }
1194 
1195  // discard any data to players not in the game
1196  if (!player_is_active(ent)) {
1197  return;
1198  }
1199 
1200  if (!filter_unicast_data(ent)) {
1201  return;
1202  }
1203 
1204  // decide where should it go
1205  if (reliable) {
1206  op = mvd_unicast_r;
1207  buf = &mvd.message;
1208  } else {
1209  op = mvd_unicast;
1210  buf = &mvd.datagram;
1211  }
1212 
1213  // write it
1214  bits = (msg_write.cursize >> 8) & 7;
1215  SZ_WriteByte(buf, op | (bits << SVCMD_BITS));
1216  SZ_WriteByte(buf, msg_write.cursize & 255);
1217  SZ_WriteByte(buf, clientNum);
1218  SZ_Write(buf, msg_write.data, msg_write.cursize);
1219 }
1220 
1221 /*
1222 ==============
1223 SV_MvdConfigstring
1224 ==============
1225 */
1226 void SV_MvdConfigstring(int index, const char *string, size_t len)
1227 {
1228  if (mvd.active) {
1229  SZ_WriteByte(&mvd.message, mvd_configstring);
1230  SZ_WriteShort(&mvd.message, index);
1231  SZ_Write(&mvd.message, string, len);
1232  SZ_WriteByte(&mvd.message, 0);
1233  }
1234 }
1235 
1236 /*
1237 ==============
1238 SV_MvdBroadcastPrint
1239 ==============
1240 */
1241 void SV_MvdBroadcastPrint(int level, const char *string)
1242 {
1243  if (mvd.active) {
1244  SZ_WriteByte(&mvd.message, mvd_print);
1246  SZ_WriteString(&mvd.message, string);
1247  }
1248 }
1249 
1250 /*
1251 ==============
1252 SV_MvdStartSound
1253 
1254 FIXME: origin will be incorrect on entities not captured this frame
1255 ==============
1256 */
1257 void SV_MvdStartSound(int entnum, int channel, int flags,
1258  int soundindex, int volume,
1259  int attenuation, int timeofs)
1260 {
1261  int extrabits, sendchan;
1262 
1263  // do nothing if not active
1264  if (!mvd.active) {
1265  return;
1266  }
1267 
1268  extrabits = 0;
1269  if (channel & CHAN_NO_PHS_ADD) {
1270  extrabits |= 1 << SVCMD_BITS;
1271  }
1272  if (channel & CHAN_RELIABLE) {
1273  // FIXME: write to mvd.message
1274  extrabits |= 2 << SVCMD_BITS;
1275  }
1276 
1277  SZ_WriteByte(&mvd.datagram, mvd_sound | extrabits);
1278  SZ_WriteByte(&mvd.datagram, flags);
1279  SZ_WriteByte(&mvd.datagram, soundindex);
1280 
1281  if (flags & SND_VOLUME)
1282  SZ_WriteByte(&mvd.datagram, volume);
1283  if (flags & SND_ATTENUATION)
1284  SZ_WriteByte(&mvd.datagram, attenuation);
1285  if (flags & SND_OFFSET)
1286  SZ_WriteByte(&mvd.datagram, timeofs);
1287 
1288  sendchan = (entnum << 3) | (channel & 7);
1289  SZ_WriteShort(&mvd.datagram, sendchan);
1290 }
1291 
1292 
1293 /*
1294 ==============================================================================
1295 
1296 TCP CLIENTS HANDLING
1297 
1298 ==============================================================================
1299 */
1300 
1301 
1302 static void remove_client(gtv_client_t *client)
1303 {
1304  NET_CloseStream(&client->stream);
1305  List_Remove(&client->entry);
1306  if (client->data) {
1307  Z_Free(client->data);
1308  client->data = NULL;
1309  }
1310  client->state = cs_free;
1311 }
1312 
1313 #if USE_ZLIB
1314 static void flush_stream(gtv_client_t *client, int flush)
1315 {
1316  fifo_t *fifo = &client->stream.send;
1317  z_streamp z = &client->z;
1318  byte *data;
1319  size_t len;
1320  int ret;
1321 
1322  if (client->state <= cs_zombie) {
1323  return;
1324  }
1325  if (!z->state) {
1326  return;
1327  }
1328 
1329  z->next_in = NULL;
1330  z->avail_in = 0;
1331 
1332  do {
1333  data = FIFO_Reserve(fifo, &len);
1334  if (!len) {
1335  // FIXME: this is not an error when flushing
1336  return;
1337  }
1338 
1339  z->next_out = data;
1340  z->avail_out = (uInt)len;
1341 
1342  ret = deflate(z, flush);
1343 
1344  len -= z->avail_out;
1345  if (len) {
1346  FIFO_Commit(fifo, len);
1347  client->bufcount = 0;
1348  }
1349  } while (ret == Z_OK);
1350 }
1351 #endif
1352 
1353 static void drop_client(gtv_client_t *client, const char *error)
1354 {
1355  if (client->state <= cs_zombie) {
1356  return;
1357  }
1358 
1359  if (error) {
1360  // notify console
1361  Com_Printf("TCP client %s[%s] dropped: %s\n", client->name,
1362  NET_AdrToString(&client->stream.address), error);
1363  }
1364 
1365 #if USE_ZLIB
1366  if (client->z.state) {
1367  // finish zlib stream
1368  flush_stream(client, Z_FINISH);
1369  deflateEnd(&client->z);
1370  }
1371 #endif
1372 
1373  List_Remove(&client->active);
1374  client->state = cs_zombie;
1375  client->lastmessage = svs.realtime;
1376 }
1377 
1378 
1379 static void write_stream(gtv_client_t *client, void *data, size_t len)
1380 {
1381  fifo_t *fifo = &client->stream.send;
1382 
1383  if (client->state <= cs_zombie) {
1384  return;
1385  }
1386 
1387  if (!len) {
1388  return;
1389  }
1390 
1391 #if USE_ZLIB
1392  if (client->z.state) {
1393  z_streamp z = &client->z;
1394 
1395  z->next_in = data;
1396  z->avail_in = (uInt)len;
1397 
1398  do {
1399  data = FIFO_Reserve(fifo, &len);
1400  if (!len) {
1401  drop_client(client, "overflowed");
1402  return;
1403  }
1404 
1405  z->next_out = data;
1406  z->avail_out = (uInt)len;
1407 
1408  if (deflate(z, Z_NO_FLUSH) != Z_OK) {
1409  drop_client(client, "deflate() failed");
1410  return;
1411  }
1412 
1413  len -= z->avail_out;
1414  if (len) {
1415  FIFO_Commit(fifo, len);
1416  client->bufcount = 0;
1417  }
1418  } while (z->avail_in);
1419  } else
1420 #endif
1421 
1422  if (FIFO_Write(fifo, data, len) != len) {
1423  drop_client(client, "overflowed");
1424  }
1425 }
1426 
1427 static void write_message(gtv_client_t *client, gtv_serverop_t op)
1428 {
1429  byte header[3];
1430  size_t len = msg_write.cursize + 1;
1431 
1432  header[0] = len & 255;
1433  header[1] = (len >> 8) & 255;
1434  header[2] = op;
1435  write_stream(client, header, sizeof(header));
1436 
1437  write_stream(client, msg_write.data, msg_write.cursize);
1438 }
1439 
1440 static qboolean auth_client(gtv_client_t *client, const char *password)
1441 {
1442  if (SV_MatchAddress(&gtv_white_list, &client->stream.address))
1443  return qtrue; // ALLOW whitelisted hosts without password
1444 
1445  if (SV_MatchAddress(&gtv_black_list, &client->stream.address))
1446  return qfalse; // DENY blacklisted hosts
1447 
1448  if (*sv_mvd_password->string == 0)
1449  return qtrue; // ALLOW neutral hosts if password IS NOT set
1450 
1451  // ALLOW neutral hosts if password matches, DENY otherwise
1452  return !strcmp(sv_mvd_password->string, password);
1453 }
1454 
1455 static void parse_hello(gtv_client_t *client)
1456 {
1457  char password[MAX_QPATH];
1458  int protocol, flags;
1459  size_t size;
1460  byte *data;
1461 
1462  if (client->state >= cs_primed) {
1463  drop_client(client, "duplicated hello message");
1464  return;
1465  }
1466 
1467  // client should have already consumed the magic
1468  if (FIFO_Usage(&client->stream.send)) {
1469  drop_client(client, "send buffer not empty");
1470  return;
1471  }
1472 
1473  protocol = MSG_ReadWord();
1474  if (protocol != GTV_PROTOCOL_VERSION) {
1475  write_message(client, GTS_BADREQUEST);
1476  drop_client(client, "bad protocol version");
1477  return;
1478  }
1479 
1480  flags = MSG_ReadLong();
1481  MSG_ReadLong();
1482  MSG_ReadString(client->name, sizeof(client->name));
1483  MSG_ReadString(password, sizeof(password));
1484  MSG_ReadString(client->version, sizeof(client->version));
1485 
1486  // authorize access
1487  if (!auth_client(client, password)) {
1488  write_message(client, GTS_NOACCESS);
1489  drop_client(client, "not authorized");
1490  return;
1491  }
1492 
1493  if (sv_mvd_allow_stufftext->integer >= 0) {
1494  flags &= ~GTF_STRINGCMDS;
1495  }
1496 
1497 #if !USE_ZLIB
1498  flags &= ~GTF_DEFLATE;
1499 #endif
1500 
1502 
1503  // allocate larger send buffer
1504  size = MAX_GTS_MSGLEN * sv_mvd_bufsize->integer;
1505  data = SV_Malloc(size);
1506  client->stream.send.data = data;
1507  client->stream.send.size = size;
1508  client->data = data;
1509  client->flags = flags;
1510  client->state = cs_primed;
1511 
1512  // send hello
1513  MSG_WriteLong(flags);
1514  write_message(client, GTS_HELLO);
1515  SZ_Clear(&msg_write);
1516 
1517 #if USE_ZLIB
1518  // the rest of the stream will be deflated
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) {
1523  drop_client(client, "deflateInit failed");
1524  return;
1525  }
1526  }
1527 #endif
1528 
1529  Com_Printf("Accepted MVD client %s[%s]\n", client->name,
1530  NET_AdrToString(&client->stream.address));
1531 }
1532 
1533 static void parse_ping(gtv_client_t *client)
1534 {
1535  if (client->state < cs_primed) {
1536  return;
1537  }
1538 
1539  // send ping reply
1540  write_message(client, GTS_PONG);
1541 
1542 #if USE_ZLIB
1543  flush_stream(client, Z_SYNC_FLUSH);
1544 #endif
1545 }
1546 
1547 static void parse_stream_start(gtv_client_t *client)
1548 {
1549  int maxbuf;
1550 
1551  if (client->state != cs_primed) {
1552  drop_client(client, "unexpected stream start message");
1553  return;
1554  }
1555 
1556  if (!mvd_enable()) {
1557  write_message(client, GTS_ERROR);
1558  drop_client(client, "couldn't create MVD dummy");
1559  return;
1560  }
1561 
1562  maxbuf = MSG_ReadShort();
1563  if (maxbuf < 10) {
1564  maxbuf = 10;
1565  }
1566 
1567  client->maxbuf = maxbuf;
1568  client->state = cs_spawned;
1569 
1570  List_Append(&gtv_active_list, &client->active);
1571 
1572  // send ack to client
1573  write_message(client, GTS_STREAM_START);
1574 
1575  // send gamestate if active
1576  if (mvd.active) {
1577  emit_gamestate();
1578  write_message(client, GTS_STREAM_DATA);
1579  SZ_Clear(&msg_write);
1580  } else {
1581  // send stream suspend marker
1582  write_message(client, GTS_STREAM_DATA);
1583  }
1584 
1585 #if USE_ZLIB
1586  flush_stream(client, Z_SYNC_FLUSH);
1587 #endif
1588 }
1589 
1590 static void parse_stream_stop(gtv_client_t *client)
1591 {
1592  if (client->state != cs_spawned) {
1593  drop_client(client, "unexpected stream stop message");
1594  return;
1595  }
1596 
1597  client->state = cs_primed;
1598 
1599  List_Delete(&client->active);
1600 
1601  // send ack to client
1602  write_message(client, GTS_STREAM_STOP);
1603 #if USE_ZLIB
1604  flush_stream(client, Z_SYNC_FLUSH);
1605 #endif
1606 }
1607 
1608 static void parse_stringcmd(gtv_client_t *client)
1609 {
1610  char string[MAX_GTC_MSGLEN];
1611 
1612  if (client->state < cs_primed) {
1613  drop_client(client, "unexpected stringcmd message");
1614  return;
1615  }
1616 
1617  if (!mvd.dummy || !(client->flags & GTF_STRINGCMDS)) {
1618  Com_DPrintf("ignored stringcmd from %s[%s]\n", client->name,
1619  NET_AdrToString(&client->stream.address));
1620  return;
1621  }
1622 
1623  MSG_ReadString(string, sizeof(string));
1624 
1625  Cmd_TokenizeString(string, qfalse);
1626 
1627  Com_DPrintf("dummy stringcmd from %s[%s]: %s\n", client->name,
1628  NET_AdrToString(&client->stream.address), string);
1629  dummy_command();
1630 }
1631 
1632 static qboolean parse_message(gtv_client_t *client)
1633 {
1634  uint32_t magic;
1635  uint16_t msglen;
1636  int cmd;
1637 
1638  if (client->state <= cs_zombie) {
1639  return qfalse;
1640  }
1641 
1642  // check magic
1643  if (client->state < cs_connected) {
1644  if (!FIFO_TryRead(&client->stream.recv, &magic, 4)) {
1645  return qfalse;
1646  }
1647  if (magic != MVD_MAGIC) {
1648  drop_client(client, "not a MVD/GTV stream");
1649  return qfalse;
1650  }
1651  client->state = cs_connected;
1652 
1653  // send it back
1654  write_stream(client, &magic, 4);
1655  return qfalse;
1656  }
1657 
1658  // parse msglen
1659  if (!client->msglen) {
1660  if (!FIFO_TryRead(&client->stream.recv, &msglen, 2)) {
1661  return qfalse;
1662  }
1663  msglen = LittleShort(msglen);
1664  if (!msglen) {
1665  drop_client(client, "end of stream");
1666  return qfalse;
1667  }
1668  if (msglen > MAX_GTC_MSGLEN) {
1669  drop_client(client, "oversize message");
1670  return qfalse;
1671  }
1672  client->msglen = msglen;
1673  }
1674 
1675  // read this message
1676  if (!FIFO_ReadMessage(&client->stream.recv, client->msglen)) {
1677  return qfalse;
1678  }
1679 
1680  client->msglen = 0;
1681 
1682  cmd = MSG_ReadByte();
1683  switch (cmd) {
1684  case GTC_HELLO:
1685  parse_hello(client);
1686  break;
1687  case GTC_PING:
1688  parse_ping(client);
1689  break;
1690  case GTC_STREAM_START:
1691  parse_stream_start(client);
1692  break;
1693  case GTC_STREAM_STOP:
1694  parse_stream_stop(client);
1695  break;
1696  case GTC_STRINGCMD:
1697  parse_stringcmd(client);
1698  break;
1699  default:
1700  drop_client(client, "unknown command byte");
1701  return qfalse;
1702  }
1703 
1704  if (msg_read.readcount > msg_read.cursize) {
1705  drop_client(client, "read past end of message");
1706  return qfalse;
1707  }
1708 
1709  client->lastmessage = svs.realtime; // don't timeout
1710  return qtrue;
1711 }
1712 
1714 {
1715  gtv_client_t *client;
1716  int i;
1717 
1718  for (i = 0; i < sv_mvd_maxclients->integer; i++) {
1719  client = &mvd.clients[i];
1720  if (!client->state) {
1721  return client;
1722  }
1723  }
1724 
1725  return NULL;
1726 }
1727 
1728 static void accept_client(netstream_t *stream)
1729 {
1730  gtv_client_t *client;
1731  netstream_t *s;
1732 
1733  // limit number of connections from single IPv4 address or /48 IPv6 network
1734  if (sv_iplimit->integer > 0) {
1735  int count = 0;
1736 
1737  FOR_EACH_GTV(client) {
1738  if (stream->address.type != client->stream.address.type)
1739  continue;
1740  if (stream->address.type == NA_IP && stream->address.ip.u32[0] != client->stream.address.ip.u32[0])
1741  continue;
1742  if (stream->address.type == NA_IP6 && memcmp(stream->address.ip.u8, client->stream.address.ip.u8, 48 / CHAR_BIT))
1743  continue;
1744  count++;
1745  }
1746  if (count >= sv_iplimit->integer) {
1747  Com_Printf("TCP client [%s] rejected: too many connections\n",
1748  NET_AdrToString(&stream->address));
1749  NET_CloseStream(stream);
1750  return;
1751  }
1752  }
1753 
1754  // find a free client slot
1755  client = find_slot();
1756  if (!client) {
1757  Com_Printf("TCP client [%s] rejected: no free slots\n",
1758  NET_AdrToString(&stream->address));
1759  NET_CloseStream(stream);
1760  return;
1761  }
1762 
1763  memset(client, 0, sizeof(*client));
1764 
1765  s = &client->stream;
1766  s->recv.data = client->buffer;
1767  s->recv.size = MAX_GTC_MSGLEN;
1768  s->send.data = client->buffer + MAX_GTC_MSGLEN;
1769  s->send.size = 4; // need no more than that initially
1770  s->socket = stream->socket;
1771  s->address = stream->address;
1772  s->state = stream->state;
1773 
1774  client->lastmessage = svs.realtime;
1775  client->state = cs_assigned;
1776  List_SeqAdd(&gtv_client_list, &client->entry);
1777  List_Init(&client->active);
1778 
1779  Com_DPrintf("TCP client [%s] accepted\n",
1780  NET_AdrToString(&stream->address));
1781 }
1782 
1784 {
1785  gtv_client_t *client;
1786  neterr_t ret;
1787  netstream_t stream;
1788  unsigned zombie_time = 1000 * sv_zombietime->value;
1789  unsigned drop_time = 1000 * sv_timeout->value;
1790  unsigned ghost_time = 1000 * sv_ghostime->value;
1791  unsigned delta;
1792 
1793  if (!mvd.clients) {
1794  return; // do nothing if disabled
1795  }
1796 
1797  // accept new connections
1798  ret = NET_Accept(&stream);
1799  if (ret == NET_ERROR) {
1800  Com_DPrintf("%s from %s, ignored\n", NET_ErrorString(),
1802  } else if (ret == NET_OK) {
1803  accept_client(&stream);
1804  }
1805 
1806  // run existing connections
1807  FOR_EACH_GTV(client) {
1808  // check timeouts
1809  delta = svs.realtime - client->lastmessage;
1810  switch (client->state) {
1811  case cs_zombie:
1812  if (delta > zombie_time || !FIFO_Usage(&client->stream.send)) {
1813  remove_client(client);
1814  continue;
1815  }
1816  break;
1817  case cs_assigned:
1818  case cs_connected:
1819  if (delta > ghost_time || delta > drop_time) {
1820  drop_client(client, "request timed out");
1821  remove_client(client);
1822  continue;
1823  }
1824  break;
1825  default:
1826  if (delta > drop_time) {
1827  drop_client(client, "connection timed out");
1828  remove_client(client);
1829  continue;
1830  }
1831  break;
1832  }
1833 
1834  // run network stream
1835  ret = NET_RunStream(&client->stream);
1836  switch (ret) {
1837  case NET_AGAIN:
1838  break;
1839  case NET_OK:
1840  // parse the message
1841  while (parse_message(client))
1842  ;
1843  NET_UpdateStream(&client->stream);
1844  break;
1845  case NET_CLOSED:
1846  drop_client(client, "EOF from client");
1847  remove_client(client);
1848  break;
1849  case NET_ERROR:
1850  drop_client(client, "connection reset by peer");
1851  remove_client(client);
1852  break;
1853  }
1854  }
1855 }
1856 
1857 static void dump_clients(void)
1858 {
1859  gtv_client_t *client;
1860  int count;
1861 
1862  Com_Printf(
1863  "num name buf lastmsg address state\n"
1864  "--- ---------------- --- ------- --------------------- -----\n");
1865  count = 0;
1866  FOR_EACH_GTV(client) {
1867  Com_Printf("%3d %-16.16s %3"PRIz" %7u %-21s ",
1868  count, client->name, FIFO_Usage(&client->stream.send),
1869  svs.realtime - client->lastmessage,
1870  NET_AdrToString(&client->stream.address));
1871 
1872  switch (client->state) {
1873  case cs_zombie:
1874  Com_Printf("ZMBI ");
1875  break;
1876  case cs_assigned:
1877  Com_Printf("ASGN ");
1878  break;
1879  case cs_connected:
1880  Com_Printf("CNCT ");
1881  break;
1882  case cs_primed:
1883  Com_Printf("PRIM ");
1884  break;
1885  default:
1886  Com_Printf("SEND ");
1887  break;
1888  }
1889  Com_Printf("\n");
1890 
1891  count++;
1892  }
1893 }
1894 
1895 static void dump_versions(void)
1896 {
1897  gtv_client_t *client;
1898  int count;
1899 
1900  Com_Printf(
1901  "num name version\n"
1902  "--- ---------------- -----------------------------------------\n");
1903 
1904  FOR_EACH_GTV(client) {
1905  count = 0;
1906  Com_Printf("%3i %-16.16s %-40.40s\n",
1907  count, client->name, client->version);
1908  count++;
1909  }
1910 }
1911 
1912 void SV_MvdStatus_f(void)
1913 {
1914  if (LIST_EMPTY(&gtv_client_list)) {
1915  Com_Printf("No TCP clients.\n");
1916  } else {
1917  if (Cmd_Argc() > 1) {
1918  dump_versions();
1919  } else {
1920  dump_clients();
1921  }
1922  }
1923  Com_Printf("\n");
1924 }
1925 
1926 static void mvd_drop(gtv_serverop_t op)
1927 {
1928  gtv_client_t *client;
1929 
1930  // drop GTV clients
1931  FOR_EACH_GTV(client) {
1932  switch (client->state) {
1933  case cs_spawned:
1934  case cs_primed:
1935  write_message(client, op);
1936  drop_client(client, NULL);
1937  NET_UpdateStream(&client->stream);
1938  break;
1939  default:
1940  drop_client(client, NULL);
1941  remove_client(client);
1942  break;
1943  }
1944  }
1945 
1946  // update I/O status
1947  NET_Sleep(0);
1948 
1949  // push error message
1950  FOR_EACH_GTV(client) {
1951  NET_RunStream(&client->stream);
1952  NET_RunStream(&client->stream);
1953  remove_client(client);
1954  }
1955 
1956  List_Init(&gtv_client_list);
1957  List_Init(&gtv_active_list);
1958 }
1959 
1960 // something bad happened, remove all clients
1961 static void mvd_error(const char *reason)
1962 {
1963  Com_EPrintf("Fatal MVD error: %s\n", reason);
1964 
1965  // stop recording
1966  rec_stop();
1967 
1968  mvd_drop(GTS_ERROR);
1969 
1970  mvd_disable();
1971 }
1972 
1973 /*
1974 ==============================================================================
1975 
1976 SERVER HOOKS
1977 
1978 These hooks are called by server code when some event occurs.
1979 
1980 ==============================================================================
1981 */
1982 
1983 /*
1984 ==================
1985 SV_MvdMapChanged
1986 
1987 Server has just changed the map, spawn the MVD dummy and go!
1988 ==================
1989 */
1991 {
1992  gtv_client_t *client;
1993  int ret;
1994 
1995  if (!mvd.entities) {
1996  return; // do nothing if disabled
1997  }
1998 
1999  // spawn MVD dummy now if listening for autorecord command
2000  if (sv_mvd_autorecord->integer) {
2001  ret = dummy_create();
2002  if (ret < 0) {
2003  return;
2004  }
2005  if (ret > 0) {
2006  Com_DPrintf("Spawning MVD dummy for auto-recording\n");
2007  Cvar_Set("sv_mvd_suspend_time", "0");
2008  }
2009  }
2010 
2011  dummy_spawn();
2012 
2013  if (mvd.active) {
2014  // build and emit gamestate
2015  build_gamestate();
2016  emit_gamestate();
2017 
2018  // send gamestate to all MVD clients
2019  FOR_EACH_ACTIVE_GTV(client) {
2020  write_message(client, GTS_STREAM_DATA);
2021  NET_UpdateStream(&client->stream);
2022  }
2023  }
2024 
2025  if (mvd.recording) {
2026  int maxlevels = sv_mvd_maxmaps->integer;
2027 
2028  // check if it is time to stop recording
2029  if (maxlevels > 0 && ++mvd.numlevels >= maxlevels) {
2030  Com_Printf("Stopping MVD recording, "
2031  "maximum number of level changes reached.\n");
2032  rec_stop();
2033  } else if (mvd.active) {
2034  // write gamestate to demofile
2035  rec_write();
2036  }
2037  }
2038 
2039  // clear gamestate
2040  SZ_Clear(&msg_write);
2041 
2042  SZ_Clear(&mvd.datagram);
2043  SZ_Clear(&mvd.message);
2044 }
2045 
2046 /*
2047 ==================
2048 SV_MvdClientDropped
2049 
2050 Server has just dropped a client. Drop all TCP clients if that was our MVD
2051 dummy client.
2052 ==================
2053 */
2055 {
2056  if (client == mvd.dummy) {
2057  mvd_error("dummy client was dropped");
2058  }
2059 }
2060 
2061 /*
2062 ==================
2063 SV_MvdInit
2064 
2065 Server is initializing, prepare MVD server for this game.
2066 ==================
2067 */
2068 void SV_MvdInit(void)
2069 {
2070  if (!sv_mvd_enable->integer) {
2071  return; // do nothing if disabled
2072  }
2073 
2074  // allocate buffers
2075  Z_TagReserve(sizeof(player_packed_t) * sv_maxclients->integer +
2076  sizeof(entity_packed_t) * MAX_EDICTS + MAX_MSGLEN * 2, TAG_SERVER);
2077  SZ_Init(&mvd.message, Z_ReservedAlloc(MAX_MSGLEN), MAX_MSGLEN);
2078  SZ_Init(&mvd.datagram, Z_ReservedAlloc(MAX_MSGLEN), MAX_MSGLEN);
2079  mvd.players = Z_ReservedAlloc(sizeof(player_packed_t) * sv_maxclients->integer);
2080  mvd.entities = Z_ReservedAlloc(sizeof(entity_packed_t) * MAX_EDICTS);
2081 
2082  // reserve the slot for dummy MVD client
2083  if (!sv_reserved_slots->integer) {
2084  Cvar_Set("sv_reserved_slots", "1");
2085  }
2086 
2088 
2089  // open server TCP socket
2090  if (sv_mvd_enable->integer > 1) {
2091  neterr_t ret;
2092 
2093  ret = NET_Listen(qtrue);
2094  if (ret == NET_OK) {
2095  mvd.clients = SV_Mallocz(sizeof(gtv_client_t) * sv_mvd_maxclients->integer);
2096  } else {
2097  if (ret == NET_ERROR)
2098  Com_EPrintf("%s while opening server TCP port.\n", NET_ErrorString());
2099  else
2100  Com_EPrintf("Server TCP port already in use.\n");
2101  Cvar_Set("sv_mvd_enable", "1");
2102  }
2103  }
2104 
2105  dummy_buffer.from = FROM_CONSOLE;
2107  dummy_buffer.maxsize = sizeof(dummy_buffer_text);
2109 }
2110 
2111 /*
2112 ==================
2113 SV_MvdShutdown
2114 
2115 Server is shutting down, clean everything up.
2116 ==================
2117 */
2118 void SV_MvdShutdown(error_type_t type)
2119 {
2120  // stop recording
2121  rec_stop();
2122 
2123  // remove MVD dummy
2124  if (mvd.dummy) {
2126  mvd.dummy = NULL;
2127  }
2128 
2129  memset(&dummy_buffer, 0, sizeof(dummy_buffer));
2130 
2131  // drop all clients
2132  mvd_drop(type == ERR_RECONNECT ? GTS_RECONNECT : GTS_DISCONNECT);
2133 
2134  // free static data
2135  Z_Free(mvd.message.data);
2136  Z_Free(mvd.clients);
2137 
2138  // close server TCP socket
2139  NET_Listen(qfalse);
2140 
2141  memset(&mvd, 0, sizeof(mvd));
2142 }
2143 
2144 
2145 /*
2146 ==============================================================================
2147 
2148 LOCAL MVD RECORDER
2149 
2150 ==============================================================================
2151 */
2152 
2153 static void rec_write(void)
2154 {
2155  uint16_t msglen;
2156  ssize_t ret;
2157 
2158  if (!msg_write.cursize)
2159  return;
2160 
2161  msglen = LittleShort(msg_write.cursize);
2162  ret = FS_Write(&msglen, 2, mvd.recording);
2163  if (ret != 2)
2164  goto fail;
2165  ret = FS_Write(msg_write.data, msg_write.cursize, mvd.recording);
2166  if (ret == msg_write.cursize)
2167  return;
2168 
2169 fail:
2170  Com_EPrintf("Couldn't write local MVD: %s\n", Q_ErrorString(ret));
2171  rec_stop();
2172 }
2173 
2174 // Stops server local MVD recording.
2175 static void rec_stop(void)
2176 {
2177  uint16_t msglen;
2178 
2179  if (!mvd.recording) {
2180  return;
2181  }
2182 
2183  // write demo EOF marker
2184  msglen = 0;
2185  FS_Write(&msglen, 2, mvd.recording);
2186 
2188  mvd.recording = 0;
2189 }
2190 
2191 static qboolean rec_allowed(void)
2192 {
2193  if (!mvd.entities) {
2194  Com_Printf("MVD recording is disabled on this server.\n");
2195  return qfalse;
2196  }
2197 
2198  if (mvd.recording) {
2199  Com_Printf("Already recording a local MVD.\n");
2200  return qfalse;
2201  }
2202 
2203  return qtrue;
2204 }
2205 
2206 static void rec_start(qhandle_t demofile)
2207 {
2208  uint32_t magic;
2209 
2210  mvd.recording = demofile;
2211  mvd.numlevels = 0;
2212  mvd.numframes = 0;
2214 
2215  magic = MVD_MAGIC;
2216  FS_Write(&magic, 4, demofile);
2217 
2218  if (mvd.active) {
2219  emit_gamestate();
2220  rec_write();
2221  SZ_Clear(&msg_write);
2222  }
2223 }
2224 
2225 /*
2226 ==============
2227 SV_MvdRecord_f
2228 
2229 Begins server MVD recording.
2230 Every entity, every playerinfo and every message will be recorded.
2231 ==============
2232 */
2233 void SV_MvdRecord_f(void)
2234 {
2235  char buffer[MAX_OSPATH];
2236  qhandle_t f;
2237  unsigned mode = FS_MODE_WRITE;
2238  int c;
2239 
2240  if (sv.state != ss_game) {
2241  Com_Printf("No server running.\n");
2242  return;
2243  }
2244 
2245  while ((c = Cmd_ParseOptions(o_record)) != -1) {
2246  switch (c) {
2247  case 'h':
2248  Cmd_PrintUsage(o_record, "<filename>");
2249  Com_Printf("Begin local MVD recording.\n");
2251  return;
2252  case 'z':
2253  mode |= FS_FLAG_GZIP;
2254  break;
2255  default:
2256  return;
2257  }
2258  }
2259 
2260  if (!cmd_optarg[0]) {
2261  Com_Printf("Missing filename argument.\n");
2262  Cmd_PrintHint();
2263  return;
2264  }
2265 
2266  if (!rec_allowed()) {
2267  return;
2268  }
2269 
2270  //
2271  // open the demo file
2272  //
2273  f = FS_EasyOpenFile(buffer, sizeof(buffer), mode,
2274  "demos/", cmd_optarg, ".mvd2");
2275  if (!f) {
2276  return;
2277  }
2278 
2279  if (!mvd_enable()) {
2280  FS_FCloseFile(f);
2281  return;
2282  }
2283 
2284  Com_Printf("Recording local MVD to %s\n", buffer);
2285 
2286  rec_start(f);
2287 }
2288 
2289 
2290 /*
2291 ==============
2292 SV_MvdStop_f
2293 
2294 Ends server MVD recording
2295 ==============
2296 */
2297 void SV_MvdStop_f(void)
2298 {
2299  if (!mvd.recording) {
2300  Com_Printf("Not recording a local MVD.\n");
2301  return;
2302  }
2303 
2304  Com_Printf("Stopped local MVD recording.\n");
2305  rec_stop();
2306 }
2307 
2308 /*
2309 ==============================================================================
2310 
2311 CVARS AND COMMANDS
2312 
2313 ==============================================================================
2314 */
2315 
2316 static void SV_MvdStuff_f(void)
2317 {
2318  if (mvd.dummy) {
2320  Cbuf_AddText(&dummy_buffer, "\n");
2321  } else {
2322  Com_Printf("Can't '%s', dummy MVD client is not active\n", Cmd_Argv(0));
2323  }
2324 }
2325 
2326 static void SV_AddGtvHost_f(void)
2327 {
2328  SV_AddMatch_f(&gtv_white_list);
2329 }
2330 static void SV_DelGtvHost_f(void)
2331 {
2332  SV_DelMatch_f(&gtv_white_list);
2333 }
2334 static void SV_ListGtvHosts_f(void)
2335 {
2336  SV_ListMatches_f(&gtv_white_list);
2337 }
2338 
2339 static void SV_AddGtvBan_f(void)
2340 {
2341  SV_AddMatch_f(&gtv_black_list);
2342 }
2343 static void SV_DelGtvBan_f(void)
2344 {
2345  SV_DelMatch_f(&gtv_black_list);
2346 }
2347 static void SV_ListGtvBans_f(void)
2348 {
2349  SV_ListMatches_f(&gtv_black_list);
2350 }
2351 
2352 static const cmdreg_t c_svmvd[] = {
2353  { "mvdstuff", SV_MvdStuff_f },
2354  { "addgtvhost", SV_AddGtvHost_f },
2355  { "delgtvhost", SV_DelGtvHost_f },
2356  { "listgtvhosts", SV_ListGtvHosts_f },
2357  { "addgtvban", SV_AddGtvBan_f },
2358  { "delgtvban", SV_DelGtvBan_f },
2359  { "listgtvbans", SV_ListGtvBans_f },
2360 
2361  { NULL }
2362 };
2363 
2364 void SV_MvdRegister(void)
2365 {
2366  sv_mvd_enable = Cvar_Get("sv_mvd_enable", "0", CVAR_LATCH);
2367  sv_mvd_maxclients = Cvar_Get("sv_mvd_maxclients", "8", CVAR_LATCH);
2368  sv_mvd_bufsize = Cvar_Get("sv_mvd_bufsize", "2", CVAR_LATCH);
2369  sv_mvd_password = Cvar_Get("sv_mvd_password", "", CVAR_PRIVATE);
2370  sv_mvd_maxsize = Cvar_Get("sv_mvd_maxsize", "0", 0);
2371  sv_mvd_maxtime = Cvar_Get("sv_mvd_maxtime", "0", 0);
2372  sv_mvd_maxmaps = Cvar_Get("sv_mvd_maxmaps", "1", 0);
2373  sv_mvd_noblend = Cvar_Get("sv_mvd_noblend", "0", CVAR_LATCH);
2374  sv_mvd_nogun = Cvar_Get("sv_mvd_nogun", "1", CVAR_LATCH);
2375  sv_mvd_nomsgs = Cvar_Get("sv_mvd_nomsgs", "1", CVAR_LATCH);
2376  sv_mvd_begincmd = Cvar_Get("sv_mvd_begincmd",
2377  "wait 50; putaway; wait 10; help;", 0);
2378  sv_mvd_scorecmd = Cvar_Get("sv_mvd_scorecmd",
2379  "putaway; wait 10; help;", 0);
2380  sv_mvd_autorecord = Cvar_Get("sv_mvd_autorecord", "0", CVAR_LATCH);
2381  sv_mvd_capture_flags = Cvar_Get("sv_mvd_capture_flags", "5", 0);
2382  sv_mvd_disconnect_time = Cvar_Get("sv_mvd_disconnect_time", "15", 0);
2383  sv_mvd_suspend_time = Cvar_Get("sv_mvd_suspend_time", "5", 0);
2384  sv_mvd_allow_stufftext = Cvar_Get("sv_mvd_allow_stufftext", "0", CVAR_LATCH);
2385  sv_mvd_spawn_dummy = Cvar_Get("sv_mvd_spawn_dummy", "1", 0);
2386 
2388 }
2389 
mvd_server_t::players_active
unsigned players_active
Definition: mvd.c:60
parse_hello
static void parse_hello(gtv_client_t *client)
Definition: mvd.c:1455
dummy_add_message
static void dummy_add_message(client_t *client, byte *data, size_t length, qboolean reliable)
Definition: mvd.c:272
Z_ReservedAlloc
void * Z_ReservedAlloc(size_t size)
Definition: zone.c:349
gtv_client_t::entry
list_t entry
Definition: mvd.c:33
sv_mvd_disconnect_time
static cvar_t * sv_mvd_disconnect_time
Definition: mvd.c:104
SV_DelMatch_f
void SV_DelMatch_f(list_t *list)
Definition: commands.c:1119
Cvar_Set
cvar_t * Cvar_Set(const char *var_name, const char *value)
Definition: cvar.c:466
FS_EasyOpenFile
qhandle_t FS_EasyOpenFile(char *buf, size_t size, unsigned mode, const char *dir, const char *name, const char *ext)
Definition: files.c:1846
SV_MvdRunClients
void SV_MvdRunClients(void)
Definition: mvd.c:1783
mvd_server_t::numlevels
int numlevels
Definition: mvd.c:74
mvd_server_t::numframes
int numframes
Definition: mvd.c:75
rec_write
static void rec_write(void)
Definition: mvd.c:2153
cs_spawned
@ cs_spawned
Definition: server.h:192
SV_MvdStatus_f
void SV_MvdStatus_f(void)
Definition: mvd.c:1912
mvd_server_t::players
player_packed_t * players
Definition: mvd.c:69
SV_MvdStartSound
void SV_MvdStartSound(int entnum, int channel, int flags, int soundindex, int volume, int attenuation, int timeofs)
Definition: mvd.c:1257
msg_read
sizebuf_t msg_read
Definition: msg.c:37
parse_stringcmd
static void parse_stringcmd(gtv_client_t *client)
Definition: mvd.c:1608
mvd_disable
static void mvd_disable(void)
Definition: mvd.c:940
mvd_server_t::message
sizebuf_t message
Definition: mvd.c:63
svs
server_static_t svs
Definition: init.c:21
NET_AdrToString
char * NET_AdrToString(const netadr_t *a)
Definition: net.c:257
cmd_optarg
char * cmd_optarg
Definition: cmd.c:848
sv_iplimit
cvar_t * sv_iplimit
Definition: main.c:87
players_active
static qboolean players_active(void)
Definition: mvd.c:868
mvd_drop
static void mvd_drop(gtv_serverop_t op)
Definition: mvd.c:1926
gtv_client_t::msglen
unsigned msglen
Definition: mvd.c:40
remove_client
static void remove_client(gtv_client_t *client)
Definition: mvd.c:1302
gtv_client_t::version
char version[MAX_QPATH]
Definition: mvd.c:51
client_s::protocol
int protocol
Definition: server.h:324
gtv_client_t::flags
unsigned flags
Definition: mvd.c:43
dump_clients
static void dump_clients(void)
Definition: mvd.c:1857
dummy_spawn
static void dummy_spawn(void)
Definition: mvd.c:291
FIFO_ReadMessage
qboolean FIFO_ReadMessage(fifo_t *fifo, size_t msglen)
Definition: fifo.c:89
SV_AddGtvHost_f
static void SV_AddGtvHost_f(void)
Definition: mvd.c:2326
dummy_run
static void dummy_run(void)
Definition: mvd.c:418
password
cvar_t * password
Definition: g_main.c:39
gtv_client_t::state
clstate_t state
Definition: mvd.c:35
gtv_client_t::active
list_t active
Definition: mvd.c:34
Cvar_Get
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags)
Definition: cvar.c:257
Q_ErrorString
const char * Q_ErrorString(qerror_t error)
Definition: error.c:51
dummy_exec_string
static void dummy_exec_string(cmdbuf_t *buf, const char *line)
Definition: mvd.c:229
SV_MvdShutdown
void SV_MvdShutdown(error_type_t type)
Definition: mvd.c:2118
client_s::state
clstate_t state
Definition: server.h:260
SV_MvdRegister
void SV_MvdRegister(void)
Definition: mvd.c:2364
client_s::number
int number
Definition: server.h:262
sv_ghostime
cvar_t * sv_ghostime
Definition: main.c:45
sv_mvd_begincmd
static cvar_t * sv_mvd_begincmd
Definition: mvd.c:100
MSG_ReadWord
int MSG_ReadWord(void)
Definition: msg.c:1503
sv_mvd_maxtime
static cvar_t * sv_mvd_maxtime
Definition: mvd.c:98
client_s::slot
int slot
Definition: server.h:347
MVD_USERINFO1
#define MVD_USERINFO1
Definition: mvd.c:336
emit_gamestate
static void emit_gamestate(void)
Definition: mvd.c:595
SV_FRAMESYNC
#define SV_FRAMESYNC
Definition: server.h:141
NET_UpdateStream
void NET_UpdateStream(netstream_t *s)
Definition: net.c:1628
SV_MvdUnicast
void SV_MvdUnicast(edict_t *ent, int clientNum, qboolean reliable)
Definition: mvd.c:1184
SV_MvdEndFrame
void SV_MvdEndFrame(void)
Definition: mvd.c:1023
SZ_WriteByte
void SZ_WriteByte(sizebuf_t *sb, int c)
Definition: sizebuf.c:82
rec_start
static void rec_start(qhandle_t demofile)
Definition: mvd.c:2206
sv_mvd_password
static cvar_t * sv_mvd_password
Definition: mvd.c:93
SZ_WriteShort
void SZ_WriteShort(sizebuf_t *sb, int c)
Definition: sizebuf.c:90
FS_Tell
ssize_t FS_Tell(qhandle_t f)
Definition: files.c:505
Cmd_PrintUsage
void Cmd_PrintUsage(const cmd_option_t *opt, const char *suffix)
Definition: cmd.c:1143
Cmd_Args
char * Cmd_Args(void)
Definition: cmd.c:933
clstate_t
clstate_t
Definition: server.h:185
NET_CloseStream
void NET_CloseStream(netstream_t *s)
Definition: net.c:1371
SV_ListMatches_f
void SV_ListMatches_f(list_t *list)
Definition: commands.c:1176
FOR_EACH_ACTIVE_GTV
#define FOR_EACH_ACTIVE_GTV(client)
Definition: mvd.c:29
SV_DelGtvBan_f
static void SV_DelGtvBan_f(void)
Definition: mvd.c:2343
Cbuf_InsertText
void Cbuf_InsertText(cmdbuf_t *buf, const char *text)
Definition: cmd.c:128
Cmd_TokenizeString
void Cmd_TokenizeString(const char *text, qboolean macroExpand)
Definition: cmd.c:1399
mvd_server_t::datagram
sizebuf_t datagram
Definition: mvd.c:66
Cmd_Shift
void Cmd_Shift(void)
Definition: cmd.c:1034
sv_timeout
cvar_t * sv_timeout
Definition: main.c:43
sv_mvd_allow_stufftext
static cvar_t * sv_mvd_allow_stufftext
Definition: mvd.c:106
SV_MvdInit
void SV_MvdInit(void)
Definition: mvd.c:2068
sv_client
client_t * sv_client
Definition: main.c:32
dummy_record_f
static void dummy_record_f(void)
Definition: mvd.c:166
MSG_WriteByte
void MSG_WriteByte(int c)
Definition: msg.c:107
dummy_command
static void dummy_command(void)
Definition: mvd.c:150
mvd_server_t::clients_active
unsigned clients_active
Definition: mvd.c:59
sv_mvd_suspend_time
static cvar_t * sv_mvd_suspend_time
Definition: mvd.c:105
mvd_server_t::entities
entity_packed_t * entities
Definition: mvd.c:70
Cmd_Argv
char * Cmd_Argv(int arg)
Definition: cmd.c:899
svc_stufftext
#define svc_stufftext
Definition: g_local.h:41
SZ_Init
void SZ_Init(sizebuf_t *buf, void *data, size_t size)
Definition: sizebuf.c:31
Cmd_Argc
int Cmd_Argc(void)
Definition: cmd.c:889
net_from
netadr_t net_from
Definition: net.c:90
mvd_server_t::active
qboolean active
Definition: mvd.c:56
SV_UserinfoChanged
void SV_UserinfoChanged(client_t *cl)
Definition: main.c:1927
MVD_USERINFO2
#define MVD_USERINFO2
Definition: mvd.c:339
SV_MvdMapChanged
void SV_MvdMapChanged(void)
Definition: mvd.c:1990
mvd_error
static void mvd_error(const char *reason)
Definition: mvd.c:1961
write_message
static void write_message(gtv_client_t *client, gtv_serverop_t op)
Definition: mvd.c:1427
SV_Malloc
#define SV_Malloc(size)
Definition: server.h:54
sv_reserved_slots
cvar_t * sv_reserved_slots
Definition: main.c:59
NET_Accept
neterr_t NET_Accept(netstream_t *s)
Definition: net.c:1525
gtv_client_t::maxbuf
unsigned maxbuf
Definition: mvd.c:44
ES_INUSE
#define ES_INUSE(s)
Definition: server.h:718
parse_stream_start
static void parse_stream_start(gtv_client_t *client)
Definition: mvd.c:1547
mvd_server_t::recording
qhandle_t recording
Definition: mvd.c:73
SV_MvdStuff_f
static void SV_MvdStuff_f(void)
Definition: mvd.c:2316
FOR_EACH_GTV
#define FOR_EACH_GTV(client)
Definition: mvd.c:26
MSG_PackEntity
void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, qboolean short_angles)
Definition: msg.c:468
NET_Listen
neterr_t NET_Listen(qboolean arg)
Definition: net.c:1466
dummy_cmds
static const ucmd_t dummy_cmds[]
Definition: mvd.c:215
resume_streams
static void resume_streams(void)
Definition: mvd.c:836
dummy_buffer
static cmdbuf_t dummy_buffer
Definition: mvd.c:137
MSG_PackPlayer
void MSG_PackPlayer(player_packed_t *out, const player_state_t *in)
Definition: msg.c:763
c_svmvd
static const cmdreg_t c_svmvd[]
Definition: mvd.c:2352
NUM_FOR_EDICT
#define NUM_FOR_EDICT(e)
Definition: server.h:174
sv
server_t sv
Definition: init.c:22
mvd
static mvd_server_t mvd
Definition: mvd.c:81
Cmd_ParseOptions
int Cmd_ParseOptions(const cmd_option_t *opt)
Definition: cmd.c:1057
msg_write
sizebuf_t msg_write
Definition: msg.c:34
NET_ErrorString
const char * NET_ErrorString(void)
Definition: net.c:659
SV_MvdMulticast
void SV_MvdMulticast(int leafnum, multicast_t to)
Definition: mvd.c:1122
suspend_streams
static void suspend_streams(void)
Definition: mvd.c:819
client_s::entry
list_t entry
Definition: server.h:257
Cbuf_AddText
void Cbuf_AddText(cmdbuf_t *buf, const char *text)
Definition: cmd.c:95
gtv_client_t::data
byte * data
Definition: mvd.c:48
SV_MvdStop_f
void SV_MvdStop_f(void)
Definition: mvd.c:2297
mvd_enable
static qboolean mvd_enable(void)
Definition: mvd.c:913
Info_ValueForKey
char * Info_ValueForKey(const char *s, const char *key)
Definition: shared.c:945
dump_versions
static void dump_versions(void)
Definition: mvd.c:1895
Z_Free
void Z_Free(void *ptr)
Definition: zone.c:147
sv_zombietime
cvar_t * sv_zombietime
Definition: main.c:44
server_static_s::client_pool
client_t * client_pool
Definition: server.h:456
find_slot
static gtv_client_t * find_slot(void)
Definition: mvd.c:1713
CM_WritePortalBits
int CM_WritePortalBits(cm_t *cm, byte *buffer)
Definition: cmodel.c:955
rec_stop
static void rec_stop(void)
Definition: mvd.c:2175
Cmd_Register
void Cmd_Register(const cmdreg_t *reg)
Definition: cmd.c:1572
MSG_ReadLong
int MSG_ReadLong(void)
Definition: msg.c:1517
sv_mvd_maxclients
static cvar_t * sv_mvd_maxclients
Definition: mvd.c:91
MSG_WriteDeltaEntity
void MSG_WriteDeltaEntity(const entity_packed_t *from, const entity_packed_t *to, msgEsFlags_t flags)
Definition: msg.c:505
cs_free
@ cs_free
Definition: server.h:186
sv_mvd_noblend
static cvar_t * sv_mvd_noblend
Definition: mvd.c:94
dummy_stop_f
static void dummy_stop_f(void)
Definition: mvd.c:200
cs_zombie
@ cs_zombie
Definition: server.h:187
rec_frame
static void rec_frame(size_t total)
Definition: mvd.c:961
SV_MvdClientDropped
void SV_MvdClientDropped(client_t *client)
Definition: mvd.c:2054
fs_game
cvar_t * fs_game
Definition: files.c:202
gtv_client_t
Definition: mvd.c:32
copy_entity_state
static void copy_entity_state(entity_packed_t *dst, const entity_packed_t *src, int flags)
Definition: mvd.c:686
gtv_client_t::lastmessage
unsigned lastmessage
Definition: mvd.c:41
SV_MvdBeginFrame
void SV_MvdBeginFrame(void)
Definition: mvd.c:1009
Q_strlcpy
size_t Q_strlcpy(char *dst, const char *src, size_t size)
Definition: shared.c:715
sv_mvd_bufsize
static cvar_t * sv_mvd_bufsize
Definition: mvd.c:92
check_clients_activity
static void check_clients_activity(void)
Definition: mvd.c:885
sv_mvd_nogun
static cvar_t * sv_mvd_nogun
Definition: mvd.c:95
Cmd_Alias_f
void Cmd_Alias_f(void)
Definition: cmd.c:307
MSG_WriteShort
void MSG_WriteShort(int c)
Definition: msg.c:125
ge
game_export_t * ge
Definition: game.c:22
Cmd_AliasCommand
char * Cmd_AliasCommand(const char *name)
Definition: cmd.c:251
write_stream
static void write_stream(gtv_client_t *client, void *data, size_t len)
Definition: mvd.c:1379
SV_DropClient
void SV_DropClient(client_t *client, const char *reason)
Definition: main.c:212
sv_mvd_maxmaps
static cvar_t * sv_mvd_maxmaps
Definition: mvd.c:99
sv_mvd_scorecmd
static cvar_t * sv_mvd_scorecmd
Definition: mvd.c:101
o_record
static const cmd_option_t o_record[]
Definition: demo.c:299
dummy_wait_f
static void dummy_wait_f(void)
Definition: mvd.c:140
drop_client
static void drop_client(gtv_client_t *client, const char *error)
Definition: mvd.c:1353
parse_message
static qboolean parse_message(gtv_client_t *client)
Definition: mvd.c:1632
parse_ping
static void parse_ping(gtv_client_t *client)
Definition: mvd.c:1533
check_players_activity
static void check_players_activity(void)
Definition: mvd.c:897
Z_TagReserve
void Z_TagReserve(size_t size, memtag_t tag)
Definition: zone.c:342
gtv_client_t::stream
netstream_t stream
Definition: mvd.c:36
FS_Write
ssize_t FS_Write(const void *buf, size_t len, qhandle_t f)
Definition: files.c:1643
SV_DelGtvHost_f
static void SV_DelGtvHost_f(void)
Definition: mvd.c:2330
mvd_server_t::dummy
client_t * dummy
Definition: mvd.c:57
sv_mvd_capture_flags
static cvar_t * sv_mvd_capture_flags
Definition: mvd.c:103
SV_ListGtvHosts_f
static void SV_ListGtvHosts_f(void)
Definition: mvd.c:2334
mvd_server_t::layout_time
unsigned layout_time
Definition: mvd.c:58
server_t::cm
cm_t cm
Definition: server.h:161
MSG_WriteString
void MSG_WriteString(const char *string)
Definition: msg.c:160
c
statCounters_t c
Definition: main.c:30
LIST_DECL
static LIST_DECL(gtv_client_list)
MSG_WriteLong
void MSG_WriteLong(int c)
Definition: msg.c:144
Cvar_Command
void Cvar_Command(cvar_t *v)
Definition: cvar.c:672
cs_assigned
@ cs_assigned
Definition: server.h:189
accept_client
static void accept_client(netstream_t *stream)
Definition: mvd.c:1728
Cvar_ClampInteger
int Cvar_ClampInteger(cvar_t *var, int min, int max)
Definition: cvar.c:549
g_features
cvar_t * g_features
Definition: main.c:99
Cvar_Set_f
void Cvar_Set_f(void)
Definition: cvar.c:717
MSG_ReadString
size_t MSG_ReadString(char *dest, size_t size)
Definition: msg.c:1531
dummy_buffer_text
static char dummy_buffer_text[MAX_STRING_CHARS]
Definition: mvd.c:138
sv_mvd_autorecord
static cvar_t * sv_mvd_autorecord
Definition: mvd.c:102
server_static_s::realtime
unsigned realtime
Definition: server.h:454
level
level_locals_t level
Definition: g_main.c:22
client_s::AddMessage
void(* AddMessage)(struct client_s *, byte *, size_t, qboolean)
Definition: server.h:352
Cbuf_Execute
void Cbuf_Execute(cmdbuf_t *buf)
Definition: cmd.c:152
Cmd_PrintHelp
void Cmd_PrintHelp(const cmd_option_t *opt)
Definition: cmd.c:1160
cs_primed
@ cs_primed
Definition: server.h:191
Cmd_PrintHint
void Cmd_PrintHint(void)
Definition: cmd.c:1178
server_t::state
server_state_t state
Definition: server.h:146
gtv_client_t::buffer
byte buffer[MAX_GTC_MSGLEN+4]
Definition: mvd.c:47
gtv_client_t::bufcount
unsigned bufcount
Definition: mvd.c:45
sv_player
edict_t * sv_player
Definition: main.c:33
SV_MvdConfigstring
void SV_MvdConfigstring(int index, const char *string, size_t len)
Definition: mvd.c:1226
client_s::netchan
netchan_t * netchan
Definition: server.h:357
client_s
Definition: server.h:256
SV_Mallocz
#define SV_Mallocz(size)
Definition: server.h:55
svc_layout
#define svc_layout
Definition: g_local.h:39
player_is_active
static qboolean player_is_active(const edict_t *ent)
Definition: mvd.c:479
Cvar_FindVar
cvar_t * Cvar_FindVar(const char *var_name)
Definition: cvar.c:45
SV_MatchAddress
addrmatch_t * SV_MatchAddress(list_t *list, netadr_t *addr)
Definition: main.c:370
NET_RunStream
neterr_t NET_RunStream(netstream_t *s)
Definition: net.c:1647
dummy_forward_f
static void dummy_forward_f(void)
Definition: mvd.c:159
emit_frame
static void emit_frame(void)
Definition: mvd.c:711
FS_FCloseFile
void FS_FCloseFile(qhandle_t f)
Definition: files.c:759
rec_allowed
static qboolean rec_allowed(void)
Definition: mvd.c:2191
MSG_ReadByte
int MSG_ReadByte(void)
Definition: msg.c:1475
EDICT_NUM
#define EDICT_NUM(n)
Definition: server.h:173
NET_Sleep
int NET_Sleep(int msec)
Definition: net.c:712
server_t::spawncount
int spawncount
Definition: server.h:147
mvd_server_t
Definition: mvd.c:54
sv_mvd_enable
static cvar_t * sv_mvd_enable
Definition: mvd.c:90
parse_stream_stop
static void parse_stream_stop(gtv_client_t *client)
Definition: mvd.c:1590
server.h
server_t::configstrings
char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]
Definition: server.h:164
FIFO_Write
size_t FIFO_Write(fifo_t *fifo, const void *buffer, size_t len)
Definition: fifo.c:50
filter_unicast_data
static qboolean filter_unicast_data(edict_t *ent)
Definition: mvd.c:1149
SV_RemoveClient
void SV_RemoveClient(client_t *client)
Definition: main.c:107
build_gamestate
static void build_gamestate(void)
Definition: mvd.c:560
auth_client
static qboolean auth_client(gtv_client_t *client, const char *password)
Definition: mvd.c:1440
mvd_server_t::enabled
qboolean enabled
Definition: mvd.c:55
SV_MvdRecord_f
void SV_MvdRecord_f(void)
Definition: mvd.c:2233
SV_AddGtvBan_f
static void SV_AddGtvBan_f(void)
Definition: mvd.c:2339
dummy_find_slot
static client_t * dummy_find_slot(void)
Definition: mvd.c:311
sv_mvd_spawn_dummy
static cvar_t * sv_mvd_spawn_dummy
Definition: mvd.c:107
client_s::userinfo
char userinfo[MAX_INFO_STRING]
Definition: server.h:275
SV_AddMatch_f
void SV_AddMatch_f(list_t *list)
Definition: commands.c:1082
dummy_create
static int dummy_create(void)
Definition: mvd.c:342
cs_connected
@ cs_connected
Definition: server.h:190
entity_is_active
static qboolean entity_is_active(const edict_t *ent)
Definition: mvd.c:546
SZ_Clear
void SZ_Clear(sizebuf_t *buf)
Definition: sizebuf.c:40
mvd_server_t::clients
gtv_client_t * clients
Definition: mvd.c:78
sv_mvd_maxsize
static cvar_t * sv_mvd_maxsize
Definition: mvd.c:97
gtv_client_t::name
char name[MAX_CLIENT_NAME]
Definition: mvd.c:50
sv_mvd_nomsgs
static cvar_t * sv_mvd_nomsgs
Definition: mvd.c:96
SV_ListGtvBans_f
static void SV_ListGtvBans_f(void)
Definition: mvd.c:2347
MSG_ReadShort
int MSG_ReadShort(void)
Definition: msg.c:1489
SV_MvdBroadcastPrint
void SV_MvdBroadcastPrint(int level, const char *string)
Definition: mvd.c:1241
client_s::edict
edict_t * edict
Definition: server.h:261
sv_maxclients
cvar_t * sv_maxclients
Definition: main.c:58
Cmd_RawArgs
char * Cmd_RawArgs(void)
Definition: cmd.c:951