Quake II RTX doxygen  1.0 dev
client.c File Reference
#include "client.h"
#include "server/mvd/protocol.h"

Go to the source code of this file.

Classes

struct  gtv_s
 

Macros

#define FOR_EACH_GTV(gtv)   LIST_FOR_EACH(gtv_t, gtv, &mvd_gtv_list, entry)
 
#define GTV_DEFAULT_BACKOFF   (5 * 1000)
 
#define GTV_MAXIMUM_BACKOFF   (5 * 3600 * 1000)
 
#define GTV_PING_INTERVAL   (60 * 1000)
 

Typedefs

typedef struct gtv_s gtv_t
 

Enumerations

enum  gtv_state_t {
  GTV_DISCONNECTED, GTV_CONNECTING, GTV_PREPARING, GTV_CONNECTED,
  GTV_RESUMING, GTV_WAITING, GTV_READING, GTV_SUSPENDING,
  GTV_NUM_STATES
}
 

Functions

 LIST_DECL (mvd_gtv_list)
 
 LIST_DECL (mvd_channel_list)
 
void MVD_StopRecord (mvd_t *mvd)
 
static void MVD_Free (mvd_t *mvd)
 
static void MVD_Destroy (mvd_t *mvd)
 
void MVD_Destroyf (mvd_t *mvd, const char *fmt,...)
 
mvd_tMVD_SetChannel (int arg)
 
static void q_noreturn q_printf (2, 3)
 
static mvd_tcreate_channel (gtv_t *gtv)
 
static gtv_tgtv_set_conn (int arg)
 
static void set_mvd_active (void)
 
int MVD_Frame (void)
 
static void demo_play_next (gtv_t *gtv, string_entry_t *entry)
 
static void emit_base_frame (mvd_t *mvd)
 
static ssize_t demo_load_message (qhandle_t f)
 
static ssize_t demo_skip_map (qhandle_t f)
 
static ssize_t demo_read_message (qhandle_t f)
 
static ssize_t demo_read_first (qhandle_t f)
 
static void demo_emit_snapshot (mvd_t *mvd)
 
static mvd_snap_tdemo_find_snapshot (mvd_t *mvd, int framenum)
 
static void demo_update (gtv_t *gtv)
 
static void demo_finish (gtv_t *gtv, ssize_t ret)
 
static qboolean demo_read_frame (mvd_t *mvd)
 
static void demo_free_playlist (gtv_t *gtv)
 
static void demo_destroy (gtv_t *gtv)
 
static void write_stream (gtv_t *gtv, void *data, size_t len)
 
static void write_message (gtv_t *gtv, gtv_clientop_t op)
 
static void q_noreturn gtv_oob_kill (mvd_t *mvd)
 
static qboolean gtv_wait_stop (mvd_t *mvd)
 
static void gtv_wait_start (mvd_t *mvd)
 
static qboolean gtv_read_frame (mvd_t *mvd)
 
static qboolean gtv_forward_cmd (mvd_client_t *client)
 
static void send_hello (gtv_t *gtv)
 
static void send_stream_start (gtv_t *gtv)
 
static void send_stream_stop (gtv_t *gtv)
 
static void parse_hello (gtv_t *gtv)
 
static void parse_stream_start (gtv_t *gtv)
 
static void parse_stream_stop (gtv_t *gtv)
 
static void parse_stream_data (gtv_t *gtv)
 
static qboolean parse_message (gtv_t *gtv, fifo_t *fifo)
 
static neterr_t run_connect (gtv_t *gtv)
 
static neterr_t run_stream (gtv_t *gtv)
 
static void check_timeouts (gtv_t *gtv)
 
static qboolean check_reconnect (gtv_t *gtv)
 
static void gtv_run (gtv_t *gtv)
 
static void gtv_destroy (gtv_t *gtv)
 
static void gtv_drop (gtv_t *gtv)
 
void MVD_Spawn (void)
 
static void MVD_Spawn_f (void)
 
static void list_generic (void)
 
static void list_recordings (void)
 
static void MVD_ListChannels_f (void)
 
static void MVD_ListServers_f (void)
 
void MVD_StreamedStop_f (void)
 
static int player_flags (mvd_t *mvd, mvd_player_t *player)
 
static int entity_flags (mvd_t *mvd, edict_t *ent)
 
static void emit_gamestate (mvd_t *mvd)
 
void MVD_StreamedRecord_f (void)
 
static void MVD_Connect_c (genctx_t *ctx, int argnum)
 
static void MVD_Connect_f (void)
 
static void MVD_Disconnect_f (void)
 
static void MVD_Kill_f (void)
 
static void MVD_Pause_f (void)
 
static void MVD_Skip_f (void)
 
static void MVD_Seek_f (void)
 
static void MVD_Control_f (void)
 
void MVD_File_g (genctx_t *ctx)
 
static void MVD_Play_c (genctx_t *ctx, int argnum)
 
static void MVD_Play_f (void)
 
void MVD_Shutdown (void)
 
void MVD_Register (void)
 

Variables

static const char *const gtv_states [GTV_NUM_STATES]
 
static const char *const mvd_states [MVD_NUM_STATES]
 
mvd_t mvd_waitingRoom
 
qboolean mvd_dirty
 
int mvd_chanid
 
qboolean mvd_active
 
unsigned mvd_last_activity
 
jmp_buf mvd_jmpbuf
 
static cvar_t * mvd_timeout
 
static cvar_t * mvd_suspend_time
 
static cvar_t * mvd_wait_delay
 
static cvar_t * mvd_wait_percent
 
static cvar_t * mvd_buffer_size
 
static cvar_t * mvd_username
 
static cvar_t * mvd_password
 
static cvar_t * mvd_snaps
 
static const cmd_option_t o_mvdconnect []
 
static const cmd_option_t o_mvdplay []
 
static const cmdreg_t c_mvd []
 

Macro Definition Documentation

◆ FOR_EACH_GTV

#define FOR_EACH_GTV (   gtv)    LIST_FOR_EACH(gtv_t, gtv, &mvd_gtv_list, entry)

Definition at line 26 of file client.c.

◆ GTV_DEFAULT_BACKOFF

#define GTV_DEFAULT_BACKOFF   (5 * 1000)

Definition at line 29 of file client.c.

◆ GTV_MAXIMUM_BACKOFF

#define GTV_MAXIMUM_BACKOFF   (5 * 3600 * 1000)

Definition at line 30 of file client.c.

◆ GTV_PING_INTERVAL

#define GTV_PING_INTERVAL   (60 * 1000)

Definition at line 32 of file client.c.

Typedef Documentation

◆ gtv_t

typedef struct gtv_s gtv_t

Enumeration Type Documentation

◆ gtv_state_t

Enumerator
GTV_DISCONNECTED 
GTV_CONNECTING 
GTV_PREPARING 
GTV_CONNECTED 
GTV_RESUMING 
GTV_WAITING 
GTV_READING 
GTV_SUSPENDING 
GTV_NUM_STATES 

Definition at line 34 of file client.c.

34  {
35  GTV_DISCONNECTED, // disconnected
36  GTV_CONNECTING, // connect() in progress
37  GTV_PREPARING, // waiting for server hello
38  GTV_CONNECTED, // keeping connection alive
39  GTV_RESUMING, // stream start request sent
40  GTV_WAITING, // server is suspended
41  GTV_READING, // server is resumed
42  GTV_SUSPENDING, // stream stop request sent
44 } gtv_state_t;

Function Documentation

◆ check_reconnect()

static qboolean check_reconnect ( gtv_t gtv)
static

Definition at line 1498 of file client.c.

1499 {
1500  netadr_t adr;
1501 
1502  if (svs.realtime - gtv->retry_time < gtv->retry_backoff) {
1503  return qfalse;
1504  }
1505 
1506  Com_Printf("[%s] -=- Attempting to reconnect to %s...\n",
1507  gtv->name, gtv->address);
1508 
1509  gtv->state = GTV_CONNECTING;
1510 
1511  // don't timeout
1512  gtv->last_sent = gtv->last_rcvd = svs.realtime;
1513 
1514  if (!NET_StringToAdr(gtv->address, &adr, PORT_SERVER)) {
1515  gtv_dropf(gtv, "Unable to lookup %s\n", gtv->address);
1516  }
1517 
1518  if (NET_Connect(&adr, &gtv->stream) == NET_ERROR) {
1519  gtv_dropf(gtv, "%s to %s\n", NET_ErrorString(),
1520  NET_AdrToString(&adr));
1521  }
1522 
1523  return qtrue;
1524 }

Referenced by gtv_run().

◆ check_timeouts()

static void check_timeouts ( gtv_t gtv)
static

Definition at line 1470 of file client.c.

1471 {
1472  unsigned timeout = mvd_timeout->value * 1000;
1473 
1474  // drop if no data has been received for too long
1475  if (svs.realtime - gtv->last_rcvd > timeout) {
1476  gtv_dropf(gtv, "Server connection timed out.");
1477  }
1478 
1479  if (gtv->state < GTV_CONNECTED) {
1480  return;
1481  }
1482 
1483  // stop/start stream depending on global state
1484  if (mvd_active) {
1485  if (gtv->state == GTV_CONNECTED) {
1486  send_stream_start(gtv);
1487  }
1488  } else if (gtv->state == GTV_WAITING || gtv->state == GTV_READING) {
1489  send_stream_stop(gtv);
1490  }
1491 
1492  // ping if no data has been sent for too long
1493  if (svs.realtime - gtv->last_sent > GTV_PING_INTERVAL) {
1494  write_message(gtv, GTC_PING);
1495  }
1496 }

Referenced by gtv_run().

◆ create_channel()

static mvd_t* create_channel ( gtv_t gtv)
static

Definition at line 318 of file client.c.

319 {
320  mvd_t *mvd;
321 
322  mvd = MVD_Mallocz(sizeof(*mvd));
323  mvd->gtv = gtv;
324  mvd->id = gtv->id;
325  Q_strlcpy(mvd->name, gtv->name, sizeof(mvd->name));
326  mvd->pool.edicts = mvd->edicts;
327  mvd->pool.edict_size = sizeof(edict_t);
328  mvd->pool.max_edicts = MAX_EDICTS;
329  mvd->pm_type = PM_SPECTATOR;
330  mvd->min_packets = mvd_wait_delay->value * 10;
331  List_Init(&mvd->snapshots);
332  List_Init(&mvd->clients);
333  List_Init(&mvd->entry);
334 
335  return mvd;
336 }

Referenced by demo_play_next(), and parse_stream_data().

◆ demo_destroy()

static void demo_destroy ( gtv_t gtv)
static

Definition at line 788 of file client.c.

789 {
790  mvd_t *mvd = gtv->mvd;
791 
792  // destroy any associated MVD channel
793  if (mvd) {
794  mvd->gtv = NULL;
795  MVD_Destroy(mvd);
796  }
797 
798  if (gtv->demoplayback) {
800  gtv->demoplayback = 0;
801  }
802 
803  demo_free_playlist(gtv);
804 
805  Z_Free(gtv);
806 }

Referenced by MVD_Play_f().

◆ demo_emit_snapshot()

static void demo_emit_snapshot ( mvd_t mvd)
static

Definition at line 563 of file client.c.

564 {
565  mvd_snap_t *snap;
566  gtv_t *gtv;
567  off_t pos;
568  char *from, *to;
569  size_t len;
570  int i;
571 
572  if (mvd_snaps->integer <= 0)
573  return;
574 
575  if (mvd->framenum < mvd->last_snapshot + mvd_snaps->integer * 10)
576  return;
577 
578  gtv = mvd->gtv;
579  if (!gtv)
580  return;
581 
582  if (!gtv->demosize)
583  return;
584 
585  pos = FS_Tell(gtv->demoplayback);
586  if (pos < gtv->demopos)
587  return;
588 
589  // write baseline frame
590  MSG_WriteByte(mvd_frame);
592 
593  // write configstrings
594  for (i = 0; i < MAX_CONFIGSTRINGS; i++) {
595  from = mvd->baseconfigstrings[i];
596  to = mvd->configstrings[i];
597 
598  if (!strcmp(from, to))
599  continue;
600 
601  len = strlen(to);
602  if (len > MAX_QPATH)
603  len = MAX_QPATH;
604 
605  MSG_WriteByte(mvd_configstring);
606  MSG_WriteShort(i);
607  MSG_WriteData(to, len);
608  MSG_WriteByte(0);
609  }
610 
611  // TODO: write private layouts/configstrings
612 
613  snap = MVD_Malloc(sizeof(*snap) + msg_write.cursize - 1);
614  snap->framenum = mvd->framenum;
615  snap->filepos = pos;
616  snap->msglen = msg_write.cursize;
617  memcpy(snap->data, msg_write.data, msg_write.cursize);
618  List_Append(&mvd->snapshots, &snap->entry);
619 
620  Com_DPrintf("[%d] snaplen %"PRIz"\n", mvd->framenum, msg_write.cursize);
621 
623 
624  mvd->last_snapshot = mvd->framenum;
625 }

Referenced by demo_play_next(), demo_read_frame(), and MVD_Seek_f().

◆ demo_find_snapshot()

static mvd_snap_t* demo_find_snapshot ( mvd_t mvd,
int  framenum 
)
static

Definition at line 627 of file client.c.

628 {
629  mvd_snap_t *snap, *prev;
630 
631  if (LIST_EMPTY(&mvd->snapshots))
632  return NULL;
633 
634  prev = LIST_FIRST(mvd_snap_t, &mvd->snapshots, entry);
635 
636  LIST_FOR_EACH(mvd_snap_t, snap, &mvd->snapshots, entry) {
637  if (snap->framenum > framenum)
638  break;
639  prev = snap;
640  }
641 
642  return prev;
643 }

Referenced by MVD_Seek_f().

◆ demo_finish()

static void demo_finish ( gtv_t gtv,
ssize_t  ret 
)
static

Definition at line 652 of file client.c.

653 {
654  if (ret < 0) {
655  gtv_destroyf(gtv, "Couldn't read %s: %s", gtv->demoentry->string, Q_ErrorString(ret));
656  }
657 
658  demo_play_next(gtv, gtv->demoentry->next);
659 }

Referenced by demo_read_frame(), and MVD_Seek_f().

◆ demo_free_playlist()

static void demo_free_playlist ( gtv_t gtv)
static

Definition at line 776 of file client.c.

777 {
778  string_entry_t *entry, *next;
779 
780  for (entry = gtv->demohead; entry; entry = next) {
781  next = entry->next;
782  Z_Free(entry);
783  }
784 
785  gtv->demohead = gtv->demoentry = NULL;
786 }

Referenced by demo_destroy(), and MVD_Play_f().

◆ demo_load_message()

static ssize_t demo_load_message ( qhandle_t  f)
static

Definition at line 469 of file client.c.

470 {
471  uint16_t us;
472  ssize_t msglen, read;
473 
474  read = FS_Read(&us, 2, f);
475  if (read != 2) {
476  return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
477  }
478 
479  if (!us) {
480  return 0;
481  }
482 
483  msglen = LittleShort(us);
484  if (msglen > MAX_MSGLEN) {
485  return Q_ERR_INVALID_FORMAT;
486  }
487 
488  read = FS_Read(msg_read_buffer, msglen, f);
489  if (read != msglen) {
490  return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
491  }
492 
493  return read;
494 }

Referenced by demo_read_message(), and demo_skip_map().

◆ demo_play_next()

static void demo_play_next ( gtv_t gtv,
string_entry_t *  entry 
)
static

Definition at line 709 of file client.c.

710 {
711  ssize_t len, ret;
712 
713  if (!entry) {
714  if (gtv->demoloop) {
715  if (--gtv->demoloop == 0) {
716  gtv_destroyf(gtv, "End of play list reached");
717  }
718  }
719  entry = gtv->demohead;
720  }
721 
722  // close previous file
723  if (gtv->demoplayback) {
725  gtv->demoplayback = 0;
726  }
727 
728  // open new file
729  len = FS_FOpenFile(entry->string, &gtv->demoplayback, FS_MODE_READ);
730  if (!gtv->demoplayback) {
731  gtv_destroyf(gtv, "Couldn't open %s: %s", entry->string, Q_ErrorString(len));
732  }
733 
734  // read the first message
735  ret = demo_read_first(gtv->demoplayback);
736  if (ret < 0) {
737  gtv_destroyf(gtv, "Couldn't read %s: %s", entry->string, Q_ErrorString(ret));
738  }
739 
740  // create MVD channel
741  if (!gtv->mvd) {
742  gtv->mvd = create_channel(gtv);
744  } else {
745  gtv->mvd->demoseeking = qfalse;
746  }
747 
748  Com_Printf("[%s] -=- Reading from %s\n", gtv->name, entry->string);
749 
750  // parse gamestate
751  MVD_ParseMessage(gtv->mvd);
752  if (!gtv->mvd->state) {
753  gtv_destroyf(gtv, "First message of %s does not contain gamestate", entry->string);
754  }
755 
756  gtv->mvd->state = MVD_READING;
757 
758  // reset state
759  gtv->demoentry = entry;
760 
761  // set channel address
762  Q_strlcpy(gtv->address, COM_SkipPath(entry->string), sizeof(gtv->address));
763 
764  len = FS_Length(gtv->demoplayback);
765  ret = FS_Tell(gtv->demoplayback);
766  if (len > 0 && ret > 0) {
767  gtv->demosize = len;
768  gtv->demopos = ret;
769  } else {
770  gtv->demosize = gtv->demopos = 0;
771  }
772 
773  demo_emit_snapshot(gtv->mvd);
774 }

Referenced by demo_finish(), and MVD_Play_f().

◆ demo_read_first()

static ssize_t demo_read_first ( qhandle_t  f)
static

Definition at line 529 of file client.c.

530 {
531  uint32_t magic;
532  ssize_t read;
533  qerror_t ret;
534 
535  // read magic
536  read = FS_Read(&magic, 4, f);
537  if (read != 4) {
538  return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
539  }
540 
541  // check for gzip header
542  if (CHECK_GZIP_HEADER(magic)) {
543  ret = FS_FilterFile(f);
544  if (ret) {
545  return ret;
546  }
547  read = FS_Read(&magic, 4, f);
548  if (read != 4) {
549  return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
550  }
551  }
552  if (magic != MVD_MAGIC) {
553  return Q_ERR_UNKNOWN_FORMAT;
554  }
555 
556  // read the first message
557  read = demo_read_message(f);
558  return read ? read : Q_ERR_UNEXPECTED_EOF;
559 }

Referenced by demo_play_next().

◆ demo_read_frame()

static qboolean demo_read_frame ( mvd_t mvd)
static

Definition at line 661 of file client.c.

662 {
663  gtv_t *gtv = mvd->gtv;
664  int count;
665  ssize_t ret;
666 
667  if (mvd->state == MVD_WAITING) {
668  return qfalse; // paused by user
669  }
670 
671  if (!gtv) {
672  MVD_Destroyf(mvd, "End of MVD stream reached");
673  }
674 
675  if (gtv->demowait) {
676  gtv->demowait = qfalse;
677  return qfalse;
678  }
679 
680  count = gtv->demoskip;
681  gtv->demoskip = 0;
682 
683  if (count) {
684  Com_Printf("[%s] -=- Skipping map%s...\n", gtv->name, count == 1 ? "" : "s");
685  do {
686  ret = demo_skip_map(gtv->demoplayback);
687  if (ret <= 0) {
688  goto next;
689  }
690  } while (--count);
691  } else {
692  ret = demo_read_message(gtv->demoplayback);
693  if (ret <= 0) {
694  goto next;
695  }
696  }
697 
698  demo_update(gtv);
699 
702  return qtrue;
703 
704 next:
705  demo_finish(gtv, ret);
706  return qtrue;
707 }

Referenced by demo_play_next().

◆ demo_read_message()

static ssize_t demo_read_message ( qhandle_t  f)
static

Definition at line 515 of file client.c.

516 {
517  ssize_t msglen;
518 
519  if ((msglen = demo_load_message(f)) <= 0) {
520  return msglen;
521  }
522 
524  msg_read.cursize = msglen;
525 
526  return msglen;
527 }

Referenced by demo_read_first(), demo_read_frame(), and MVD_Seek_f().

◆ demo_skip_map()

static ssize_t demo_skip_map ( qhandle_t  f)
static

Definition at line 496 of file client.c.

497 {
498  ssize_t msglen;
499 
500  while (1) {
501  if ((msglen = demo_load_message(f)) <= 0) {
502  return msglen;
503  }
504  if (msg_read_buffer[0] == mvd_serverdata) {
505  break;
506  }
507  }
508 
510  msg_read.cursize = msglen;
511 
512  return msglen;
513 }

Referenced by demo_read_frame().

◆ demo_update()

static void demo_update ( gtv_t gtv)
static

Definition at line 645 of file client.c.

646 {
647  if (gtv->demosize) {
648  gtv->demopos = FS_Tell(gtv->demoplayback);
649  }
650 }

Referenced by demo_read_frame(), and MVD_Seek_f().

◆ emit_base_frame()

static void emit_base_frame ( mvd_t mvd)
static

Definition at line 1823 of file client.c.

1824 {
1825  edict_t *ent;
1826  mvd_player_t *player;
1827  int i, portalbytes;
1828  byte portalbits[MAX_MAP_PORTAL_BYTES];
1829  entity_packed_t es;
1830  player_packed_t ps;
1831 
1832  portalbytes = CM_WritePortalBits(&mvd->cm, portalbits);
1833  MSG_WriteByte(portalbytes);
1834  MSG_WriteData(portalbits, portalbytes);
1835 
1836  // send base player states
1837  for (i = 0; i < mvd->maxclients; i++) {
1838  player = &mvd->players[i];
1839  MSG_PackPlayer(&ps, &player->ps);
1840  MSG_WriteDeltaPlayerstate_Packet(NULL, &ps, i, player_flags(mvd, player));
1841  }
1842  MSG_WriteByte(CLIENTNUM_NONE);
1843 
1844  // send base entity states
1845  for (i = 1; i < MAX_EDICTS; i++) {
1846  ent = &mvd->edicts[i];
1847  if (!(ent->svflags & SVF_MONSTER))
1848  continue; // entity never seen
1849  ent->s.number = i;
1850  MSG_PackEntity(&es, &ent->s, qfalse);
1851  MSG_WriteDeltaEntity(NULL, &es, entity_flags(mvd, ent));
1852  }
1853  MSG_WriteShort(0);
1854 }

Referenced by demo_emit_snapshot(), and emit_gamestate().

◆ emit_gamestate()

static void emit_gamestate ( mvd_t mvd)
static

Definition at line 1856 of file client.c.

1857 {
1858  int i, extra;
1859  char *s;
1860  size_t len;
1861 
1862  // pack MVD stream flags into extra bits
1863  extra = mvd->flags << SVCMD_BITS;
1864 
1865  // send the serverdata
1866  MSG_WriteByte(mvd_serverdata | extra);
1867  MSG_WriteLong(PROTOCOL_VERSION_MVD);
1868  MSG_WriteShort(PROTOCOL_VERSION_MVD_CURRENT);
1869  MSG_WriteLong(mvd->servercount);
1870  MSG_WriteString(mvd->gamedir);
1871  MSG_WriteShort(mvd->clientNum);
1872 
1873  // send configstrings
1874  for (i = 0; i < MAX_CONFIGSTRINGS; i++) {
1875  s = mvd->configstrings[i];
1876  if (!*s)
1877  continue;
1878 
1879  len = strlen(s);
1880  if (len > MAX_QPATH)
1881  len = MAX_QPATH;
1882 
1883  MSG_WriteShort(i);
1884  MSG_WriteData(s, len);
1885  MSG_WriteByte(0);
1886  }
1887 
1888  MSG_WriteShort(MAX_CONFIGSTRINGS);
1889 
1890  // send baseline frame
1892 
1893  // TODO: write private layouts/configstrings
1894 }

Referenced by MVD_StreamedRecord_f().

◆ entity_flags()

static int entity_flags ( mvd_t mvd,
edict_t *  ent 
)
inlinestatic

Definition at line 1808 of file client.c.

1809 {
1810  int flags = MSG_ES_UMASK;
1811 
1812  if (!ent->inuse) {
1813  flags |= MSG_ES_REMOVE;
1814  } else if (ent->s.number <= mvd->maxclients) {
1815  mvd_player_t *player = &mvd->players[ent->s.number - 1];
1816  if (player->inuse && player->ps.pmove.pm_type == PM_NORMAL)
1817  flags |= MSG_ES_FIRSTPERSON;
1818  }
1819 
1820  return flags;
1821 }

Referenced by emit_base_frame().

◆ gtv_destroy()

static void gtv_destroy ( gtv_t gtv)
static

Definition at line 1569 of file client.c.

1570 {
1571  mvd_t *mvd = gtv->mvd;
1572 
1573  // drop any associated MVD channel
1574  if (mvd) {
1575  mvd->gtv = NULL; // don't double destroy
1576  if (!mvd->state) {
1577  // free it here, since it is not yet
1578  // added to global channel list
1579  MVD_Free(mvd);
1580  } else if (COM_DEDICATED) {
1581  // notify spectators
1582  MVD_BroadcastPrintf(mvd, PRINT_HIGH, 0,
1583  "[MVD] Disconnected from the game server!\n");
1584  }
1585  }
1586 
1587  // make sure network connection is closed
1588  NET_CloseStream(&gtv->stream);
1589 
1590  // unlink from the list of connections
1591  List_Remove(&gtv->entry);
1592 
1593  // free all memory buffers
1594  Z_Free(gtv->username);
1595  Z_Free(gtv->password);
1596 #if USE_ZLIB
1597  inflateEnd(&gtv->z_str);
1598  Z_Free(gtv->z_buf.data);
1599 #endif
1600  Z_Free(gtv->data);
1601  Z_Free(gtv);
1602 }

Referenced by MVD_Connect_f().

◆ gtv_drop()

static void gtv_drop ( gtv_t gtv)
static

Definition at line 1604 of file client.c.

1605 {
1606  time_t sec;
1607  char buffer[MAX_QPATH];
1608 
1609  if (gtv->stream.state < NS_CONNECTED) {
1610  gtv->retry_backoff += 15 * 1000;
1611  } else {
1612  // notify spectators
1613  if (COM_DEDICATED && gtv->mvd) {
1614  MVD_BroadcastPrintf(gtv->mvd, PRINT_HIGH, 0,
1615  "[MVD] Lost connection to the game server!\n");
1616  }
1617 
1618  if (gtv->state >= GTV_CONNECTED) {
1620  } else {
1621  gtv->retry_backoff += 30 * 1000;
1622  }
1623  }
1624 
1625  if (gtv->retry_backoff > GTV_MAXIMUM_BACKOFF) {
1627  }
1628 
1629  sec = gtv->retry_backoff / 1000;
1630  Com_FormatTimeLong(buffer, sizeof(buffer), sec);
1631  Com_Printf("[%s] -=- Reconnecting in %s.\n", gtv->name, buffer);
1632 
1633  NET_CloseStream(&gtv->stream);
1634 #if USE_ZLIB
1635  inflateReset(&gtv->z_str);
1636  FIFO_Clear(&gtv->z_buf);
1637  gtv->z_act = qfalse;
1638 #endif
1639  gtv->msglen = 0;
1640  gtv->state = GTV_DISCONNECTED;
1641  gtv->retry_time = svs.realtime;
1642 }

Referenced by MVD_Connect_f().

◆ gtv_forward_cmd()

static qboolean gtv_forward_cmd ( mvd_client_t client)
static

Definition at line 978 of file client.c.

979 {
980  mvd_t *mvd = client->mvd;
981  gtv_t *gtv = mvd->gtv;
982  char *text;
983  size_t len;
984 
985  if (!gtv || gtv->state < GTV_CONNECTED) {
986  SV_ClientPrintf(client->cl, PRINT_HIGH,
987  "[MVD] Not connected to the game server.\n");
988  return qfalse;
989  }
990  if (!(gtv->flags & GTF_STRINGCMDS)) {
991  SV_ClientPrintf(client->cl, PRINT_HIGH,
992  "[MVD] Game server does not allow command forwarding.\n");
993  return qfalse;
994  }
995  if (FIFO_Usage(&gtv->stream.send)) {
996  SV_ClientPrintf(client->cl, PRINT_HIGH,
997  "[MVD] Send buffer not empty, please wait.\n");
998  return qfalse;
999  }
1000 
1001  text = Cmd_Args();
1002  len = strlen(text);
1003  if (len > 150) {
1004  len = 150;
1005  }
1006 
1007  // send it
1008  MSG_WriteData(text, len);
1009  MSG_WriteByte(0);
1010  write_message(gtv, GTC_STRINGCMD);
1011  SZ_Clear(&msg_write);
1012  NET_UpdateStream(&gtv->stream);
1013  return qtrue;
1014 }

Referenced by parse_stream_data().

◆ gtv_oob_kill()

static void q_noreturn gtv_oob_kill ( mvd_t mvd)
static

Definition at line 840 of file client.c.

841 {
842  gtv_t *gtv = mvd->gtv;
843 
844  if (gtv) {
845  // don't kill connection!
846  gtv->mvd = NULL;
847  mvd->gtv = NULL;
848  }
849 
850  MVD_Destroyf(mvd, "Ran out of buffers");
851 }

Referenced by gtv_wait_start(), and gtv_wait_stop().

◆ gtv_read_frame()

static qboolean gtv_read_frame ( mvd_t mvd)
static

Definition at line 932 of file client.c.

933 {
934  uint16_t msglen;
935 
936  switch (mvd->state) {
937  case MVD_WAITING:
938  if (!gtv_wait_stop(mvd)) {
939  return qfalse;
940  }
941  break;
942  case MVD_READING:
943  if (!mvd->num_packets) {
945  return qfalse;
946  }
947  break;
948  default:
949  MVD_Destroyf(mvd, "%s: bad mvd->state", __func__);
950  }
951 
952  // NOTE: if we got here, delay buffer MUST contain
953  // at least one complete, non-empty packet
954 
955  // parse msglen
956  if (FIFO_Read(&mvd->delay, &msglen, 2) != 2) {
957  MVD_Destroyf(mvd, "%s: partial data", __func__);
958  }
959 
960  msglen = LittleShort(msglen);
961  if (msglen < 1 || msglen > MAX_MSGLEN) {
962  MVD_Destroyf(mvd, "%s: invalid msglen", __func__);
963  }
964 
965  // read this message
966  if (!FIFO_ReadMessage(&mvd->delay, msglen)) {
967  MVD_Destroyf(mvd, "%s: partial data", __func__);
968  }
969 
970  // decrement buffered packets counter
971  mvd->num_packets--;
972 
973  // parse it
975  return qtrue;
976 }

Referenced by parse_stream_data().

◆ gtv_run()

static void gtv_run ( gtv_t gtv)
static

Definition at line 1526 of file client.c.

1527 {
1528  neterr_t ret = NET_AGAIN;
1529 
1530  // check if it is time to reconnect
1531  if (!gtv->state) {
1532  if (!check_reconnect(gtv)) {
1533  return;
1534  }
1535  }
1536 
1537  // run network stream
1538  switch (gtv->stream.state) {
1539  case NS_CONNECTING:
1540  ret = run_connect(gtv);
1541  if (ret == NET_AGAIN) {
1542  return;
1543  }
1544  if (ret == NET_OK) {
1545  case NS_CONNECTED:
1546  ret = run_stream(gtv);
1547  }
1548  break;
1549  default:
1550  return;
1551  }
1552 
1553  switch (ret) {
1554  case NET_AGAIN:
1555  case NET_OK:
1556  check_timeouts(gtv);
1557  NET_UpdateStream(&gtv->stream);
1558  break;
1559  case NET_ERROR:
1560  gtv_dropf(gtv, "%s to %s", NET_ErrorString(),
1561  NET_AdrToString(&gtv->stream.address));
1562  break;
1563  case NET_CLOSED:
1564  gtv_dropf(gtv, "Server has closed connection.");
1565  break;
1566  }
1567 }

Referenced by MVD_Connect_f().

◆ gtv_set_conn()

static gtv_t* gtv_set_conn ( int  arg)
static

Definition at line 338 of file client.c.

339 {
340  char *s = Cmd_Argv(arg);
341  gtv_t *gtv;
342  int id;
343 
344  if (LIST_EMPTY(&mvd_gtv_list)) {
345  Com_Printf("No GTV connections.\n");
346  return NULL;
347  }
348 
349  if (!*s) {
350  if (LIST_SINGLE(&mvd_gtv_list)) {
351  return LIST_FIRST(gtv_t, &mvd_gtv_list, entry);
352  }
353  Com_Printf("Please specify an exact connection ID.\n");
354  return NULL;
355  }
356 
357  if (COM_IsUint(s)) {
358  id = atoi(s);
359  FOR_EACH_GTV(gtv) {
360  if (gtv->id == id) {
361  return gtv;
362  }
363  }
364  } else {
365  FOR_EACH_GTV(gtv) {
366  if (!strcmp(gtv->name, s)) {
367  return gtv;
368  }
369  }
370  }
371 
372  Com_Printf("No such connection ID: %s\n", s);
373  return NULL;
374 }

Referenced by MVD_Disconnect_f().

◆ gtv_wait_start()

static void gtv_wait_start ( mvd_t mvd)
static

Definition at line 893 of file client.c.

894 {
895  gtv_t *gtv = mvd->gtv;
896  int tr = mvd_wait_delay->value * 10;
897 
898  // if not connected, kill the channel
899  if (!gtv) {
900  MVD_Destroyf(mvd, "End of MVD stream reached");
901  }
902 
903  // FIXME: if connection is suspended, kill the channel
904  if (gtv->state != GTV_READING) {
905  gtv_oob_kill(mvd);
906  }
907 
908  Com_Printf("[%s] -=- Buffering data...\n", mvd->name);
909 
910  // oops, underflowed in the middle of the game,
911  // resume as quickly as possible after there is some
912  // data available again
913  mvd->min_packets = 50 + 5 * mvd->underflows;
914  if (mvd->min_packets > tr) {
915  mvd->min_packets = tr;
916  }
917  mvd->underflows++;
918  mvd->state = MVD_WAITING;
919  mvd->dirty = qtrue;
920 
921  // notify spectators
922  if (COM_DEDICATED) {
923  MVD_BroadcastPrintf(mvd, PRINT_HIGH, 0,
924  "[MVD] Buffering data, please wait...\n");
925  }
926 
927  // send ping to force server to flush
928  write_message(gtv, GTC_PING);
929  NET_UpdateStream(&gtv->stream);
930 }

Referenced by gtv_read_frame().

◆ gtv_wait_stop()

static qboolean gtv_wait_stop ( mvd_t mvd)
static

Definition at line 853 of file client.c.

854 {
855  gtv_t *gtv = mvd->gtv;
856  int min_packets = mvd->min_packets, usage;
857 
858  // if not connected, flush any data left
859  if (!gtv || gtv->state != GTV_READING) {
860  if (!mvd->num_packets) {
861  gtv_oob_kill(mvd);
862  }
863  min_packets = 1;
864  }
865 
866  // see how many frames are buffered
867  if (mvd->num_packets >= min_packets) {
868  Com_Printf("[%s] -=- Waiting finished, reading...\n", mvd->name);
869  goto stop;
870  }
871 
872  // see how much data is buffered
873  usage = FIFO_Percent(&mvd->delay);
874  if (usage >= mvd_wait_percent->value) {
875  Com_Printf("[%s] -=- Buffering finished, reading...\n", mvd->name);
876  goto stop;
877  }
878 
879  return qfalse;
880 
881 stop:
882  // notify spectators
883  if (COM_DEDICATED) {
884  MVD_BroadcastPrintf(mvd, PRINT_HIGH, 0,
885  "[MVD] Streaming resumed.\n");
886  }
887  mvd->state = MVD_READING;
888  mvd->dirty = qtrue;
889  return qtrue;
890 }

Referenced by gtv_read_frame().

◆ LIST_DECL() [1/2]

LIST_DECL ( mvd_channel_list  )

◆ LIST_DECL() [2/2]

LIST_DECL ( mvd_gtv_list  )

◆ list_generic()

static void list_generic ( void  )
static

Definition at line 1691 of file client.c.

1692 {
1693  mvd_t *mvd;
1694 
1695  Com_Printf(
1696  "id name map spc plr stat buf pckt address \n"
1697  "-- ------------ -------- --- --- ---- --- ---- --------------\n");
1698 
1699  FOR_EACH_MVD(mvd) {
1700  Com_Printf("%2d %-12.12s %-8.8s %3d %3d %-4.4s %3d %4u %s\n",
1701  mvd->id, mvd->name, mvd->mapname,
1702  List_Count(&mvd->clients), mvd->numplayers,
1703  mvd_states[mvd->state],
1704  FIFO_Percent(&mvd->delay), mvd->num_packets,
1705  mvd->gtv ? mvd->gtv->address : "<disconnected>");
1706  }
1707 }

Referenced by MVD_ListChannels_f().

◆ list_recordings()

static void list_recordings ( void  )
static

Definition at line 1709 of file client.c.

1710 {
1711  mvd_t *mvd;
1712  char buffer[8];
1713  off_t pos;
1714 
1715  Com_Printf(
1716  "id name map size name\n"
1717  "-- ------------ -------- ---- --------------\n");
1718 
1719  FOR_EACH_MVD(mvd) {
1720  if (mvd->demorecording) {
1721  pos = FS_Tell(mvd->demorecording);
1722  Com_FormatSize(buffer, sizeof(buffer), pos);
1723  } else {
1724  strcpy(buffer, "-");
1725  }
1726  Com_Printf("%2d %-12.12s %-8.8s %-4s %s\n",
1727  mvd->id, mvd->name, mvd->mapname,
1728  buffer, mvd->demoname ? mvd->demoname : "-");
1729  }
1730 }

Referenced by MVD_ListChannels_f().

◆ MVD_Connect_c()

static void MVD_Connect_c ( genctx_t *  ctx,
int  argnum 
)
static

Definition at line 1987 of file client.c.

1988 {
1989  Cmd_Option_c(o_mvdconnect, Com_Address_g, ctx, argnum);
1990 }

◆ MVD_Connect_f()

static void MVD_Connect_f ( void  )
static

Definition at line 1997 of file client.c.

1998 {
1999  netadr_t adr;
2000  netstream_t stream;
2001  char *name = NULL, *username = NULL, *password = NULL;
2002  gtv_t *gtv;
2003  int c;
2004 
2005  while ((c = Cmd_ParseOptions(o_mvdconnect)) != -1) {
2006  switch (c) {
2007  case 'h':
2008  Cmd_PrintUsage(o_mvdconnect, "<address[:port]>");
2009  Com_Printf("Connect to the specified MVD/GTV server.\n");
2011  return;
2012  case 'n':
2013  name = cmd_optarg;
2014  break;
2015  case 'u':
2016  username = cmd_optarg;
2017  break;
2018  case 'p':
2019  password = cmd_optarg;
2020  break;
2021  default:
2022  return;
2023  }
2024  }
2025 
2026  if (!cmd_optarg[0]) {
2027  Com_Printf("Missing address argument.\n");
2028  Cmd_PrintHint();
2029  return;
2030  }
2031 
2032  // resolve hostname
2033  if (!NET_StringToAdr(cmd_optarg, &adr, PORT_SERVER)) {
2034  Com_Printf("Bad server address: %s\n", cmd_optarg);
2035  return;
2036  }
2037 
2038  // don't allow multiple connections
2039  FOR_EACH_GTV(gtv) {
2040  if (NET_IsEqualAdr(&adr, &gtv->stream.address)) {
2041  Com_Printf("[%s] =!= Connection to %s already exists.\n",
2042  gtv->name, NET_AdrToString(&adr));
2043  return;
2044  }
2045  }
2046 
2047  // create new socket and start connecting
2048  if (NET_Connect(&adr, &stream) == NET_ERROR) {
2049  Com_EPrintf("%s to %s\n", NET_ErrorString(),
2050  NET_AdrToString(&adr));
2051  return;
2052  }
2053 
2054  // create new connection
2055  gtv = MVD_Mallocz(sizeof(*gtv));
2056  gtv->id = mvd_chanid++;
2057  gtv->state = GTV_CONNECTING;
2058  gtv->stream = stream;
2059  gtv->last_sent = gtv->last_rcvd = svs.realtime;
2060  gtv->run = gtv_run;
2061  gtv->drop = gtv_drop;
2062  gtv->destroy = gtv_destroy;
2063  gtv->username = MVD_CopyString(username);
2065  List_Append(&mvd_gtv_list, &gtv->entry);
2066 
2067  // set channel name
2068  if (name) {
2069  Q_strlcpy(gtv->name, name, sizeof(gtv->name));
2070  } else {
2071  Q_snprintf(gtv->name, sizeof(gtv->name), "net%d", gtv->id);
2072  }
2073 
2074  Q_strlcpy(gtv->address, cmd_optarg, sizeof(gtv->address));
2075 
2076  Com_Printf("[%s] -=- Connecting to %s...\n",
2077  gtv->name, NET_AdrToString(&adr));
2078 }

◆ MVD_Control_f()

static void MVD_Control_f ( void  )
static

Definition at line 2335 of file client.c.

2336 {
2337  static const cmd_option_t options[] = {
2338  { "h", "help", "display this message" },
2339  { "l:number", "loop", "replay <number> of times (0 means forever)" },
2340  { "n:string", "name", "specify channel name as <string>" },
2341  { NULL }
2342  };
2343  mvd_t *mvd;
2344  char *name = NULL;
2345  int loop = -1;
2346  int todo = 0;
2347  int c;
2348 
2349  while ((c = Cmd_ParseOptions(options)) != -1) {
2350  switch (c) {
2351  case 'h':
2352  Cmd_PrintUsage(options, "[chanid]");
2353  Com_Printf("Change attributes of existing MVD channel.\n");
2354  Cmd_PrintHelp(options);
2355  return;
2356  case 'l':
2357  loop = atoi(cmd_optarg);
2358  if (loop < 0) {
2359  Com_Printf("Invalid value for %s option.\n", cmd_optopt);
2360  Cmd_PrintHint();
2361  return;
2362  }
2363  todo |= 1;
2364  break;
2365  case 'n':
2366  name = cmd_optarg;
2367  todo |= 2;
2368  break;
2369  default:
2370  return;
2371  }
2372  }
2373 
2374  if (!todo) {
2375  Com_Printf("At least one option needed.\n");
2376  Cmd_PrintHint();
2377  return;
2378  }
2379 
2381  if (!mvd) {
2382  Cmd_PrintHint();
2383  return;
2384  }
2385 
2386  if (name) {
2387  Com_Printf("[%s] Channel renamed to %s.\n", mvd->name, name);
2388  Q_strlcpy(mvd->name, name, sizeof(mvd->name));
2389  }
2390  if (loop != -1) {
2391  //Com_Printf("[%s] Loop count changed to %d.\n", mvd->name, loop);
2392  //mvd->demoloop = loop;
2393  }
2394 }

◆ MVD_Destroy()

static void MVD_Destroy ( mvd_t mvd)
static

Definition at line 167 of file client.c.

168 {
169  mvd_client_t *client, *next;
170 
171  // update channel menus
172  if (!LIST_EMPTY(&mvd->entry)) {
173  mvd_dirty = qtrue;
174  }
175 
176  // cause UDP clients to reconnect
177  LIST_FOR_EACH_SAFE(mvd_client_t, client, next, &mvd->clients, entry) {
179  }
180 
181  // destroy any existing GTV connection
182  if (mvd->gtv) {
183  mvd->gtv->mvd = NULL; // don't double destroy
184  mvd->gtv->destroy(mvd->gtv);
185  }
186 
187  // free all channel data
188  MVD_Free(mvd);
189 }

Referenced by demo_destroy(), MVD_Destroyf(), and MVD_Kill_f().

◆ MVD_Destroyf()

void MVD_Destroyf ( mvd_t mvd,
const char *  fmt,
  ... 
)

Definition at line 191 of file client.c.

192 {
193  va_list argptr;
194  char text[MAXERRORMSG];
195 
196  va_start(argptr, fmt);
197  Q_vsnprintf(text, sizeof(text), fmt, argptr);
198  va_end(argptr);
199 
200  Com_Printf("[%s] =X= %s\n", mvd->name, text);
201 
202  // notify spectators
203  if (COM_DEDICATED) {
204  MVD_BroadcastPrintf(mvd, PRINT_HIGH, 0,
205  "[MVD] %s\n", text);
206  }
207 
208  MVD_Destroy(mvd);
209 
210  longjmp(mvd_jmpbuf, -1);
211 }

Referenced by demo_read_frame(), gtv_oob_kill(), gtv_read_frame(), gtv_wait_start(), MVD_ParseConfigstring(), MVD_ParseFrame(), MVD_ParseMessage(), MVD_ParseMulticast(), MVD_ParsePacketEntities(), MVD_ParsePacketPlayers(), MVD_ParseServerData(), MVD_ParseSound(), MVD_ParseUnicast(), and MVD_UnicastString().

◆ MVD_Disconnect_f()

static void MVD_Disconnect_f ( void  )
static

Definition at line 2080 of file client.c.

2081 {
2082  gtv_t *gtv;
2083 
2084  gtv = gtv_set_conn(1);
2085  if (!gtv) {
2086  return;
2087  }
2088 
2089  Com_Printf("[%s] =X= Connection destroyed.\n", gtv->name);
2090  gtv->destroy(gtv);
2091 }

◆ MVD_File_g()

void MVD_File_g ( genctx_t *  ctx)

Definition at line 2406 of file client.c.

2407 {
2408  FS_File_g("demos", "*.mvd2;*.mvd2.gz", FS_SEARCH_SAVEPATH | FS_SEARCH_BYFILTER, ctx);
2409 }

Referenced by MVD_Play_c().

◆ MVD_Frame()

int MVD_Frame ( void  )

Definition at line 406 of file client.c.

407 {
408  gtv_t *gtv, *next;
409  int connections = 0;
410 
411  if (sv.state == ss_broadcast) {
412  set_mvd_active();
413  }
414 
415  // run all GTV connections (but not demos)
416  LIST_FOR_EACH_SAFE(gtv_t, gtv, next, &mvd_gtv_list, entry) {
417  if (setjmp(mvd_jmpbuf)) {
419  continue;
420  }
421 
422  gtv->run(gtv);
423 
424  connections++;
425  }
426 
427  return connections;
428 }

Referenced by SV_Frame().

◆ MVD_Free()

static void MVD_Free ( mvd_t mvd)
static

Definition at line 139 of file client.c.

140 {
141  mvd_snap_t *snap, *next;
142  int i;
143 
144  LIST_FOR_EACH_SAFE(mvd_snap_t, snap, next, &mvd->snapshots, entry) {
145  Z_Free(snap);
146  }
147 
148  // stop demo recording
149  if (mvd->demorecording) {
151  }
152 
153  for (i = 0; i < mvd->maxclients; i++) {
154  MVD_FreePlayer(&mvd->players[i]);
155  }
156 
157  Z_Free(mvd->players);
158 
159  CM_FreeMap(&mvd->cm);
160 
161  Z_Free(mvd->delay.data);
162 
163  List_Remove(&mvd->entry);
164  Z_Free(mvd);
165 }

Referenced by gtv_destroy(), MVD_Destroy(), and MVD_Shutdown().

◆ MVD_Kill_f()

static void MVD_Kill_f ( void  )
static

Definition at line 2093 of file client.c.

2094 {
2095  mvd_t *mvd;
2096 
2097  mvd = MVD_SetChannel(1);
2098  if (!mvd) {
2099  return;
2100  }
2101 
2102  Com_Printf("[%s] =X= Channel was killed.\n", mvd->name);
2103  MVD_Destroy(mvd);
2104 }

◆ MVD_ListChannels_f()

static void MVD_ListChannels_f ( void  )
static

Definition at line 1732 of file client.c.

1733 {
1734  char *s;
1735 
1736  if (LIST_EMPTY(&mvd_channel_list)) {
1737  Com_Printf("No MVD channels.\n");
1738  return;
1739  }
1740 
1741  s = Cmd_Argv(1);
1742  if (*s == 'r') {
1743  list_recordings();
1744  } else {
1745  list_generic();
1746  }
1747 }

◆ MVD_ListServers_f()

static void MVD_ListServers_f ( void  )
static

Definition at line 1749 of file client.c.

1750 {
1751  gtv_t *gtv;
1752  unsigned ratio;
1753 
1754  if (LIST_EMPTY(&mvd_gtv_list)) {
1755  Com_Printf("No GTV connections.\n");
1756  return;
1757  }
1758 
1759  Com_Printf(
1760  "id name state ratio lastmsg address \n"
1761  "-- ------------ ------------ ----- ------- --------------\n");
1762 
1763  FOR_EACH_GTV(gtv) {
1764  ratio = 100;
1765 #if USE_ZLIB
1766  if (gtv->z_act && gtv->z_str.total_out) {
1767  ratio = 100 * ((double)gtv->z_str.total_in /
1768  gtv->z_str.total_out);
1769  }
1770 #endif
1771  Com_Printf("%2d %-12.12s %-12.12s %4u%% %7u %s\n",
1772  gtv->id, gtv->name, gtv_states[gtv->state],
1773  ratio, svs.realtime - gtv->last_rcvd,
1774  NET_AdrToString(&gtv->stream.address));
1775  }
1776 }

◆ MVD_Pause_f()

static void MVD_Pause_f ( void  )
static

Definition at line 2106 of file client.c.

2107 {
2108  mvd_t *mvd;
2109 
2110  mvd = MVD_SetChannel(1);
2111  if (!mvd) {
2112  return;
2113  }
2114 
2115  if (!mvd->gtv || !mvd->gtv->demoplayback) {
2116  Com_Printf("[%s] Only demo channels can be paused.\n", mvd->name);
2117  return;
2118  }
2119 
2120  switch (mvd->state) {
2121  case MVD_WAITING:
2122  //Com_Printf("[%s] Channel was resumed.\n", mvd->name);
2123  mvd->state = MVD_READING;
2124  break;
2125  case MVD_READING:
2126  //Com_Printf("[%s] Channel was paused.\n", mvd->name);
2127  mvd->state = MVD_WAITING;
2128  break;
2129  default:
2130  break;
2131  }
2132 }

◆ MVD_Play_c()

static void MVD_Play_c ( genctx_t *  ctx,
int  argnum 
)
static

Definition at line 2411 of file client.c.

2412 {
2413  Cmd_Option_c(o_mvdplay, MVD_File_g, ctx, argnum);
2414 }

◆ MVD_Play_f()

static void MVD_Play_f ( void  )
static

Definition at line 2416 of file client.c.

2417 {
2418  char *name = NULL;
2419  char buffer[MAX_OSPATH];
2420  int loop = -1, chan_id = -1;
2421  qhandle_t f;
2422  size_t len;
2423  gtv_t *gtv = NULL;
2424  int c, argc;
2425  string_entry_t *entry, *head;
2426  int i;
2427 
2428  while ((c = Cmd_ParseOptions(o_mvdplay)) != -1) {
2429  switch (c) {
2430  case 'h':
2431  Cmd_PrintUsage(o_mvdplay, "[/]<filename> [...]");
2432  Com_Printf("Create new MVD channel and begin demo playback.\n");
2434  Com_Printf("Final path is formatted as demos/<filename>.mvd2.\n"
2435  "Prepend slash to specify raw path.\n");
2436  return;
2437  case 'l':
2438  loop = atoi(cmd_optarg);
2439  if (loop < 0) {
2440  Com_Printf("Invalid value for %s option.\n", cmd_optopt);
2441  Cmd_PrintHint();
2442  return;
2443  }
2444  break;
2445  case 'n':
2446  name = cmd_optarg;
2447  break;
2448  case 'r':
2449  chan_id = cmd_optind - 1;
2450  break;
2451  default:
2452  return;
2453  }
2454  }
2455 
2456  argc = Cmd_Argc();
2457  if (cmd_optind == argc) {
2458  Com_Printf("Missing filename argument.\n");
2459  Cmd_PrintHint();
2460  return;
2461  }
2462 
2463  if (chan_id != -1) {
2464  mvd_t *mvd = MVD_SetChannel(chan_id);
2465  if (mvd) {
2466  gtv = mvd->gtv;
2467  }
2468  }
2469 
2470  // build the playlist
2471  head = NULL;
2472  for (i = argc - 1; i >= cmd_optind; i--) {
2473  // try to open it
2474  f = FS_EasyOpenFile(buffer, sizeof(buffer), FS_MODE_READ,
2475  "demos/", Cmd_Argv(i), ".mvd2");
2476  if (!f) {
2477  continue;
2478  }
2479 
2480  FS_FCloseFile(f);
2481 
2482  len = strlen(buffer);
2483  entry = MVD_Malloc(sizeof(*entry) + len);
2484  memcpy(entry->string, buffer, len + 1);
2485  entry->next = head;
2486  head = entry;
2487  }
2488 
2489  if (!head) {
2490  return;
2491  }
2492 
2493  if (gtv) {
2494  // free existing playlist
2495  demo_free_playlist(gtv);
2496  } else {
2497  // create new connection
2498  gtv = MVD_Mallocz(sizeof(*gtv));
2499  gtv->id = mvd_chanid++;
2500  gtv->state = GTV_READING;
2501  gtv->drop = demo_destroy;
2502  gtv->destroy = demo_destroy;
2503  gtv->demoloop = 1;
2504  Q_snprintf(gtv->name, sizeof(gtv->name), "dem%d", gtv->id);
2505  }
2506 
2507  // set channel name
2508  if (name) {
2509  Q_strlcpy(gtv->name, name, sizeof(gtv->name));
2510  }
2511 
2512  // set loop parameter
2513  if (loop != -1) {
2514  gtv->demoloop = loop;
2515  }
2516 
2517  // set new playlist
2518  gtv->demohead = head;
2519 
2520  if (setjmp(mvd_jmpbuf)) {
2521  return;
2522  }
2523 
2524  demo_play_next(gtv, head);
2525 }

◆ MVD_Register()

void MVD_Register ( void  )

Definition at line 2582 of file client.c.

2583 {
2584 #ifdef _DEBUG
2585  mvd_shownet = Cvar_Get("mvd_shownet", "0", 0);
2586 #endif
2587  mvd_timeout = Cvar_Get("mvd_timeout", "90", 0);
2588  mvd_suspend_time = Cvar_Get("mvd_suspend_time", "5", 0);
2589  mvd_wait_delay = Cvar_Get("mvd_wait_delay", "20", 0);
2590  mvd_wait_percent = Cvar_Get("mvd_wait_percent", "35", 0);
2591  mvd_buffer_size = Cvar_Get("mvd_buffer_size", "3", 0);
2592  mvd_username = Cvar_Get("mvd_username", "unnamed", 0);
2593  mvd_password = Cvar_Get("mvd_password", "", CVAR_PRIVATE);
2594  mvd_snaps = Cvar_Get("mvd_snaps", "10", 0);
2595 
2597 }

Referenced by SV_Init().

◆ MVD_Seek_f()

static void MVD_Seek_f ( void  )
static

Definition at line 2158 of file client.c.

2159 {
2160  mvd_t *mvd;
2161  gtv_t *gtv;
2162  mvd_snap_t *snap;
2163  int i, j, ret, index, frames, dest;
2164  char *from, *to;
2165  edict_t *ent;
2166  qboolean gamestate;
2167 
2168  if (Cmd_Argc() < 2) {
2169  Com_Printf("Usage: %s [+-]<timespec> [chanid]\n", Cmd_Argv(0));
2170  return;
2171  }
2172 
2173  mvd = MVD_SetChannel(2);
2174  if (!mvd) {
2175  return;
2176  }
2177 
2178  gtv = mvd->gtv;
2179  if (!gtv || !gtv->demoplayback) {
2180  Com_Printf("[%s] Seeking is only supported on demo channels.\n", mvd->name);
2181  return;
2182  }
2183 
2184  if (mvd->demorecording) {
2185  // need some sort of nodelta frame support for that :(
2186  Com_Printf("[%s] Seeking is not yet supported during demo recording, sorry.\n", mvd->name);
2187  return;
2188  }
2189 
2190  to = Cmd_Argv(1);
2191 
2192  if (*to == '-' || *to == '+') {
2193  // relative to current frame
2194  if (!Com_ParseTimespec(to + 1, &frames)) {
2195  Com_Printf("Invalid relative timespec.\n");
2196  return;
2197  }
2198  if (*to == '-')
2199  frames = -frames;
2200  dest = mvd->framenum + frames;
2201  } else {
2202  // relative to first frame
2203  if (!Com_ParseTimespec(to, &dest)) {
2204  Com_Printf("Invalid absolute timespec.\n");
2205  return;
2206  }
2207  frames = dest - mvd->framenum;
2208  }
2209 
2210  if (!frames)
2211  // already there
2212  return;
2213 
2214  if (setjmp(mvd_jmpbuf))
2215  return;
2216 
2217  // disable effects processing
2218  mvd->demoseeking = qtrue;
2219 
2220  // clear dirty configstrings
2221  memset(mvd->dcs, 0, sizeof(mvd->dcs));
2222 
2223  Com_DPrintf("[%d] seeking to %d\n", mvd->framenum, dest);
2224 
2225  // seek to the previous most recent snapshot
2226  if (frames < 0 || mvd->last_snapshot > mvd->framenum) {
2227  snap = demo_find_snapshot(mvd, dest);
2228 
2229  if (snap) {
2230  Com_DPrintf("found snap at %d\n", snap->framenum);
2231  ret = FS_Seek(gtv->demoplayback, snap->filepos);
2232  if (ret < 0) {
2233  Com_EPrintf("[%s] Couldn't seek demo: %s\n", mvd->name, Q_ErrorString(ret));
2234  goto done;
2235  }
2236 
2237  // clear delta state
2238  MVD_ClearState(mvd, qfalse);
2239 
2240  // reset configstrings
2241  for (i = 0; i < MAX_CONFIGSTRINGS; i++) {
2242  from = mvd->baseconfigstrings[i];
2243  to = mvd->configstrings[i];
2244 
2245  if (!strcmp(from, to))
2246  continue;
2247 
2248  Q_SetBit(mvd->dcs, i);
2249  strcpy(to, from);
2250  }
2251 
2252  // set player names
2254 
2255  SZ_Init(&msg_read, snap->data, snap->msglen);
2256  msg_read.cursize = snap->msglen;
2257 
2259  mvd->framenum = snap->framenum;
2260  } else if (frames < 0) {
2261  Com_Printf("[%s] Couldn't seek backwards without snapshots!\n", mvd->name);
2262  goto done;
2263  }
2264  }
2265 
2266  // skip forward to destination frame
2267  while (mvd->framenum < dest) {
2268  ret = demo_read_message(gtv->demoplayback);
2269  if (ret <= 0) {
2270  demo_finish(gtv, ret);
2271  return;
2272  }
2273 
2274  gamestate = MVD_ParseMessage(mvd);
2275 
2277 
2278  if (gamestate) {
2279  // got a gamestate, abort seek
2280  Com_DPrintf("got gamestate while seeking!\n");
2281  goto done;
2282  }
2283  }
2284 
2285  Com_DPrintf("[%d] after skip\n", mvd->framenum);
2286 
2287  // update dirty configstrings
2288  for (i = 0; i < CS_BITMAP_LONGS; i++) {
2289  if (((uint32_t *)mvd->dcs)[i] == 0)
2290  continue;
2291 
2292  index = i << 5;
2293  for (j = 0; j < 32; j++, index++) {
2294  if (Q_IsBitSet(mvd->dcs, index))
2295  MVD_UpdateConfigstring(mvd, index);
2296  }
2297  }
2298 
2299  // ouch
2300  CM_SetPortalStates(&mvd->cm, NULL, 0);
2301 
2302  // init world entity
2303  ent = &mvd->edicts[0];
2304  ent->solid = SOLID_BSP;
2305  ent->inuse = qtrue;
2306 
2307  // relink all seen entities, reset old origins and events
2308  for (i = 1; i < MAX_EDICTS; i++) {
2309  ent = &mvd->edicts[i];
2310 
2311  if (ent->svflags & SVF_MONSTER)
2312  MVD_LinkEdict(mvd, ent);
2313 
2314  if (!ent->inuse)
2315  continue;
2316 
2317  if (!(ent->s.renderfx & RF_BEAM))
2318  VectorCopy(ent->s.origin, ent->s.old_origin);
2319 
2320  ent->s.event = EV_OTHER_TELEPORT;
2321  }
2322 
2324 
2325  // wait one frame to give entity events a chance to be communicated back to
2326  // clients
2327  gtv->demowait = qtrue;
2328 
2329  demo_update(gtv);
2330 
2331 done:
2332  mvd->demoseeking = qfalse;
2333 }

◆ MVD_SetChannel()

mvd_t* MVD_SetChannel ( int  arg)

Definition at line 230 of file client.c.

231 {
232  char *s = Cmd_Argv(arg);
233  mvd_t *mvd;
234  int id;
235 
236  if (LIST_EMPTY(&mvd_channel_list)) {
237  Com_Printf("No active channels.\n");
238  return NULL;
239  }
240 
241  if (!*s) {
242  if (LIST_SINGLE(&mvd_channel_list)) {
243  return LIST_FIRST(mvd_t, &mvd_channel_list, entry);
244  }
245  Com_Printf("Please specify an exact channel ID.\n");
246  return NULL;
247  }
248 
249 #if USE_CLIENT
250  // special value of @@ returns the channel local client is on
251  if (!dedicated->integer && !strcmp(s, "@@")) {
252  if ((mvd = find_local_channel()) != NULL) {
253  return mvd;
254  }
255  } else
256 #endif
257  if (COM_IsUint(s)) {
258  id = atoi(s);
259  FOR_EACH_MVD(mvd) {
260  if (mvd->id == id) {
261  return mvd;
262  }
263  }
264  } else {
265  FOR_EACH_MVD(mvd) {
266  if (!strcmp(mvd->name, s)) {
267  return mvd;
268  }
269  }
270  }
271 
272  Com_Printf("No such channel ID: %s\n", s);
273  return NULL;
274 }

Referenced by MVD_Control_f(), MVD_Join_f(), MVD_Kill_f(), MVD_Pause_f(), MVD_Play_f(), MVD_Seek_f(), MVD_Skip_f(), MVD_StreamedRecord_f(), and MVD_StreamedStop_f().

◆ MVD_Shutdown()

void MVD_Shutdown ( void  )

Definition at line 2528 of file client.c.

2529 {
2530  gtv_t *gtv, *gtv_next;
2531  mvd_t *mvd, *mvd_next;
2532 
2533  // kill all GTV connections
2534  LIST_FOR_EACH_SAFE(gtv_t, gtv, gtv_next, &mvd_gtv_list, entry) {
2535  gtv->destroy(gtv);
2536  }
2537 
2538  // kill all MVD channels (including demo GTVs)
2539  LIST_FOR_EACH_SAFE(mvd_t, mvd, mvd_next, &mvd_channel_list, entry) {
2540  if (mvd->gtv) {
2541  mvd->gtv->mvd = NULL; // don't double destroy
2542  mvd->gtv->destroy(mvd->gtv);
2543  }
2544  MVD_Free(mvd);
2545  }
2546 
2547  List_Init(&mvd_gtv_list);
2548  List_Init(&mvd_channel_list);
2549 
2551  mvd_clients = NULL;
2552 
2553  mvd_chanid = 0;
2554 
2555  mvd_active = qfalse;
2556 
2557  Z_LeakTest(TAG_MVD);
2558 }

Referenced by MVD_GameShutdown(), and SV_Shutdown().

◆ MVD_Skip_f()

static void MVD_Skip_f ( void  )
static

Definition at line 2134 of file client.c.

2135 {
2136  mvd_t *mvd;
2137  int count;
2138 
2139  mvd = MVD_SetChannel(1);
2140  if (!mvd) {
2141  Com_Printf("Usage: %s [chan_id] [count]\n", Cmd_Argv(0));
2142  return;
2143  }
2144 
2145  count = atoi(Cmd_Argv(2));
2146  if (count < 1) {
2147  count = 1;
2148  }
2149 
2150  if (!mvd->gtv || !mvd->gtv->demoplayback) {
2151  Com_Printf("[%s] Maps can be skipped only on demo channels.\n", mvd->name);
2152  return;
2153  }
2154 
2155  mvd->gtv->demoskip = count;
2156 }

◆ MVD_Spawn()

void MVD_Spawn ( void  )

Definition at line 1653 of file client.c.

1654 {
1655  Cvar_SetInteger(sv_running, ss_broadcast, FROM_CODE);
1656  Cvar_Set("sv_paused", "0");
1657  Cvar_Set("timedemo", "0");
1658  SV_InfoSet("port", net_port->string);
1659 
1660 #if USE_SYSCON
1661  SV_SetConsoleTitle();
1662 #endif
1663 
1664  // generate spawncount for Waiting Room
1665  sv.spawncount = (rand() | (rand() << 16)) ^ Sys_Milliseconds();
1666  sv.spawncount &= 0x7FFFFFFF;
1667 
1668 #if USE_FPS
1669  // just fixed base FPS
1670  sv.framerate = BASE_FRAMERATE;
1671  sv.frametime = BASE_FRAMETIME;
1672  sv.framediv = 1;
1673 #endif
1674 
1675  // set externally visible server name
1677 
1678  sv.state = ss_broadcast;
1679 
1680  // start as inactive
1681  mvd_last_activity = INT_MIN;
1682  set_mvd_active();
1683 }

Referenced by MVD_ChangeLevel(), MVD_Spawn_f(), and parse_hello().

◆ MVD_Spawn_f()

static void MVD_Spawn_f ( void  )
static

Definition at line 1685 of file client.c.

1686 {
1688  MVD_Spawn();
1689 }

◆ MVD_StopRecord()

void MVD_StopRecord ( mvd_t mvd)

Definition at line 125 of file client.c.

126 {
127  uint16_t msglen;
128 
129  msglen = 0;
130  FS_Write(&msglen, 2, mvd->demorecording);
131 
132  FS_FCloseFile(mvd->demorecording);
133  mvd->demorecording = 0;
134 
135  Z_Free(mvd->demoname);
136  mvd->demoname = NULL;
137 }

Referenced by MVD_Free(), MVD_StreamedRecord_f(), MVD_StreamedStop_f(), and MVD_WriteDemoMessage().

◆ MVD_StreamedRecord_f()

void MVD_StreamedRecord_f ( void  )

Definition at line 1896 of file client.c.

1897 {
1898  char buffer[MAX_OSPATH];
1899  qhandle_t f;
1900  mvd_t *mvd;
1901  uint32_t magic;
1902  uint16_t msglen;
1903  unsigned mode = FS_MODE_WRITE;
1904  ssize_t ret;
1905  int c;
1906 
1907  while ((c = Cmd_ParseOptions(o_record)) != -1) {
1908  switch (c) {
1909  case 'h':
1910  Cmd_PrintUsage(o_record, "<filename> [chanid]");
1911  Com_Printf("Begin MVD recording on the specified channel.\n");
1913  return;
1914  case 'z':
1915  mode |= FS_FLAG_GZIP;
1916  break;
1917  default:
1918  return;
1919  }
1920  }
1921 
1922  if (!cmd_optarg[0]) {
1923  Com_Printf("Missing filename argument.\n");
1924  Cmd_PrintHint();
1925  return;
1926  }
1927 
1928  if ((mvd = MVD_SetChannel(cmd_optind + 1)) == NULL) {
1929  Cmd_PrintHint();
1930  return;
1931  }
1932 
1933  if (mvd->demorecording) {
1934  Com_Printf("[%s] Already recording into %s.\n",
1935  mvd->name, mvd->demoname);
1936  return;
1937  }
1938 
1939  //
1940  // open the demo file
1941  //
1942  f = FS_EasyOpenFile(buffer, sizeof(buffer), mode,
1943  "demos/", cmd_optarg, ".mvd2");
1944  if (!f) {
1945  return;
1946  }
1947 
1948  Com_Printf("[%s] Recording into %s\n", mvd->name, buffer);
1949 
1950  mvd->demorecording = f;
1951  mvd->demoname = MVD_CopyString(buffer);
1952 
1954 
1955  // write magic
1956  magic = MVD_MAGIC;
1957  ret = FS_Write(&magic, 4, f);
1958  if (ret != 4)
1959  goto fail;
1960 
1961  // write gamestate
1962  msglen = LittleShort(msg_write.cursize);
1963  ret = FS_Write(&msglen, 2, f);
1964  if (ret != 2)
1965  goto fail;
1966  ret = FS_Write(msg_write.data, msg_write.cursize, f);
1967  if (ret != msg_write.cursize)
1968  goto fail;
1969 
1970  SZ_Clear(&msg_write);
1971  return;
1972 
1973 fail:
1974  SZ_Clear(&msg_write);
1975  Com_EPrintf("[%s] Couldn't write demo: %s\n", mvd->name, Q_ErrorString(ret));
1977 }

◆ MVD_StreamedStop_f()

void MVD_StreamedStop_f ( void  )

Definition at line 1778 of file client.c.

1779 {
1780  mvd_t *mvd;
1781 
1782  mvd = MVD_SetChannel(1);
1783  if (!mvd) {
1784  Com_Printf("Usage: %s [chanid]\n", Cmd_Argv(0));
1785  return;
1786  }
1787 
1788  if (!mvd->demorecording) {
1789  Com_Printf("[%s] Not recording a demo.\n", mvd->name);
1790  return;
1791  }
1792 
1794 
1795  Com_Printf("[%s] Stopped recording.\n", mvd->name);
1796 }

◆ parse_hello()

static void parse_hello ( gtv_t gtv)
static

Definition at line 1084 of file client.c.

1085 {
1086  int flags;
1087 
1088  if (gtv->state >= GTV_CONNECTED) {
1089  gtv_destroyf(gtv, "Duplicated server hello");
1090  }
1091 
1092  flags = MSG_ReadLong();
1093 
1094  if (flags & GTF_DEFLATE) {
1095 #if USE_ZLIB
1096  if (!gtv->z_str.state) {
1097  gtv->z_str.zalloc = gtv_zalloc;
1098  gtv->z_str.zfree = gtv_zfree;
1099  if (inflateInit(&gtv->z_str) != Z_OK) {
1100  gtv_destroyf(gtv, "inflateInit() failed: %s",
1101  gtv->z_str.msg);
1102  }
1103  }
1104  if (!gtv->z_buf.data) {
1105  gtv->z_buf.data = MVD_Malloc(MAX_GTS_MSGLEN);
1106  gtv->z_buf.size = MAX_GTS_MSGLEN;
1107  }
1108  gtv->z_act = qtrue; // remaining data is deflated
1109 #else
1110  gtv_destroyf(gtv, "Server sending deflated data");
1111 #endif
1112  }
1113 
1114  Com_Printf("[%s] -=- Server hello done.\n", gtv->name);
1115 
1116  if (sv.state != ss_broadcast) {
1117  // the game is just starting
1119  MVD_Spawn();
1120  } else {
1121  // notify spectators
1122  if (COM_DEDICATED && gtv->mvd) {
1123  MVD_BroadcastPrintf(gtv->mvd, PRINT_HIGH, 0,
1124  "[MVD] Restored connection to the game server!\n");
1125  }
1126  }
1127 
1128  gtv->flags = flags;
1129  gtv->state = GTV_CONNECTED;
1130 }

Referenced by parse_message().

◆ parse_message()

static qboolean parse_message ( gtv_t gtv,
fifo_t *  fifo 
)
static

Definition at line 1251 of file client.c.

1252 {
1253  uint32_t magic;
1254  uint16_t msglen;
1255  int cmd;
1256 
1257  // check magic
1258  if (gtv->state < GTV_PREPARING) {
1259  if (!FIFO_TryRead(fifo, &magic, 4)) {
1260  return qfalse;
1261  }
1262  if (magic != MVD_MAGIC) {
1263  gtv_destroyf(gtv, "Not a MVD/GTV stream");
1264  }
1265 
1266  // send client hello
1267  send_hello(gtv);
1268  }
1269 
1270  // parse msglen
1271  if (!gtv->msglen) {
1272  if (!FIFO_TryRead(fifo, &msglen, 2)) {
1273  return qfalse;
1274  }
1275  msglen = LittleShort(msglen);
1276  if (!msglen) {
1277  gtv_dropf(gtv, "End of MVD/GTV stream");
1278  }
1279  if (msglen > MAX_MSGLEN) {
1280  gtv_destroyf(gtv, "Oversize message");
1281  }
1282  gtv->msglen = msglen;
1283  }
1284 
1285  // read this message
1286  if (!FIFO_ReadMessage(fifo, gtv->msglen)) {
1287  return qfalse;
1288  }
1289 
1290  gtv->msglen = 0;
1291 
1292  cmd = MSG_ReadByte();
1293 
1294  switch (cmd) {
1295  case GTS_HELLO:
1296  parse_hello(gtv);
1297  break;
1298  case GTS_PONG:
1299  break;
1300  case GTS_STREAM_START:
1301  parse_stream_start(gtv);
1302  break;
1303  case GTS_STREAM_STOP:
1304  parse_stream_stop(gtv);
1305  break;
1306  case GTS_STREAM_DATA:
1307  parse_stream_data(gtv);
1308  break;
1309  case GTS_ERROR:
1310  gtv_destroyf(gtv, "Server side error occured.");
1311  break;
1312  case GTS_BADREQUEST:
1313  gtv_destroyf(gtv, "Server refused to process our request.");
1314  break;
1315  case GTS_NOACCESS:
1316  gtv_destroyf(gtv,
1317  "You don't have permission to access "
1318  "MVD/GTV stream on this server.");
1319  break;
1320  case GTS_DISCONNECT:
1321  gtv_destroyf(gtv, "Server has been shut down.");
1322  break;
1323  case GTS_RECONNECT:
1324  gtv_dropf(gtv, "Server has been restarted.");
1325  break;
1326  default:
1327  gtv_destroyf(gtv, "Unknown command byte");
1328  }
1329 
1330  if (msg_read.readcount > msg_read.cursize) {
1331  gtv_destroyf(gtv, "Read past end of message");
1332  }
1333 
1334  gtv->last_rcvd = svs.realtime; // don't timeout
1335  return qtrue;
1336 }

Referenced by run_stream().

◆ parse_stream_data()

static void parse_stream_data ( gtv_t gtv)
static

Definition at line 1154 of file client.c.

1155 {
1156  mvd_t *mvd = gtv->mvd;
1157  size_t size;
1158 
1159  if (gtv->state < GTV_WAITING) {
1160  gtv_destroyf(gtv, "Unexpected stream data packet");
1161  }
1162 
1163  // ignore any pending data while suspending
1164  if (gtv->state == GTV_SUSPENDING) {
1165  msg_read.readcount = msg_read.cursize;
1166  return;
1167  }
1168 
1169  // empty data part acts as stream suspend marker
1170  if (msg_read.readcount == msg_read.cursize) {
1171  if (gtv->state == GTV_READING) {
1172  Com_Printf("[%s] -=- Stream suspended by server.\n", gtv->name);
1173  gtv->state = GTV_WAITING;
1174  }
1175  return;
1176  }
1177 
1178  // non-empty data part acts as stream resume marker
1179  if (gtv->state == GTV_WAITING) {
1180  Com_Printf("[%s] -=- Stream resumed by server.\n", gtv->name);
1181  gtv->state = GTV_READING;
1182  }
1183 
1184  // create the channel, if not yet created
1185  if (!mvd) {
1186  mvd = create_channel(gtv);
1187 
1189 
1190  // allocate delay buffer
1191  size = mvd_buffer_size->integer * MAX_MSGLEN;
1192  mvd->delay.data = MVD_Malloc(size);
1193  mvd->delay.size = size;
1194  mvd->read_frame = gtv_read_frame;
1195  mvd->forward_cmd = gtv_forward_cmd;
1196 
1197  gtv->mvd = mvd;
1198  }
1199 
1200  if (!mvd->state) {
1201  // parse it in place until we get a gamestate
1203  } else {
1204  byte *data = msg_read.data + 1;
1205  size_t len = msg_read.cursize - 1;
1206  uint16_t msglen;
1207 
1208  // see if this packet fits
1209  if (FIFO_Write(&mvd->delay, NULL, len + 2) != len + 2) {
1210  if (mvd->state == MVD_WAITING) {
1211  // if delay buffer overflowed in waiting state,
1212  // something is seriously wrong, disconnect for safety
1213  gtv_destroyf(gtv, "Delay buffer overflowed in waiting state");
1214  }
1215 
1216  // oops, overflowed
1217  Com_Printf("[%s] =!= Delay buffer overflowed!\n", gtv->name);
1218 
1219  if (COM_DEDICATED) {
1220  // notify spectators
1221  MVD_BroadcastPrintf(mvd, PRINT_HIGH, 0,
1222  "[MVD] Delay buffer overflowed!\n");
1223  }
1224 
1225  // clear entire delay buffer
1226  // minimize the delay
1227  FIFO_Clear(&mvd->delay);
1228  mvd->state = MVD_WAITING;
1229  mvd->num_packets = 0;
1230  mvd->min_packets = 50;
1231  mvd->overflows++;
1232 
1233  // send stream stop request
1234  write_message(gtv, GTC_STREAM_STOP);
1235  gtv->state = GTV_SUSPENDING;
1236  return;
1237  }
1238 
1239  // write it into delay buffer
1240  msglen = LittleShort(len);
1241  FIFO_Write(&mvd->delay, &msglen, 2);
1242  FIFO_Write(&mvd->delay, data, len);
1243 
1244  // increment buffered packets counter
1245  mvd->num_packets++;
1246 
1247  msg_read.readcount = msg_read.cursize;
1248  }
1249 }

Referenced by parse_message().

◆ parse_stream_start()

static void parse_stream_start ( gtv_t gtv)
static

Definition at line 1132 of file client.c.

1133 {
1134  if (gtv->state != GTV_RESUMING) {
1135  gtv_destroyf(gtv, "Unexpected stream start ack in state %u", gtv->state);
1136  }
1137 
1138  Com_Printf("[%s] -=- Stream start ack received.\n", gtv->name);
1139 
1140  gtv->state = GTV_READING;
1141 }

Referenced by parse_message().

◆ parse_stream_stop()

static void parse_stream_stop ( gtv_t gtv)
static

Definition at line 1143 of file client.c.

1144 {
1145  if (gtv->state != GTV_SUSPENDING) {
1146  gtv_destroyf(gtv, "Unexpected stream stop ack in state %u", gtv->state);
1147  }
1148 
1149  Com_Printf("[%s] -=- Stream stop ack received.\n", gtv->name);
1150 
1151  gtv->state = GTV_CONNECTED;
1152 }

Referenced by parse_message().

◆ player_flags()

static int player_flags ( mvd_t mvd,
mvd_player_t player 
)
inlinestatic

Definition at line 1798 of file client.c.

1799 {
1800  int flags = 0;
1801 
1802  if (!player->inuse)
1803  flags |= MSG_PS_REMOVE;
1804 
1805  return flags;
1806 }

Referenced by emit_base_frame().

◆ q_printf()

static void q_noreturn q_printf ( ,
 
)
static

Definition at line 284 of file client.c.

286 {
287  va_list argptr;
288  char text[MAXERRORMSG];
289 
290  va_start(argptr, fmt);
291  Q_vsnprintf(text, sizeof(text), fmt, argptr);
292  va_end(argptr);
293 
294  Com_Printf("[%s] =!= %s\n", gtv->name, text);
295 
296  gtv->drop(gtv);
297 
298  longjmp(mvd_jmpbuf, -1);
299 }

◆ run_connect()

static neterr_t run_connect ( gtv_t gtv)
static

Definition at line 1387 of file client.c.

1388 {
1389  neterr_t ret;
1390  uint32_t magic;
1391 
1392  // run connection
1393  if ((ret = NET_RunConnect(&gtv->stream)) != NET_OK) {
1394  return ret;
1395  }
1396 
1397  Com_Printf("[%s] -=- Connected to the game server!\n", gtv->name);
1398 
1399  // allocate buffers
1400  if (!gtv->data) {
1401  gtv->data = MVD_Malloc(MAX_GTS_MSGLEN + MAX_GTC_MSGLEN);
1402  }
1403  gtv->stream.recv.data = gtv->data;
1404  gtv->stream.recv.size = MAX_GTS_MSGLEN;
1405  gtv->stream.send.data = gtv->data + MAX_GTS_MSGLEN;
1406  gtv->stream.send.size = MAX_GTC_MSGLEN;
1407 
1408  // don't timeout
1409  gtv->last_rcvd = svs.realtime;
1410 
1411  // send magic
1412  magic = MVD_MAGIC;
1413  write_stream(gtv, &magic, 4);
1414 
1415  return NET_OK;
1416 }

Referenced by gtv_run().

◆ run_stream()

static neterr_t run_stream ( gtv_t gtv)
static

Definition at line 1418 of file client.c.

1419 {
1420  neterr_t ret;
1421 #ifdef _DEBUG
1422  int count;
1423  size_t usage;
1424 #endif
1425 
1426  // run network stream
1427  if ((ret = NET_RunStream(&gtv->stream)) != NET_OK) {
1428  return ret;
1429  }
1430 
1431 #ifdef _DEBUG
1432  count = 0;
1433  usage = FIFO_Usage(&gtv->stream.recv);
1434 #endif
1435 
1436 #if USE_ZLIB
1437  if (gtv->z_act) {
1438  while (1) {
1439  // decompress more data
1440  if (gtv->z_act) {
1441  inflate_more(gtv);
1442  }
1443  if (!parse_message(gtv, &gtv->z_buf)) {
1444  break;
1445  }
1446 #ifdef _DEBUG
1447  count++;
1448 #endif
1449  }
1450  } else
1451 #endif
1452  while (parse_message(gtv, &gtv->stream.recv)) {
1453 #ifdef _DEBUG
1454  count++;
1455 #endif
1456  }
1457 
1458 #ifdef _DEBUG
1459  if (mvd_shownet->integer == -1) {
1460  size_t total = usage - FIFO_Usage(&gtv->stream.recv);
1461 
1462  Com_Printf("[%s] %"PRIz" bytes, %d msgs\n",
1463  gtv->name, total, count);
1464  }
1465 #endif
1466 
1467  return NET_OK;
1468 }

Referenced by gtv_run().

◆ send_hello()

static void send_hello ( gtv_t gtv)
static

Definition at line 1016 of file client.c.

1017 {
1018  int flags = GTF_STRINGCMDS;
1019 
1020 #if USE_ZLIB
1021  flags |= GTF_DEFLATE;
1022 #endif
1023 
1024  MSG_WriteShort(GTV_PROTOCOL_VERSION);
1025  MSG_WriteLong(flags);
1026  MSG_WriteLong(0); // reserved
1027  MSG_WriteString(gtv->username ? gtv->username : mvd_username->string);
1028  MSG_WriteString(gtv->password ? gtv->password : mvd_password->string);
1029  MSG_WriteString(com_version->string);
1030  write_message(gtv, GTC_HELLO);
1031  SZ_Clear(&msg_write);
1032 
1033  Com_Printf("[%s] -=- Sending client hello...\n", gtv->name);
1034 
1035  gtv->state = GTV_PREPARING;
1036 }

Referenced by parse_message().

◆ send_stream_start()

static void send_stream_start ( gtv_t gtv)
static

Definition at line 1038 of file client.c.

1039 {
1040  int maxbuf;
1041 
1042  if (gtv->mvd) {
1043  maxbuf = gtv->mvd->min_packets / 2;
1044  } else {
1045  maxbuf = mvd_wait_delay->value * 10 / 2;
1046  }
1047  if (maxbuf < 10) {
1048  maxbuf = 10;
1049  }
1050 
1051  // send stream start request
1052  MSG_WriteShort(maxbuf);
1053  write_message(gtv, GTC_STREAM_START);
1054  SZ_Clear(&msg_write);
1055 
1056  Com_Printf("[%s] -=- Sending stream start request...\n", gtv->name);
1057 
1058  gtv->state = GTV_RESUMING;
1059 }

Referenced by check_timeouts().

◆ send_stream_stop()

static void send_stream_stop ( gtv_t gtv)
static

Definition at line 1061 of file client.c.

1062 {
1063  // send stream stop request
1064  write_message(gtv, GTC_STREAM_STOP);
1065 
1066  Com_Printf("[%s] -=- Sending stream stop request...\n", gtv->name);
1067 
1068  gtv->state = GTV_SUSPENDING;
1069 }

Referenced by check_timeouts().

◆ set_mvd_active()

static void set_mvd_active ( void  )
static

Definition at line 376 of file client.c.

377 {
378  unsigned delta = mvd_suspend_time->value * 60 * 1000;
379 
380  // zero timeout = always active
381  if (delta == 0)
383 
384  if (svs.realtime - mvd_last_activity > delta) {
385  if (mvd_active) {
386  Com_DPrintf("Suspending MVD streams.\n");
387  mvd_active = qfalse;
388  mvd_dirty = qtrue;
389  }
390  } else {
391  if (!mvd_active) {
392  Com_DPrintf("Resuming MVD streams.\n");
393  mvd_active = qtrue;
394  mvd_dirty = qtrue;
395  }
396  }
397 }

Referenced by MVD_Frame(), and MVD_Spawn().

◆ write_message()

static void write_message ( gtv_t gtv,
gtv_clientop_t  op 
)
static

Definition at line 827 of file client.c.

828 {
829  byte header[3];
830  size_t len = msg_write.cursize + 1;
831 
832  header[0] = len & 255;
833  header[1] = (len >> 8) & 255;
834  header[2] = op;
835  write_stream(gtv, header, sizeof(header));
836 
837  write_stream(gtv, msg_write.data, msg_write.cursize);
838 }

Referenced by check_timeouts(), gtv_forward_cmd(), gtv_wait_start(), parse_stream_data(), send_hello(), send_stream_start(), and send_stream_stop().

◆ write_stream()

static void write_stream ( gtv_t gtv,
void data,
size_t  len 
)
static

Definition at line 817 of file client.c.

818 {
819  if (FIFO_Write(&gtv->stream.send, data, len) != len) {
820  gtv_destroyf(gtv, "Send buffer overflowed");
821  }
822 
823  // don't timeout
824  gtv->last_sent = svs.realtime;
825 }

Referenced by run_connect(), and write_message().

Variable Documentation

◆ c_mvd

const cmdreg_t c_mvd[]
static
Initial value:
= {
{ "mvdplay", MVD_Play_f, MVD_Play_c },
{ "mvdconnect", MVD_Connect_f, MVD_Connect_c },
{ "mvdisconnect", MVD_Disconnect_f },
{ "mvdkill", MVD_Kill_f },
{ "mvdspawn", MVD_Spawn_f },
{ "mvdchannels", MVD_ListChannels_f },
{ "mvdservers", MVD_ListServers_f },
{ "mvdcontrol", MVD_Control_f },
{ "mvdpause", MVD_Pause_f },
{ "mvdskip", MVD_Skip_f },
{ "mvdseek", MVD_Seek_f },
{ NULL }
}

Definition at line 2560 of file client.c.

Referenced by MVD_Register().

◆ gtv_states

const char* const gtv_states[GTV_NUM_STATES]
static
Initial value:
= {
"disconnected",
"connecting",
"preparing",
"connected",
"resuming",
"waiting",
"reading",
"suspending"
}

Definition at line 83 of file client.c.

Referenced by MVD_ListServers_f().

◆ mvd_active

qboolean mvd_active

Definition at line 105 of file client.c.

Referenced by check_timeouts(), MVD_LayoutChannels(), MVD_Shutdown(), and set_mvd_active().

◆ mvd_buffer_size

cvar_t* mvd_buffer_size
static

Definition at line 118 of file client.c.

Referenced by MVD_Register(), and parse_stream_data().

◆ mvd_chanid

int mvd_chanid

Definition at line 103 of file client.c.

Referenced by MVD_Connect_f(), MVD_Play_f(), and MVD_Shutdown().

◆ mvd_dirty

qboolean mvd_dirty

◆ mvd_jmpbuf

jmp_buf mvd_jmpbuf

Definition at line 108 of file client.c.

Referenced by MVD_Destroyf(), MVD_Frame(), MVD_GameRunFrame(), MVD_Play_f(), MVD_Seek_f(), and q_printf().

◆ mvd_last_activity

unsigned mvd_last_activity

◆ mvd_password

cvar_t* mvd_password
static

Definition at line 120 of file client.c.

Referenced by MVD_Register(), and send_hello().

◆ mvd_snaps

cvar_t* mvd_snaps
static

Definition at line 121 of file client.c.

Referenced by demo_emit_snapshot(), and MVD_Register().

◆ mvd_states

const char* const mvd_states[MVD_NUM_STATES]
static
Initial value:
= {
"DEAD", "WAIT", "READ"
}

Definition at line 94 of file client.c.

Referenced by list_generic().

◆ mvd_suspend_time

cvar_t* mvd_suspend_time
static

Definition at line 115 of file client.c.

Referenced by MVD_Register(), and set_mvd_active().

◆ mvd_timeout

cvar_t* mvd_timeout
static

Definition at line 114 of file client.c.

Referenced by check_timeouts(), and MVD_Register().

◆ mvd_username

cvar_t* mvd_username
static

Definition at line 119 of file client.c.

Referenced by MVD_Register(), and send_hello().

◆ mvd_wait_delay

cvar_t* mvd_wait_delay
static

Definition at line 116 of file client.c.

Referenced by create_channel(), gtv_wait_start(), MVD_Register(), and send_stream_start().

◆ mvd_wait_percent

cvar_t* mvd_wait_percent
static

Definition at line 117 of file client.c.

Referenced by gtv_wait_stop(), and MVD_Register().

◆ mvd_waitingRoom

◆ o_mvdconnect

const cmd_option_t o_mvdconnect[]
static
Initial value:
= {
{ "h", "help", "display this message" },
{ "n:string", "name", "specify channel name as <string>" },
{ "u:string", "user", "specify username as <string>" },
{ "p:string", "pass", "specify password as <string>" },
{ NULL }
}

Definition at line 1979 of file client.c.

Referenced by MVD_Connect_c(), and MVD_Connect_f().

◆ o_mvdplay

const cmd_option_t o_mvdplay[]
static
Initial value:
= {
{ "h", "help", "display this message" },
{ "l:number", "loop", "replay <number> of times (0 means forever)" },
{ "n:string", "name", "specify channel name as <string>" },
{ "r:chan_id", "replace", "replace <chan_id> playlist with new entries" },
{ NULL }
}

Definition at line 2396 of file client.c.

Referenced by MVD_Play_c(), and MVD_Play_f().

gtv_s::address
char address[MAX_QPATH]
Definition: client.c:61
gtv_s::msglen
size_t msglen
Definition: client.c:63
mvd_channel_list
list_t mvd_channel_list
gtv_s::name
char name[MAX_MVD_NAME]
Definition: client.c:51
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
gtv_s::last_sent
unsigned last_sent
Definition: client.c:71
CM_FreeMap
void CM_FreeMap(cm_t *cm)
Definition: cmodel.c:47
emit_base_frame
static void emit_base_frame(mvd_t *mvd)
Definition: client.c:1823
MVD_SPAWN_INTERNAL
#define MVD_SPAWN_INTERNAL
Definition: server.h:96
mvd_snaps
static cvar_t * mvd_snaps
Definition: client.c:121
write_stream
static void write_stream(gtv_t *gtv, void *data, size_t len)
Definition: client.c:817
gtv_s::mvd
mvd_t * mvd
Definition: client.c:53
mvd_server_t::players
player_packed_t * players
Definition: mvd.c:69
MVD_Spawn
void MVD_Spawn(void)
Definition: client.c:1653
msg_read
sizebuf_t msg_read
Definition: msg.c:37
NET_Connect
neterr_t NET_Connect(const netadr_t *peer, netstream_t *s)
Definition: net.c:1536
mvd_last_activity
unsigned mvd_last_activity
Definition: client.c:106
msg_read_buffer
byte msg_read_buffer[MAX_MSGLEN]
Definition: msg.c:38
mvd_waitingRoom
mvd_t mvd_waitingRoom
Definition: client.c:101
c_mvd
static const cmdreg_t c_mvd[]
Definition: client.c:2560
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
gtv_s::demowait
qboolean demowait
Definition: client.c:80
parse_stream_stop
static void parse_stream_stop(gtv_t *gtv)
Definition: client.c:1143
MVD_SetChannel
mvd_t * MVD_SetChannel(int arg)
Definition: client.c:230
Com_FormatTimeLong
size_t Com_FormatTimeLong(char *buffer, size_t size, time_t t)
Definition: utils.c:408
Q_snprintf
size_t Q_snprintf(char *dest, size_t size, const char *fmt,...)
Definition: shared.c:846
GTV_READING
@ GTV_READING
Definition: client.c:41
FS_Read
ssize_t FS_Read(void *buf, size_t len, qhandle_t f)
Definition: files.c:1547
player_flags
static int player_flags(mvd_t *mvd, mvd_player_t *player)
Definition: client.c:1798
gtv_forward_cmd
static qboolean gtv_forward_cmd(mvd_client_t *client)
Definition: client.c:978
FIFO_ReadMessage
qboolean FIFO_ReadMessage(fifo_t *fifo, size_t msglen)
Definition: fifo.c:89
send_hello
static void send_hello(gtv_t *gtv)
Definition: client.c:1016
GTV_SUSPENDING
@ GTV_SUSPENDING
Definition: client.c:42
create_channel
static mvd_t * create_channel(gtv_t *gtv)
Definition: client.c:318
FS_FilterFile
qerror_t FS_FilterFile(qhandle_t f)
Definition: files.c:661
MVD_StopRecord
void MVD_StopRecord(mvd_t *mvd)
Definition: client.c:125
password
cvar_t * password
Definition: g_main.c:39
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
Z_LeakTest
void Z_LeakTest(memtag_t tag)
Definition: zone.c:120
parse_message
static qboolean parse_message(gtv_t *gtv, fifo_t *fifo)
Definition: client.c:1251
MVD_Kill_f
static void MVD_Kill_f(void)
Definition: client.c:2093
mvd_states
static const char *const mvd_states[MVD_NUM_STATES]
Definition: client.c:94
list_recordings
static void list_recordings(void)
Definition: client.c:1709
check_timeouts
static void check_timeouts(gtv_t *gtv)
Definition: client.c:1470
gtv_s
Definition: client.c:46
gtv_destroy
static void gtv_destroy(gtv_t *gtv)
Definition: client.c:1569
set_mvd_active
static void set_mvd_active(void)
Definition: client.c:376
mvd_s::read_frame
qboolean(* read_frame)(struct mvd_s *)
Definition: client.h:134
mvd_s::demoseeking
qboolean demoseeking
Definition: client.h:140
entity_flags
static int entity_flags(mvd_t *mvd, edict_t *ent)
Definition: client.c:1808
MVD_UpdateConfigstring
void MVD_UpdateConfigstring(mvd_t *mvd, int index)
Definition: game.c:1616
gtv_s::demopos
size_t demopos
Definition: client.c:79
mvd_snap_t::framenum
int framenum
Definition: client.h:117
mvd_password
static cvar_t * mvd_password
Definition: client.c:120
demo_finish
static void demo_finish(gtv_t *gtv, ssize_t ret)
Definition: client.c:652
NET_UpdateStream
void NET_UpdateStream(netstream_t *s)
Definition: net.c:1628
GTV_RESUMING
@ GTV_RESUMING
Definition: client.c:39
GTV_CONNECTING
@ GTV_CONNECTING
Definition: client.c:36
mvd_snap_t
Definition: client.h:115
gtv_s::drop
void(* drop)(struct gtv_s *)
Definition: client.c:54
NET_RunConnect
neterr_t NET_RunConnect(netstream_t *s)
Definition: net.c:1581
cmd_optind
int cmd_optind
Definition: cmd.c:847
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
SV_InfoSet
#define SV_InfoSet(var, val)
Definition: server.h:73
mvd_dirty
qboolean mvd_dirty
Definition: client.c:102
NET_CloseStream
void NET_CloseStream(netstream_t *s)
Definition: net.c:1371
Q_vsnprintf
size_t Q_vsnprintf(char *dest, size_t size, const char *fmt, va_list argptr)
Definition: shared.c:791
gtv_s::demosize
size_t demosize
Definition: client.c:79
gtv_s::id
int id
Definition: client.c:50
gtv_s::retry_time
unsigned retry_time
Definition: client.c:72
FOR_EACH_MVD
#define FOR_EACH_MVD(mvd)
Definition: client.h:26
GTV_WAITING
@ GTV_WAITING
Definition: client.c:40
MSG_WriteByte
void MSG_WriteByte(int c)
Definition: msg.c:107
demo_read_frame
static qboolean demo_read_frame(mvd_t *mvd)
Definition: client.c:661
MVD_Seek_f
static void MVD_Seek_f(void)
Definition: client.c:2158
Cmd_Argv
char * Cmd_Argv(int arg)
Definition: cmd.c:899
GTV_NUM_STATES
@ GTV_NUM_STATES
Definition: client.c:43
Sys_Milliseconds
unsigned Sys_Milliseconds(void)
Definition: system.c:644
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
mvd_client_t::mvd
struct mvd_s * mvd
Definition: client.h:77
check_reconnect
static qboolean check_reconnect(gtv_t *gtv)
Definition: client.c:1498
FS_FOpenFile
ssize_t FS_FOpenFile(const char *name, qhandle_t *f, unsigned mode)
Definition: files.c:1692
gtv_s::run
void(* run)(struct gtv_s *)
Definition: client.c:56
gtv_s::last_rcvd
unsigned last_rcvd
Definition: client.c:70
mvd_s::state
mvd_state_t state
Definition: client.h:130
MVD_ParseMessage
qboolean MVD_ParseMessage(mvd_t *mvd)
Definition: parse.c:1098
com_version
cvar_t * com_version
Definition: common.c:84
MSG_PackEntity
void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, qboolean short_angles)
Definition: msg.c:468
mvd_username
static cvar_t * mvd_username
Definition: client.c:119
dedicated
cvar_t * dedicated
Definition: g_main.c:46
mvd_player_t::inuse
qboolean inuse
Definition: client.h:64
mvd_s
Definition: client.h:127
gtv_s::username
char * username
Definition: client.c:59
MSG_PackPlayer
void MSG_PackPlayer(player_packed_t *out, const player_state_t *in)
Definition: msg.c:763
mvd_active
qboolean mvd_active
Definition: client.c:105
sv
server_t sv
Definition: init.c:22
mvd
static mvd_server_t mvd
Definition: mvd.c:81
gtv_s::flags
unsigned flags
Definition: client.c:64
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
gtv_read_frame
static qboolean gtv_read_frame(mvd_t *mvd)
Definition: client.c:932
MVD_Disconnect_f
static void MVD_Disconnect_f(void)
Definition: client.c:2080
gtv_s::stream
netstream_t stream
Definition: client.c:60
net_port
cvar_t * net_port
Definition: net.c:88
MVD_Skip_f
static void MVD_Skip_f(void)
Definition: client.c:2134
gtv_s::password
char * password
Definition: client.c:59
FS_File_g
void FS_File_g(const char *path, const char *ext, unsigned flags, genctx_t *ctx)
Definition: files.c:2954
Z_Free
void Z_Free(void *ptr)
Definition: zone.c:147
FIFO_Read
size_t FIFO_Read(fifo_t *fifo, void *buffer, size_t len)
Definition: fifo.c:23
mvd_buffer_size
static cvar_t * mvd_buffer_size
Definition: client.c:118
MVD_Play_c
static void MVD_Play_c(genctx_t *ctx, int argnum)
Definition: client.c:2411
mvd_chanid
int mvd_chanid
Definition: client.c:103
CM_WritePortalBits
int CM_WritePortalBits(cm_t *cm, byte *buffer)
Definition: cmodel.c:955
mvd_timeout
static cvar_t * mvd_timeout
Definition: client.c:114
Cmd_Register
void Cmd_Register(const cmdreg_t *reg)
Definition: cmd.c:1572
MSG_ReadLong
int MSG_ReadLong(void)
Definition: msg.c:1517
head
unsigned head
Definition: screen.c:529
MSG_WriteDeltaEntity
void MSG_WriteDeltaEntity(const entity_packed_t *from, const entity_packed_t *to, msgEsFlags_t flags)
Definition: msg.c:505
mvd_s::mapname
char mapname[MAX_QPATH]
Definition: client.h:153
Com_Address_g
void Com_Address_g(genctx_t *ctx)
Definition: common.c:729
MVD_Play_f
static void MVD_Play_f(void)
Definition: client.c:2416
emit_gamestate
static void emit_gamestate(mvd_t *mvd)
Definition: client.c:1856
NET_StringToAdr
qboolean NET_StringToAdr(const char *s, netadr_t *a, int default_port)
Definition: net.c:332
mvd_snap_t::data
byte data[1]
Definition: client.h:120
Q_strlcpy
size_t Q_strlcpy(char *dst, const char *src, size_t size)
Definition: shared.c:715
parse_hello
static void parse_hello(gtv_t *gtv)
Definition: client.c:1084
list_generic
static void list_generic(void)
Definition: client.c:1691
MVD_Connect_c
static void MVD_Connect_c(genctx_t *ctx, int argnum)
Definition: client.c:1987
run_connect
static neterr_t run_connect(gtv_t *gtv)
Definition: client.c:1387
MVD_ListChannels_f
static void MVD_ListChannels_f(void)
Definition: client.c:1732
mvd_client_t::cl
client_t * cl
Definition: client.h:78
MSG_WriteShort
void MSG_WriteShort(int c)
Definition: msg.c:125
Cvar_SetInteger
void Cvar_SetInteger(cvar_t *var, int value, from_t from)
Definition: cvar.c:509
MVD_WAITING
@ MVD_WAITING
Definition: client.h:109
gtv_state_t
gtv_state_t
Definition: client.c:34
mvd_client_t
Definition: client.h:69
MVD_LinkEdict
void MVD_LinkEdict(mvd_t *mvd, edict_t *ent)
Definition: game.c:1652
MVD_Destroy
static void MVD_Destroy(mvd_t *mvd)
Definition: client.c:167
o_record
static const cmd_option_t o_record[]
Definition: demo.c:299
MVD_Free
static void MVD_Free(mvd_t *mvd)
Definition: client.c:139
MVD_SetPlayerNames
void MVD_SetPlayerNames(mvd_t *mvd)
Definition: game.c:1607
FS_Length
ssize_t FS_Length(qhandle_t f)
Definition: files.c:487
gtv_s::destroy
void(* destroy)(struct gtv_s *)
Definition: client.c:55
MVD_Malloc
#define MVD_Malloc(size)
Definition: client.h:22
mvd_snap_t::msglen
size_t msglen
Definition: client.h:119
MVD_READING
@ MVD_READING
Definition: client.h:110
FS_Write
ssize_t FS_Write(const void *buf, size_t len, qhandle_t f)
Definition: files.c:1643
MVD_Spawn_f
static void MVD_Spawn_f(void)
Definition: client.c:1685
gtv_s::demoplayback
qhandle_t demoplayback
Definition: client.c:76
GTV_MAXIMUM_BACKOFF
#define GTV_MAXIMUM_BACKOFF
Definition: client.c:30
MVD_SPAWN_ENABLED
#define MVD_SPAWN_ENABLED
Definition: server.h:95
demo_load_message
static ssize_t demo_load_message(qhandle_t f)
Definition: client.c:469
demo_read_message
static ssize_t demo_read_message(qhandle_t f)
Definition: client.c:515
FOR_EACH_GTV
#define FOR_EACH_GTV(gtv)
Definition: client.c:26
mvd_jmpbuf
jmp_buf mvd_jmpbuf
Definition: client.c:108
COM_IsUint
qboolean COM_IsUint(const char *s)
Definition: shared.c:330
mvd_wait_percent
static cvar_t * mvd_wait_percent
Definition: client.c:117
MSG_WriteString
void MSG_WriteString(const char *string)
Definition: msg.c:160
c
statCounters_t c
Definition: main.c:30
MVD_Control_f
static void MVD_Control_f(void)
Definition: client.c:2335
MSG_WriteLong
void MSG_WriteLong(int c)
Definition: msg.c:144
MVD_SwitchChannel
void MVD_SwitchChannel(mvd_client_t *client, mvd_t *mvd)
Definition: game.c:765
GTV_PING_INTERVAL
#define GTV_PING_INTERVAL
Definition: client.c:32
mvd_player_t
Definition: client.h:62
MVD_UpdateClients
void MVD_UpdateClients(mvd_t *mvd)
Definition: game.c:2175
gtv_s::entry
list_t entry
Definition: client.c:47
MVD_ClearState
void MVD_ClearState(mvd_t *mvd, qboolean full)
Definition: parse.c:856
gtv_drop
static void gtv_drop(gtv_t *gtv)
Definition: client.c:1604
gtv_s::state
gtv_state_t state
Definition: client.c:52
mvd_snap_t::filepos
off_t filepos
Definition: client.h:118
gtv_s::demohead
string_entry_t * demohead
Definition: client.c:78
Cvar_ClampInteger
int Cvar_ClampInteger(cvar_t *var, int min, int max)
Definition: cvar.c:549
SV_InitGame
void SV_InitGame(unsigned mvd_spawn)
Definition: init.c:361
MVD_Connect_f
static void MVD_Connect_f(void)
Definition: client.c:1997
GTV_PREPARING
@ GTV_PREPARING
Definition: client.c:37
server_static_s::realtime
unsigned realtime
Definition: server.h:454
COM_SkipPath
char * COM_SkipPath(const char *pathname)
Definition: shared.c:152
gtv_s::demoskip
int demoskip
Definition: client.c:77
Com_FormatSize
size_t Com_FormatSize(char *dest, size_t destsize, off_t bytes)
Definition: utils.c:465
Cmd_PrintHelp
void Cmd_PrintHelp(const cmd_option_t *opt)
Definition: cmd.c:1160
Cmd_Option_c
void Cmd_Option_c(const cmd_option_t *opt, xgenerator_t g, genctx_t *ctx, int argnum)
Definition: cmd.c:1183
demo_find_snapshot
static mvd_snap_t * demo_find_snapshot(mvd_t *mvd, int framenum)
Definition: client.c:627
MVD_File_g
void MVD_File_g(genctx_t *ctx)
Definition: client.c:2406
Cmd_PrintHint
void Cmd_PrintHint(void)
Definition: cmd.c:1178
demo_skip_map
static ssize_t demo_skip_map(qhandle_t f)
Definition: client.c:496
SV_ClientPrintf
void SV_ClientPrintf(client_t *client, int level, const char *fmt,...)
Definition: send.c:121
gtv_run
static void gtv_run(gtv_t *gtv)
Definition: client.c:1526
server_t::state
server_state_t state
Definition: server.h:146
gtv_s::demoentry
string_entry_t * demoentry
Definition: client.c:78
cmd_optopt
char * cmd_optopt
Definition: cmd.c:849
gtv_s::data
byte * data
Definition: client.c:62
demo_update
static void demo_update(gtv_t *gtv)
Definition: client.c:645
sv_running
cvar_t * sv_running
Definition: common.c:95
MVD_BroadcastPrintf
void MVD_BroadcastPrintf(mvd_t *mvd, int level, int mask, const char *fmt,...) q_printf(4
o_mvdconnect
static const cmd_option_t o_mvdconnect[]
Definition: client.c:1979
NET_RunStream
neterr_t NET_RunStream(netstream_t *s)
Definition: net.c:1647
MVD_FreePlayer
void MVD_FreePlayer(mvd_player_t *player)
Definition: game.c:1540
FS_FCloseFile
void FS_FCloseFile(qhandle_t f)
Definition: files.c:759
gtv_set_conn
static gtv_t * gtv_set_conn(int arg)
Definition: client.c:338
MSG_ReadByte
int MSG_ReadByte(void)
Definition: msg.c:1475
gtv_s::retry_backoff
unsigned retry_backoff
Definition: client.c:73
demo_free_playlist
static void demo_free_playlist(gtv_t *gtv)
Definition: client.c:776
MVD_Pause_f
static void MVD_Pause_f(void)
Definition: client.c:2106
server_t::spawncount
int spawncount
Definition: server.h:147
write_message
static void write_message(gtv_t *gtv, gtv_clientop_t op)
Definition: client.c:827
FS_Seek
qerror_t FS_Seek(qhandle_t f, off_t offset)
Definition: files.c:564
send_stream_start
static void send_stream_start(gtv_t *gtv)
Definition: client.c:1038
MVD_CopyString
#define MVD_CopyString(s)
Definition: client.h:24
gtv_wait_stop
static qboolean gtv_wait_stop(mvd_t *mvd)
Definition: client.c:853
GTV_CONNECTED
@ GTV_CONNECTED
Definition: client.c:38
FIFO_Write
size_t FIFO_Write(fifo_t *fifo, const void *buffer, size_t len)
Definition: fifo.c:50
mvd_snap_t::entry
list_t entry
Definition: client.h:116
parse_stream_data
static void parse_stream_data(gtv_t *gtv)
Definition: client.c:1154
mvd_player_t::ps
player_state_t ps
Definition: client.h:63
CM_SetPortalStates
void CM_SetPortalStates(cm_t *cm, byte *buffer, int bytes)
Definition: cmodel.c:982
demo_emit_snapshot
static void demo_emit_snapshot(mvd_t *mvd)
Definition: client.c:563
mvd_s::min_packets
unsigned min_packets
Definition: client.h:147
mvd_wait_delay
static cvar_t * mvd_wait_delay
Definition: client.c:116
demo_destroy
static void demo_destroy(gtv_t *gtv)
Definition: client.c:788
gtv_wait_start
static void gtv_wait_start(mvd_t *mvd)
Definition: client.c:893
demo_play_next
static void demo_play_next(gtv_t *gtv, string_entry_t *entry)
Definition: client.c:709
mvd_suspend_time
static cvar_t * mvd_suspend_time
Definition: client.c:115
gtv_s::demoloop
int demoloop
Definition: client.c:77
server_t::name
char name[MAX_QPATH]
Definition: server.h:160
GTV_DEFAULT_BACKOFF
#define GTV_DEFAULT_BACKOFF
Definition: client.c:29
run_stream
static neterr_t run_stream(gtv_t *gtv)
Definition: client.c:1418
MVD_ListServers_f
static void MVD_ListServers_f(void)
Definition: client.c:1749
MVD_Destroyf
void MVD_Destroyf(mvd_t *mvd, const char *fmt,...)
Definition: client.c:191
SZ_Clear
void SZ_Clear(sizebuf_t *buf)
Definition: sizebuf.c:40
MVD_Mallocz
#define MVD_Mallocz(size)
Definition: client.h:23
mvd_server_t::clients
gtv_client_t * clients
Definition: mvd.c:78
demo_read_first
static ssize_t demo_read_first(qhandle_t f)
Definition: client.c:529
GTV_DISCONNECTED
@ GTV_DISCONNECTED
Definition: client.c:35
gtv_states
static const char *const gtv_states[GTV_NUM_STATES]
Definition: client.c:83
mvd_clients
mvd_client_t * mvd_clients
Definition: game.c:37
o_mvdplay
static const cmd_option_t o_mvdplay[]
Definition: client.c:2396
gtv_oob_kill
static void q_noreturn gtv_oob_kill(mvd_t *mvd)
Definition: client.c:840
parse_stream_start
static void parse_stream_start(gtv_t *gtv)
Definition: client.c:1132
send_stream_stop
static void send_stream_stop(gtv_t *gtv)
Definition: client.c:1061