Quake II RTX doxygen  1.0 dev
parse.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 2003-2006 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 // mvd_parse.c
21 //
22 
23 #include "client.h"
24 #include "server/mvd/protocol.h"
25 
26 static qboolean match_ended_hack;
27 
28 #ifdef _DEBUG
29 #define SHOWNET(level, ...) \
30  if (mvd_shownet->integer > level) \
31  Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__)
32 
33 #define MVD_ShowSVC(cmd) \
34  Com_Printf("%3"PRIz":%s\n", msg_read.readcount - 1, MVD_ServerCommandString(cmd))
35 
36 static const char *MVD_ServerCommandString(int cmd)
37 {
38  switch (cmd) {
39  case -1: return "END OF MESSAGE";
40  default: return "UNKNOWN COMMAND";
41 #define M(x) \
42  case mvd_##x: return "mvd_" #x;
43  M(bad)
44  M(nop)
45  M(serverdata)
46  M(configstring)
47  M(frame)
48  M(unicast)
49  M(unicast_r)
50  M(multicast_all)
51  M(multicast_pvs)
52  M(multicast_phs)
53  M(multicast_all_r)
54  M(multicast_pvs_r)
55  M(multicast_phs_r)
56  M(sound)
57  M(print)
58  }
59 }
60 #else
61 #define SHOWNET(...)
62 #endif
63 
64 void MVD_ParseEntityString(mvd_t *mvd, const char *data)
65 {
66  const char *p;
67  char key[MAX_STRING_CHARS];
68  char value[MAX_STRING_CHARS];
69  char classname[MAX_QPATH];
70  vec3_t origin;
71  vec3_t angles;
72 
73  while (data) {
74  p = COM_Parse(&data);
75  if (!p[0]) {
76  break;
77  }
78  if (p[0] != '{') {
79  Com_Error(ERR_DROP, "expected '{', found '%s'", p);
80  }
81 
82  classname[0] = 0;
83  VectorClear(origin);
84  VectorClear(angles);
85  while (1) {
86  p = COM_Parse(&data);
87  if (p[0] == '}') {
88  break;
89  }
90  if (p[0] == '{') {
91  Com_Error(ERR_DROP, "expected key, found '{'");
92  }
93 
94  Q_strlcpy(key, p, sizeof(key));
95 
96  p = COM_Parse(&data);
97  if (!data) {
98  Com_Error(ERR_DROP, "expected key/value pair, found EOF");
99  }
100  if (p[0] == '}' || p[0] == '{') {
101  Com_Error(ERR_DROP, "expected value, found '%s'", p);
102  }
103 
104  if (!strcmp(key, "classname")) {
105  Q_strlcpy(classname, p, sizeof(classname));
106  continue;
107  }
108 
109  Q_strlcpy(value, p, sizeof(value));
110 
111  p = value;
112  if (!strcmp(key, "origin")) {
113  origin[0] = atof(COM_Parse(&p));
114  origin[1] = atof(COM_Parse(&p));
115  origin[2] = atof(COM_Parse(&p));
116  } else if (!strncmp(key, "angle", 5)) {
117  if (key[5] == 0) {
118  angles[0] = 0;
119  angles[1] = atof(COM_Parse(&p));
120  angles[2] = 0;
121  } else if (key[5] == 's' && key[6] == 0) {
122  angles[0] = atof(COM_Parse(&p));
123  angles[1] = atof(COM_Parse(&p));
124  angles[2] = atof(COM_Parse(&p));
125  }
126  }
127  }
128 
129  if (!classname[0]) {
130  Com_Error(ERR_DROP, "entity with no classname");
131  }
132 
133  if (strncmp(classname, "info_player_", 12)) {
134  continue;
135  }
136 
137  if (!strcmp(classname + 12, "intermission")) {
138  VectorCopy(origin, mvd->spawnOrigin);
139  VectorCopy(angles, mvd->spawnAngles);
140  break;
141  }
142 
143  if (!strcmp(classname + 12, "start") ||
144  !strcmp(classname + 12, "deathmatch")) {
145  VectorCopy(origin, mvd->spawnOrigin);
146  VectorCopy(angles, mvd->spawnAngles);
147  }
148 
149  }
150 }
151 
152 static void MVD_ParseMulticast(mvd_t *mvd, mvd_ops_t op, int extrabits)
153 {
154  mvd_client_t *client;
155  client_t *cl;
156  byte mask[VIS_MAX_BYTES];
157  mleaf_t *leaf1, *leaf2;
158  vec3_t org;
159  qboolean reliable = qfalse;
160  player_state_t *ps;
161  byte *data;
162  int length, leafnum;
163 
164  length = MSG_ReadByte();
165  length |= extrabits << 8;
166 
167  switch (op) {
168  case mvd_multicast_all_r:
169  reliable = qtrue;
170  // intentional fallthrough
171  case mvd_multicast_all:
172  leaf1 = NULL;
173  break;
174  case mvd_multicast_phs_r:
175  reliable = qtrue;
176  // intentional fallthrough
177  case mvd_multicast_phs:
178  leafnum = MSG_ReadWord();
179  if (mvd->demoseeking) {
180  leaf1 = NULL;
181  break;
182  }
183  leaf1 = CM_LeafNum(&mvd->cm, leafnum);
184  BSP_ClusterVis(mvd->cm.cache, mask, leaf1->cluster, DVIS_PHS);
185  break;
186  case mvd_multicast_pvs_r:
187  reliable = qtrue;
188  // intentional fallthrough
189  case mvd_multicast_pvs:
190  leafnum = MSG_ReadWord();
191  if (mvd->demoseeking) {
192  leaf1 = NULL;
193  break;
194  }
195  leaf1 = CM_LeafNum(&mvd->cm, leafnum);
196  BSP_ClusterVis(mvd->cm.cache, mask, leaf1->cluster, DVIS_PVS);
197  break;
198  default:
199  MVD_Destroyf(mvd, "bad op");
200  }
201 
202  // skip data payload
203  data = msg_read.data + msg_read.readcount;
204  msg_read.readcount += length;
205  if (msg_read.readcount > msg_read.cursize) {
206  MVD_Destroyf(mvd, "read past end of message");
207  }
208 
209  if (mvd->demoseeking)
210  return;
211 
212  // send the data to all relevent clients
213  FOR_EACH_MVDCL(client, mvd) {
214  cl = client->cl;
215  if (cl->state < cs_primed) {
216  continue;
217  }
218 
219  // do not send unreliables to connecting clients
220  if (!reliable && (cl->state != cs_spawned || cl->download || cl->nodata)) {
221  continue;
222  }
223 
224  if (leaf1) {
225  // find the client's PVS
226  ps = &client->ps;
227 #if 0
228  VectorMA(ps->viewoffset, 0.125f, ps->pmove.origin, org);
229 #else
230  // FIXME: for some strange reason, game code assumes the server
231  // uses entity origin for PVS/PHS culling, not the view origin
232  VectorScale(ps->pmove.origin, 0.125f, org);
233 #endif
234  leaf2 = CM_PointLeaf(&mvd->cm, org);
235  if (!CM_AreasConnected(&mvd->cm, leaf1->area, leaf2->area))
236  continue;
237  if (leaf2->cluster == -1)
238  continue;
239  if (!Q_IsBitSet(mask, leaf2->cluster))
240  continue;
241  }
242 
243  cl->AddMessage(cl, data, length, reliable);
244  }
245 }
246 
247 static void MVD_UnicastSend(mvd_t *mvd, qboolean reliable, byte *data, size_t length, mvd_player_t *player)
248 {
249  mvd_player_t *target;
250  mvd_client_t *client;
251  client_t *cl;
252 
253  // send to all relevant clients
254  FOR_EACH_MVDCL(client, mvd) {
255  cl = client->cl;
256  if (cl->state < cs_spawned) {
257  continue;
258  }
259  target = client->target ? client->target : mvd->dummy;
260  if (target == player) {
261  cl->AddMessage(cl, data, length, reliable);
262  }
263  }
264 }
265 
266 static void MVD_UnicastLayout(mvd_t *mvd, mvd_player_t *player)
267 {
268  mvd_client_t *client;
269 
270  if (mvd->dummy && player != mvd->dummy) {
271  MSG_ReadString(NULL, 0);
272  return; // we don't care about others
273  }
274 
275  MSG_ReadString(mvd->layout, sizeof(mvd->layout));
276 
277  // HACK: if we got "match ended" string this frame, save oldscores
278  if (match_ended_hack) {
279  strcpy(mvd->oldscores, mvd->layout);
280  }
281 
282  if (mvd->demoseeking)
283  return;
284 
285  // force an update to all relevant clients
286  FOR_EACH_MVDCL(client, mvd) {
287  if (client->cl->state < cs_spawned) {
288  continue;
289  }
290  if (client->layout_type == LAYOUT_SCORES) {
291  client->layout_time = 0;
292  }
293  }
294 }
295 
296 static void MVD_UnicastString(mvd_t *mvd, qboolean reliable, mvd_player_t *player)
297 {
298  int index;
299  char string[MAX_QPATH];
300  mvd_cs_t *cs;
301  byte *data;
302  size_t readcount, length;
303 
304  data = msg_read.data + msg_read.readcount - 1;
305  readcount = msg_read.readcount - 1;
306 
307  index = MSG_ReadShort();
308  length = MSG_ReadString(string, sizeof(string));
309 
310  if (index < 0 || index >= MAX_CONFIGSTRINGS) {
311  MVD_Destroyf(mvd, "%s: bad index: %d", __func__, index);
312  }
313  if (index < CS_GENERAL) {
314  Com_DPrintf("%s: common configstring: %d\n", __func__, index);
315  return;
316  }
317  if (length >= sizeof(string)) {
318  Com_DPrintf("%s: oversize configstring: %d\n", __func__, index);
319  return;
320  }
321 
322  for (cs = player->configstrings; cs; cs = cs->next) {
323  if (cs->index == index) {
324  break;
325  }
326  }
327  if (!cs) {
328  cs = MVD_Malloc(sizeof(*cs) + MAX_QPATH - 1);
329  cs->index = index;
330  cs->next = player->configstrings;
331  player->configstrings = cs;
332  }
333 
334  memcpy(cs->string, string, length + 1);
335 
336  if (mvd->demoseeking)
337  return;
338 
339  length = msg_read.readcount - readcount;
340  MVD_UnicastSend(mvd, reliable, data, length, player);
341 }
342 
343 static void MVD_UnicastPrint(mvd_t *mvd, qboolean reliable, mvd_player_t *player)
344 {
345  int level;
346  byte *data;
347  size_t readcount, length;
348  mvd_client_t *client;
349  client_t *cl;
350  mvd_player_t *target;
351 
352  data = msg_read.data + msg_read.readcount - 1;
353  readcount = msg_read.readcount - 1;
354 
355  level = MSG_ReadByte();
356  MSG_ReadString(NULL, 0);
357 
358  if (mvd->demoseeking)
359  return;
360 
361  length = msg_read.readcount - readcount;
362 
363  // send to all relevant clients
364  FOR_EACH_MVDCL(client, mvd) {
365  cl = client->cl;
366  if (cl->state < cs_spawned) {
367  continue;
368  }
369  if (level < cl->messagelevel) {
370  continue;
371  }
372  if (level == PRINT_CHAT && (client->uf & UF_MUTE_PLAYERS)) {
373  continue;
374  }
375  // decide if message should be routed or not
376  target = (mvd->flags & MVF_NOMSGS) ? mvd->dummy :
377  client->target ? client->target : mvd->dummy;
378  if (target == player) {
379  cl->AddMessage(cl, data, length, reliable);
380  }
381  }
382 }
383 
384 static void MVD_UnicastStuff(mvd_t *mvd, qboolean reliable, mvd_player_t *player)
385 {
386  char string[8];
387  byte *data;
388  size_t readcount, length;
389 
390  if (mvd->demoseeking) {
391  MSG_ReadString(NULL, 0);
392  return;
393  }
394 
395  data = msg_read.data + msg_read.readcount - 1;
396  readcount = msg_read.readcount - 1;
397 
398  MSG_ReadString(string, sizeof(string));
399  if (strncmp(string, "play ", 5)) {
400  return;
401  }
402 
403  length = msg_read.readcount - readcount;
404  MVD_UnicastSend(mvd, reliable, data, length, player);
405 }
406 
407 /*
408 MVD_ParseUnicast
409 
410 Attempt to parse the datagram and find custom configstrings,
411 layouts, etc. Give up as soon as unknown command byte is encountered.
412 */
413 static void MVD_ParseUnicast(mvd_t *mvd, mvd_ops_t op, int extrabits)
414 {
415  int clientNum;
416  size_t length, last;
417  mvd_player_t *player;
418  byte *data;
419  qboolean reliable;
420  int cmd;
421 
422  length = MSG_ReadByte();
423  length |= extrabits << 8;
424  clientNum = MSG_ReadByte();
425 
426  if (clientNum < 0 || clientNum >= mvd->maxclients) {
427  MVD_Destroyf(mvd, "%s: bad number: %d", __func__, clientNum);
428  }
429 
430  last = msg_read.readcount + length;
431  if (last > msg_read.cursize) {
432  MVD_Destroyf(mvd, "%s: read past end of message", __func__);
433  }
434 
435  player = &mvd->players[clientNum];
436 
437  reliable = op == mvd_unicast_r ? qtrue : qfalse;
438 
439  while (msg_read.readcount < last) {
440  cmd = MSG_ReadByte();
441 #ifdef _DEBUG
442  if (mvd_shownet->integer > 1) {
443  MSG_ShowSVC(cmd);
444  }
445 #endif
446  switch (cmd) {
447  case svc_layout:
448  MVD_UnicastLayout(mvd, player);
449  break;
450  case svc_configstring:
451  MVD_UnicastString(mvd, reliable, player);
452  break;
453  case svc_print:
454  MVD_UnicastPrint(mvd, reliable, player);
455  break;
456  case svc_stufftext:
457  MVD_UnicastStuff(mvd, reliable, player);
458  break;
459  default:
460  SHOWNET(1, "%"PRIz":SKIPPING UNICAST\n", msg_read.readcount - 1);
461  // send remaining data and return
462  data = msg_read.data + msg_read.readcount - 1;
463  length = last - msg_read.readcount + 1;
464  if (!mvd->demoseeking)
465  MVD_UnicastSend(mvd, reliable, data, length, player);
466  msg_read.readcount = last;
467  return;
468  }
469  }
470 
471  SHOWNET(1, "%"PRIz":END OF UNICAST\n", msg_read.readcount - 1);
472 
473  if (msg_read.readcount > last) {
474  MVD_Destroyf(mvd, "%s: read past end of unicast", __func__);
475  }
476 }
477 
478 /*
479 MVD_ParseSound
480 
481 Entity positioned sounds need special handling since origins need to be
482 explicitly specified for entities out of client PVS, and not all clients
483 are able to postition sounds on BSP models properly.
484 
485 FIXME: this duplicates code in sv_game.c
486 */
487 static void MVD_ParseSound(mvd_t *mvd, int extrabits)
488 {
489  int flags, index;
490  int volume, attenuation, offset, sendchan;
491  int entnum;
492  vec3_t origin;
493  mvd_client_t *client;
494  client_t *cl;
495  byte mask[VIS_MAX_BYTES];
496  mleaf_t *leaf;
497  int area;
498  player_state_t *ps;
500  edict_t *entity;
501  int i;
502 
503  flags = MSG_ReadByte();
504  index = MSG_ReadByte();
505 
506  volume = attenuation = offset = 0;
507  if (flags & SND_VOLUME)
508  volume = MSG_ReadByte();
509  if (flags & SND_ATTENUATION)
510  attenuation = MSG_ReadByte();
511  if (flags & SND_OFFSET)
512  offset = MSG_ReadByte();
513 
514  // entity relative
515  sendchan = MSG_ReadShort();
516  entnum = sendchan >> 3;
517  if (entnum < 0 || entnum >= MAX_EDICTS) {
518  MVD_Destroyf(mvd, "%s: bad entnum: %d", __func__, entnum);
519  }
520 
521  entity = &mvd->edicts[entnum];
522  if (!entity->inuse) {
523  Com_DPrintf("%s: entnum not in use: %d\n", __func__, entnum);
524  return;
525  }
526 
527  if (mvd->demoseeking)
528  return;
529 
530  FOR_EACH_MVDCL(client, mvd) {
531  cl = client->cl;
532 
533  // do not send unreliables to connecting clients
534  if (cl->state != cs_spawned || cl->download || cl->nodata) {
535  continue;
536  }
537 
538  // PHS cull this sound
539  if (!(extrabits & 1)) {
540  // get client viewpos
541  ps = &client->ps;
542  VectorMA(ps->viewoffset, 0.125f, ps->pmove.origin, origin);
543  leaf = CM_PointLeaf(&mvd->cm, origin);
544  area = CM_LeafArea(leaf);
545  if (!CM_AreasConnected(&mvd->cm, area, entity->areanum)) {
546  // doors can legally straddle two areas, so
547  // we may need to check another one
548  if (!entity->areanum2 || !CM_AreasConnected(&mvd->cm, area, entity->areanum2)) {
549  continue; // blocked by a door
550  }
551  }
552  BSP_ClusterVis(mvd->cm.cache, mask, leaf->cluster, DVIS_PHS);
553  if (!SV_EdictIsVisible(&mvd->cm, entity, mask)) {
554  continue; // not in PHS
555  }
556  }
557 
558  // use the entity origin unless it is a bmodel
559  if (entity->solid == SOLID_BSP) {
560  VectorAvg(entity->mins, entity->maxs, origin);
561  VectorAdd(entity->s.origin, origin, origin);
562  } else {
563  VectorCopy(entity->s.origin, origin);
564  }
565 
566  // reliable sounds will always have position explicitly set,
567  // as no one gurantees reliables to be delivered in time
568  if (extrabits & 2) {
569  MSG_WriteByte(svc_sound);
570  MSG_WriteByte(flags | SND_POS);
571  MSG_WriteByte(index);
572 
573  if (flags & SND_VOLUME)
574  MSG_WriteByte(volume);
575  if (flags & SND_ATTENUATION)
576  MSG_WriteByte(attenuation);
577  if (flags & SND_OFFSET)
578  MSG_WriteByte(offset);
579 
580  MSG_WriteShort(sendchan);
582 
584  continue;
585  }
586 
587  if (LIST_EMPTY(&cl->msg_free_list)) {
588  Com_WPrintf("%s: %s: out of message slots\n",
589  __func__, cl->name);
590  continue;
591  }
592 
593  // default client doesn't know that bmodels have weird origins
594  if (entity->solid == SOLID_BSP && cl->protocol == PROTOCOL_VERSION_DEFAULT) {
595  flags |= SND_POS;
596  }
597 
598  msg = LIST_FIRST(message_packet_t, &cl->msg_free_list, entry);
599 
600  msg->cursize = 0;
601  msg->flags = flags;
602  msg->index = index;
603  msg->volume = volume;
604  msg->attenuation = attenuation;
605  msg->timeofs = offset;
606  msg->sendchan = sendchan;
607  for (i = 0; i < 3; i++) {
608  msg->pos[i] = origin[i] * 8;
609  }
610 
611  List_Remove(&msg->entry);
612  List_Append(&cl->msg_unreliable_list, &msg->entry);
613  cl->msg_unreliable_bytes += MAX_SOUND_PACKET;
614 
615  flags &= ~SND_POS;
616  }
617 }
618 
620 {
621  int index;
622  size_t len, maxlen;
623  char *s;
624 
625  index = MSG_ReadShort();
626  if (index < 0 || index >= MAX_CONFIGSTRINGS) {
627  MVD_Destroyf(mvd, "%s: bad index: %d", __func__, index);
628  }
629 
630  s = mvd->configstrings[index];
631  maxlen = CS_SIZE(index);
632  len = MSG_ReadString(s, maxlen);
633  if (len >= maxlen) {
634  MVD_Destroyf(mvd, "%s: index %d overflowed", __func__, index);
635  }
636 
637  if (mvd->demoseeking) {
638  Q_SetBit(mvd->dcs, index);
639  return;
640  }
641 
642  MVD_UpdateConfigstring(mvd, index);
643 }
644 
645 static void MVD_ParsePrint(mvd_t *mvd)
646 {
647  int level;
648  char string[MAX_STRING_CHARS];
649 
650  level = MSG_ReadByte();
651  MSG_ReadString(string, sizeof(string));
652 
653  if (level == PRINT_HIGH && strstr(string, "Match ended.")) {
654  match_ended_hack = qtrue;
655  }
656 
657  if (mvd->demoseeking)
658  return;
659 
660  MVD_BroadcastPrintf(mvd, level, level == PRINT_CHAT ?
661  UF_MUTE_PLAYERS : 0, "%s", string);
662 }
663 
664 /*
665 Fix origin and angles on each player entity by
666 extracting data from player state.
667 */
669 {
670  mvd_player_t *player;
671  edict_t *edict;
672  int i;
673 
674  mvd->numplayers = 0;
675  for (i = 1, player = mvd->players; i <= mvd->maxclients; i++, player++) {
676  if (!player->inuse || player == mvd->dummy) {
677  continue;
678  }
679 
680  mvd->numplayers++;
681  if (player->ps.pmove.pm_type != PM_NORMAL) {
682  continue; // can be out of sync, in this case
683  // server should provide valid data
684  }
685 
686  edict = &mvd->edicts[i];
687  if (!edict->inuse) {
688  continue; // not present in this frame
689  }
690 
691  Com_PlayerToEntityState(&player->ps, &edict->s);
692 
693  MVD_LinkEdict(mvd, edict);
694  }
695 }
696 
697 #define RELINK_MASK (U_MODEL|U_ORIGIN1|U_ORIGIN2|U_ORIGIN3|U_SOLID)
698 
699 /*
700 ==================
701 MVD_ParsePacketEntities
702 ==================
703 */
705 {
706  int number;
707  int bits;
708  edict_t *ent;
709 
710  while (1) {
711  if (msg_read.readcount > msg_read.cursize) {
712  MVD_Destroyf(mvd, "%s: read past end of message", __func__);
713  }
714 
715  number = MSG_ParseEntityBits(&bits);
716  if (number < 0 || number >= MAX_EDICTS) {
717  MVD_Destroyf(mvd, "%s: bad number: %d", __func__, number);
718  }
719 
720  if (!number) {
721  break;
722  }
723 
724  ent = &mvd->edicts[number];
725 
726 #ifdef _DEBUG
727  if (mvd_shownet->integer > 2) {
728  Com_Printf(" %s: %d ", ent->inuse ?
729  "delta" : "baseline", number);
730  MSG_ShowDeltaEntityBits(bits);
731  Com_Printf("\n");
732  }
733 #endif
734 
735  MSG_ParseDeltaEntity(&ent->s, &ent->s, number, bits, 0);
736 
737  // lazily relink even if removed
738  if ((bits & RELINK_MASK) && !mvd->demoseeking) {
739  MVD_LinkEdict(mvd, ent);
740  }
741 
742  // mark this entity as seen even if removed
743  ent->svflags |= SVF_MONSTER;
744 
745  // shuffle current origin to old if removed
746  if (bits & U_REMOVE) {
747  SHOWNET(2, " remove: %d\n", number);
748  if (!(ent->s.renderfx & RF_BEAM)) {
749  VectorCopy(ent->s.origin, ent->s.old_origin);
750  }
751  ent->inuse = qfalse;
752  continue;
753  }
754 
755  ent->inuse = qtrue;
756  if (number >= mvd->pool.num_edicts) {
757  mvd->pool.num_edicts = number + 1;
758  }
759  }
760 }
761 
762 /*
763 ==================
764 MVD_ParsePacketPlayers
765 ==================
766 */
768 {
769  int number;
770  int bits;
771  mvd_player_t *player;
772 
773  while (1) {
774  if (msg_read.readcount > msg_read.cursize) {
775  MVD_Destroyf(mvd, "%s: read past end of message", __func__);
776  }
777 
778  number = MSG_ReadByte();
779  if (number == CLIENTNUM_NONE) {
780  break;
781  }
782 
783  if (number < 0 || number >= mvd->maxclients) {
784  MVD_Destroyf(mvd, "%s: bad number: %d", __func__, number);
785  }
786 
787  player = &mvd->players[number];
788 
789  bits = MSG_ReadShort();
790 
791 #ifdef _DEBUG
792  if (mvd_shownet->integer > 2) {
793  Com_Printf(" %s: %d ", player->inuse ?
794  "delta" : "baseline", number);
795  MSG_ShowDeltaPlayerstateBits_Packet(bits);
796  Com_Printf("\n");
797  }
798 #endif
799 
800  MSG_ParseDeltaPlayerstate_Packet(&player->ps, &player->ps, bits);
801 
802  if (bits & PPS_REMOVE) {
803  SHOWNET(2, " remove: %d\n", number);
804  player->inuse = qfalse;
805  continue;
806  }
807 
808  player->inuse = qtrue;
809  }
810 }
811 
812 /*
813 ================
814 MVD_ParseFrame
815 ================
816 */
817 static void MVD_ParseFrame(mvd_t *mvd)
818 {
819  byte *data;
820  int length;
821 
822  // read portalbits
823  length = MSG_ReadByte();
824  if (length) {
825  if (length < 0 || msg_read.readcount + length > msg_read.cursize) {
826  MVD_Destroyf(mvd, "%s: read past end of message", __func__);
827  }
828  if (length > MAX_MAP_PORTAL_BYTES) {
829  MVD_Destroyf(mvd, "%s: bad portalbits length: %d", __func__, length);
830  }
831  data = msg_read.data + msg_read.readcount;
832  msg_read.readcount += length;
833  } else {
834  data = NULL;
835  }
836 
837  if (!mvd->demoseeking)
838  CM_SetPortalStates(&mvd->cm, data, length);
839 
840  SHOWNET(1, "%3"PRIz":playerinfo\n", msg_read.readcount - 1);
842  SHOWNET(1, "%3"PRIz":packetentities\n", msg_read.readcount - 1);
844  SHOWNET(1, "%3"PRIz":frame:%u\n", msg_read.readcount - 1, mvd->framenum);
846 
847  // update clients now so that effects datagram that
848  // follows can reference current view positions
849  if (mvd->state && mvd->framenum && !mvd->demoseeking) {
851  }
852 
853  mvd->framenum++;
854 }
855 
856 void MVD_ClearState(mvd_t *mvd, qboolean full)
857 {
858  mvd_player_t *player;
859  mvd_snap_t *snap, *next;
860  int i;
861 
862  // clear all entities, don't trust num_edicts as it is possible
863  // to miscount removed but seen entities
864  memset(mvd->edicts, 0, sizeof(mvd->edicts));
865  mvd->pool.num_edicts = 0;
866 
867  // clear all players
868  for (i = 0; i < mvd->maxclients; i++) {
869  player = &mvd->players[i];
870  MVD_FreePlayer(player);
871  memset(player, 0, sizeof(*player));
872  }
873 
874  mvd->numplayers = 0;
875 
876  if (!full)
877  return;
878 
879  // free all snapshots
880  LIST_FOR_EACH_SAFE(mvd_snap_t, snap, next, &mvd->snapshots, entry) {
881  Z_Free(snap);
882  }
883 
884  List_Init(&mvd->snapshots);
885 
886  // free current map
887  CM_FreeMap(&mvd->cm);
888 
889  if (mvd->intermission) {
890  // save oldscores
891  //strcpy(mvd->oldscores, mvd->layout);
892  }
893 
894  memset(mvd->configstrings, 0, sizeof(mvd->configstrings));
895  mvd->layout[0] = 0;
896 
897  mvd->framenum = 0;
898  // intermission flag will be cleared in MVD_ChangeLevel
899 }
900 
901 static void MVD_ChangeLevel(mvd_t *mvd)
902 {
903  mvd_client_t *client;
904 
905  if (sv.state != ss_broadcast) {
906  // the game is just starting
908  MVD_Spawn();
909  return;
910  }
911 
912  // cause all UDP clients to reconnect
914  MSG_WriteString(va("changing map=%s; reconnect\n", mvd->mapname));
915 
916  FOR_EACH_MVDCL(client, mvd) {
917  if (client->target != mvd->dummy) {
918  // make them switch to previous target instead of MVD dummy
919  client->oldtarget = client->target;
920  }
921  client->target = NULL;
922  SV_ClientReset(client->cl);
923  client->cl->spawncount = mvd->servercount;
925  }
926 
928 
929  mvd->intermission = qfalse;
930 
931  mvd_dirty = qtrue;
932 
934 }
935 
936 static void MVD_ParseServerData(mvd_t *mvd, int extrabits)
937 {
938  int protocol;
939  size_t len, maxlen;
940  char *string;
941  int index;
942  qerror_t ret;
943  edict_t *ent;
944 
945  // clear the leftover from previous level
946  MVD_ClearState(mvd, qtrue);
947 
948  // parse major protocol version
949  protocol = MSG_ReadLong();
950  if (protocol != PROTOCOL_VERSION_MVD) {
951  MVD_Destroyf(mvd, "Unsupported protocol: %d", protocol);
952  }
953 
954  // parse minor protocol version
955  protocol = MSG_ReadShort();
956  if (!MVD_SUPPORTED(protocol)) {
957  MVD_Destroyf(mvd, "Unsupported MVD protocol version: %d.\n"
958  "Current version is %d.\n", protocol, PROTOCOL_VERSION_MVD_CURRENT);
959  }
960 
961  mvd->servercount = MSG_ReadLong();
962  len = MSG_ReadString(mvd->gamedir, sizeof(mvd->gamedir));
963  if (len >= sizeof(mvd->gamedir)) {
964  MVD_Destroyf(mvd, "Oversize gamedir string");
965  }
966  mvd->clientNum = MSG_ReadShort();
967  mvd->flags = extrabits;
968 
969 #if 0
970  // change gamedir unless playing a demo
971  Cvar_UserSet("game", mvd->gamedir);
972 #endif
973 
974  // parse configstrings
975  while (1) {
976  index = MSG_ReadShort();
977  if (index == MAX_CONFIGSTRINGS) {
978  break;
979  }
980 
981  if (index < 0 || index >= MAX_CONFIGSTRINGS) {
982  MVD_Destroyf(mvd, "Bad configstring index: %d", index);
983  }
984 
985  string = mvd->configstrings[index];
986  maxlen = CS_SIZE(index);
987  len = MSG_ReadString(string, maxlen);
988  if (len >= maxlen) {
989  MVD_Destroyf(mvd, "Configstring %d overflowed", index);
990  }
991 
992  if (msg_read.readcount > msg_read.cursize) {
993  MVD_Destroyf(mvd, "Read past end of message");
994  }
995  }
996 
997  // parse maxclients
998  index = atoi(mvd->configstrings[CS_MAXCLIENTS]);
999  if (index < 1 || index > MAX_CLIENTS) {
1000  MVD_Destroyf(mvd, "Invalid maxclients");
1001  }
1002 
1003  // check if maxclients changed
1004  if (index != mvd->maxclients) {
1005  mvd_client_t *client;
1006 
1007  // free any old players
1008  Z_Free(mvd->players);
1009 
1010  // allocate new players
1011  mvd->players = MVD_Mallocz(sizeof(mvd_player_t) * index);
1012  mvd->maxclients = index;
1013 
1014  // clear chase targets
1015  FOR_EACH_MVDCL(client, mvd) {
1016  client->target = NULL;
1017  client->oldtarget = NULL;
1018  client->chase_mask = 0;
1019  client->chase_auto = qfalse;
1020  client->chase_wait = qfalse;
1021  memset(client->chase_bitmap, 0, sizeof(client->chase_bitmap));
1022  }
1023  }
1024 
1025  if (mvd->clientNum == -1) {
1026  mvd->dummy = NULL;
1027  } else {
1028  // validate clientNum
1029  if (mvd->clientNum < 0 || mvd->clientNum >= mvd->maxclients) {
1030  MVD_Destroyf(mvd, "Invalid client num: %d", mvd->clientNum);
1031  }
1032  mvd->dummy = mvd->players + mvd->clientNum;
1033  }
1034 
1035  // parse world model
1036  string = mvd->configstrings[CS_MODELS + 1];
1037  len = strlen(string);
1038  if (len <= 9) {
1039  MVD_Destroyf(mvd, "Bad world model: %s", string);
1040  }
1041  memcpy(mvd->mapname, string + 5, len - 9); // skip "maps/"
1042  mvd->mapname[len - 9] = 0; // cut off ".bsp"
1043 
1044  // load the world model (we are only interesed in visibility info)
1045  Com_Printf("[%s] -=- Loading %s...\n", mvd->name, string);
1046  ret = CM_LoadMap(&mvd->cm, string);
1047  if (ret) {
1048  Com_EPrintf("[%s] =!= Couldn't load %s: %s\n", mvd->name, string, Q_ErrorString(ret));
1049  // continue with null visibility
1050  }
1051 #if USE_MAPCHECKSUM
1052  else if (mvd->cm.cache->checksum != atoi(mvd->configstrings[CS_MAPCHECKSUM])) {
1053  Com_EPrintf("[%s] =!= Local map version differs from server!\n", mvd->name);
1054  CM_FreeMap(&mvd->cm);
1055  }
1056 #endif
1057 
1058  // set player names
1060 
1061  // init world entity
1062  ent = &mvd->edicts[0];
1063  ent->solid = SOLID_BSP;
1064  ent->inuse = qtrue;
1065 
1066  if (mvd->cm.cache) {
1067  // get the spawn point for spectators
1068  MVD_ParseEntityString(mvd, mvd->cm.cache->entitystring);
1069  }
1070 
1071  // parse baseline frame
1073 
1074  // save base configstrings
1075  memcpy(mvd->baseconfigstrings, mvd->configstrings, sizeof(mvd->baseconfigstrings));
1076 
1077  // force inital snapshot
1078  mvd->last_snapshot = INT_MIN;
1079 
1080  // if the channel has been just created, init some things
1081  if (!mvd->state) {
1082  mvd_t *cur;
1083 
1084  // sort this one into the list of active channels
1085  FOR_EACH_MVD(cur) {
1086  if (cur->id > mvd->id) {
1087  break;
1088  }
1089  }
1090  List_Append(&cur->entry, &mvd->entry);
1091  mvd->state = MVD_WAITING;
1092  }
1093 
1094  // case all UDP clients to reconnect
1096 }
1097 
1099 {
1100  int cmd, extrabits;
1101  qboolean ret = qfalse;
1102 
1103 #ifdef _DEBUG
1104  if (mvd_shownet->integer == 1) {
1105  Com_Printf("%"PRIz" ", msg_read.cursize);
1106  } else if (mvd_shownet->integer > 1) {
1107  Com_Printf("------------------\n");
1108  }
1109 #endif
1110 
1111 //
1112 // parse the message
1113 //
1114  match_ended_hack = qfalse;
1115  while (1) {
1116  if (msg_read.readcount > msg_read.cursize) {
1117  MVD_Destroyf(mvd, "Read past end of message");
1118  }
1119  if (msg_read.readcount == msg_read.cursize) {
1120  SHOWNET(1, "%3"PRIz":END OF MESSAGE\n", msg_read.readcount - 1);
1121  break;
1122  }
1123 
1124  cmd = MSG_ReadByte();
1125  extrabits = cmd >> SVCMD_BITS;
1126  cmd &= SVCMD_MASK;
1127 
1128 #ifdef _DEBUG
1129  if (mvd_shownet->integer > 1) {
1130  MVD_ShowSVC(cmd);
1131  }
1132 #endif
1133 
1134  switch (cmd) {
1135  case mvd_serverdata:
1136  MVD_ParseServerData(mvd, extrabits);
1137  ret |= qtrue;
1138  break;
1139  case mvd_multicast_all:
1140  case mvd_multicast_pvs:
1141  case mvd_multicast_phs:
1142  case mvd_multicast_all_r:
1143  case mvd_multicast_pvs_r:
1144  case mvd_multicast_phs_r:
1145  MVD_ParseMulticast(mvd, cmd, extrabits);
1146  break;
1147  case mvd_unicast:
1148  case mvd_unicast_r:
1149  MVD_ParseUnicast(mvd, cmd, extrabits);
1150  break;
1151  case mvd_configstring:
1153  break;
1154  case mvd_frame:
1156  break;
1157  case mvd_sound:
1158  MVD_ParseSound(mvd, extrabits);
1159  break;
1160  case mvd_print:
1162  break;
1163  case mvd_nop:
1164  break;
1165  default:
1166  MVD_Destroyf(mvd, "Illegible command at %"PRIz": %d",
1167  msg_read.readcount - 1, cmd);
1168  }
1169  }
1170 
1171  return ret;
1172 }
1173 
mvd_player_t::configstrings
mvd_cs_t * configstrings
Definition: client.h:66
MVD_ClearState
void MVD_ClearState(mvd_t *mvd, qboolean full)
Definition: parse.c:856
mvd_client_t::layout_time
unsigned layout_time
Definition: client.h:93
CM_FreeMap
void CM_FreeMap(cm_t *cm)
Definition: cmodel.c:47
MVD_SPAWN_INTERNAL
#define MVD_SPAWN_INTERNAL
Definition: server.h:96
RELINK_MASK
#define RELINK_MASK
Definition: parse.c:697
cs_spawned
@ cs_spawned
Definition: server.h:192
mvd_server_t::players
player_packed_t * players
Definition: mvd.c:69
MVD_Spawn
void MVD_Spawn(void)
Definition: client.c:1653
mvd_client_t::chase_bitmap
byte chase_bitmap[MAX_CLIENTS/CHAR_BIT]
Definition: client.h:90
msg_read
sizebuf_t msg_read
Definition: msg.c:37
match_ended_hack
static qboolean match_ended_hack
Definition: parse.c:26
maxclients
cvar_t * maxclients
Definition: g_main.c:42
CM_LoadMap
qerror_t CM_LoadMap(cm_t *cm, const char *name)
Definition: cmodel.c:64
message_packet_t
Definition: server.h:220
MVD_UnicastPrint
static void MVD_UnicastPrint(mvd_t *mvd, qboolean reliable, mvd_player_t *player)
Definition: parse.c:343
Q_ErrorString
const char * Q_ErrorString(qerror_t error)
Definition: error.c:51
mvd_client_t::chase_mask
int chase_mask
Definition: client.h:87
BSP_ClusterVis
byte * BSP_ClusterVis(bsp_t *bsp, byte *mask, int cluster, int vis)
Definition: bsp.c:1339
client_s::state
clstate_t state
Definition: server.h:260
MSG_CLEAR
#define MSG_CLEAR
Definition: server.h:215
mvd_cs_s::string
char string[1]
Definition: client.h:59
MSG_RELIABLE
#define MSG_RELIABLE
Definition: server.h:214
MSG_ReadWord
int MSG_ReadWord(void)
Definition: msg.c:1503
client_s::spawncount
int spawncount
Definition: server.h:348
MVD_UpdateConfigstring
void MVD_UpdateConfigstring(mvd_t *mvd, int index)
Definition: game.c:1616
Com_PlayerToEntityState
void Com_PlayerToEntityState(const player_state_t *ps, entity_state_t *es)
Definition: utils.c:269
LAYOUT_SCORES
@ LAYOUT_SCORES
Definition: client.h:46
mvd_snap_t
Definition: client.h:115
mvd_client_t::layout_type
mvd_layout_t layout_type
Definition: client.h:92
mvd_dirty
qboolean mvd_dirty
Definition: client.c:102
mvd_s::id
int id
Definition: client.h:131
SHOWNET
#define SHOWNET(...)
Definition: parse.c:61
FOR_EACH_MVD
#define FOR_EACH_MVD(mvd)
Definition: client.h:26
MSG_WriteByte
void MSG_WriteByte(int c)
Definition: msg.c:107
svc_stufftext
#define svc_stufftext
Definition: g_local.h:41
mvd_client_t::uf
int uf
Definition: client.h:83
SV_ClientAddMessage
void SV_ClientAddMessage(client_t *client, int flags)
Definition: send.c:399
mvd_cs_s::index
int index
Definition: client.h:58
MVD_ParseServerData
static void MVD_ParseServerData(mvd_t *mvd, int extrabits)
Definition: parse.c:936
CM_AreasConnected
qboolean CM_AreasConnected(cm_t *cm, int area1, int area2)
Definition: cmodel.c:890
MSG_WritePos
void MSG_WritePos(const vec3_t pos)
Definition: msg.c:198
MVD_ParseMulticast
static void MVD_ParseMulticast(mvd_t *mvd, mvd_ops_t op, int extrabits)
Definition: parse.c:152
Com_Error
void Com_Error(error_type_t type, const char *fmt,...)
Definition: g_main.c:258
mvd_player_t::inuse
qboolean inuse
Definition: client.h:64
mvd_s
Definition: client.h:127
SV_SendAsyncPackets
void SV_SendAsyncPackets(void)
Definition: send.c:987
sv
server_t sv
Definition: init.c:22
mvd
static mvd_server_t mvd
Definition: mvd.c:81
msg_write
sizebuf_t msg_write
Definition: msg.c:34
mvd_client_t::target
mvd_player_t * target
Definition: client.h:85
va
char * va(const char *format,...)
Definition: shared.c:429
Z_Free
void Z_Free(void *ptr)
Definition: zone.c:147
MVD_ChangeLevel
static void MVD_ChangeLevel(mvd_t *mvd)
Definition: parse.c:901
MSG_ReadLong
int MSG_ReadLong(void)
Definition: msg.c:1517
MVD_ParseUnicast
static void MVD_ParseUnicast(mvd_t *mvd, mvd_ops_t op, int extrabits)
Definition: parse.c:413
origin
static vec3_t origin
Definition: mesh.c:27
MVD_UnicastStuff
static void MVD_UnicastStuff(mvd_t *mvd, qboolean reliable, mvd_player_t *player)
Definition: parse.c:384
MVD_UnicastLayout
static void MVD_UnicastLayout(mvd_t *mvd, mvd_player_t *player)
Definition: parse.c:266
Q_strlcpy
size_t Q_strlcpy(char *dst, const char *src, size_t size)
Definition: shared.c:715
mvd_client_t::oldtarget
mvd_player_t * oldtarget
Definition: client.h:86
mvd_client_t::cl
client_t * cl
Definition: client.h:78
MSG_WriteShort
void MSG_WriteShort(int c)
Definition: msg.c:125
MVD_ParsePacketPlayers
static void MVD_ParsePacketPlayers(mvd_t *mvd)
Definition: parse.c:767
MVD_WAITING
@ MVD_WAITING
Definition: client.h:109
mvd_client_t
Definition: client.h:69
MVD_LinkEdict
void MVD_LinkEdict(mvd_t *mvd, edict_t *ent)
Definition: game.c:1652
MVD_SetPlayerNames
void MVD_SetPlayerNames(mvd_t *mvd)
Definition: game.c:1607
Cvar_UserSet
cvar_t * Cvar_UserSet(const char *var_name, const char *value)
Definition: cvar.c:476
MVD_ParsePacketEntities
static void MVD_ParsePacketEntities(mvd_t *mvd)
Definition: parse.c:704
MVD_Malloc
#define MVD_Malloc(size)
Definition: client.h:22
FOR_EACH_MVDCL
#define FOR_EACH_MVDCL(cl, mvd)
Definition: client.h:29
mvd_client_t::ps
player_state_t ps
Definition: client.h:71
cl
client_state_t cl
Definition: main.c:99
mvd_cs_s
Definition: client.h:56
mvd_server_t::dummy
client_t * dummy
Definition: mvd.c:57
MVD_ParseMessage
qboolean MVD_ParseMessage(mvd_t *mvd)
Definition: parse.c:1098
MVD_ParseEntityString
void MVD_ParseEntityString(mvd_t *mvd, const char *data)
Definition: parse.c:64
mvd_client_t::chase_auto
qboolean chase_auto
Definition: client.h:88
MSG_WriteString
void MSG_WriteString(const char *string)
Definition: msg.c:160
MVD_ParseConfigstring
static void MVD_ParseConfigstring(mvd_t *mvd)
Definition: parse.c:619
mvd_client_t::chase_wait
qboolean chase_wait
Definition: client.h:89
mvd_player_t
Definition: client.h:62
MVD_UpdateClients
void MVD_UpdateClients(mvd_t *mvd)
Definition: game.c:2175
CM_PointLeaf
mleaf_t * CM_PointLeaf(cm_t *cm, vec3_t p)
Definition: cmodel.c:209
MAX_SOUND_PACKET
#define MAX_SOUND_PACKET
Definition: server.h:218
SV_InitGame
void SV_InitGame(unsigned mvd_spawn)
Definition: init.c:361
MSG_ReadString
size_t MSG_ReadString(char *dest, size_t size)
Definition: msg.c:1531
MVD_UnicastSend
static void MVD_UnicastSend(mvd_t *mvd, qboolean reliable, byte *data, size_t length, mvd_player_t *player)
Definition: parse.c:247
mvd_cs_s::next
struct mvd_cs_s * next
Definition: client.h:57
level
level_locals_t level
Definition: g_main.c:22
cs_primed
@ cs_primed
Definition: server.h:191
server_t::state
server_state_t state
Definition: server.h:146
msg
const char * msg
Definition: win.h:25
CM_LeafNum
mleaf_t * CM_LeafNum(cm_t *cm, int number)
Definition: cmodel.c:98
client_s
Definition: server.h:256
MVD_ParseFrame
static void MVD_ParseFrame(mvd_t *mvd)
Definition: parse.c:817
svc_layout
#define svc_layout
Definition: g_local.h:39
client.h
COM_Parse
char * COM_Parse(const char **data_p)
Definition: shared.c:455
MVD_BroadcastPrintf
void MVD_BroadcastPrintf(mvd_t *mvd, int level, int mask, const char *fmt,...) q_printf(4
MVD_UnicastString
static void MVD_UnicastString(mvd_t *mvd, qboolean reliable, mvd_player_t *player)
Definition: parse.c:296
MVD_FreePlayer
void MVD_FreePlayer(mvd_player_t *player)
Definition: game.c:1540
MSG_ReadByte
int MSG_ReadByte(void)
Definition: msg.c:1475
SV_EdictIsVisible
qboolean SV_EdictIsVisible(cm_t *cm, edict_t *ent, byte *mask)
Definition: world.c:129
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
MVD_PlayerToEntityStates
static void MVD_PlayerToEntityStates(mvd_t *mvd)
Definition: parse.c:668
MVD_Destroyf
void MVD_Destroyf(mvd_t *mvd, const char *fmt,...)
Definition: client.c:191
MVD_ParseSound
static void MVD_ParseSound(mvd_t *mvd, int extrabits)
Definition: parse.c:487
SZ_Clear
void SZ_Clear(sizebuf_t *buf)
Definition: sizebuf.c:40
MVD_Mallocz
#define MVD_Mallocz(size)
Definition: client.h:23
SV_ClientReset
void SV_ClientReset(client_t *client)
Definition: init.c:24
mvd_s::entry
list_t entry
Definition: client.h:128
MVD_ParsePrint
static void MVD_ParsePrint(mvd_t *mvd)
Definition: parse.c:645
MSG_ReadShort
int MSG_ReadShort(void)
Definition: msg.c:1489