1 /* $Id: metaserver.c,v 5.5 2001/11/29 14:48:12 bertg Exp $
2 *
3 * XPilot, a multiplayer gravity war game. Copyright (C) 1991-2001 by
4 *
5 * Bj�rn Stabell <bjoern@xpilot.org>
6 * Ken Ronny Schouten <ken@xpilot.org>
7 * Bert Gijsbers <bert@xpilot.org>
8 * Dick Balaska <dick@xpilot.org>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <time.h>
31 #include <sys/types.h>
32
33 #ifndef _WINDOWS
34 # include <unistd.h>
35 # ifndef __hpux
36 # include <sys/time.h>
37 # endif
38 #endif
39
40 #ifdef _WINDOWS
41 # include "NT/winServer.h"
42 #endif
43
44 #define SERVER
45 #include "config.h"
46 #include "version.h"
47 #include "serverconst.h"
48 #include "types.h"
49 #include "global.h"
50 #include "proto.h"
51 #include "socklib.h"
52 #include "map.h"
53 #include "pack.h"
54 #include "metaserver.h"
55 #include "saudio.h"
56 #include "error.h"
57 #include "netserver.h"
58 #include "commonproto.h"
59
60 #if defined(_WINDOWS)
61 #define META_VERSION TITLE
62 #else
63 #define META_VERSION VERSION
64 #endif
65
66 char metaserver_version[] = VERSION;
67
68
69 struct MetaServer {
70 char name[64];
71 char addr[16];
72 };
73 struct MetaServer meta_servers[2] = {
74 {
75 META_HOST,
76 META_IP
77 },
78 {
79 META_HOST_TWO,
80 META_IP_TWO
81 },
82 };
83
84 extern sock_t contactSocket;
85 static char msg[MSG_LEN];
86
87 extern int NumPlayers, NumRobots, NumPseudoPlayers, NumQueuedPlayers;
88 extern int login_in_progress;
89 extern time_t serverTime;
90
Meta_send(char * mesg,int len)91 void Meta_send(char *mesg, int len)
92 {
93 int i;
94
95 if (!reportToMetaServer) {
96 return;
97 }
98
99 for (i = 0; i < NELEM(meta_servers); i++) {
100 if (sock_send_dest(&contactSocket, meta_servers[i].addr, META_PORT, mesg, len) != len) {
101 sock_get_error(&contactSocket);
102 sock_send_dest(&contactSocket, meta_servers[i].addr, META_PORT, mesg, len);
103 }
104 }
105 }
106
Meta_from(char * addr,int port)107 int Meta_from(char *addr, int port)
108 {
109 int i;
110
111 for (i = 0; i < NELEM(meta_servers); i++) {
112 if (!strcmp(addr, meta_servers[i].addr)) {
113 return (port == META_PORT);
114 }
115 }
116 return 0;
117 }
118
Meta_gone(void)119 void Meta_gone(void)
120 {
121 if (reportToMetaServer) {
122 sprintf(msg, "server %s\nremove", Server.host);
123 Meta_send(msg, strlen(msg) + 1);
124 }
125 }
126
Meta_init(void)127 void Meta_init(void)
128 {
129 int i;
130 char *addr;
131
132 if (!reportToMetaServer) {
133 return;
134 }
135
136 #ifndef SILENT
137 xpprintf("%s Locating Internet Meta server... ", showtime()); fflush(stdout);
138 #endif
139 for (i = 0; i < NELEM(meta_servers); i++) {
140 addr = sock_get_addr_by_name(meta_servers[i].name);
141 if (addr) {
142 strlcpy(meta_servers[i].addr, addr,
143 sizeof(meta_servers[i].addr));
144 }
145 #ifndef SILENT
146 if (addr) {
147 xpprintf("found %d", i + 1);
148 } else {
149 xpprintf("%d not found", i + 1);
150 }
151 if (i + 1 == NELEM(meta_servers)) {
152 xpprintf("\n");
153 } else {
154 xpprintf("... ");
155 }
156 fflush(stdout);
157 #endif
158 }
159 }
160
Meta_update(int change)161 void Meta_update(int change)
162 {
163 #ifdef SOUND
164 #define SOUND_SUPPORT_STR "yes"
165 #else
166 #define SOUND_SUPPORT_STR "no"
167 #endif
168 #define GIVE_META_SERVER_A_HINT 180
169
170 char string[MAX_STR_LEN];
171 int i, j, len;
172 int num_active_players;
173 bool first = true;
174 time_t currentTime;
175 const char *game_mode;
176 char freebases[120];
177 int active_per_team[MAX_TEAMS];
178 static time_t lastMetaSendTime = 0;
179 static int queue_length = 0;
180
181
182 if (!reportToMetaServer)
183 return;
184
185 currentTime = time(NULL);
186 if (!change) {
187 if (currentTime - lastMetaSendTime < GIVE_META_SERVER_A_HINT) {
188 if (NumQueuedPlayers == queue_length ||
189 currentTime - lastMetaSendTime < 5) {
190 return;
191 }
192 }
193 }
194 lastMetaSendTime = currentTime;
195 queue_length = NumQueuedPlayers;
196
197 /* Find out the number of active players. */
198 num_active_players = 0;
199 memset(active_per_team, 0, sizeof active_per_team);
200 for (i = 0; i < NumPlayers; i++) {
201 if (IS_HUMAN_IND(i) && !BIT(Players[i]->status, PAUSE)) {
202 num_active_players++;
203 if (BIT(World.rules->mode, TEAM_PLAY)) {
204 active_per_team[i]++;
205 }
206 }
207 }
208
209 game_mode = (game_lock && ShutdownServer == -1) ? "locked"
210 : (!game_lock && ShutdownServer != -1) ? "shutting down"
211 : (game_lock && ShutdownServer != -1) ? "locked and shutting down"
212 : "ok";
213
214 /* calculate number of available homebases per team. */
215 freebases[0] = '\0';
216 if (BIT(World.rules->mode, TEAM_PLAY)) {
217 j = 0;
218 for (i = 0; i < MAX_TEAMS; i++) {
219 if (i == robotTeam && reserveRobotTeam) {
220 continue;
221 }
222 if (World.teams[i].NumBases > 0) {
223 sprintf(&freebases[j], "%d=%d,", i,
224 World.teams[i].NumBases - active_per_team[i]);
225 j += strlen(&freebases[j]);
226 }
227 }
228 /* strip trailing comma. */
229 if (j) {
230 freebases[j-1] = '\0';
231 }
232 }
233 else {
234 sprintf(freebases, "=%d",
235 World.NumBases - num_active_players - login_in_progress);
236 }
237
238 sprintf(string,
239 "add server %s\n"
240 "add users %d\n"
241 "add version %s\n"
242 "add map %s\n"
243 "add sizeMap %3dx%3d\n"
244 "add author %s\n"
245 "add bases %d\n"
246 "add fps %d\n"
247 "add port %d\n"
248 "add mode %s\n"
249 "add teams %d\n"
250 "add free %s\n"
251 "add timing %d\n"
252 "add stime %ld\n"
253 "add queue %d\n"
254 "add sound " SOUND_SUPPORT_STR "\n",
255 Server.host, num_active_players,
256 META_VERSION, World.name, World.x, World.y, World.author,
257 World.NumBases, FPS, contactPort,
258 game_mode, World.NumTeamBases, freebases,
259 BIT(World.rules->mode, TIMING) ? 1:0,
260 (long)(time(NULL) - serverTime),
261 queue_length);
262
263
264 /*
265 * 'len' must always hold the exact number of
266 * non-zero bytes which are in string[].
267 */
268 len = strlen(string);
269
270 for (i = 0; i < NumPlayers; i++) {
271 if (IS_HUMAN_IND(i) && !BIT(Players[i]->status, PAUSE)) {
272 if ((len + (4 * MAX_CHARS)) < sizeof(string)) {
273 sprintf(string + len,
274 "%s%s=%s@%s",
275 (first) ? "add players " : ",",
276 Players[i]->name,
277 Players[i]->realname,
278 Players[i]->hostname);
279 len += strlen(&string[len]);
280
281 if (BIT(World.rules->mode, TEAM_PLAY)) {
282 sprintf(string + len,"{%d}",Players[i]->team);
283 len += strlen(&string[len]);
284 }
285
286 first = false;
287 }
288 }
289 }
290
291 if (len + MSG_LEN < sizeof(string)) {
292 char status[MAX_STR_LEN];
293
294 strlcpy(&string[len], "\nadd status ", sizeof(string) - len);
295 len += strlen(&string[len]);
296
297 Server_info(status, sizeof(status));
298
299 strlcpy(&string[len], status, sizeof(string) - len);
300 len += strlen(&string[len]);
301 }
302
303 Meta_send(string, len + 1);
304 }
305
306