00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
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
00088
00089
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
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
00147
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
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
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;
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') {
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')) {
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"))) {
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
00340 Info_SetValueForKey(userinfo, sizeof(userinfo), "ip", peername);
00341
00342
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
00354
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
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
00379 cl->player->inuse = qtrue;
00380
00381
00382 strncpy(cl->userinfo, userinfo, sizeof(cl->userinfo) - 1);
00383 SV_UserinfoChanged(cl);
00384
00385
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
00406 if (!strlen(rcon_password->string))
00407 return qfalse;
00408
00409
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
00438 Com_Printf("Bad rcon_password.\n");
00439 else {
00440 char remaining[1024] = "";
00441 int i;
00442
00443
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
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
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;
00544
00545 if (!sv_public || !sv_public->integer)
00546 return;
00547
00548
00549 if (svs.lastHeartbeat > svs.realtime)
00550 svs.lastHeartbeat = svs.realtime;
00551
00552 if (svs.realtime - svs.lastHeartbeat < HEARTBEAT_SECONDS * 1000)
00553 return;
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
00573 if (sv.started)
00574 return;
00575
00576 if (sv_maxclients->integer > 1) {
00577 cl = NULL;
00578 while ((cl = SV_GetNextClient(cl)) != NULL) {
00579
00580
00581 if (cl->state != cs_began || !cl->player->isReady)
00582 return;
00583 }
00584 } else if (SV_GetClient(0)->state != cs_began) {
00585
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
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
00618 if (!svs.initialized)
00619 return;
00620
00621 svs.realtime = now;
00622
00623
00624 rand();
00625
00626 SV_CheckGameStart();
00627
00628 if (!sv_threads->integer)
00629 SV_RunGameFrame();
00630
00631
00632 if (sv.endgame && sv_maxclients->integer > 1)
00633 SV_NextMapcycle();
00634
00635
00636 Master_Heartbeat();
00637
00638
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;
00652
00653 if (!sv_public || !sv_public->integer)
00654 return;
00655
00656
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
00673 SDL_mutexP(svs.serverMutex);
00674 svs.ge->ClientUserinfoChanged(cl->player, cl->userinfo);
00675 SDL_mutexV(svs.serverMutex);
00676
00677
00678 strncpy(cl->name, Info_ValueForKey(cl->userinfo, "cl_name"), sizeof(cl->name) - 1);
00679
00680 for (i = 0; i < sizeof(cl->name); i++)
00681 cl->name[i] &= 127;
00682
00683
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
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
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
00817 memset(&sv, 0, sizeof(sv));
00818
00819
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
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
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 }