1 /**
2  * @file
3  * @brief Interface to the game library.
4  */
5 
6 /*
7  All original material Copyright (C) 2002-2013 UFO: Alien Invasion.
8 
9  Original file from Quake 2 v3.21: quake2-2.31/server/sv_game.c
10  Copyright (C) 1997-2001 Id Software, Inc.
11 
12  This program is free software; you can redistribute it and/or
13  modify it under the terms of the GNU General Public License
14  as published by the Free Software Foundation; either version 2
15  of the License, or (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 
21  See the GNU General Public License for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with this program; if not, write to the Free Software
25  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26 
27  */
28 
29 #include "server.h"
30 #include "sv_log.h"
31 #include "../common/grid.h"
32 #include "../common/routing.h"
33 #include "../ports/system.h"
34 #include "../shared/scopedmutex.h"
35 #include <SDL.h>
36 
37 /**
38  * @brief Debug print to server console
39  */
SV_dprintf(const char * fmt,...)40 static void SV_dprintf (const char* fmt, ...)
41 {
42 	va_list ap;
43 
44 	va_start(ap, fmt);
45 	SV_LogAdd(fmt, ap);
46 	va_end(ap);
47 }
48 
49 /**
50  * @brief Print to a single client
51  * @sa SV_BroadcastPrintf
52  */
SV_PlayerPrintf(const SrvPlayer * player,int level,const char * fmt,va_list ap)53 static void SV_PlayerPrintf (const SrvPlayer *player, int level, const char* fmt, va_list ap)
54 {
55 	if (level == PRINT_NONE)
56 		return;
57 
58 	if (player) {
59 		char msg[1024];
60 		Q_vsnprintf(msg, sizeof(msg), fmt, ap);
61 		client_t* cl = SV_GetClient(player->getNum());
62 		SV_ClientPrintf(cl, level, "%s", msg);
63 	} else
64 		SV_LogAdd(fmt, ap);
65 }
66 
67 /**
68  * @brief Glue function to get the visibility from a given position
69  */
SV_GetVisibility(const pos3_t position)70 static float SV_GetVisibility (const pos3_t position)
71 {
72 	return CM_GetVisibility(&sv->mapTiles, position);
73 }
74 
75 static void SV_error (const char* fmt, ...) __attribute__((noreturn));
76 /**
77  * @brief Abort the server with a game error
78  * @note The error message should not have a newline - it's added inside of this function
79  */
SV_error(const char * fmt,...)80 static void SV_error (const char* fmt, ...)
81 {
82 	char msg[1024];
83 	va_list argptr;
84 
85 	va_start(argptr, fmt);
86 	Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
87 	va_end(argptr);
88 
89 	Com_Error(ERR_DROP, "Game Error: %s", msg);
90 }
91 
92 /**
93  * @brief Search the index in the config strings relative to a given start
94  * @param name The value of the config string to search the index for
95  * @param start The relative start point for the search
96  * @param max The max. searched entries in the config string before giving up
97  * @param create if @c true the value will get written into the config strings (appended)
98  * @return @c 0 if not found
99  */
SV_FindIndex(const char * name,int start,int max,bool create)100 static unsigned int SV_FindIndex (const char* name, int start, int max, bool create)
101 {
102 	int i;
103 
104 	if (!name || !name[0])
105 		return 0;
106 
107 	for (i = 1; i < max && SV_GetConfigString(start + i)[0] != '\0'; i++) {
108 		const char* configString = SV_GetConfigString(start + i);
109 		if (Q_streq(configString, name))
110 			return i;
111 	}
112 
113 	if (!create)
114 		return 0;
115 
116 	if (i == max)
117 		SV_error("*Index: overflow '%s' start: %i, max: %i", name, start, max);
118 
119 	SV_SetConfigString(start + i, name);
120 
121 	if (Com_ServerState() != ss_loading) {	/* send the update to everyone */
122 		dbuffer msg(4 + strlen(name));
123 		NET_WriteByte(&msg, svc_configstring);
124 		NET_WriteShort(&msg, start + i);
125 		NET_WriteString(&msg, name);
126 		SV_Multicast(~0, msg);
127 	}
128 
129 	return i;
130 }
131 
SV_ModelIndex(const char * name)132 static unsigned int SV_ModelIndex (const char* name)
133 {
134 	return SV_FindIndex(name, CS_MODELS, MAX_MODELS, true);
135 }
136 
137 /**
138  * @note Also sets mins and maxs for inline bmodels
139  * @sa CM_InlineModel
140  */
SV_SetModel(edict_t * ent,const char * name)141 static void SV_SetModel (edict_t* ent, const char* name)
142 {
143 	if (!name)
144 		SV_error("SV_SetModel: nullptr");
145 
146 	ent->modelindex = SV_ModelIndex(name);
147 
148 	/* if it is an inline model, get the size information for it */
149 	if (name[0] == '*') {
150 		const cBspModel_t* mod = CM_InlineModel(&sv->mapTiles, name);
151 		/* Copy model mins and maxs to entity */
152 		VectorCopy(mod->mins, ent->mins);
153 		VectorCopy(mod->maxs, ent->maxs);
154 		/* This is to help the entity collision code out */
155 		/* Copy entity origin and angles to model*/
156 		CM_SetInlineModelOrientation(&sv->mapTiles, name, ent->origin, ent->angles);
157 	}
158 }
159 
160 /**
161  * @sa CL_ParseConfigString
162  */
SV_Configstring(int index,const char * fmt,...)163 static void SV_Configstring (int index, const char* fmt, ...)
164 {
165 	char val[MAX_TOKEN_CHARS * MAX_TILESTRINGS];
166 	va_list argptr;
167 
168 	if (!Com_CheckConfigStringIndex(index))
169 		SV_error("configstring: bad index %i", index);
170 
171 	va_start(argptr, fmt);
172 	Q_vsnprintf(val, sizeof(val), fmt, argptr);
173 	va_end(argptr);
174 
175 	SV_SetConfigString(index, val);
176 
177 	if (Com_ServerState() != ss_loading) { /* send the update to everyone */
178 		dbuffer msg(4 + strlen(val));
179 		NET_WriteByte(&msg, svc_configstring);
180 		NET_WriteShort(&msg, index);
181 		NET_WriteString(&msg, val);
182 
183 		/* send to all clients */
184 		SV_Multicast(~0, msg);
185 	}
186 }
187 
SV_WriteChar(char c)188 static void SV_WriteChar (char c)
189 {
190 	NET_WriteChar(sv->pendingEvent.buf, c);
191 }
192 
SV_WriteByte(byte c)193 static void SV_WriteByte (byte c)
194 {
195 	NET_WriteByte(sv->pendingEvent.buf, c);
196 }
197 
SV_WriteShort(int c)198 static void SV_WriteShort (int c)
199 {
200 	NET_WriteShort(sv->pendingEvent.buf, c);
201 }
202 
SV_WriteLong(int c)203 static void SV_WriteLong (int c)
204 {
205 	NET_WriteLong(sv->pendingEvent.buf, c);
206 }
207 
SV_WriteString(const char * s)208 static void SV_WriteString (const char* s)
209 {
210 	NET_WriteString(sv->pendingEvent.buf, s);
211 }
212 
SV_WritePos(const vec3_t pos)213 static void SV_WritePos (const vec3_t pos)
214 {
215 	NET_WritePos(sv->pendingEvent.buf, pos);
216 }
217 
SV_WriteGPos(const pos3_t pos)218 static void SV_WriteGPos (const pos3_t pos)
219 {
220 	NET_WriteGPos(sv->pendingEvent.buf, pos);
221 }
222 
SV_WriteDir(const vec3_t dir)223 static void SV_WriteDir (const vec3_t dir)
224 {
225 	NET_WriteDir(sv->pendingEvent.buf, dir);
226 }
227 
SV_WriteAngle(float f)228 static void SV_WriteAngle (float f)
229 {
230 	NET_WriteAngle(sv->pendingEvent.buf, f);
231 }
232 
SV_WriteFormat(const char * format,...)233 static void SV_WriteFormat (const char* format, ...)
234 {
235 	va_list ap;
236 	va_start(ap, format);
237 	NET_vWriteFormat(sv->pendingEvent.buf, format, ap);
238 	va_end(ap);
239 }
240 
SV_ReadChar(void)241 static int SV_ReadChar (void)
242 {
243 	return NET_ReadChar(sv->messageBuffer);
244 }
245 
SV_ReadByte(void)246 static int SV_ReadByte (void)
247 {
248 	return NET_ReadByte(sv->messageBuffer);
249 }
250 
SV_ReadShort(void)251 static int SV_ReadShort (void)
252 {
253 	return NET_ReadShort(sv->messageBuffer);
254 }
255 
SV_ReadLong(void)256 static int SV_ReadLong (void)
257 {
258 	return NET_ReadLong(sv->messageBuffer);
259 }
260 
SV_ReadString(char * str,size_t length)261 static int SV_ReadString (char* str, size_t length)
262 {
263 	return NET_ReadString(sv->messageBuffer, str, length);
264 }
265 
SV_ReadPos(vec3_t pos)266 static void SV_ReadPos (vec3_t pos)
267 {
268 	NET_ReadPos(sv->messageBuffer, pos);
269 }
270 
SV_ReadGPos(pos3_t pos)271 static void SV_ReadGPos (pos3_t pos)
272 {
273 	NET_ReadGPos(sv->messageBuffer, pos);
274 }
275 
SV_ReadDir(vec3_t vector)276 static void SV_ReadDir (vec3_t vector)
277 {
278 	NET_ReadDir(sv->messageBuffer, vector);
279 }
280 
SV_ReadAngle(void)281 static float SV_ReadAngle (void)
282 {
283 	return NET_ReadAngle(sv->messageBuffer);
284 }
285 
SV_ReadData(void * buffer,int size)286 static void SV_ReadData (void* buffer, int size)
287 {
288 	NET_ReadData(sv->messageBuffer, buffer, size);
289 }
290 
291 /**
292  * @sa NET_vReadFormat
293  */
SV_ReadFormat(const char * format,...)294 static void SV_ReadFormat (const char* format, ...)
295 {
296 	va_list ap;
297 
298 	assert(format);
299 	if (!*format) /* PA_NULL */
300 		return;
301 
302 	va_start(ap, format);
303 	NET_vReadFormat(sv->messageBuffer, format, ap);
304 	va_end(ap);
305 }
306 
307 /**
308  * @sa gi.AbortEvents
309  */
SV_AbortEvents(void)310 static void SV_AbortEvents (void)
311 {
312 	pending_event_t* p = &sv->pendingEvent;
313 
314 	if (!p->pending)
315 		return;
316 
317 	p->pending = false;
318 	delete p->buf;
319 	p->buf = nullptr;
320 }
321 
SV_SendQueuedEvents(void)322 static void SV_SendQueuedEvents (void)
323 {
324 	for (int i = 0; i < sv->eventQueuePos; i++) {
325 		pending_event_t &entry = sv->eventQueue[i];
326 		NET_WriteByte(entry.buf, EV_NULL);
327 		SV_Multicast(entry.playerMask, *entry.buf);
328 		delete entry.buf;
329 	}
330 	sv->eventQueuePos = 0;
331 }
332 
333 /**
334  * @sa gi.EndEvents
335  */
SV_EndEvents(void)336 static void SV_EndEvents (void)
337 {
338 	pending_event_t* p = &sv->pendingEvent;
339 
340 	if (!p->pending) {
341 		SV_SendQueuedEvents();
342 		return;
343 	}
344 
345 	NET_WriteByte(p->buf, EV_NULL);
346 	SV_Multicast(p->playerMask, *p->buf);
347 	p->pending = false;
348 	delete p->buf;
349 	p->buf = nullptr;
350 
351 	SV_SendQueuedEvents();
352 }
353 
354 typedef struct {
355 	const char* name;
356 } eventNames_t;
357 
358 #define M(x) { #x }
359 static const eventNames_t eventNames[] = {
360 	M(EV_NULL),
361 	M(EV_RESET),
362 	M(EV_START),
363 	M(EV_ENDROUND),
364 	M(EV_ENDROUNDANNOUNCE),
365 
366 	M(EV_RESULTS),
367 	M(EV_CENTERVIEW),
368 	M(EV_MOVECAMERA),
369 
370 	M(EV_ENT_APPEAR),
371 	M(EV_ENT_PERISH),
372 	M(EV_ENT_DESTROY),
373 	M(EV_ADD_BRUSH_MODEL),
374 	M(EV_ADD_EDICT),
375 
376 	M(EV_ACTOR_APPEAR),
377 	M(EV_ACTOR_ADD),
378 	M(EV_ACTOR_TURN),
379 	M(EV_ACTOR_MOVE),
380 	M(EV_ACTOR_REACTIONFIRECHANGE),
381 	M(EV_ACTOR_REACTIONFIREADDTARGET),
382 	M(EV_ACTOR_REACTIONFIREREMOVETARGET),
383 	M(EV_ACTOR_REACTIONFIRETARGETUPDATE),
384 
385 	M(EV_ACTOR_START_SHOOT),
386 	M(EV_ACTOR_SHOOT),
387 	M(EV_ACTOR_SHOOT_HIDDEN),
388 	M(EV_ACTOR_THROW),
389 	M(EV_ACTOR_END_SHOOT),
390 
391 	M(EV_ACTOR_DIE),
392 	M(EV_ACTOR_REVITALISED),
393 	M(EV_ACTOR_STATS),
394 	M(EV_ACTOR_STATECHANGE),
395 	M(EV_ACTOR_RESERVATIONCHANGE),
396 	M(EV_ACTOR_WOUND),
397 
398 	M(EV_INV_ADD),
399 	M(EV_INV_DEL),
400 	M(EV_INV_AMMO),
401 	M(EV_INV_RELOAD),
402 	M(EV_INV_TRANSFER),
403 
404 	M(EV_MODEL_EXPLODE),
405 	M(EV_MODEL_EXPLODE_TRIGGERED),
406 
407 	M(EV_PARTICLE_APPEAR),
408 	M(EV_PARTICLE_SPAWN),
409 
410 	M(EV_SOUND),
411 
412 	M(EV_DOOR_OPEN),
413 	M(EV_DOOR_CLOSE),
414 	M(EV_CLIENT_ACTION),
415 	M(EV_RESET_CLIENT_ACTION),
416 
417 	M(EV_CAMERA_APPEAR)
418 };
419 #undef M
420 CASSERT(lengthof(eventNames) == EV_NUM_EVENTS);
421 
422 /**
423  * @sa gi.AddEvent
424  * @param[in] mask The player bitmask to send the events to. Use @c PM_ALL to send to every connected player.
425  */
SV_AddEvent(unsigned int mask,int eType,int entnum)426 static void SV_AddEvent (unsigned int mask, int eType, int entnum)
427 {
428 	pending_event_t* p = &sv->pendingEvent;
429 	const int rawType = eType &~ EVENT_INSTANTLY;
430 
431 	if (rawType >= EV_NUM_EVENTS || rawType < 0)
432 		Com_Error(ERR_DROP, "SV_AddEvent: invalid event %i", rawType);
433 
434 	const char* eventName = eventNames[rawType].name;
435 	Com_DPrintf(DEBUG_EVENTSYS, "Event type: %s (%i - %i) (mask %s) (entnum: %i)\n", eventName,
436 			rawType, eType, Com_UnsignedIntToBinary(mask), entnum);
437 
438 	/* finish the last event */
439 	if (p->pending)
440 		SV_EndEvents();
441 	else
442 		SV_SendQueuedEvents();
443 
444 	/* start the new event */
445 	p->pending = true;
446 	p->playerMask = mask;
447 	p->type = eType;
448 	p->entnum = entnum;
449 	p->buf = new dbuffer();
450 
451 	/* write header */
452 	NET_WriteByte(p->buf, svc_event);
453 	NET_WriteByte(p->buf, eType);
454 	if (entnum != -1)
455 		NET_WriteShort(p->buf, entnum);
456 }
457 /**
458  * @sa gi.QueueEvent
459  * @param[in] mask The player bitmask to send the events to. Use @c PM_ALL to send to every connected player.
460  */
SV_QueueEvent(unsigned int mask,int eType,int entnum)461 static void SV_QueueEvent (unsigned int mask, int eType, int entnum)
462 {
463 	if (sv->eventQueuePos > lengthof(sv->eventQueue))
464 		Com_Error(ERR_DROP, "overflow in SV_QueueEvent");
465 
466 	pending_event_t &p = sv->eventQueue[sv->eventQueuePos++];
467 
468 	/* start the new event */
469 	p.pending = false;
470 	p.playerMask = mask;
471 	p.type = eType;
472 	p.entnum = entnum;
473 	p.buf = new dbuffer();
474 	/* write header */
475 	NET_WriteByte(p.buf, svc_event);
476 	NET_WriteByte(p.buf, eType);
477 	if (p.entnum != -1)
478 		NET_WriteShort(p.buf, p.entnum);
479 }
480 
SV_QueueWriteByte(byte c)481 static void SV_QueueWriteByte (byte c)
482 {
483 	NET_WriteByte(sv->eventQueue[sv->eventQueuePos - 1].buf, c);
484 }
485 
SV_QueueWriteString(const char * s)486 static void SV_QueueWriteString (const char* s)
487 {
488 	NET_WriteString(sv->eventQueue[sv->eventQueuePos - 1].buf, s);
489 }
490 
SV_QueueWritePos(const vec3_t pos)491 static void SV_QueueWritePos (const vec3_t pos)
492 {
493 	NET_WritePos(sv->eventQueue[sv->eventQueuePos - 1].buf, pos);
494 }
495 
SV_QueueWriteShort(int c)496 static void SV_QueueWriteShort (int c)
497 {
498 	NET_WriteShort(sv->eventQueue[sv->eventQueuePos - 1].buf, c);
499 }
500 
501 /**
502  * @return The current active event or -1 if no event is active at the moment
503  */
SV_GetEvent(void)504 static int SV_GetEvent (void)
505 {
506 	const pending_event_t* p = &sv->pendingEvent;
507 	if (!p->pending)
508 		return -1;
509 
510 	return p->type;
511 }
512 
SV_GetEventEdict(void)513 static edict_t* SV_GetEventEdict (void)
514 {
515 	const pending_event_t* p = &sv->pendingEvent;
516 	if (!p->pending)
517 		return nullptr;
518 
519 	if (p->entnum == -1)
520 		return nullptr;
521 
522 	const sv_edict_t &e = sv->edicts[p->entnum];
523 	return e.ent;
524 }
525 
526 /**
527  * @brief Makes sure the game DLL does not use client, or signed tags
528  */
SV_TagAlloc(int size,int tagNum,const char * file,int line)529 static void* SV_TagAlloc (int size, int tagNum, const char* file, int line)
530 {
531 	if (tagNum < 0)
532 		tagNum *= -1;
533 
534 	return _Mem_Alloc(size, true, sv->gameSysPool, tagNum, file, line);
535 }
536 
SV_MemFree(void * ptr,const char * file,int line)537 static void SV_MemFree (void* ptr, const char* file, int line)
538 {
539 	_Mem_Free(ptr, file, line);
540 }
541 
542 /**
543  * @brief Makes sure the game DLL does not use client, or signed tags
544  */
SV_FreeTags(int tagNum,const char * file,int line)545 static void SV_FreeTags (int tagNum, const char* file, int line)
546 {
547 	if (tagNum < 0)
548 		tagNum *= -1;
549 
550 	_Mem_FreeTag(sv->gameSysPool, tagNum, file, line);
551 }
552 
SV_TestLine(const vec3_t start,const vec3_t stop,const int levelmask)553 static bool SV_TestLine (const vec3_t start, const vec3_t stop, const int levelmask)
554 {
555 	return TR_TestLine(&sv->mapTiles, start, stop, levelmask);
556 }
557 
SV_TestLineWithEnt(const vec3_t start,const vec3_t stop,const int levelmask,const char ** entlist)558 static bool SV_TestLineWithEnt (const vec3_t start, const vec3_t stop, const int levelmask, const char** entlist)
559 {
560 	/* do the line test */
561 	const bool hit = CM_EntTestLine(&sv->mapTiles, start, stop, levelmask, entlist);
562 	return hit;
563 }
564 
SV_GridFall(const int actorSize,const pos3_t pos)565 static pos_t SV_GridFall (const int actorSize, const pos3_t pos)
566 {
567 	return Grid_Fall(sv->mapData.routing, actorSize, pos);
568 }
569 
SV_RecalcRouting(const char * name,const GridBox & box,const char ** list)570 static void SV_RecalcRouting (const char* name, const GridBox& box, const char** list)
571 {
572 	Grid_RecalcRouting(&sv->mapTiles, sv->mapData.routing, name, box, list);
573 }
574 
SV_GridPosToVec(const int actorSize,const pos3_t pos,vec3_t vec)575 static void SV_GridPosToVec (const int actorSize, const pos3_t pos, vec3_t vec)
576 {
577 	Grid_PosToVec(sv->mapData.routing, actorSize, pos, vec);
578 }
579 
SV_GridCalcPathing(actorSizeEnum_t actorSize,pathing_t * path,const pos3_t from,int distance,pos_t ** forbiddenList,int forbiddenListLength)580 static void SV_GridCalcPathing (actorSizeEnum_t actorSize, pathing_t* path, const pos3_t from, int distance, pos_t** forbiddenList, int forbiddenListLength)
581 {
582 	Grid_CalcPathing(sv->mapData.routing, actorSize, path, from, distance, forbiddenList, forbiddenListLength);
583 }
584 
SV_CanActorStandHere(const int actorSize,const pos3_t pos)585 static bool SV_CanActorStandHere (const int actorSize, const pos3_t pos)
586 {
587 	return RT_CanActorStandHere(sv->mapData.routing, actorSize, pos);
588 }
589 
SV_SetInlineModelOrientation(const char * name,const vec3_t origin,const vec3_t angles)590 static void SV_SetInlineModelOrientation (const char* name, const vec3_t origin, const vec3_t angles)
591 {
592 	CM_SetInlineModelOrientation(&sv->mapTiles, name, origin, angles);
593 }
594 
SV_GetInlineModelAABB(const char * name,AABB & aabb)595 static void SV_GetInlineModelAABB (const char* name, AABB& aabb)
596 {
597 	CM_GetInlineModelAABB(&sv->mapTiles, name, aabb);
598 }
599 
SV_UnloadGame(void)600 static void SV_UnloadGame (void)
601 {
602 #ifndef HARD_LINKED_GAME
603 	if (svs.gameLibrary) {
604 		Com_Printf("Unload the game library\n");
605 		SDL_UnloadObject(svs.gameLibrary);
606 	}
607 #endif
608 }
609 
610 #ifndef HARD_LINKED_GAME
SV_LoadGame(const char * path)611 static bool SV_LoadGame (const char* path)
612 {
613 	char name[MAX_OSPATH];
614 
615 	Com_sprintf(name, sizeof(name), "%s/game_" CPUSTRING ".%s", path, SO_EXT);
616 	svs.gameLibrary = SDL_LoadObject(name);
617 	if (!svs.gameLibrary) {
618 		Com_sprintf(name, sizeof(name), "%s/game.%s", path, SO_EXT);
619 		svs.gameLibrary = SDL_LoadObject(name);
620 	}
621 
622 	if (svs.gameLibrary) {
623 		Com_Printf("found at '%s'\n", path);
624 		return true;
625 	} else {
626 		Com_Printf("not found at '%s'\n", path);
627 		Com_DPrintf(DEBUG_SYSTEM, "%s\n", SDL_GetError());
628 		return false;
629 	}
630 }
631 #endif
632 
633 /**
634  * @brief Loads the game shared library and calls the api init function
635  */
SV_GetGameAPI(game_import_t * parms)636 static game_export_t* SV_GetGameAPI (game_import_t* parms)
637 {
638 #ifndef HARD_LINKED_GAME
639 	typedef game_export_t* (*game_api_t) (game_import_t*);
640 	game_api_t GetGameAPI;
641 	const char* path;
642 
643 	if (svs.gameLibrary)
644 		Com_Error(ERR_FATAL, "SV_GetGameAPI without SV_UnloadGame");
645 
646 	Com_Printf("------- Loading game.%s -------\n", SO_EXT);
647 
648 #ifdef PKGLIBDIR
649 	SV_LoadGame(PKGLIBDIR);
650 #endif
651 
652 	/* now run through the search paths */
653 	path = nullptr;
654 	while (!svs.gameLibrary) {
655 		path = FS_NextPath(path);
656 		if (!path)
657 			/* couldn't find one anywhere */
658 			return nullptr;
659 		else if (SV_LoadGame(path))
660 			break;
661 	}
662 
663 	GetGameAPI = (game_api_t)(uintptr_t)SDL_LoadFunction(svs.gameLibrary, "GetGameAPI");
664 	if (!GetGameAPI) {
665 		SV_UnloadGame();
666 		return nullptr;
667 	}
668 #endif
669 
670 	return GetGameAPI(parms);
671 }
672 
673 static char const* const gameSysPoolName = "Server: Game system";
674 
675 /**
676  * @brief Called when either the entire server is being killed, or it is changing to a different game directory.
677  * @sa G_Shutdown
678  * @sa SV_InitGameProgs
679  */
SV_ShutdownGameProgs(void)680 void SV_ShutdownGameProgs (void)
681 {
682 	uint32_t size;
683 
684 	if (!svs.ge)
685 		return;
686 
687 	Com_SetServerState(ss_game_shutdown);
688 
689 	if (svs.gameThread) {
690 		SDL_CondSignal(svs.gameFrameCond);
691 		SDL_WaitThread(svs.gameThread, nullptr);
692 		SDL_DestroyCond(svs.gameFrameCond);
693 		svs.gameFrameCond = nullptr;
694 		svs.gameThread = nullptr;
695 	}
696 
697 	svs.ge->Shutdown();
698 
699 	size = Mem_PoolSize(sv->gameSysPool);
700 	if (size > 0) {
701 		Com_Printf("WARNING: Game memory leak (%u bytes)\n", size);
702 		Cmd_ExecuteString("mem_stats %s", gameSysPoolName);
703 	}
704 
705 	Mem_DeletePool(sv->gameSysPool);
706 	sv->gameSysPool = nullptr;
707 
708 	SV_UnloadGame();
709 
710 	svs.ge = nullptr;
711 }
712 
713 /**
714  * @brief Thread for the game frame function
715  * @sa SV_RunGameFrame
716  * @sa SV_Frame
717  */
SV_RunGameFrameThread(void * data)718 int SV_RunGameFrameThread (void* data)
719 {
720 	do {
721 		const ScopedMutex scopedMutex(svs.serverMutex);
722 		SDL_CondWait(svs.gameFrameCond, svs.serverMutex);
723 		SV_RunGameFrame();
724 	} while (!sv->endgame);
725 
726 	return 0;
727 }
728 
729 /**
730  * @brief Calls the G_RunFrame function from game api
731  * let everything in the world think and move
732  * @sa G_RunFrame
733  * @sa SV_Frame
734  */
SV_RunGameFrame(void)735 void SV_RunGameFrame (void)
736 {
737 	sv->endgame = svs.ge->RunFrame();
738 	if (sv->state == ss_game_shutdown)
739 		sv->endgame = true;
740 }
741 
742 /**
743  * @brief Init the game subsystem for a new map
744  * @sa SV_ShutdownGameProgs
745  */
SV_InitGameProgs(void)746 void SV_InitGameProgs (void)
747 {
748 	game_import_t import;
749 
750 	/* unload anything we have now */
751 	/*SV_ShutdownGameProgs();*/
752 
753 	/* load a new game dll */
754 	import.BroadcastPrintf = SV_BroadcastPrintf;
755 	import.DPrintf = SV_dprintf;
756 	import.PlayerPrintf = SV_PlayerPrintf;
757 	import.Error = SV_error;
758 
759 	import.Trace = SV_Trace;
760 	import.LinkEdict = SV_LinkEdict;
761 	import.UnlinkEdict = SV_UnlinkEdict;
762 	import.BoxEdicts = SV_AreaEdicts;
763 
764 	import.TestLine = SV_TestLine;
765 	import.TestLineWithEnt = SV_TestLineWithEnt;
766 	import.GrenadeTarget = Com_GrenadeTarget;
767 
768 	import.GridCalcPathing = SV_GridCalcPathing;
769 	import.MoveStore = Grid_MoveStore;
770 	import.MoveLength = Grid_MoveLength;
771 	import.MoveNext = Grid_MoveNext;
772 	import.GetTUsForDirection = Grid_GetTUsForDirection;
773 	import.GridFall = SV_GridFall;
774 	import.GridPosToVec = SV_GridPosToVec;
775 	import.GridRecalcRouting = SV_RecalcRouting;
776 	import.CanActorStandHere = SV_CanActorStandHere;
777 	import.GridShouldUseAutostand = Grid_ShouldUseAutostand;
778 
779 	import.GetVisibility = SV_GetVisibility;
780 
781 	import.ModelIndex = SV_ModelIndex;
782 
783 	import.SetInlineModelOrientation = SV_SetInlineModelOrientation;
784 	import.GetInlineModelAABB = SV_GetInlineModelAABB;
785 
786 	import.SetModel = SV_SetModel;
787 
788 	import.ConfigString = SV_Configstring;
789 
790 	import.PointContents = SV_PointContents;
791 	import.GetFootstepSound = SV_GetFootstepSound;
792 	import.GetBounceFraction = SV_GetBounceFraction;
793 	import.LoadModelAABB = SV_LoadModelAABB;
794 
795 	import.FS_Gamedir = FS_Gamedir;
796 	import.FS_LoadFile = FS_LoadFile;
797 	import.FS_FreeFile = FS_FreeFile;
798 
799 	import.WriteChar = SV_WriteChar;
800 	import.WriteByte = SV_WriteByte;
801 	import.WriteShort = SV_WriteShort;
802 	import.WriteLong = SV_WriteLong;
803 	import.WriteString = SV_WriteString;
804 	import.WritePos = SV_WritePos;
805 	import.WriteGPos = SV_WriteGPos;
806 	import.WriteDir = SV_WriteDir;
807 	import.WriteAngle = SV_WriteAngle;
808 	import.WriteFormat = SV_WriteFormat;
809 
810 	import.AbortEvents = SV_AbortEvents;
811 	import.EndEvents = SV_EndEvents;
812 	import.AddEvent = SV_AddEvent;
813 	import.GetEvent = SV_GetEvent;
814 	import.GetEventEdict = SV_GetEventEdict;
815 
816 	import.QueueEvent = SV_QueueEvent;
817 	import.QueueWriteByte = SV_QueueWriteByte;
818 	import.QueueWritePos = SV_QueueWritePos;
819 	import.QueueWriteString = SV_QueueWriteString;
820 	import.QueueWriteShort = SV_QueueWriteShort;
821 
822 	import.ReadChar = SV_ReadChar;
823 	import.ReadByte = SV_ReadByte;
824 	import.ReadShort = SV_ReadShort;
825 	import.ReadLong = SV_ReadLong;
826 	import.ReadString = SV_ReadString;
827 	import.ReadPos = SV_ReadPos;
828 	import.ReadGPos = SV_ReadGPos;
829 	import.ReadDir = SV_ReadDir;
830 	import.ReadAngle = SV_ReadAngle;
831 	import.ReadData = SV_ReadData;
832 	import.ReadFormat = SV_ReadFormat;
833 
834 	import.GetConstInt = Com_GetConstInt;
835 	import.GetConstIntFromNamespace = Com_GetConstIntFromNamespace;
836 	import.GetConstVariable = Com_GetConstVariable;
837 	import.RegisterConstInt = Com_RegisterConstInt;
838 	import.UnregisterConstVariable = Com_UnregisterConstVariable;
839 
840 	import.GetCharacterValues = Com_GetCharacterValues;
841 
842 	import.TagMalloc = SV_TagAlloc;
843 	import.TagFree = SV_MemFree;
844 	import.FreeTags = SV_FreeTags;
845 
846 	import.Cvar_Get = Cvar_Get;
847 	import.Cvar_Set = Cvar_Set;
848 	import.Cvar_String = Cvar_GetString;
849 
850 	import.Cmd_Argc = Cmd_Argc;
851 	import.Cmd_Argv = Cmd_Argv;
852 	import.Cmd_Args = Cmd_Args;
853 	import.AddCommandString = Cbuf_AddText;
854 
855 	import.seed = Sys_Milliseconds();
856 	import.csi = &csi;
857 
858 	Com_Printf("setting game random seed to %i\n", import.seed);
859 
860 	svs.ge = SV_GetGameAPI(&import);
861 
862 	if (!svs.ge)
863 		Com_Error(ERR_DROP, "failed to load game library");
864 	if (svs.ge->apiversion != GAME_API_VERSION)
865 		Com_Error(ERR_DROP, "game is version %i, not %i", svs.ge->apiversion, GAME_API_VERSION);
866 
867 	sv->gameSysPool = Mem_CreatePool(gameSysPoolName);
868 
869 	svs.ge->Init();
870 
871 	if (sv_threads->integer) {
872 		svs.gameFrameCond = SDL_CreateCond();
873 #if SDL_VERSION_ATLEAST(2,0,0)
874 		svs.gameThread = SDL_CreateThread(SV_RunGameFrameThread, "GameThread", nullptr);
875 #else
876 		svs.gameThread = SDL_CreateThread(SV_RunGameFrameThread, nullptr);
877 #endif
878 	}
879 }
880