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