sv_main.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_main.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 "../ports/system.h"
00031 
00033 static cvar_t *rcon_password;
00034 static cvar_t *sv_http_downloadserver;
00035 static cvar_t *sv_enablemorale;
00036 static cvar_t *sv_maxsoldiersperteam;
00037 static cvar_t *sv_maxsoldiersperplayer;
00038 static cvar_t *sv_hostname;
00040 static cvar_t *sv_reconnect_limit;
00041 
00042 cvar_t *sv_maxclients = NULL;
00043 cvar_t *sv_dumpmapassembly;
00044 cvar_t *sv_threads;
00046 cvar_t *sv_public;
00047 cvar_t *sv_mapname;
00048 
00049 memPool_t *sv_genericPool;
00050 
00051 char *SV_GetConfigString (int index)
00052 {
00053     if (index < 0 || index >= MAX_CONFIGSTRINGS)
00054         Com_Error(ERR_FATAL, "Invalid config string index given");
00055     return sv.configstrings[index];
00056 }
00057 
00058 int SV_GetConfigStringInteger (int index)
00059 {
00060     if (index < 0 || index >= MAX_CONFIGSTRINGS)
00061         Com_Error(ERR_FATAL, "Invalid config string index given");
00062     return atoi(sv.configstrings[index]);
00063 }
00064 
00065 char *SV_SetConfigString (int index, ...)
00066 {
00067     va_list ap;
00068     const char *value;
00069 
00070     if (index < 0 || index >= MAX_CONFIGSTRINGS)
00071         Com_Error(ERR_FATAL, "Invalid config string index given");
00072 
00073     va_start(ap, index);
00074 
00075     switch (index) {
00076     case CS_LIGHTMAP:
00077     case CS_MAPCHECKSUM:
00078     case CS_UFOCHECKSUM:
00079     case CS_OBJECTAMOUNT:
00080         value = va("%i", va_arg(ap, int));
00081         break;
00082     default:
00083         value = va_arg(ap, char *);
00084         break;
00085     }
00086 
00087     /* change the string in sv
00088      * there may be overflows in i==CS_TILES - but thats ok
00089      * see definition of configstrings and MAX_TILESTRINGS */
00090     if (index == CS_TILES || index == CS_POSITIONS)
00091         Q_strncpyz(sv.configstrings[index], value, MAX_TOKEN_CHARS * MAX_TILESTRINGS);
00092     else
00093         Q_strncpyz(sv.configstrings[index], value, sizeof(sv.configstrings[index]));
00094 
00095     va_end(ap);
00096 
00097     return sv.configstrings[index];
00098 }
00099 
00104 client_t* SV_GetNextClient (client_t *lastClient)
00105 {
00106     client_t *endOfClients = &svs.clients[sv_maxclients->integer];
00107     client_t* client;
00108 
00109     if (!sv_maxclients->integer)
00110         return NULL;
00111 
00112     if (!lastClient)
00113         return svs.clients;
00114     assert(lastClient >= svs.clients);
00115     assert(lastClient < endOfClients);
00116 
00117     client = lastClient;
00118 
00119     client++;
00120     if (client >= endOfClients)
00121         return NULL;
00122     else
00123         return client;
00124 }
00125 
00126 client_t *SV_GetClient (int index)
00127 {
00128     return &svs.clients[index];
00129 }
00130 
00136 void SV_DropClient (client_t * drop, const char *message)
00137 {
00138     /* add the disconnect */
00139     struct dbuffer *msg = new_dbuffer();
00140     NET_WriteByte(msg, svc_disconnect);
00141     NET_WriteString(msg, message);
00142     NET_WriteMsg(drop->stream, msg);
00143     SV_BroadcastPrintf(PRINT_CHAT, "%s was dropped from the server - reason: %s\n", drop->name, message);
00144 
00145     if (drop->state == cs_spawned || drop->state == cs_spawning) {
00146         /* call the prog function for removing a client */
00147         /* this will remove the body, among other things */
00148         SDL_mutexP(svs.serverMutex);
00149         svs.ge->ClientDisconnect(drop->player);
00150         SDL_mutexV(svs.serverMutex);
00151     }
00152 
00153     NET_StreamFinished(drop->stream);
00154     drop->stream = NULL;
00155 
00156     drop->player->inuse = qfalse;
00157     SV_SetClientState(drop, cs_free);
00158     drop->name[0] = 0;
00159 
00160     if (svs.abandon) {
00161         int count = 0;
00162         client_t *cl = NULL;
00163         while ((cl = SV_GetNextClient(cl)) != NULL)
00164             if (cl->state >= cs_connected)
00165                 count++;
00166         if (count == 0)
00167             svs.killserver = qtrue;
00168     }
00169 }
00170 
00171 /*
00172 ==============================================================================
00173 CONNECTIONLESS COMMANDS
00174 ==============================================================================
00175 */
00176 
00181 static void SVC_TeamInfo (struct net_stream *s)
00182 {
00183     client_t *cl;
00184     struct dbuffer *msg = new_dbuffer();
00185     char infoGlobal[MAX_INFO_STRING];
00186 
00187     NET_WriteByte(msg, clc_oob);
00188     NET_WriteRawString(msg, "teaminfo\n");
00189 
00190     Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_teamplay", Cvar_GetString("sv_teamplay"));
00191     Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_maxteams", Cvar_GetString("sv_maxteams"));
00192     Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_maxplayersperteam", Cvar_GetString("sv_maxplayersperteam"));
00193     NET_WriteString(msg, infoGlobal);
00194 
00195     cl = NULL;
00196     while ((cl = SV_GetNextClient(cl)) != NULL) {
00197         if (cl->state >= cs_connected) {
00198             char infoPlayer[MAX_INFO_STRING];
00199             /* show players that already have a team with their teamnum */
00200             int teamId = svs.ge->ClientGetTeamNum(cl->player);
00201             if (!teamId)
00202                 teamId = TEAM_NO_ACTIVE;
00203             Info_SetValueForKeyAsInteger(infoPlayer, sizeof(infoPlayer), "cl_team", teamId);
00204             Info_SetValueForKeyAsInteger(infoPlayer, sizeof(infoPlayer), "cl_ready", svs.ge->ClientIsReady(cl->player));
00205             Info_SetValueForKey(infoPlayer, sizeof(infoPlayer), "cl_name", cl->name);
00206             NET_WriteString(msg, infoPlayer);
00207         }
00208     }
00209 
00210     NET_WriteByte(msg, 0);
00211 
00212     NET_WriteMsg(s, msg);
00213 }
00214 
00219 static void SVC_Status (struct net_stream *s)
00220 {
00221     client_t *cl;
00222     char player[1024];
00223     struct dbuffer *msg = new_dbuffer();
00224     NET_WriteByte(msg, clc_oob);
00225     NET_WriteRawString(msg, "print\n");
00226 
00227     NET_WriteRawString(msg, Cvar_Serverinfo());
00228     NET_WriteRawString(msg, "\n");
00229 
00230     cl = NULL;
00231     while ((cl = SV_GetNextClient(cl)) != NULL) {
00232         if (cl->state > cs_free) {
00233             Com_sprintf(player, sizeof(player), "%i \"%s\"\n", svs.ge->ClientGetTeamNum(cl->player), cl->name);
00234             NET_WriteRawString(msg, player);
00235         }
00236     }
00237 
00238     NET_WriteMsg(s, msg);
00239 }
00240 
00249 static void SVC_Info (struct net_stream *s)
00250 {
00251     int version;
00252 
00253     if (sv_maxclients->integer == 1) {
00254         Com_DPrintf(DEBUG_SERVER, "Ignore info string in singleplayer mode\n");
00255         return; /* ignore in single player */
00256     }
00257 
00258     version = atoi(Cmd_Argv(1));
00259 
00260     if (version != PROTOCOL_VERSION) {
00261         char string[MAX_VAR];
00262         Com_sprintf(string, sizeof(string), "%s: wrong version (client: %i, host: %i)\n", sv_hostname->string, version, PROTOCOL_VERSION);
00263         NET_OOB_Printf(s, "print\n%s", string);
00264     } else {
00265         client_t *cl;
00266         char infostring[MAX_INFO_STRING];
00267         int count = 0;
00268 
00269         cl = NULL;
00270         while ((cl = SV_GetNextClient(cl)) != NULL)
00271             if (cl->state >= cs_spawning)
00272                 count++;
00273 
00274         infostring[0] = '\0';
00275 
00276         Info_SetValueForKey(infostring, sizeof(infostring), "sv_protocol", DOUBLEQUOTE(PROTOCOL_VERSION));
00277         Info_SetValueForKey(infostring, sizeof(infostring), "sv_hostname", sv_hostname->string);
00278         Info_SetValueForKey(infostring, sizeof(infostring), "sv_dedicated", sv_dedicated->string);
00279         Info_SetValueForKey(infostring, sizeof(infostring), "sv_gametype", sv_gametype->string);
00280         Info_SetValueForKey(infostring, sizeof(infostring), "sv_mapname", sv.name);
00281         Info_SetValueForKeyAsInteger(infostring, sizeof(infostring), "clients", count);
00282         Info_SetValueForKey(infostring, sizeof(infostring), "sv_maxclients", sv_maxclients->string);
00283         Info_SetValueForKey(infostring, sizeof(infostring), "sv_version", UFO_VERSION);
00284         NET_OOB_Printf(s, "info\n%s", infostring);
00285     }
00286 }
00287 
00288 
00293 static void SVC_DirectConnect (struct net_stream *stream)
00294 {
00295     char userinfo[MAX_INFO_STRING];
00296     client_t *cl;
00297     player_t *player;
00298     int playernum;
00299     int version;
00300     qboolean connected;
00301     char buf[256];
00302     const char *peername = NET_StreamPeerToName(stream, buf, sizeof(buf), qfalse);
00303 
00304     Com_DPrintf(DEBUG_SERVER, "SVC_DirectConnect()\n");
00305 
00306     if (sv.started) {
00307         Com_Printf("rejected connect because match is already running\n");
00308         NET_OOB_Printf(stream, "print\nGame has started already.\n");
00309         return;
00310     }
00311 
00312     version = atoi(Cmd_Argv(1));
00313     if (version != PROTOCOL_VERSION) {
00314         Com_Printf("rejected connect from version %i - %s\n", version, peername);
00315         NET_OOB_Printf(stream, "print\nServer is version %s.\n", UFO_VERSION);
00316         return;
00317     }
00318 
00319     Q_strncpyz(userinfo, Cmd_Argv(2), sizeof(userinfo));
00320 
00321     if (userinfo[0] == '\0') {  /* catch empty userinfo */
00322         Com_Printf("Empty userinfo from %s\n", peername);
00323         NET_OOB_Printf(stream, "print\nConnection refused.\n");
00324         return;
00325     }
00326 
00327     if (strchr(userinfo, '\xFF')) {  /* catch end of message in string exploit */
00328         Com_Printf("Illegal userinfo contained xFF from %s\n", peername);
00329         NET_OOB_Printf(stream, "print\nConnection refused.\n");
00330         return;
00331     }
00332 
00333     if (strlen(Info_ValueForKey(userinfo, "ip"))) {  /* catch spoofed ips  */
00334         Com_Printf("Illegal userinfo contained ip from %s\n", peername);
00335         NET_OOB_Printf(stream, "print\nConnection refused.\n");
00336         return;
00337     }
00338 
00339     /* force the IP key/value pair so the game can filter based on ip */
00340     Info_SetValueForKey(userinfo, sizeof(userinfo), "ip", peername);
00341 
00342     /* find a client slot */
00343     cl = NULL;
00344     while ((cl = SV_GetNextClient(cl)) != NULL)
00345         if (cl->state == cs_free)
00346             break;
00347     if (cl == NULL) {
00348         NET_OOB_Printf(stream, "print\nServer is full.\n");
00349         Com_Printf("Rejected a connection - server is full.\n");
00350         return;
00351     }
00352 
00353     /* build a new connection - accept the new client
00354      * this is the only place a client_t is ever initialized */
00355     memset(cl, 0, sizeof(*cl));
00356     playernum = cl - SV_GetClient(0);
00357     player = PLAYER_NUM(playernum);
00358     cl->player = player;
00359     cl->player->num = playernum;
00360 
00361     SDL_mutexP(svs.serverMutex);
00362     connected = svs.ge->ClientConnect(player, userinfo, sizeof(userinfo));
00363     SDL_mutexV(svs.serverMutex);
00364 
00365     /* get the game a chance to reject this connection or modify the userinfo */
00366     if (!connected) {
00367         const char *rejmsg = Info_ValueForKey(userinfo, "rejmsg");
00368         if (rejmsg[0] != '\0') {
00369             NET_OOB_Printf(stream, "print\n%s\nConnection refused.\n", rejmsg);
00370             Com_Printf("Game rejected a connection from %s. Reason: %s\n", peername, rejmsg);
00371         } else {
00372             NET_OOB_Printf(stream, "print\nConnection refused.\n");
00373             Com_Printf("Game rejected a connection from %s.\n", peername);
00374         }
00375         return;
00376     }
00377 
00378     /* new player */
00379     cl->player->inuse = qtrue;
00380 
00381     /* parse some info from the info strings */
00382     strncpy(cl->userinfo, userinfo, sizeof(cl->userinfo) - 1);
00383     SV_UserinfoChanged(cl);
00384 
00385     /* send the connect packet to the client */
00386     if (sv_http_downloadserver->string[0])
00387         NET_OOB_Printf(stream, "client_connect dlserver=%s", sv_http_downloadserver->string);
00388     else
00389         NET_OOB_Printf(stream, "client_connect");
00390 
00391     SV_SetClientState(cl, cs_connected);
00392 
00393     cl->lastconnect = svs.realtime;
00394     Q_strncpyz(cl->peername, peername, sizeof(cl->peername));
00395     cl->stream = stream;
00396     NET_StreamSetData(stream, cl);
00397 }
00398 
00403 static inline qboolean Rcon_Validate (const char *password)
00404 {
00405     /* no rcon access */
00406     if (!strlen(rcon_password->string))
00407         return qfalse;
00408 
00409     /* password not valid */
00410     if (strcmp(password, rcon_password->string))
00411         return qfalse;
00412 
00413     return qtrue;
00414 }
00415 
00416 #define SV_OUTPUTBUF_LENGTH 1024
00417 
00418 static char sv_outputbuf[SV_OUTPUTBUF_LENGTH];
00419 
00423 static void SVC_RemoteCommand (struct net_stream *stream)
00424 {
00425     char buf[256];
00426     const char *peername = NET_StreamPeerToName(stream, buf, sizeof(buf), qfalse);
00427     qboolean valid = Rcon_Validate(Cmd_Argv(1));
00428 
00429     if (!valid)
00430         Com_Printf("Bad rcon from %s:\n%s\n", peername, Cmd_Argv(1));
00431     else
00432         Com_Printf("Rcon from %s:\n%s\n", peername, Cmd_Argv(1));
00433 
00434     Com_BeginRedirect(stream, sv_outputbuf, SV_OUTPUTBUF_LENGTH);
00435 
00436     if (!valid)
00437         /* inform the client */
00438         Com_Printf("Bad rcon_password.\n");
00439     else {
00440         char remaining[1024] = "";
00441         int i;
00442 
00443         /* execute the rcon commands */
00444         for (i = 2; i < Cmd_Argc(); i++) {
00445             Q_strcat(remaining, Cmd_Argv(i), sizeof(remaining));
00446             Q_strcat(remaining, " ", sizeof(remaining));
00447         }
00448 
00449         /* execute the string */
00450         Cmd_ExecuteString(remaining);
00451     }
00452 
00453     Com_EndRedirect();
00454 }
00455 
00462 static void SV_ConnectionlessPacket (struct net_stream *stream, struct dbuffer *msg)
00463 {
00464     const char *c;
00465     char s[512];
00466     char buf[256];
00467 
00468     NET_ReadStringLine(msg, s, sizeof(s));
00469 
00470     Cmd_TokenizeString(s, qfalse);
00471 
00472     c = Cmd_Argv(0);
00473     Com_DPrintf(DEBUG_SERVER, "Packet : %s\n", c);
00474 
00475     if (!strcmp(c, "teaminfo"))
00476         SVC_TeamInfo(stream);
00477     else if (!strcmp(c, "info"))
00478         SVC_Info(stream);
00479     else if (!strcmp(c, "status"))
00480         SVC_Status(stream);
00481     else if (!strcmp(c, "connect"))
00482         SVC_DirectConnect(stream);
00483     else if (!strcmp(c, "rcon"))
00484         SVC_RemoteCommand(stream);
00485     else
00486         Com_Printf("Bad connectionless packet from %s:\n%s\n", NET_StreamPeerToName(stream, buf, sizeof(buf), qtrue), s);
00487 }
00488 
00494 void SV_ReadPacket (struct net_stream *s)
00495 {
00496     client_t *cl = NET_StreamGetData(s);
00497     struct dbuffer *msg;
00498 
00499     while ((msg = NET_ReadMsg(s))) {
00500         const int cmd = NET_ReadByte(msg);
00501 
00502         if (cmd == clc_oob)
00503             SV_ConnectionlessPacket(s, msg);
00504         else if (cl)
00505             SV_ExecuteClientMessage(cl, cmd, msg);
00506         else
00507             NET_StreamFree(s);
00508 
00509         free_dbuffer(msg);
00510     }
00511 }
00512 
00513 #define HEARTBEAT_SECONDS   300
00514 
00515 static SDL_Thread *masterServerHeartBeatThread;
00516 
00521 static int Master_HeartbeatThread (void * data)
00522 {
00523     char *responseBuf;
00524 
00525     /* send to master */
00526     Com_Printf("sending heartbeat\n");
00527     responseBuf = HTTP_GetURL(va("%s/ufo/masterserver.php?heartbeat&port=%s", masterserver_url->string, port->string));
00528     if (responseBuf) {
00529         Com_DPrintf(DEBUG_SERVER, "response: %s\n", responseBuf);
00530         Mem_Free(responseBuf);
00531     }
00532 
00533     masterServerHeartBeatThread = NULL;
00534     return 0;
00535 }
00536 
00540 static void Master_Heartbeat (void)
00541 {
00542     if (!sv_dedicated || !sv_dedicated->integer)
00543         return;     /* only dedicated servers send heartbeats */
00544 
00545     if (!sv_public || !sv_public->integer)
00546         return;     /* a private dedicated game */
00547 
00548     /* check for time wraparound */
00549     if (svs.lastHeartbeat > svs.realtime)
00550         svs.lastHeartbeat = svs.realtime;
00551 
00552     if (svs.realtime - svs.lastHeartbeat < HEARTBEAT_SECONDS * 1000)
00553         return;                 /* not time to send yet */
00554 
00555     svs.lastHeartbeat = svs.realtime;
00556 
00557     if (masterServerHeartBeatThread != NULL)
00558         SDL_WaitThread(masterServerHeartBeatThread, NULL);
00559 
00560     masterServerHeartBeatThread = SDL_CreateThread(Master_HeartbeatThread, NULL);
00561 }
00562 
00568 static void SV_CheckGameStart (void)
00569 {
00570     client_t *cl;
00571 
00572     /* already started? */
00573     if (sv.started)
00574         return;
00575 
00576     if (sv_maxclients->integer > 1) {
00577         cl = NULL;
00578         while ((cl = SV_GetNextClient(cl)) != NULL) {
00579             /* all players must be connected and all of them must have set
00580              * the ready flag */
00581             if (cl->state != cs_began || !cl->player->isReady)
00582                 return;
00583         }
00584     } else if (SV_GetClient(0)->state != cs_began) {
00585         /* in single player mode we must have received the 'begin' */
00586         return;
00587     }
00588 
00589     sv.started = qtrue;
00590 
00591     cl = NULL;
00592     while ((cl = SV_GetNextClient(cl)) != NULL)
00593         if (cl->state != cs_free)
00594             SV_ClientCommand(cl, "spawnsoldiers\n");
00595 }
00596 
00600 void SV_Frame (int now, void *data)
00601 {
00602     /* change the gametype even if no server is running (e.g. the first time) */
00603     if (sv_dedicated->integer && sv_gametype->modified) {
00604         Com_SetGameType();
00605         sv_gametype->modified = qfalse;
00606     }
00607 
00608     if (sv_dedicated->integer) {
00609         const char *s;
00610         do {
00611             s = Sys_ConsoleInput();
00612             if (s)
00613                 Cbuf_AddText(va("%s\n", s));
00614         } while (s);
00615     }
00616 
00617     /* if server is not active, do nothing */
00618     if (!svs.initialized)
00619         return;
00620 
00621     svs.realtime = now;
00622 
00623     /* keep the random time dependent */
00624     rand();
00625 
00626     SV_CheckGameStart();
00627 
00628     if (!sv_threads->integer)
00629         SV_RunGameFrame();
00630 
00631     /* next map in the cycle */
00632     if (sv.endgame && sv_maxclients->integer > 1)
00633         SV_NextMapcycle();
00634 
00635     /* send a heartbeat to the master if needed */
00636     Master_Heartbeat();
00637 
00638     /* server is empty - so shutdown */
00639     if (svs.abandon && svs.killserver)
00640         SV_Shutdown("Server disconnected.", qfalse);
00641 }
00642 
00646 static void Master_Shutdown (void)
00647 {
00648     char *responseBuf;
00649 
00650     if (!sv_dedicated || !sv_dedicated->integer)
00651         return;                 /* only dedicated servers send heartbeats */
00652 
00653     if (!sv_public || !sv_public->integer)
00654         return;                 /* a private dedicated game */
00655 
00656     /* send to master */
00657     responseBuf = HTTP_GetURL(va("%s/ufo/masterserver.php?shutdown&port=%s", masterserver_url->string, port->string));
00658     if (responseBuf) {
00659         Com_DPrintf(DEBUG_SERVER, "response: %s\n", responseBuf);
00660         Mem_Free(responseBuf);
00661     }
00662 }
00663 
00667 void SV_UserinfoChanged (client_t * cl)
00668 {
00669     const char *val;
00670     unsigned int i;
00671 
00672     /* call prog code to allow overrides */
00673     SDL_mutexP(svs.serverMutex);
00674     svs.ge->ClientUserinfoChanged(cl->player, cl->userinfo);
00675     SDL_mutexV(svs.serverMutex);
00676 
00677     /* name for C code */
00678     strncpy(cl->name, Info_ValueForKey(cl->userinfo, "cl_name"), sizeof(cl->name) - 1);
00679     /* mask off high bit */
00680     for (i = 0; i < sizeof(cl->name); i++)
00681         cl->name[i] &= 127;
00682 
00683     /* msg command */
00684     val = Info_ValueForKey(cl->userinfo, "cl_msg");
00685     if (val[0] != '\0')
00686         cl->messagelevel = atoi(val);
00687 
00688     Com_DPrintf(DEBUG_SERVER, "SV_UserinfoChanged: Changed userinfo for player %s\n", cl->name);
00689 }
00690 
00691 static qboolean SV_CheckMaxSoldiersPerPlayer (cvar_t* cvar)
00692 {
00693     const int max = MAX_ACTIVETEAM;
00694     return Cvar_AssertValue(cvar, 1, max, qtrue);
00695 }
00696 
00697 mapData_t* SV_GetMapData (void)
00698 {
00699     return &sv.mapData;
00700 }
00701 
00702 mapTiles_t* SV_GetMapTiles (void)
00703 {
00704     return &sv.mapTiles;
00705 }
00706 
00710 void SV_Init (void)
00711 {
00712     Com_Printf("\n------ server initialization -------\n");
00713 
00714     sv_genericPool = Mem_CreatePool("Server: Generic");
00715 
00716     memset(&svs, 0, sizeof(svs));
00717 
00718     SV_InitOperatorCommands();
00719 
00720     rcon_password = Cvar_Get("rcon_password", "", 0, NULL);
00721     Cvar_Get("sv_cheats", "0", CVAR_SERVERINFO | CVAR_LATCH, NULL);
00722     Cvar_Get("sv_protocol", DOUBLEQUOTE(PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_NOSET, NULL);
00723     /* this cvar will become a latched cvar when you start the server */
00724     sv_maxclients = Cvar_Get("sv_maxclients", "1", CVAR_SERVERINFO, "Max. connected clients");
00725     sv_hostname = Cvar_Get("sv_hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE, "The name of the server that is displayed in the serverlist");
00726     sv_http_downloadserver = Cvar_Get("sv_http_downloadserver", "", CVAR_ARCHIVE, "URL to a location where clients can download game content over HTTP");
00727     sv_enablemorale = Cvar_Get("sv_enablemorale", "1", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_LATCH, "Enable morale changes in multiplayer");
00728     sv_maxsoldiersperteam = Cvar_Get("sv_maxsoldiersperteam", "4", CVAR_ARCHIVE | CVAR_SERVERINFO, "Max. amount of soldiers per team (see sv_maxsoldiersperplayer and sv_teamplay)");
00729     sv_maxsoldiersperplayer = Cvar_Get("sv_maxsoldiersperplayer", "8", CVAR_ARCHIVE | CVAR_SERVERINFO, "Max. amount of soldiers each player can controll (see maxsoldiers and sv_teamplay)");
00730     Cvar_SetCheckFunction("sv_maxsoldiersperplayer", SV_CheckMaxSoldiersPerPlayer);
00731 
00732     sv_dumpmapassembly = Cvar_Get("sv_dumpmapassembly", "0", CVAR_ARCHIVE, "Dump map assembly information to game console");
00733 
00734     sv_threads = Cvar_Get("sv_threads", "1", CVAR_LATCH | CVAR_ARCHIVE, "Run the server threaded");
00735     sv_public = Cvar_Get("sv_public", "1", 0, "Should heartbeats be send to the masterserver");
00736     sv_reconnect_limit = Cvar_Get("sv_reconnect_limit", "3", CVAR_ARCHIVE, "Minimum seconds between connect messages");
00737 
00738     if (sv_dedicated->integer)
00739         Cvar_Set("sv_maxclients", "8");
00740 
00741     sv_maxclients->modified = qfalse;
00742 
00743     SV_MapcycleInit();
00744 }
00745 
00751 static void SV_FinalMessage (const char *message, qboolean reconnect)
00752 {
00753     client_t *cl;
00754     struct dbuffer *msg = new_dbuffer();
00755 
00756     if (reconnect)
00757         NET_WriteByte(msg, svc_reconnect);
00758     else
00759         NET_WriteByte(msg, svc_disconnect);
00760     NET_WriteString(msg, message);
00761 
00762     cl = NULL;
00763     while ((cl = SV_GetNextClient(cl)) != NULL)
00764         if (cl->state >= cs_connected) {
00765             NET_WriteConstMsg(cl->stream, msg);
00766             NET_StreamFinished(cl->stream);
00767             cl->stream = NULL;
00768         }
00769 
00770     /* make sure, that this is send */
00771     NET_Wait(0);
00772 
00773     free_dbuffer(msg);
00774 }
00775 
00781 void SV_Clear (void)
00782 {
00783     SV_MapcycleClear();
00784 }
00785 
00792 void SV_Shutdown (const char *finalmsg, qboolean reconnect)
00793 {
00794     unsigned int i;
00795 
00796     if (!svs.initialized)
00797         return;
00798 
00799     if (svs.clients)
00800         SV_FinalMessage(finalmsg, reconnect);
00801 
00802     Com_Printf("Shutdown server: %s\n", finalmsg);
00803 
00804     Master_Shutdown();
00805     SV_ShutdownGameProgs();
00806 
00807     NET_DatagramSocketClose(svs.netDatagramSocket);
00808     SV_Stop();
00809 
00810     for (i = 0; i < sv.numSVModels; i++) {
00811         sv_model_t *model = &sv.svModels[i];
00812         if (model->name)
00813             Mem_Free(model->name);
00814     }
00815 
00816     /* free current level */
00817     memset(&sv, 0, sizeof(sv));
00818 
00819     /* free server static data */
00820     if (svs.clients)
00821         Mem_Free(svs.clients);
00822 
00823     if (svs.serverMutex != NULL)
00824         SDL_DestroyMutex(svs.serverMutex);
00825 
00826     memset(&svs, 0, sizeof(svs));
00827 
00828     /* maybe we shut down before we init - e.g. in case of an error */
00829     if (sv_maxclients)
00830         sv_maxclients->flags &= ~CVAR_LATCH;
00831 
00832     if (sv_mapname)
00833         sv_mapname->flags &= ~CVAR_NOSET;
00834 }
00835 
00840 void SV_ShutdownWhenEmpty (void)
00841 {
00842     svs.abandon = qtrue;
00843     /* pretend server is already dead, otherwise clients may try and reconnect */
00844     Com_SetServerState(ss_dead);
00845 }
00846 
00851 int SV_CountPlayers (void)
00852 {
00853     int count = 0;
00854     client_t *cl;
00855 
00856     if (!svs.initialized)
00857         return 0;
00858 
00859     cl = NULL;
00860     while ((cl = SV_GetNextClient(cl)) != NULL) {
00861         if (cl->state != cs_spawned)
00862             continue;
00863 
00864         count++;
00865     }
00866 
00867     return count;
00868 }

Generated by  doxygen 1.6.2