Quake II RTX doxygen  1.0 dev
game.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
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 // sv_game.c -- interface to the game dll
19 
20 #include "server.h"
21 
22 game_export_t *ge;
23 
24 static void PF_configstring(int index, const char *val);
25 
26 /*
27 ================
28 PF_FindIndex
29 
30 ================
31 */
32 static int PF_FindIndex(const char *name, int start, int max)
33 {
34  char *string;
35  int i;
36 
37  if (!name || !name[0])
38  return 0;
39 
40  for (i = 1; i < max; i++) {
41  string = sv.configstrings[start + i];
42  if (!string[0]) {
43  break;
44  }
45  if (!strcmp(string, name)) {
46  return i;
47  }
48  }
49 
50  if (i == max)
51  Com_Error(ERR_DROP, "PF_FindIndex: overflow");
52 
53  PF_configstring(i + start, name);
54 
55  return i;
56 }
57 
58 static int PF_ModelIndex(const char *name)
59 {
60  return PF_FindIndex(name, CS_MODELS, MAX_MODELS);
61 }
62 
63 static int PF_SoundIndex(const char *name)
64 {
65  return PF_FindIndex(name, CS_SOUNDS, MAX_SOUNDS);
66 }
67 
68 static int PF_ImageIndex(const char *name)
69 {
70  return PF_FindIndex(name, CS_IMAGES, MAX_IMAGES);
71 }
72 
73 /*
74 ===============
75 PF_Unicast
76 
77 Sends the contents of the mutlicast buffer to a single client.
78 Archived in MVD stream.
79 ===============
80 */
81 static void PF_Unicast(edict_t *ent, qboolean reliable)
82 {
83  client_t *client;
84  int cmd, flags, clientNum;
85 
86  if (!ent) {
87  goto clear;
88  }
89 
90  clientNum = NUM_FOR_EDICT(ent) - 1;
91  if (clientNum < 0 || clientNum >= sv_maxclients->integer) {
92  Com_WPrintf("%s to a non-client %d\n", __func__, clientNum);
93  goto clear;
94  }
95 
96  client = svs.client_pool + clientNum;
97  if (client->state <= cs_zombie) {
98  Com_WPrintf("%s to a free/zombie client %d\n", __func__, clientNum);
99  goto clear;
100  }
101 
102  if (!msg_write.cursize) {
103  Com_DPrintf("%s with empty data\n", __func__);
104  goto clear;
105  }
106 
107  cmd = msg_write.data[0];
108 
109  flags = 0;
110  if (reliable) {
111  flags |= MSG_RELIABLE;
112  }
113 
114  if (cmd == svc_layout) {
115  flags |= MSG_COMPRESS;
116  }
117 
118  SV_ClientAddMessage(client, flags);
119 
120  // fix anti-kicking exploit for broken mods
121  if (cmd == svc_disconnect) {
122  client->drop_hack = qtrue;
123  goto clear;
124  }
125 
126  SV_MvdUnicast(ent, clientNum, reliable);
127 
128 clear:
130 }
131 
132 /*
133 =================
134 PF_bprintf
135 
136 Sends text to all active clients.
137 Archived in MVD stream.
138 =================
139 */
140 static void PF_bprintf(int level, const char *fmt, ...)
141 {
142  va_list argptr;
143  char string[MAX_STRING_CHARS];
144  client_t *client;
145  size_t len;
146  int i;
147 
148  va_start(argptr, fmt);
149  len = Q_vsnprintf(string, sizeof(string), fmt, argptr);
150  va_end(argptr);
151 
152  if (len >= sizeof(string)) {
153  Com_WPrintf("%s: overflow\n", __func__);
154  return;
155  }
156 
157  SV_MvdBroadcastPrint(level, string);
158 
159  MSG_WriteByte(svc_print);
161  MSG_WriteData(string, len + 1);
162 
163  // echo to console
164  if (COM_DEDICATED) {
165  // mask off high bits
166  for (i = 0; i < len; i++)
167  string[i] &= 127;
168  Com_Printf("%s", string);
169  }
170 
171  FOR_EACH_CLIENT(client) {
172  if (client->state != cs_spawned)
173  continue;
174  if (level >= client->messagelevel) {
176  }
177  }
178 
180 }
181 
182 
183 /*
184 ===============
185 PF_dprintf
186 
187 Debug print to server console.
188 ===============
189 */
190 static void PF_dprintf(const char *fmt, ...)
191 {
192  char msg[MAXPRINTMSG];
193  va_list argptr;
194 
195  va_start(argptr, fmt);
196  Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
197  va_end(argptr);
198 
199  Com_Printf("%s", msg);
200 }
201 
202 
203 /*
204 ===============
205 PF_cprintf
206 
207 Print to a single client if the level passes.
208 Archived in MVD stream.
209 ===============
210 */
211 static void PF_cprintf(edict_t *ent, int level, const char *fmt, ...)
212 {
213  char msg[MAX_STRING_CHARS];
214  va_list argptr;
215  int clientNum;
216  size_t len;
217  client_t *client;
218 
219  va_start(argptr, fmt);
220  len = Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
221  va_end(argptr);
222 
223  if (len >= sizeof(msg)) {
224  Com_WPrintf("%s: overflow\n", __func__);
225  return;
226  }
227 
228  if (!ent) {
229  Com_LPrintf(level == PRINT_CHAT ? PRINT_TALK : PRINT_ALL, "%s", msg);
230  return;
231  }
232 
233  clientNum = NUM_FOR_EDICT(ent) - 1;
234  if (clientNum < 0 || clientNum >= sv_maxclients->integer) {
235  Com_Error(ERR_DROP, "%s to a non-client %d", __func__, clientNum);
236  }
237 
238  client = svs.client_pool + clientNum;
239  if (client->state <= cs_zombie) {
240  Com_WPrintf("%s to a free/zombie client %d\n", __func__, clientNum);
241  return;
242  }
243 
244  MSG_WriteByte(svc_print);
246  MSG_WriteData(msg, len + 1);
247 
248  if (level >= client->messagelevel) {
250  }
251 
252  SV_MvdUnicast(ent, clientNum, qtrue);
253 
255 }
256 
257 
258 /*
259 ===============
260 PF_centerprintf
261 
262 Centerprint to a single client.
263 Archived in MVD stream.
264 ===============
265 */
266 static void PF_centerprintf(edict_t *ent, const char *fmt, ...)
267 {
268  char msg[MAX_STRING_CHARS];
269  va_list argptr;
270  int n;
271  size_t len;
272 
273  if (!ent) {
274  return;
275  }
276 
277  n = NUM_FOR_EDICT(ent);
278  if (n < 1 || n > sv_maxclients->integer) {
279  Com_WPrintf("%s to a non-client %d\n", __func__, n - 1);
280  return;
281  }
282 
283  va_start(argptr, fmt);
284  len = Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
285  va_end(argptr);
286 
287  if (len >= sizeof(msg)) {
288  Com_WPrintf("%s: overflow\n", __func__);
289  return;
290  }
291 
292  MSG_WriteByte(svc_centerprint);
293  MSG_WriteData(msg, len + 1);
294 
295  PF_Unicast(ent, qtrue);
296 }
297 
298 
299 /*
300 ===============
301 PF_error
302 
303 Abort the server with a game error
304 ===============
305 */
306 static q_noreturn void PF_error(const char *fmt, ...)
307 {
308  char msg[MAXERRORMSG];
309  va_list argptr;
310 
311  va_start(argptr, fmt);
312  Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
313  va_end(argptr);
314 
315  Com_Error(ERR_DROP, "Game Error: %s", msg);
316 }
317 
318 
319 /*
320 =================
321 PF_setmodel
322 
323 Also sets mins and maxs for inline bmodels
324 =================
325 */
326 static void PF_setmodel(edict_t *ent, const char *name)
327 {
328  int i;
329  mmodel_t *mod;
330 
331  if (!name)
332  Com_Error(ERR_DROP, "PF_setmodel: NULL");
333 
334  i = PF_ModelIndex(name);
335 
336  ent->s.modelindex = i;
337 
338 // if it is an inline model, get the size information for it
339  if (name[0] == '*') {
340  mod = CM_InlineModel(&sv.cm, name);
341  VectorCopy(mod->mins, ent->mins);
342  VectorCopy(mod->maxs, ent->maxs);
343  PF_LinkEdict(ent);
344  }
345 
346 }
347 
348 /*
349 ===============
350 PF_configstring
351 
352 If game is actively running, broadcasts configstring change.
353 Archived in MVD stream.
354 ===============
355 */
356 static void PF_configstring(int index, const char *val)
357 {
358  size_t len, maxlen;
359  client_t *client;
360  char *dst;
361 
362  if (index < 0 || index >= MAX_CONFIGSTRINGS)
363  Com_Error(ERR_DROP, "%s: bad index: %d", __func__, index);
364 
365  if (sv.state == ss_dead) {
366  Com_WPrintf("%s: not yet initialized\n", __func__);
367  return;
368  }
369 
370  if (!val)
371  val = "";
372 
373  // error out entirely if it exceedes array bounds
374  len = strlen(val);
375  maxlen = (MAX_CONFIGSTRINGS - index) * MAX_QPATH;
376  if (len >= maxlen) {
377  Com_Error(ERR_DROP,
378  "%s: index %d overflowed: %"PRIz" > %"PRIz,
379  __func__, index, len, maxlen - 1);
380  }
381 
382  // print a warning and truncate everything else
383  maxlen = CS_SIZE(index);
384  if (len >= maxlen) {
385  Com_WPrintf(
386  "%s: index %d overflowed: %"PRIz" > %"PRIz"\n",
387  __func__, index, len, maxlen - 1);
388  len = maxlen - 1;
389  }
390 
391  dst = sv.configstrings[index];
392  if (!strncmp(dst, val, len)) {
393  return;
394  }
395 
396  // change the string in sv
397  memcpy(dst, val, len);
398  dst[len] = 0;
399 
400  if (sv.state == ss_loading) {
401  return;
402  }
403 
404  SV_MvdConfigstring(index, val, len);
405 
406  // send the update to everyone
407  MSG_WriteByte(svc_configstring);
408  MSG_WriteShort(index);
409  MSG_WriteData(val, len);
410  MSG_WriteByte(0);
411 
412  FOR_EACH_CLIENT(client) {
413  if (client->state < cs_primed) {
414  continue;
415  }
417  }
418 
420 }
421 
422 static void PF_WriteFloat(float f)
423 {
424  Com_Error(ERR_DROP, "PF_WriteFloat not implemented");
425 }
426 
427 static qboolean PF_inVIS(vec3_t p1, vec3_t p2, int vis)
428 {
429  mleaf_t *leaf1, *leaf2;
430  byte mask[VIS_MAX_BYTES];
431  bsp_t *bsp = sv.cm.cache;
432 
433  if (!bsp) {
434  Com_Error(ERR_DROP, "%s: no map loaded", __func__);
435  }
436 
437  leaf1 = BSP_PointLeaf(bsp->nodes, p1);
438  BSP_ClusterVis(bsp, mask, leaf1->cluster, vis);
439 
440  leaf2 = BSP_PointLeaf(bsp->nodes, p2);
441  if (leaf2->cluster == -1)
442  return qfalse;
443  if (!Q_IsBitSet(mask, leaf2->cluster))
444  return qfalse;
445  if (!CM_AreasConnected(&sv.cm, leaf1->area, leaf2->area))
446  return qfalse; // a door blocks it
447  return qtrue;
448 }
449 
450 /*
451 =================
452 PF_inPVS
453 
454 Also checks portalareas so that doors block sight
455 =================
456 */
457 static qboolean PF_inPVS(vec3_t p1, vec3_t p2)
458 {
459  return PF_inVIS(p1, p2, DVIS_PVS);
460 }
461 
462 /*
463 =================
464 PF_inPHS
465 
466 Also checks portalareas so that doors block sound
467 =================
468 */
469 static qboolean PF_inPHS(vec3_t p1, vec3_t p2)
470 {
471  return PF_inVIS(p1, p2, DVIS_PHS);
472 }
473 
474 /*
475 ==================
476 PF_StartSound
477 
478 Each entity can have eight independant sound sources, like voice,
479 weapon, feet, etc.
480 
481 If channel & 8, the sound will be sent to everyone, not just
482 things in the PHS.
483 
484 FIXME: if entity isn't in PHS, they must be forced to be sent or
485 have the origin explicitly sent.
486 
487 Channel 0 is an auto-allocate channel, the others override anything
488 already running on that entity/channel pair.
489 
490 An attenuation of 0 will play full volume everywhere in the level.
491 Larger attenuations will drop off. (max 4 attenuation)
492 
493 Timeofs can range from 0.0 to 0.1 to cause sounds to be started
494 later in the frame than they normally would.
495 
496 If origin is NULL, the origin is determined from the entity origin
497 or the midpoint of the entity box for bmodels.
498 ==================
499 */
500 
501 #define CHECK_PARAMS \
502  if (volume < 0 || volume > 1.0) \
503  Com_Error(ERR_DROP, "%s: volume = %f", __func__, volume); \
504  if (attenuation < 0 || attenuation > 4) \
505  Com_Error(ERR_DROP, "%s: attenuation = %f", __func__, attenuation); \
506  if (timeofs < 0 || timeofs > 0.255) \
507  Com_Error(ERR_DROP, "%s: timeofs = %f", __func__, timeofs); \
508  if (soundindex < 0 || soundindex >= MAX_SOUNDS) \
509  Com_Error(ERR_DROP, "%s: soundindex = %d", __func__, soundindex);
510 
511 static void PF_StartSound(edict_t *edict, int channel,
512  int soundindex, float volume,
513  float attenuation, float timeofs)
514 {
515  int sendchan;
516  int flags;
517  int ent;
518  vec3_t origin;
519  client_t *client;
520  byte mask[VIS_MAX_BYTES];
521  mleaf_t *leaf;
522  int area;
523  player_state_t *ps;
525  int i;
526 
527  if (!edict)
528  return;
529 
531 
532  ent = NUM_FOR_EDICT(edict);
533 
534  if ((g_features->integer & GMF_PROPERINUSE) && !edict->inuse) {
535  Com_DPrintf("%s: entnum not in use: %d\n", __func__, ent);
536  return;
537  }
538 
539  sendchan = (ent << 3) | (channel & 7);
540 
541  // always send the entity number for channel overrides
542  flags = SND_ENT;
543  if (volume != DEFAULT_SOUND_PACKET_VOLUME)
544  flags |= SND_VOLUME;
545  if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
546  flags |= SND_ATTENUATION;
547  if (timeofs)
548  flags |= SND_OFFSET;
549 
550  // if the sound doesn't attenuate,send it to everyone
551  // (global radio chatter, voiceovers, etc)
552  if (attenuation == ATTN_NONE) {
553  channel |= CHAN_NO_PHS_ADD;
554  }
555 
556  FOR_EACH_CLIENT(client) {
557  // do not send sounds to connecting clients
558  if (client->state != cs_spawned || client->download || client->nodata) {
559  continue;
560  }
561 
562  // PHS cull this sound
563  if (!(channel & CHAN_NO_PHS_ADD)) {
564  // get client viewpos
565  ps = &client->edict->client->ps;
566  VectorMA(ps->viewoffset, 0.125f, ps->pmove.origin, origin);
567  leaf = CM_PointLeaf(&sv.cm, origin);
568  area = CM_LeafArea(leaf);
569  if (!CM_AreasConnected(&sv.cm, area, edict->areanum)) {
570  // doors can legally straddle two areas, so
571  // we may need to check another one
572  if (!edict->areanum2 || !CM_AreasConnected(&sv.cm, area, edict->areanum2)) {
573  continue; // blocked by a door
574  }
575  }
576  BSP_ClusterVis(sv.cm.cache, mask, leaf->cluster, DVIS_PHS);
577  if (!SV_EdictIsVisible(&sv.cm, edict, mask)) {
578  continue; // not in PHS
579  }
580  }
581 
582  // use the entity origin unless it is a bmodel
583  if (edict->solid == SOLID_BSP) {
584  VectorAvg(edict->mins, edict->maxs, origin);
585  VectorAdd(edict->s.origin, origin, origin);
586  } else {
587  VectorCopy(edict->s.origin, origin);
588  }
589 
590  // reliable sounds will always have position explicitly set,
591  // as no one gurantees reliables to be delivered in time
592  if (channel & CHAN_RELIABLE) {
593  MSG_WriteByte(svc_sound);
594  MSG_WriteByte(flags | SND_POS);
595  MSG_WriteByte(soundindex);
596 
597  if (flags & SND_VOLUME)
598  MSG_WriteByte(volume * 255);
599  if (flags & SND_ATTENUATION)
600  MSG_WriteByte(attenuation * 64);
601  if (flags & SND_OFFSET)
602  MSG_WriteByte(timeofs * 1000);
603 
604  MSG_WriteShort(sendchan);
606 
608  continue;
609  }
610 
611  if (LIST_EMPTY(&client->msg_free_list)) {
612  Com_WPrintf("%s: %s: out of message slots\n",
613  __func__, client->name);
614  continue;
615  }
616 
617  // send origin for invisible entities
618  if (edict->svflags & SVF_NOCLIENT) {
619  flags |= SND_POS;
620  }
621 
622  // default client doesn't know that bmodels have weird origins
623  if (edict->solid == SOLID_BSP && client->protocol == PROTOCOL_VERSION_DEFAULT) {
624  flags |= SND_POS;
625  }
626 
627  msg = LIST_FIRST(message_packet_t, &client->msg_free_list, entry);
628 
629  msg->cursize = 0;
630  msg->flags = flags;
631  msg->index = soundindex;
632  msg->volume = volume * 255;
633  msg->attenuation = attenuation * 64;
634  msg->timeofs = timeofs * 1000;
635  msg->sendchan = sendchan;
636  for (i = 0; i < 3; i++) {
637  msg->pos[i] = origin[i] * 8;
638  }
639 
640  List_Remove(&msg->entry);
641  List_Append(&client->msg_unreliable_list, &msg->entry);
643 
644  flags &= ~SND_POS;
645  }
646 
647  SV_MvdStartSound(ent, channel, flags, soundindex,
648  volume * 255, attenuation * 64, timeofs * 1000);
649 }
650 
651 static void PF_PositionedSound(vec3_t origin, edict_t *entity, int channel,
652  int soundindex, float volume,
653  float attenuation, float timeofs)
654 {
655  int sendchan;
656  int flags;
657  int ent;
658 
659  if (!origin)
660  Com_Error(ERR_DROP, "%s: NULL origin", __func__);
662 
663  ent = NUM_FOR_EDICT(entity);
664 
665  sendchan = (ent << 3) | (channel & 7);
666 
667  // always send the entity number for channel overrides
668  flags = SND_ENT | SND_POS;
669  if (volume != DEFAULT_SOUND_PACKET_VOLUME)
670  flags |= SND_VOLUME;
671  if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
672  flags |= SND_ATTENUATION;
673  if (timeofs)
674  flags |= SND_OFFSET;
675 
676  MSG_WriteByte(svc_sound);
677  MSG_WriteByte(flags);
678  MSG_WriteByte(soundindex);
679 
680  if (flags & SND_VOLUME)
681  MSG_WriteByte(volume * 255);
682  if (flags & SND_ATTENUATION)
683  MSG_WriteByte(attenuation * 64);
684  if (flags & SND_OFFSET)
685  MSG_WriteByte(timeofs * 1000);
686 
687  MSG_WriteShort(sendchan);
689 
690  // if the sound doesn't attenuate,send it to everyone
691  // (global radio chatter, voiceovers, etc)
692  if (attenuation == ATTN_NONE || (channel & CHAN_NO_PHS_ADD)) {
693  if (channel & CHAN_RELIABLE) {
694  SV_Multicast(NULL, MULTICAST_ALL_R);
695  } else {
696  SV_Multicast(NULL, MULTICAST_ALL);
697  }
698  } else {
699  if (channel & CHAN_RELIABLE) {
700  SV_Multicast(origin, MULTICAST_PHS_R);
701  } else {
702  SV_Multicast(origin, MULTICAST_PHS);
703  }
704  }
705 }
706 
707 
708 void PF_Pmove(pmove_t *pm)
709 {
710  if (sv_client) {
711  Pmove(pm, &sv_client->pmp);
712  } else {
713  Pmove(pm, &sv_pmp);
714  }
715 }
716 
717 static cvar_t *PF_cvar(const char *name, const char *value, int flags)
718 {
719  if (flags & CVAR_EXTENDED_MASK) {
720  Com_WPrintf("Game attemped to set extended flags on '%s', masked out.\n", name);
721  flags &= ~CVAR_EXTENDED_MASK;
722  }
723 
724  return Cvar_Get(name, value, flags | CVAR_GAME);
725 }
726 
727 static void PF_AddCommandString(const char *string)
728 {
729  Cbuf_AddText(&cmd_buffer, string);
730 }
731 
732 static void PF_SetAreaPortalState(int portalnum, qboolean open)
733 {
734  if (!sv.cm.cache) {
735  Com_Error(ERR_DROP, "%s: no map loaded", __func__);
736  }
737  CM_SetAreaPortalState(&sv.cm, portalnum, open);
738 }
739 
740 static qboolean PF_AreasConnected(int area1, int area2)
741 {
742  if (!sv.cm.cache) {
743  Com_Error(ERR_DROP, "%s: no map loaded", __func__);
744  }
745  return CM_AreasConnected(&sv.cm, area1, area2);
746 }
747 
748 static void *PF_TagMalloc(size_t size, unsigned tag)
749 {
750  if (tag + TAG_MAX < tag) {
751  Com_Error(ERR_FATAL, "%s: bad tag", __func__);
752  }
753  if (!size) {
754  return NULL;
755  }
756  return memset(Z_TagMalloc(size, tag + TAG_MAX), 0, size);
757 }
758 
759 static void PF_FreeTags(unsigned tag)
760 {
761  if (tag + TAG_MAX < tag) {
762  Com_Error(ERR_FATAL, "%s: bad tag", __func__);
763  }
764  Z_FreeTags(tag + TAG_MAX);
765 }
766 
767 static void PF_DebugGraph(float value, int color)
768 {
769 #if (defined _DEBUG) && USE_CLIENT
770  SCR_DebugGraph(value, color);
771 #endif
772 }
773 
774 //==============================================
775 
776 static void *game_library;
777 
778 /*
779 ===============
780 SV_ShutdownGameProgs
781 
782 Called when either the entire server is being killed, or
783 it is changing to a different game directory.
784 ===============
785 */
787 {
788  if (ge) {
789  ge->Shutdown();
790  ge = NULL;
791  }
792  if (game_library) {
794  game_library = NULL;
795  }
796  Cvar_Set("g_features", "0");
797 }
798 
799 static void *_SV_LoadGameLibrary(const char *path)
800 {
801  void *entry;
802 
803  entry = Sys_LoadLibrary(path, "GetGameAPI", &game_library);
804  if (!entry)
805  Com_EPrintf("Failed to load game library: %s\n", Com_GetLastError());
806  else
807  Com_Printf("Loaded game library from %s\n", path);
808 
809  return entry;
810 }
811 
812 static void *SV_LoadGameLibrary(const char *game, const char *prefix)
813 {
814  char path[MAX_OSPATH];
815  size_t len;
816 
817  len = Q_concat(path, sizeof(path), sys_libdir->string,
818  PATH_SEP_STRING, game, PATH_SEP_STRING,
819  prefix, "game" CPUSTRING LIBSUFFIX, NULL);
820  if (len >= sizeof(path)) {
821  Com_EPrintf("Game library path length exceeded\n");
822  return NULL;
823  }
824 
825  if (os_access(path, F_OK)) {
826  if (!*prefix)
827  Com_Printf("Can't access %s: %s\n", path, strerror(errno));
828  return NULL;
829  }
830 
831  return _SV_LoadGameLibrary(path);
832 }
833 
834 /*
835 ===============
836 SV_InitGameProgs
837 
838 Init the game subsystem for a new map
839 ===============
840 */
842 {
843  game_import_t import;
844  game_export_t *(*entry)(game_import_t *) = NULL;
845 
846  // unload anything we have now
848 
849  // for debugging or `proxy' mods
850  if (sys_forcegamelib->string[0])
851  entry = _SV_LoadGameLibrary(sys_forcegamelib->string);
852 
853  // try game first
854  if (!entry && fs_game->string[0]) {
855  entry = SV_LoadGameLibrary(fs_game->string, "q2pro_");
856  if (!entry)
857  entry = SV_LoadGameLibrary(fs_game->string, "");
858  }
859 
860  // then try baseq2
861  if (!entry) {
862  entry = SV_LoadGameLibrary(BASEGAME, "q2pro_");
863  if (!entry)
864  entry = SV_LoadGameLibrary(BASEGAME, "");
865  }
866 
867  // all paths failed
868  if (!entry)
869  Com_Error(ERR_DROP, "Failed to load game library");
870 
871  // load a new game dll
872  import.multicast = SV_Multicast;
873  import.unicast = PF_Unicast;
874  import.bprintf = PF_bprintf;
875  import.dprintf = PF_dprintf;
876  import.cprintf = PF_cprintf;
877  import.centerprintf = PF_centerprintf;
878  import.error = PF_error;
879 
880  import.linkentity = PF_LinkEdict;
881  import.unlinkentity = PF_UnlinkEdict;
882  import.BoxEdicts = SV_AreaEdicts;
883  import.trace = SV_Trace;
884  import.pointcontents = SV_PointContents;
885  import.setmodel = PF_setmodel;
886  import.inPVS = PF_inPVS;
887  import.inPHS = PF_inPHS;
888  import.Pmove = PF_Pmove;
889 
890  import.modelindex = PF_ModelIndex;
891  import.soundindex = PF_SoundIndex;
892  import.imageindex = PF_ImageIndex;
893 
894  import.configstring = PF_configstring;
895  import.sound = PF_StartSound;
896  import.positioned_sound = PF_PositionedSound;
897 
898  import.WriteChar = MSG_WriteChar;
899  import.WriteByte = MSG_WriteByte;
900  import.WriteShort = MSG_WriteShort;
901  import.WriteLong = MSG_WriteLong;
902  import.WriteFloat = PF_WriteFloat;
903  import.WriteString = MSG_WriteString;
904  import.WritePosition = MSG_WritePos;
905  import.WriteDir = MSG_WriteDir;
906  import.WriteAngle = MSG_WriteAngle;
907 
908  import.TagMalloc = PF_TagMalloc;
909  import.TagFree = Z_Free;
910  import.FreeTags = PF_FreeTags;
911 
912  import.cvar = PF_cvar;
913  import.cvar_set = Cvar_UserSet;
914  import.cvar_forceset = Cvar_Set;
915 
916  import.argc = Cmd_Argc;
917  import.argv = Cmd_Argv;
918  // original Cmd_Args() did actually return raw arguments
919  import.args = Cmd_RawArgs;
920  import.AddCommandString = PF_AddCommandString;
921 
922  import.DebugGraph = PF_DebugGraph;
923  import.SetAreaPortalState = PF_SetAreaPortalState;
924  import.AreasConnected = PF_AreasConnected;
925 
926  ge = entry(&import);
927  if (!ge) {
928  Com_Error(ERR_DROP, "Game DLL returned NULL exports");
929  }
930 
931  if (ge->apiversion != GAME_API_VERSION) {
932  Com_Error(ERR_DROP, "Game DLL is version %d, expected %d",
933  ge->apiversion, GAME_API_VERSION);
934  }
935 
936  // initialize
937  ge->Init();
938 
939  // sanitize edict_size
940  if (ge->edict_size < sizeof(edict_t) || ge->edict_size > SIZE_MAX / MAX_EDICTS) {
941  Com_Error(ERR_DROP, "Game DLL returned bad size of edict_t");
942  }
943 
944  // sanitize max_edicts
945  if (ge->max_edicts <= sv_maxclients->integer || ge->max_edicts > MAX_EDICTS) {
946  Com_Error(ERR_DROP, "Game DLL returned bad number of max_edicts");
947  }
948 }
949 
PF_inPVS
static qboolean PF_inPVS(vec3_t p1, vec3_t p2)
Definition: game.c:457
Cvar_Set
cvar_t * Cvar_Set(const char *var_name, const char *value)
Definition: cvar.c:466
cs_spawned
@ cs_spawned
Definition: server.h:192
SV_MvdStartSound
void SV_MvdStartSound(int entnum, int channel, int flags, int soundindex, int volume, int attenuation, int timeofs)
Definition: mvd.c:1257
pm
static pmove_t * pm
Definition: pmove.c:44
svs
server_static_t svs
Definition: init.c:21
PF_inVIS
static qboolean PF_inVIS(vec3_t p1, vec3_t p2, int vis)
Definition: game.c:427
client_s::protocol
int protocol
Definition: server.h:324
message_packet_t
Definition: server.h:220
PF_AddCommandString
static void PF_AddCommandString(const char *string)
Definition: game.c:727
client_s::msg_free_list
list_t msg_free_list
Definition: server.h:332
Cvar_Get
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags)
Definition: cvar.c:257
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
PF_bprintf
static void PF_bprintf(int level, const char *fmt,...)
Definition: game.c:140
sys_libdir
cvar_t * sys_libdir
Definition: system.c:51
PF_cvar
static cvar_t * PF_cvar(const char *name, const char *value, int flags)
Definition: game.c:717
MSG_RELIABLE
#define MSG_RELIABLE
Definition: server.h:214
_SV_LoadGameLibrary
static void * _SV_LoadGameLibrary(const char *path)
Definition: game.c:799
SV_MvdUnicast
void SV_MvdUnicast(edict_t *ent, int clientNum, qboolean reliable)
Definition: mvd.c:1184
PF_PositionedSound
static void PF_PositionedSound(vec3_t origin, edict_t *entity, int channel, int soundindex, float volume, float attenuation, float timeofs)
Definition: game.c:651
Q_vsnprintf
size_t Q_vsnprintf(char *dest, size_t size, const char *fmt, va_list argptr)
Definition: shared.c:791
sv_client
client_t * sv_client
Definition: main.c:32
PF_ImageIndex
static int PF_ImageIndex(const char *name)
Definition: game.c:68
MSG_WriteByte
void MSG_WriteByte(int c)
Definition: msg.c:107
Cmd_Argv
char * Cmd_Argv(int arg)
Definition: cmd.c:899
SCR_DebugGraph
void SCR_DebugGraph(float value, int color)
SV_Multicast
void SV_Multicast(vec3_t origin, multicast_t to)
Definition: send.c:255
PF_setmodel
static void PF_setmodel(edict_t *ent, const char *name)
Definition: game.c:326
BSP_PointLeaf
mleaf_t * BSP_PointLeaf(mnode_t *node, vec3_t p)
Definition: bsp.c:1439
PF_LinkEdict
void PF_LinkEdict(edict_t *ent)
Definition: world.c:264
Cmd_Argc
int Cmd_Argc(void)
Definition: cmd.c:889
SV_ClientAddMessage
void SV_ClientAddMessage(client_t *client, int flags)
Definition: send.c:399
client_s::drop_hack
qboolean drop_hack
Definition: server.h:268
FOR_EACH_CLIENT
#define FOR_EACH_CLIENT(client)
Definition: server.h:239
SV_AreaEdicts
int SV_AreaEdicts(vec3_t mins, vec3_t maxs, edict_t **list, int maxcount, int areatype)
Definition: world.c:407
CM_AreasConnected
qboolean CM_AreasConnected(cm_t *cm, int area1, int area2)
Definition: cmodel.c:890
PF_SetAreaPortalState
static void PF_SetAreaPortalState(int portalnum, qboolean open)
Definition: game.c:732
cmd_buffer
cmdbuf_t cmd_buffer
Definition: cmd.c:49
MSG_WritePos
void MSG_WritePos(const vec3_t pos)
Definition: msg.c:198
client_s::nodata
qboolean nodata
Definition: server.h:266
Z_TagMalloc
void * Z_TagMalloc(size_t size, memtag_t tag)
Definition: zone.c:275
sv_pmp
pmoveParams_t sv_pmp
Definition: main.c:22
MSG_COMPRESS
#define MSG_COMPRESS
Definition: server.h:216
Com_Error
void Com_Error(error_type_t type, const char *fmt,...)
Definition: g_main.c:258
PF_error
static q_noreturn void PF_error(const char *fmt,...)
Definition: game.c:306
client_s::download
byte * download
Definition: server.h:315
NUM_FOR_EDICT
#define NUM_FOR_EDICT(e)
Definition: server.h:174
sv
server_t sv
Definition: init.c:22
PF_Pmove
void PF_Pmove(pmove_t *pm)
Definition: game.c:708
msg_write
sizebuf_t msg_write
Definition: msg.c:34
Cbuf_AddText
void Cbuf_AddText(cmdbuf_t *buf, const char *text)
Definition: cmd.c:95
Z_Free
void Z_Free(void *ptr)
Definition: zone.c:147
Pmove
void Pmove(pmove_t *pmove, pmoveParams_t *params)
Definition: pmove.c:1127
server_static_s::client_pool
client_t * client_pool
Definition: server.h:456
game
game_locals_t game
Definition: g_main.c:21
cs_zombie
@ cs_zombie
Definition: server.h:187
PF_UnlinkEdict
void PF_UnlinkEdict(edict_t *ent)
Definition: world.c:256
origin
static vec3_t origin
Definition: mesh.c:27
PF_ModelIndex
static int PF_ModelIndex(const char *name)
Definition: game.c:58
PF_centerprintf
static void PF_centerprintf(edict_t *ent, const char *fmt,...)
Definition: game.c:266
fs_game
cvar_t * fs_game
Definition: files.c:202
PF_DebugGraph
static void PF_DebugGraph(float value, int color)
Definition: game.c:767
SV_InitGameProgs
void SV_InitGameProgs(void)
Definition: game.c:841
Com_LPrintf
void Com_LPrintf(print_type_t type, const char *fmt,...)
Definition: g_main.c:242
Com_GetLastError
char * Com_GetLastError(void)
Definition: common.c:391
MSG_WriteShort
void MSG_WriteShort(int c)
Definition: msg.c:125
ge
game_export_t * ge
Definition: game.c:22
client_s::messagelevel
int messagelevel
Definition: server.h:277
Cvar_UserSet
cvar_t * Cvar_UserSet(const char *var_name, const char *value)
Definition: cvar.c:476
PF_dprintf
static void PF_dprintf(const char *fmt,...)
Definition: game.c:190
PF_WriteFloat
static void PF_WriteFloat(float f)
Definition: game.c:422
Sys_LoadLibrary
void * Sys_LoadLibrary(const char *path, const char *sym, void **handle)
Definition: system.c:794
server_t::cm
cm_t cm
Definition: server.h:161
MSG_WriteString
void MSG_WriteString(const char *string)
Definition: msg.c:160
MSG_WriteLong
void MSG_WriteLong(int c)
Definition: msg.c:144
PF_inPHS
static qboolean PF_inPHS(vec3_t p1, vec3_t p2)
Definition: game.c:469
CHECK_PARAMS
#define CHECK_PARAMS
Definition: game.c:501
sys_forcegamelib
cvar_t * sys_forcegamelib
Definition: system.c:53
CM_PointLeaf
mleaf_t * CM_PointLeaf(cm_t *cm, vec3_t p)
Definition: cmodel.c:209
SV_PointContents
int SV_PointContents(vec3_t p)
Definition: world.c:454
MAX_SOUND_PACKET
#define MAX_SOUND_PACKET
Definition: server.h:218
g_features
cvar_t * g_features
Definition: main.c:99
PF_SoundIndex
static int PF_SoundIndex(const char *name)
Definition: game.c:63
PF_FindIndex
static int PF_FindIndex(const char *name, int start, int max)
Definition: game.c:32
level
level_locals_t level
Definition: g_main.c:22
SV_ShutdownGameProgs
void SV_ShutdownGameProgs(void)
Definition: game.c:786
cs_primed
@ cs_primed
Definition: server.h:191
PF_Unicast
static void PF_Unicast(edict_t *ent, qboolean reliable)
Definition: game.c:81
server_t::state
server_state_t state
Definition: server.h:146
Z_FreeTags
void Z_FreeTags(memtag_t tag)
Definition: zone.c:257
MSG_WriteChar
void MSG_WriteChar(int c)
Definition: msg.c:89
client_s::msg_unreliable_bytes
size_t msg_unreliable_bytes
Definition: server.h:336
msg
const char * msg
Definition: win.h:25
client_s::msg_unreliable_list
list_t msg_unreliable_list
Definition: server.h:333
SV_MvdConfigstring
void SV_MvdConfigstring(int index, const char *string, size_t len)
Definition: mvd.c:1226
client_s
Definition: server.h:256
svc_layout
#define svc_layout
Definition: g_local.h:39
color
static vec4_t color
Definition: mesh.c:33
PF_TagMalloc
static void * PF_TagMalloc(size_t size, unsigned tag)
Definition: game.c:748
Q_concat
size_t Q_concat(char *dest, size_t size,...)
Definition: shared.c:758
SV_LoadGameLibrary
static void * SV_LoadGameLibrary(const char *game, const char *prefix)
Definition: game.c:812
Sys_FreeLibrary
void Sys_FreeLibrary(void *handle)
Definition: system.c:787
PF_cprintf
static void PF_cprintf(edict_t *ent, int level, const char *fmt,...)
Definition: game.c:211
server.h
server_t::configstrings
char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]
Definition: server.h:164
SV_EdictIsVisible
qboolean SV_EdictIsVisible(cm_t *cm, edict_t *ent, byte *mask)
Definition: world.c:129
SV_Trace
trace_t q_gameabi SV_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passedict, int contentmask)
Definition: world.c:546
PF_StartSound
static void PF_StartSound(edict_t *edict, int channel, int soundindex, float volume, float attenuation, float timeofs)
Definition: game.c:511
MSG_WriteDir
void MSG_WriteDir(const vec3_t dir)
Definition: msg.c:460
MSG_WriteAngle
void MSG_WriteAngle(float f)
Definition: msg.c:214
CM_SetAreaPortalState
void CM_SetAreaPortalState(cm_t *cm, int portalnum, qboolean open)
Definition: cmodel.c:869
PF_configstring
static void PF_configstring(int index, const char *val)
Definition: game.c:356
client_s::pmp
pmoveParams_t pmp
Definition: server.h:328
SZ_Clear
void SZ_Clear(sizebuf_t *buf)
Definition: sizebuf.c:40
PF_AreasConnected
static qboolean PF_AreasConnected(int area1, int area2)
Definition: game.c:740
game_library
static void * game_library
Definition: game.c:776
PF_FreeTags
static void PF_FreeTags(unsigned tag)
Definition: game.c:759
SV_MvdBroadcastPrint
void SV_MvdBroadcastPrint(int level, const char *string)
Definition: mvd.c:1241
client_s::name
char name[MAX_CLIENT_NAME]
Definition: server.h:276
client_s::edict
edict_t * edict
Definition: server.h:261
sv_maxclients
cvar_t * sv_maxclients
Definition: main.c:58
Cmd_RawArgs
char * Cmd_RawArgs(void)
Definition: cmd.c:951