Crossfire Client, Trunk  R19858
metaserver.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
21 #include "client.h"
22 
23 #ifdef HAVE_CURL_CURL_H
24 #include <curl/curl.h>
25 #include <curl/easy.h>
26 #endif
27 
28 #include "metaserver.h"
29 
30 /* list of metaserver URL to get information from - this should generally
31  * correspond to the value in the metaserver2 server file, but instead
32  * of meta_update.php, use meta_client.php.
33  *
34  * These could perhaps be in some other file (config.h or the like), but
35  * it seems unlikely that these will change very often, and certainly not
36  * at a level where we would expect users to go about changing the values.
37  */
38 static const char *metaservers[] = {
39  "http://crossfire.real-time.com/metaserver2/meta_client.php",
40  "http://metaserver.eu.cross-fire.org/meta_client.php",
41 };
42 
45 
50 void ms_set_callback(ms_callback function) {
51  callback = function;
52 }
53 
67  /* No version information - nothing to do. */
68  if (!server->sc_version || !server->cs_version) {
69  return true;
70  }
71 
72  if (server->sc_version != VERSION_SC) {
73  /* 1027->1028 removed a bunch of old commands, so a 1028
74  * version client can still play on a 1027 server, so
75  * special hard code that.
76  *
77  * Likewise, 1028->1029 just changed how weapon_speed
78  * should be interperted on the client - the client
79  * does the right thing, so not problem with a 1029
80  * client playing on 1028 or 1027 server.
81  *
82  * A 1028 client could in practice play on a 1029
83  * server, since at the protocol level, data is the same -
84  * the client would just have screwed up weapon_sp values.
85  */
86  if ((VERSION_SC == 1028 || VERSION_SC == 1029) &&
87  (server->sc_version == 1027 ||
88  server->sc_version == 1028)) {
89  return true;
90  }
91  }
92  if (server->cs_version != VERSION_CS) {
93  return false;
94  }
95 
96  return true;
97 }
98 
123 static size_t ms_writer(void *ptr, size_t size, size_t nmemb, void *data) {
124  Meta_Info metaserver;
125 
126 #ifdef HAVE_CURL_CURL_H
127  size_t realsize = size * nmemb;
128  char *cp, *newline, *eq, inbuf[CURL_MAX_WRITE_SIZE * 2 + 1], *leftover;
129 
130  leftover = (char*) data;
131 
132  if (realsize > CURL_MAX_WRITE_SIZE) {
133  LOG(LOG_CRITICAL, "common::metaserver2_writer", "Function called with more data than allowed!");
134  }
135 
136  /* This memcpy here is to just give us a null terminated character
137  * array - easier to do with than having to check lengths as well as other
138  * values. Also, it makes it easier to deal with unprocessed data from
139  * the last call.
140  */
141  memcpy(inbuf, leftover, strlen(leftover));
142  memcpy(inbuf + strlen(leftover), ptr, realsize);
143  inbuf[strlen(leftover) + realsize] = 0;
144  leftover[0] = 0;
145 
146  for (cp = inbuf; cp != NULL && *cp != 0; cp = newline) {
147  newline = strchr(cp, '\n');
148  if (newline) {
149  *newline = 0;
150  newline++;
151  } else {
152  /* If we didn't get a newline, then this is the
153  * end of the block of data for this call - store
154  * away the extra for the next call.
155  */
156  strncpy(leftover, cp, CURL_MAX_WRITE_SIZE - 1);
157  leftover[CURL_MAX_WRITE_SIZE - 1] = 0;
158  break;
159  }
160 
161  eq = strchr(cp, '=');
162  if (eq) {
163  *eq = 0;
164  eq++;
165  }
166 
167  if (!strcmp(cp, "START_SERVER_DATA")) {
168  /* Clear out all data - MS2 doesn't necessarily use all the
169  * fields, so blank out any that we are not using.
170  */
171  memset(&metaserver, 0, sizeof (Meta_Info));
172  } else if (!strcmp(cp, "END_SERVER_DATA")) {
173  char buf[MS_LARGE_BUF];
174 
175  // If server is running custom port, add it to server name.
176  if (metaserver.port != EPORT) {
177  snprintf(buf, sizeof(buf), "%s:%d",
178  metaserver.hostname, metaserver.port);
179  } else {
180  snprintf(buf, sizeof(buf), "%s", metaserver.hostname);
181  }
182 
183  callback(buf, metaserver.idle_time, metaserver.num_players,
184  metaserver.version, metaserver.text_comment,
185  ms_check_version(&metaserver));
186  } else {
187  /* If we get here, these should be variable=value pairs.
188  * if we don't have a value, can't do anything, and
189  * report an error. This would normally be incorrect
190  * data from the server.
191  */
192  if (!eq) {
193  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s", cp);
194  continue;
195  }
196  if (!strcmp(cp, "hostname")) {
197  strncpy(metaserver.hostname, eq, sizeof (metaserver.hostname));
198  } else if (!strcmp(cp, "port")) {
199  metaserver.port = atoi(eq);
200  } else if (!strcmp(cp, "html_comment")) {
201  strncpy(metaserver.html_comment, eq, sizeof (metaserver.html_comment));
202  } else if (!strcmp(cp, "text_comment")) {
203  strncpy(metaserver.text_comment, eq, sizeof (metaserver.text_comment));
204  } else if (!strcmp(cp, "archbase")) {
205  strncpy(metaserver.archbase, eq, sizeof (metaserver.archbase));
206  } else if (!strcmp(cp, "mapbase")) {
207  strncpy(metaserver.mapbase, eq, sizeof (metaserver.mapbase));
208  } else if (!strcmp(cp, "codebase")) {
209  strncpy(metaserver.codebase, eq, sizeof (metaserver.codebase));
210  } else if (!strcmp(cp, "flags")) {
211  strncpy(metaserver.flags, eq, sizeof (metaserver.flags));
212  } else if (!strcmp(cp, "version")) {
213  strncpy(metaserver.version, eq, sizeof (metaserver.version));
214  } else if (!strcmp(cp, "num_players")) {
215  metaserver.num_players = atoi(eq);
216  } else if (!strcmp(cp, "in_bytes")) {
217  metaserver.in_bytes = atoi(eq);
218  } else if (!strcmp(cp, "out_bytes")) {
219  metaserver.out_bytes = atoi(eq);
220  } else if (!strcmp(cp, "uptime")) {
221  metaserver.uptime = atoi(eq);
222  } else if (!strcmp(cp, "sc_version")) {
223  metaserver.sc_version = atoi(eq);
224  } else if (!strcmp(cp, "cs_version")) {
225  metaserver.cs_version = atoi(eq);
226  } else if (!strcmp(cp, "last_update")) {
227  /* MS2 reports update time as when it last got an update,
228  * where as we want actual elapsed time since last update.
229  * So do the conversion. Second check is because of clock
230  * skew - my clock may be fast, and we don't want negative times.
231  */
232  metaserver.idle_time = time(NULL) - atoi(eq);
233  if (metaserver.idle_time < 0) {
234  metaserver.idle_time = 0;
235  }
236  } else {
237  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s=%s", cp, eq);
238  }
239  }
240  }
241  return realsize;
242 #else
243  return 0;
244 #endif
245 }
246 
253 bool ms_fetch_server(const char *metaserver2) {
254 #ifdef HAVE_CURL_CURL_H
255  CURL *curl;
256  CURLcode res;
257  char leftover[CURL_MAX_WRITE_SIZE];
258 
259  curl = curl_easy_init();
260  if (!curl) {
261  return false;
262  }
263 
264  leftover[0] = 0;
265  curl_easy_setopt(curl, CURLOPT_URL, metaserver2);
266  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ms_writer);
267  curl_easy_setopt(curl, CURLOPT_WRITEDATA, leftover);
268  res = curl_easy_perform(curl);
269  curl_easy_cleanup(curl);
270 
271  if (res) {
272  return false;
273  } else {
274  return true;
275  }
276 #else
277  return true;
278 #endif
279 }
280 
287 void ms_fetch() {
288  const int count = sizeof(metaservers) / sizeof(char *);
289 
290  for (int i = 0; i < count; i++) {
292  }
293 }
294 
299 void ms_init() {
300 #ifdef HAVE_CURL_CURL_H
301  curl_global_init(CURL_GLOBAL_ALL);
302 #endif
303 }
#define MS_LARGE_BUF
Definition: metaserver.h:11
#define VERSION_CS
Definition: client.h:77
Fatal crash-worthy error.
Definition: client.h:457
int sc_version
Definition: metaserver.h:32
void ms_init()
Initialize metaserver client.
Definition: metaserver.c:299
guint32 in_bytes
Definition: metaserver.h:27
int port
Definition: metaserver.h:19
int cs_version
Definition: metaserver.h:33
int uptime
Definition: metaserver.h:30
static bool ms_check_version(Meta_Info *server)
This checks the servers sc_version and cs_version to see if they are compatible.
Definition: metaserver.c:66
char codebase[MS_SMALL_BUF]
Definition: metaserver.h:24
char hostname[MS_LARGE_BUF]
Definition: metaserver.h:18
char text_comment[MS_LARGE_BUF]
Definition: metaserver.h:21
char * server
Definition: client.c:44
void ms_fetch()
Fetch a list of servers from the built-in official metaservers.
Definition: metaserver.c:287
#define EPORT
Definition: client.h:72
Metaserver functions and data structures.
void LOG(LogLevel level, const char *origin, const char *format,...)
Log messages of a certain importance to stderr.
Definition: misc.c:112
void ms_set_callback(ms_callback function)
Set a callback function to run whenever another metaserver is available.
Definition: metaserver.c:50
static ms_callback callback
Function to call when a new metaserver is available.
Definition: metaserver.c:44
Information about individual servers from the metaserver.
Definition: metaserver.h:17
char archbase[MS_SMALL_BUF]
Definition: metaserver.h:22
char flags[MS_SMALL_BUF]
Definition: metaserver.h:25
char html_comment[MS_LARGE_BUF]
Definition: metaserver.h:20
int num_players
Definition: metaserver.h:26
Warning that something definitely didn't work.
Definition: client.h:456
char mapbase[MS_SMALL_BUF]
Definition: metaserver.h:23
#define VERSION_SC
Definition: client.h:78
static size_t ms_writer(void *ptr, size_t size, size_t nmemb, void *data)
Curl doesn't really have any built in way to get data from the URL into string data - instead...
Definition: metaserver.c:123
Includes various dependencies header files needed by most everything.
int idle_time
Definition: metaserver.h:29
bool ms_fetch_server(const char *metaserver2)
Fetch a list of servers from the given URL.
Definition: metaserver.c:253
char version[MS_SMALL_BUF]
Definition: metaserver.h:31
guint32 out_bytes
Definition: metaserver.h:28
static const char * metaservers[]
Definition: metaserver.c:38
void(* ms_callback)(char *, int, int, char *, char *, bool)
Definition: metaserver.h:8