sv_game.c

Go to the documentation of this file.
00001 
00006 /*
00007  All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009  Original file from Quake 2 v3.21: quake2-2.31/server/sv_game.c
00010  Copyright (C) 1997-2001 Id Software, Inc.
00011 
00012  This program is free software; you can redistribute it and/or
00013  modify it under the terms of the GNU General Public License
00014  as published by the Free Software Foundation; either version 2
00015  of the License, or (at your option) any later version.
00016 
00017  This program is distributed in the hope that it will be useful,
00018  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00020 
00021  See the GNU General Public License for more details.
00022 
00023  You should have received a copy of the GNU General Public License
00024  along with this program; if not, write to the Free Software
00025  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00026 
00027  */
00028 
00029 #include "server.h"
00030 #include "../common/grid.h"
00031 #include "../ports/system.h"
00032 #include <SDL.h>
00033 
00037 static void SV_dprintf (const char *fmt, ...)
00038 {
00039     va_list ap;
00040 
00041     va_start(ap, fmt);
00042     Com_vPrintf(fmt, ap);
00043     va_end(ap);
00044 }
00045 
00050 static void SV_PlayerPrintf (const player_t * player, int level, const char *fmt, va_list ap)
00051 {
00052     char msg[1024];
00053 
00054     if (level == PRINT_NONE)
00055         return;
00056 
00057     Q_vsnprintf(msg, sizeof(msg), fmt, ap);
00058 
00059     if (player) {
00060         client_t *cl = SV_GetClient(player->num);
00061         SV_ClientPrintf(cl, level, "%s", msg);
00062     } else
00063         Com_Printf("%s", msg);
00064 }
00065 
00066 static void SV_error (const char *fmt, ...) __attribute__((noreturn));
00071 static void SV_error (const char *fmt, ...)
00072 {
00073     char msg[1024];
00074     va_list argptr;
00075 
00076     va_start(argptr, fmt);
00077     Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
00078     va_end(argptr);
00079 
00080     Com_Error(ERR_DROP, "Game Error: %s", msg);
00081 }
00082 
00083 static int SV_FindIndex (const char *name, int start, int max, qboolean create)
00084 {
00085     int i;
00086 
00087     if (!name || !name[0])
00088         return 0;
00089 
00090     for (i = 1; i < max && SV_GetConfigString(start + i)[0] != '\0'; i++) {
00091         const char *configString = SV_GetConfigString(start + i);
00092         if (!strcmp(configString, name))
00093             return i;
00094     }
00095 
00096     if (!create)
00097         return 0;
00098 
00099     if (i == max)
00100         Com_Error(ERR_DROP, "*Index: overflow '%s' start: %i, max: %i", name, start, max);
00101 
00102     SV_SetConfigString(start + i, name);
00103 
00104     if (Com_ServerState() != ss_loading) {  /* send the update to everyone */
00105         struct dbuffer *msg = new_dbuffer();
00106         NET_WriteByte(msg, svc_configstring);
00107         NET_WriteShort(msg, start + i);
00108         NET_WriteString(msg, name);
00109         SV_Multicast(~0, msg);
00110     }
00111 
00112     return i;
00113 }
00114 
00115 static int SV_ModelIndex (const char *name)
00116 {
00117     return SV_FindIndex(name, CS_MODELS, MAX_MODELS, qtrue);
00118 }
00119 
00124 static void SV_SetModel (edict_t * ent, const char *name)
00125 {
00126     if (!name)
00127         Com_Error(ERR_DROP, "SV_SetModel: NULL");
00128 
00129     ent->modelindex = SV_ModelIndex(name);
00130 
00131     /* if it is an inline model, get the size information for it */
00132     if (name[0] == '*') {
00133         const cBspModel_t *mod = CM_InlineModel(&sv.mapTiles, name);
00134         /* Copy model mins and maxs to entity */
00135         VectorCopy(mod->mins, ent->mins);
00136         VectorCopy(mod->maxs, ent->maxs);
00137         /* This is to help the entity collision code out */
00138         /* Copy entity origin and angles to model*/
00139         CM_SetInlineModelOrientation(&sv.mapTiles, name, ent->origin, ent->angles);
00140     }
00141 }
00142 
00146 static void SV_Configstring (int index, const char *fmt, ...)
00147 {
00148     char val[MAX_TOKEN_CHARS * MAX_TILESTRINGS];
00149     va_list argptr;
00150 
00151     if (index < 0 || index >= MAX_CONFIGSTRINGS)
00152         Com_Error(ERR_DROP, "configstring: bad index %i", index);
00153 
00154     va_start(argptr, fmt);
00155     Q_vsnprintf(val, sizeof(val), fmt, argptr);
00156     va_end(argptr);
00157 
00158     SV_SetConfigString(index, val);
00159 
00160     if (Com_ServerState() != ss_loading) { /* send the update to everyone */
00161         struct dbuffer *msg = new_dbuffer();
00162         NET_WriteByte(msg, svc_configstring);
00163         NET_WriteShort(msg, index);
00164         NET_WriteString(msg, val);
00165 
00166         /* send to all clients */
00167         SV_Multicast(~0, msg);
00168     }
00169 }
00170 
00171 static void SV_WriteChar (char c)
00172 {
00173     NET_WriteChar(sv.pendingEvent.buf, c);
00174 }
00175 
00176 static void SV_WriteByte (byte c)
00177 {
00178     NET_WriteByte(sv.pendingEvent.buf, c);
00179 }
00180 
00185 static byte* SV_WriteDummyByte (byte c)
00186 {
00187     pending_event_t *p = &sv.pendingEvent;
00188     byte *pos = (byte*) p->buf->end;
00189     NET_WriteByte(p->buf, c);
00190     assert(pos != NULL);
00191     return pos;
00192 }
00193 
00194 static void SV_WriteShort (int c)
00195 {
00196     NET_WriteShort(sv.pendingEvent.buf, c);
00197 }
00198 
00199 static void SV_WriteLong (int c)
00200 {
00201     NET_WriteLong(sv.pendingEvent.buf, c);
00202 }
00203 
00204 static void SV_WriteString (const char *s)
00205 {
00206     NET_WriteString(sv.pendingEvent.buf, s);
00207 }
00208 
00209 static void SV_WritePos (const vec3_t pos)
00210 {
00211     NET_WritePos(sv.pendingEvent.buf, pos);
00212 }
00213 
00214 static void SV_WriteGPos (const pos3_t pos)
00215 {
00216     NET_WriteGPos(sv.pendingEvent.buf, pos);
00217 }
00218 
00219 static void SV_WriteDir (const vec3_t dir)
00220 {
00221     NET_WriteDir(sv.pendingEvent.buf, dir);
00222 }
00223 
00224 static void SV_WriteAngle (float f)
00225 {
00226     NET_WriteAngle(sv.pendingEvent.buf, f);
00227 }
00228 
00229 static void SV_WriteFormat (const char *format, ...)
00230 {
00231     va_list ap;
00232     va_start(ap, format);
00233     NET_vWriteFormat(sv.pendingEvent.buf, format, ap);
00234     va_end(ap);
00235 }
00236 
00237 static int SV_ReadChar (void)
00238 {
00239     return NET_ReadChar(sv.messageBuffer);
00240 }
00241 
00242 static int SV_ReadByte (void)
00243 {
00244     return NET_ReadByte(sv.messageBuffer);
00245 }
00246 
00247 static int SV_ReadShort (void)
00248 {
00249     return NET_ReadShort(sv.messageBuffer);
00250 }
00251 
00252 static int SV_ReadLong (void)
00253 {
00254     return NET_ReadLong(sv.messageBuffer);
00255 }
00256 
00257 static int SV_ReadString (char *str, size_t length)
00258 {
00259     return NET_ReadString(sv.messageBuffer, str, length);
00260 }
00261 
00262 static void SV_ReadPos (vec3_t pos)
00263 {
00264     NET_ReadPos(sv.messageBuffer, pos);
00265 }
00266 
00267 static void SV_ReadGPos (pos3_t pos)
00268 {
00269     NET_ReadGPos(sv.messageBuffer, pos);
00270 }
00271 
00272 static void SV_ReadDir (vec3_t vector)
00273 {
00274     NET_ReadDir(sv.messageBuffer, vector);
00275 }
00276 
00277 static float SV_ReadAngle (void)
00278 {
00279     return NET_ReadAngle(sv.messageBuffer);
00280 }
00281 
00282 static void SV_ReadData (void *buffer, int size)
00283 {
00284     NET_ReadData(sv.messageBuffer, buffer, size);
00285 }
00286 
00290 static void SV_ReadFormat (const char *format, ...)
00291 {
00292     va_list ap;
00293 
00294     assert(format);
00295     if (!*format) /* PA_NULL */
00296         return;
00297 
00298     va_start(ap, format);
00299     NET_vReadFormat(sv.messageBuffer, format, ap);
00300     va_end(ap);
00301 }
00302 
00306 static void SV_AbortEvents (void)
00307 {
00308     pending_event_t *p = &sv.pendingEvent;
00309 
00310     if (!p->pending)
00311         return;
00312 
00313     p->pending = qfalse;
00314     free_dbuffer(p->buf);
00315     p->buf = NULL;
00316 }
00317 
00321 static void SV_EndEvents (void)
00322 {
00323     pending_event_t *p = &sv.pendingEvent;
00324 
00325     if (!p->pending)
00326         return;
00327 
00328     NET_WriteByte(p->buf, EV_NULL);
00329     SV_Multicast(p->playerMask, p->buf);
00330     p->pending = qfalse;
00331     /* freed in SV_Multicast */
00332     p->buf = NULL;
00333 }
00334 
00339 static void SV_AddEvent (unsigned int mask, int eType)
00340 {
00341     pending_event_t *p = &sv.pendingEvent;
00342     Com_DPrintf(DEBUG_EVENTSYS, "Event type: %i (mask %i)\n", eType, mask);
00343 
00344     /* finish the last event */
00345     if (p->pending)
00346         SV_EndEvents();
00347 
00348     /* start the new event */
00349     p->pending = qtrue;
00350     p->playerMask = mask;
00351     p->type = eType;
00352     p->buf = new_dbuffer();
00353 
00354     /* write header */
00355     NET_WriteByte(p->buf, svc_event);
00356     NET_WriteByte(p->buf, eType);
00357 }
00358 
00362 static int SV_GetEvent (void)
00363 {
00364     const pending_event_t *p = &sv.pendingEvent;
00365     if (!p->pending)
00366         return -1;
00367 
00368     return p->type;
00369 }
00370 
00374 static void *SV_TagAlloc (int size, int tagNum, const char *file, int line)
00375 {
00376     if (tagNum < 0)
00377         tagNum *= -1;
00378 
00379     return _Mem_Alloc(size, qtrue, sv.gameSysPool, tagNum, file, line);
00380 }
00381 
00382 static void SV_MemFree (void *ptr, const char *file, int line)
00383 {
00384     _Mem_Free(ptr, file, line);
00385 }
00386 
00390 static void SV_FreeTags (int tagNum, const char *file, int line)
00391 {
00392     if (tagNum < 0)
00393         tagNum *= -1;
00394 
00395     _Mem_FreeTag(sv.gameSysPool, tagNum, file, line);
00396 }
00397 
00398 static qboolean SV_TestLine (const vec3_t start, const vec3_t stop, const int levelmask)
00399 {
00400     return TR_TestLine(&sv.mapTiles, start, stop, levelmask);
00401 }
00402 
00403 static qboolean SV_TestLineWithEnt (const vec3_t start, const vec3_t stop, const int levelmask, const char **entlist)
00404 {
00405     /* do the line test */
00406     const qboolean hit = CM_EntTestLine(&sv.mapTiles, start, stop, levelmask, entlist);
00407     return hit;
00408 }
00409 
00410 static void SV_RecalcRouting (routing_t *map, const char *name, const char **list)
00411 {
00412     Grid_RecalcRouting(&sv.mapTiles, map, name, list);
00413 }
00414 
00415 static void SV_SetInlineModelOrientation (const char *name, const vec3_t origin, const vec3_t angles)
00416 {
00417     CM_SetInlineModelOrientation(&sv.mapTiles, name, origin, angles);
00418 }
00419 
00420 static void SV_UnloadGame (void)
00421 {
00422 #ifndef GAME_HARD_LINKED
00423     if (svs.gameLibrary) {
00424         Com_Printf("Unload the game library\n");
00425         SDL_UnloadObject(svs.gameLibrary);
00426     }
00427 #endif
00428     svs.gameLibrary = NULL;
00429 }
00430 
00431 #ifndef HARD_LINKED_GAME
00432 static qboolean SV_LoadGame (const char *path)
00433 {
00434     char name[MAX_OSPATH];
00435 
00436     Com_sprintf(name, sizeof(name), "%s/game_"CPUSTRING".%s", path, SHARED_EXT);
00437     svs.gameLibrary = SDL_LoadObject(name);
00438     if (!svs.gameLibrary) {
00439         Com_sprintf(name, sizeof(name), "%s/game.%s", path, SHARED_EXT);
00440         svs.gameLibrary = SDL_LoadObject(name);
00441     }
00442 
00443     if (svs.gameLibrary) {
00444         Com_Printf("found at '%s'\n", path);
00445         return qtrue;
00446     } else {
00447         Com_Printf("not found at '%s'\n", path);
00448         Com_DPrintf(DEBUG_SYSTEM, "%s\n", SDL_GetError());
00449         return qfalse;
00450     }
00451 }
00452 #endif
00453 
00457 static game_export_t *SV_GetGameAPI (game_import_t *parms)
00458 {
00459 #ifndef HARD_LINKED_GAME
00460     void *(*GetGameAPI) (void *);
00461 
00462     const char *path;
00463 #endif
00464 
00465     if (svs.gameLibrary)
00466         Com_Error(ERR_FATAL, "SV_GetGameAPI without SV_UnloadGame");
00467 
00468 #ifndef HARD_LINKED_GAME
00469     Com_Printf("------- Loading game.%s -------\n", SHARED_EXT);
00470 
00471 #ifdef PKGLIBDIR
00472     SV_LoadGame(PKGLIBDIR);
00473 #endif
00474 
00475     /* now run through the search paths */
00476     path = NULL;
00477     while (!svs.gameLibrary) {
00478         path = FS_NextPath(path);
00479         if (!path)
00480             /* couldn't find one anywhere */
00481             return NULL;
00482         else if (SV_LoadGame(path))
00483             break;
00484     }
00485 
00486     GetGameAPI = (void *)SDL_LoadFunction(svs.gameLibrary, "GetGameAPI");
00487     if (!GetGameAPI) {
00488         SV_UnloadGame();
00489         return NULL;
00490     }
00491 #endif
00492 
00493     return GetGameAPI(parms);
00494 }
00495 
00501 void SV_ShutdownGameProgs (void)
00502 {
00503     uint32_t size;
00504 
00505     if (!svs.ge)
00506         return;
00507 
00508     if (svs.gameThread) {
00509         Com_Printf("Shutdown the game thread\n");
00510         SDL_KillThread(svs.gameThread);
00511         svs.gameThread = NULL;
00512     }
00513 
00514     svs.ge->Shutdown();
00515 
00516     size = Mem_PoolSize(sv.gameSysPool);
00517     if (size > 0) {
00518         Com_Printf("WARNING: Game memory leak (%u bytes)\n", size);
00519         Cmd_ExecuteString(va("mem_stats %s", sv.gameSysPool->name));
00520     }
00521 
00522     Mem_DeletePool(sv.gameSysPool);
00523     sv.gameSysPool = NULL;
00524 
00525     SV_UnloadGame();
00526 
00527     svs.ge = NULL;
00528 }
00529 
00535 int SV_RunGameFrameThread (void *data)
00536 {
00537     do {
00538         SV_RunGameFrame();
00539     } while (!sv.endgame);
00540 
00541     return 0;
00542 }
00543 
00550 void SV_RunGameFrame (void)
00551 {
00552     SDL_mutexP(svs.serverMutex);
00553 
00554     sv.endgame = svs.ge->RunFrame();
00555 
00556     SDL_mutexV(svs.serverMutex);
00557 }
00558 
00563 void SV_InitGameProgs (void)
00564 {
00565     game_import_t import;
00566 
00567     /* unload anything we have now */
00568     /*SV_ShutdownGameProgs();*/
00569 
00570     /* load a new game dll */
00571     import.BroadcastPrintf = SV_BroadcastPrintf;
00572     import.DPrintf = SV_dprintf;
00573     import.PlayerPrintf = SV_PlayerPrintf;
00574     import.Error = SV_error;
00575 
00576     import.Trace = SV_Trace;
00577     import.LinkEdict = SV_LinkEdict;
00578     import.UnlinkEdict = SV_UnlinkEdict;
00579     import.BoxEdicts = SV_AreaEdicts;
00580     import.TouchEdicts = SV_TouchEdicts;
00581 
00582     import.TestLine = SV_TestLine;
00583     import.TestLineWithEnt = SV_TestLineWithEnt;
00584     import.GrenadeTarget = Com_GrenadeTarget;
00585 
00586     import.MoveCalc = Grid_MoveCalc;
00587     import.MoveStore = Grid_MoveStore;
00588     import.MoveLength = Grid_MoveLength;
00589     import.MoveNext = Grid_MoveNext;
00590     import.GridFloor = Grid_Floor;
00591     import.GetTUsForDirection = Grid_GetTUsForDirection;
00592     import.GridFall = Grid_Fall;
00593     import.GridPosToVec = Grid_PosToVec;
00594     import.GridRecalcRouting = SV_RecalcRouting;
00595 
00596     import.ModelIndex = SV_ModelIndex;
00597 
00598     import.SetInlineModelOrientation = SV_SetInlineModelOrientation;
00599 
00600     import.SetModel = SV_SetModel;
00601 
00602     import.ConfigString = SV_Configstring;
00603     import.PositionedSound = SV_StartSound;
00604 
00605     import.PointContents = SV_PointContents;
00606     import.GetFootstepSound = SV_GetFootstepSound;
00607     import.GetBounceFraction = SV_GetBounceFraction;
00608     import.LoadModelMinsMaxs = SV_LoadModelMinsMaxs;
00609 
00610     import.FS_Gamedir = FS_Gamedir;
00611     import.FS_LoadFile = FS_LoadFile;
00612     import.FS_FreeFile = FS_FreeFile;
00613 
00614     import.WriteChar = SV_WriteChar;
00615     import.WriteByte = SV_WriteByte;
00616     import.WriteDummyByte = SV_WriteDummyByte;
00617     import.WriteShort = SV_WriteShort;
00618     import.WriteLong = SV_WriteLong;
00619     import.WriteString = SV_WriteString;
00620     import.WritePos = SV_WritePos;
00621     import.WriteGPos = SV_WriteGPos;
00622     import.WriteDir = SV_WriteDir;
00623     import.WriteAngle = SV_WriteAngle;
00624     import.WriteFormat = SV_WriteFormat;
00625 
00626     import.AbortEvents = SV_AbortEvents;
00627     import.EndEvents = SV_EndEvents;
00628     import.AddEvent = SV_AddEvent;
00629     import.GetEvent = SV_GetEvent;
00630 
00631     import.ReadChar = SV_ReadChar;
00632     import.ReadByte = SV_ReadByte;
00633     import.ReadShort = SV_ReadShort;
00634     import.ReadLong = SV_ReadLong;
00635     import.ReadString = SV_ReadString;
00636     import.ReadPos = SV_ReadPos;
00637     import.ReadGPos = SV_ReadGPos;
00638     import.ReadDir = SV_ReadDir;
00639     import.ReadAngle = SV_ReadAngle;
00640     import.ReadData = SV_ReadData;
00641     import.ReadFormat = SV_ReadFormat;
00642 
00643     import.GetConstInt = Com_GetConstInt;
00644     import.GetConstIntFromNamespace = Com_GetConstIntFromNamespace;
00645     import.GetConstVariable = Com_GetConstVariable;
00646     import.RegisterConstInt = Com_RegisterConstInt;
00647     import.UnregisterConstVariable = Com_UnregisterConstVariable;
00648 
00649     import.GetCharacterValues = Com_GetCharacterValues;
00650 
00651     import.TagMalloc = SV_TagAlloc;
00652     import.TagFree = SV_MemFree;
00653     import.FreeTags = SV_FreeTags;
00654 
00655     import.Cvar_Get = Cvar_Get;
00656     import.Cvar_Set = Cvar_Set;
00657     import.Cvar_String = Cvar_GetString;
00658 
00659     import.Cmd_Argc = Cmd_Argc;
00660     import.Cmd_Argv = Cmd_Argv;
00661     import.Cmd_Args = Cmd_Args;
00662     import.AddCommandString = Cbuf_AddText;
00663 
00664     import.seed = Sys_Milliseconds();
00665     import.csi = &csi;
00666 
00667     /* import the server routing table */
00668     import.routingMap = sv.mapData.map;
00669 
00670     svs.ge = SV_GetGameAPI(&import);
00671 
00672     if (!svs.ge)
00673         Com_Error(ERR_DROP, "failed to load game library");
00674     if (svs.ge->apiversion != GAME_API_VERSION)
00675         Com_Error(ERR_DROP, "game is version %i, not %i", svs.ge->apiversion, GAME_API_VERSION);
00676 
00677     sv.gameSysPool = Mem_CreatePool("Server: Game system");
00678 
00679     svs.ge->Init();
00680 
00681     if (sv_threads->integer)
00682         svs.gameThread = SDL_CreateThread(SV_RunGameFrameThread, NULL);
00683 }

Generated by  doxygen 1.6.2