Clarify character encoding arrangements, and stop claiming in various
[freeciv.git] / server / meta.c
blobd1c52b70809115f7f51a807125f3478786ccbf30
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <ctype.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #ifdef HAVE_NETINET_IN_H
25 #include <netinet/in.h>
26 #endif
27 #ifdef HAVE_SYS_SOCKET_H
28 #include <sys/socket.h>
29 #endif
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
38 #endif
39 #ifdef HAVE_WINSOCK
40 #include <winsock.h>
41 #endif
43 /* utility */
44 #include "fcintl.h"
45 #include "fcthread.h"
46 #include "log.h"
47 #include "mem.h"
48 #include "netintf.h"
49 #include "netfile.h"
50 #include "support.h"
51 #include "timing.h"
53 /* common */
54 #include "capstr.h"
55 #include "connection.h"
56 #include "dataio.h"
57 #include "game.h"
58 #include "version.h"
60 /* server */
61 #include "console.h"
62 #include "plrhand.h"
63 #include "settings.h"
64 #include "srv_main.h"
66 #include "meta.h"
68 static bool server_is_open = FALSE;
70 static char meta_patches[256] = "";
71 static char meta_message[256] = "";
73 static fc_thread *meta_srv_thread = NULL;
75 /*************************************************************************
76 the default metaserver patches for this server
77 *************************************************************************/
78 const char *default_meta_patches_string(void)
80 return "none";
83 /*************************************************************************
84 Return static string with default info line to send to metaserver.
85 *************************************************************************/
86 const char *default_meta_message_string(void)
88 #if IS_BETA_VERSION
89 return "unstable pre-" NEXT_STABLE_VERSION ": beware";
90 #else /* IS_BETA_VERSION */
91 #if IS_DEVEL_VERSION
92 return "development version: beware";
93 #else /* IS_DEVEL_VERSION */
94 return "-";
95 #endif /* IS_DEVEL_VERSION */
96 #endif /* IS_BETA_VERSION */
99 /*************************************************************************
100 the metaserver patches
101 *************************************************************************/
102 const char *get_meta_patches_string(void)
104 return meta_patches;
107 /*************************************************************************
108 the metaserver message
109 *************************************************************************/
110 const char *get_meta_message_string(void)
112 return meta_message;
115 /*************************************************************************
116 The metaserver message set by user
117 *************************************************************************/
118 const char *get_user_meta_message_string(void)
120 if (game.server.meta_info.user_message_set) {
121 return game.server.meta_info.user_message;
124 return NULL;
127 /*************************************************************************
128 Update meta message. Set it to user meta message, if it is available.
129 Otherwise use provided message.
130 It is ok to call this with NULL message. Then it only replaces current
131 meta message with user meta message if available.
132 *************************************************************************/
133 void maybe_automatic_meta_message(const char *automatic)
135 const char *user_message;
137 user_message = get_user_meta_message_string();
139 if (user_message == NULL) {
140 /* No user message */
141 if (automatic != NULL) {
142 set_meta_message_string(automatic);
144 return;
147 set_meta_message_string(user_message);
150 /*************************************************************************
151 set the metaserver patches string
152 *************************************************************************/
153 void set_meta_patches_string(const char *string)
155 sz_strlcpy(meta_patches, string);
158 /*************************************************************************
159 set the metaserver message string
160 *************************************************************************/
161 void set_meta_message_string(const char *string)
163 sz_strlcpy(meta_message, string);
166 /*************************************************************************
167 set user defined metaserver message string
168 *************************************************************************/
169 void set_user_meta_message_string(const char *string)
171 if (string != NULL && string[0] != '\0') {
172 sz_strlcpy(game.server.meta_info.user_message, string);
173 game.server.meta_info.user_message_set = TRUE;
174 set_meta_message_string(string);
175 } else {
176 /* Remove user meta message. We will use automatic messages instead */
177 game.server.meta_info.user_message[0] = '\0';
178 game.server.meta_info.user_message_set = FALSE;
179 set_meta_message_string(default_meta_message_string());
183 /*************************************************************************
184 Return string describing both metaserver name and port.
185 *************************************************************************/
186 char *meta_addr_port(void)
188 return srvarg.metaserver_addr;
191 /*************************************************************************
192 we couldn't find or connect to the metaserver.
193 *************************************************************************/
194 static void metaserver_failed(void)
196 con_puts(C_METAERROR, _("Not reporting to the metaserver in this game."));
197 con_flush();
199 server_close_meta();
202 /****************************************************************************
203 Insert a setting in the metaserver message. Return TRUE if it succeded.
204 ****************************************************************************/
205 static inline bool meta_insert_setting(struct netfile_post *post,
206 const char *set_name)
208 const struct setting *pset = setting_by_name(set_name);
209 char buf[256];
211 fc_assert_ret_val_msg(NULL != pset, FALSE,
212 "Setting \"%s\" not found!", set_name);
213 netfile_add_form_str(post, "vn[]", setting_name(pset));
214 netfile_add_form_str(post, "vv[]",
215 setting_value_name(pset, FALSE, buf, sizeof(buf)));
216 return TRUE;
219 /*************************************************************************
220 Send POST to metaserver. This runs in its own thread.
221 *************************************************************************/
222 static void send_metaserver_post(void *arg)
224 struct netfile_post *post = (struct netfile_post *) arg;
225 char *addr;
227 if (srvarg.bind_meta_addr != NULL) {
228 addr = srvarg.bind_meta_addr;
229 } else {
230 addr = srvarg.bind_addr;
233 if (!netfile_send_post(srvarg.metaserver_addr, post, NULL, addr)) {
234 con_puts(C_METAERROR, _("Error connecting to metaserver"));
235 metaserver_failed();
238 netfile_close_post(post);
241 /*************************************************************************
242 construct the POST message and send info to metaserver.
243 *************************************************************************/
244 static bool send_to_metaserver(enum meta_flag flag)
246 int players = 0;
247 int humans = 0;
248 char host[512];
249 char state[20];
250 char rs[256];
251 struct netfile_post *post;
253 switch(server_state()) {
254 case S_S_INITIAL:
255 sz_strlcpy(state, "Pregame");
256 break;
257 case S_S_RUNNING:
258 sz_strlcpy(state, "Running");
259 break;
260 case S_S_OVER:
261 sz_strlcpy(state, "Game Ended");
262 break;
265 /* get hostname */
266 if (srvarg.metaserver_name[0] != '\0') {
267 sz_strlcpy(host, srvarg.metaserver_name);
268 } else if (fc_gethostname(host, sizeof(host)) != 0) {
269 sz_strlcpy(host, "unknown");
272 sz_strlcpy(rs, game.control.name);
274 /* Freed in metaserver thread function send_metaserver_post() */
275 post = netfile_start_post();
277 netfile_add_form_str(post, "host", host);
278 netfile_add_form_int(post, "port", srvarg.port);
279 netfile_add_form_str(post, "state", state);
280 netfile_add_form_str(post, "ruleset", rs);
282 if (flag == META_GOODBYE) {
283 netfile_add_form_int(post, "bye", 1);
284 } else {
285 netfile_add_form_str(post, "version", VERSION_STRING);
286 netfile_add_form_str(post, "patches",
287 get_meta_patches_string());
288 netfile_add_form_str(post, "capability", our_capability);
290 netfile_add_form_str(post, "serverid", srvarg.serverid);
291 netfile_add_form_str(post, "message",
292 get_meta_message_string());
294 /* NOTE: send info for ALL players or none at all. */
295 if (normal_player_count() == 0) {
296 netfile_add_form_int(post, "dropplrs", 1);
297 } else {
298 players = 0; /* a counter for players_available */
299 humans = 0;
301 players_iterate(plr) {
302 bool is_player_available = TRUE;
303 char type[15];
304 struct connection *pconn = conn_by_user(plr->username);
306 if (!plr->is_alive) {
307 sz_strlcpy(type, "Dead");
308 } else if (is_barbarian(plr)) {
309 sz_strlcpy(type, "Barbarian");
310 } else if (plr->ai_controlled) {
311 sz_strlcpy(type, "A.I.");
312 } else {
313 sz_strlcpy(type, "Human");
316 netfile_add_form_str(post, "plu[]", plr->username);
317 netfile_add_form_str(post, "plt[]", type);
318 netfile_add_form_str(post, "pll[]", player_name(plr));
319 netfile_add_form_str(post, "pln[]",
320 plr->nation != NO_NATION_SELECTED
321 ? nation_plural_for_player(plr)
322 : "none");
323 netfile_add_form_str(post, "plf[]",
324 plr->nation != NO_NATION_SELECTED
325 ? nation_of_player(plr)->flag_graphic_str
326 : "none");
327 netfile_add_form_str(post, "plh[]",
328 pconn ? pconn->addr : "");
330 /* is this player available to take?
331 * TODO: there's some duplication here with
332 * stdinhand.c:is_allowed_to_take() */
333 if (is_barbarian(plr) && !strchr(game.server.allow_take, 'b')) {
334 is_player_available = FALSE;
335 } else if (!plr->is_alive && !strchr(game.server.allow_take, 'd')) {
336 is_player_available = FALSE;
337 } else if (plr->ai_controlled
338 && !strchr(game.server.allow_take,
339 (game.info.is_new_game ? 'A' : 'a'))) {
340 is_player_available = FALSE;
341 } else if (!plr->ai_controlled
342 && !strchr(game.server.allow_take,
343 (game.info.is_new_game ? 'H' : 'h'))) {
344 is_player_available = FALSE;
347 if (pconn) {
348 is_player_available = FALSE;
351 if (is_player_available) {
352 players++;
355 if (!plr->ai_controlled && plr->is_alive) {
356 humans++;
358 } players_iterate_end;
360 /* send the number of available players. */
361 netfile_add_form_int(post, "available", players);
362 netfile_add_form_int(post, "humans", humans);
365 /* Send some variables: should be listed in inverted order? */
367 static const char *settings[] = {
368 "timeout", "endturn", "minplayers", "maxplayers",
369 "aifill", "allowtake", "generator"
371 int i;
373 for (i = 0; i < ARRAY_SIZE(settings); i++) {
374 meta_insert_setting(post, settings[i]);
377 /* HACK: send the most determinant setting for the map size. */
378 switch (map.server.mapsize) {
379 case MAPSIZE_FULLSIZE:
380 meta_insert_setting(post, "size");
381 break;
382 case MAPSIZE_PLAYER:
383 meta_insert_setting(post, "tilesperplayer");
384 break;
385 case MAPSIZE_XYSIZE:
386 meta_insert_setting(post, "xsize");
387 meta_insert_setting(post, "ysize");
388 break;
392 /* Turn and year. */
393 netfile_add_form_str(post, "vn[]", "turn");
394 netfile_add_form_int(post, "vv[]", game.info.turn);
395 netfile_add_form_str(post, "vn[]", "year");
397 if (server_state() != S_S_INITIAL) {
398 netfile_add_form_int(post, "vv[]", game.info.year);
399 } else {
400 netfile_add_form_str(post, "vv[]", "Calendar not set up");
404 if (meta_srv_thread != NULL) {
405 /* Previously started thread */
406 fc_thread_wait(meta_srv_thread);
407 } else {
408 meta_srv_thread = fc_malloc(sizeof(meta_srv_thread));
411 /* Send POST in new thread */
412 fc_thread_start(meta_srv_thread, &send_metaserver_post, post);
414 return TRUE;
417 /*************************************************************************
418 Stop sending updates to metaserver
419 *************************************************************************/
420 void server_close_meta(void)
422 server_is_open = FALSE;
425 /*************************************************************************
426 lookup the correct address for the metaserver.
427 *************************************************************************/
428 bool server_open_meta(void)
430 if (meta_patches[0] == '\0') {
431 set_meta_patches_string(default_meta_patches_string());
433 if (meta_message[0] == '\0') {
434 set_meta_message_string(default_meta_message_string());
437 server_is_open = TRUE;
439 return TRUE;
442 /**************************************************************************
443 are we sending info to the metaserver?
444 **************************************************************************/
445 bool is_metaserver_open(void)
447 return server_is_open;
450 /**************************************************************************
451 control when we send info to the metaserver.
452 **************************************************************************/
453 bool send_server_info_to_metaserver(enum meta_flag flag)
455 static struct timer *last_send_timer = NULL;
456 static bool want_update;
458 if (!server_is_open) {
459 return FALSE;
462 /* if we're bidding farewell, ignore all timers */
463 if (flag == META_GOODBYE) {
464 if (last_send_timer) {
465 timer_destroy(last_send_timer);
466 last_send_timer = NULL;
468 send_to_metaserver(flag);
470 /* Wait metaserver thread to finish */
471 fc_thread_wait(meta_srv_thread);
472 meta_srv_thread = NULL;
474 return TRUE;
477 /* don't allow the user to spam the metaserver with updates */
478 if (last_send_timer && (timer_read_seconds(last_send_timer)
479 < METASERVER_MIN_UPDATE_INTERVAL)) {
480 if (flag == META_INFO) {
481 want_update = TRUE; /* we couldn't update now, but update a.s.a.p. */
483 return FALSE;
486 /* if we're asking for a refresh, only do so if
487 * we've exceeded the refresh interval */
488 if ((flag == META_REFRESH) && !want_update && last_send_timer
489 && (timer_read_seconds(last_send_timer) < METASERVER_REFRESH_INTERVAL)) {
490 return FALSE;
493 /* start a new timer if we haven't already */
494 if (!last_send_timer) {
495 last_send_timer = timer_new(TIMER_USER, TIMER_ACTIVE);
498 timer_clear(last_send_timer);
499 timer_start(last_send_timer);
500 want_update = FALSE;
502 return send_to_metaserver(flag);