Crossfire Client, Trunk  R19900
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  "http://metaserver.us.cross-fire.org/meta_update.php",
42 };
43 
46 
51 void ms_set_callback(ms_callback function) {
52  callback = function;
53 }
54 
68  /* No version information - nothing to do. */
69  if (!server->sc_version || !server->cs_version) {
70  return true;
71  }
72 
73  if (server->sc_version != VERSION_SC) {
74  /* 1027->1028 removed a bunch of old commands, so a 1028
75  * version client can still play on a 1027 server, so
76  * special hard code that.
77  *
78  * Likewise, 1028->1029 just changed how weapon_speed
79  * should be interperted on the client - the client
80  * does the right thing, so not problem with a 1029
81  * client playing on 1028 or 1027 server.
82  *
83  * A 1028 client could in practice play on a 1029
84  * server, since at the protocol level, data is the same -
85  * the client would just have screwed up weapon_sp values.
86  */
87  if ((VERSION_SC == 1028 || VERSION_SC == 1029) &&
88  (server->sc_version == 1027 ||
89  server->sc_version == 1028)) {
90  return true;
91  }
92  }
93  if (server->cs_version != VERSION_CS) {
94  return false;
95  }
96 
97  return true;
98 }
99 
124 static size_t ms_writer(void *ptr, size_t size, size_t nmemb, void *data) {
125  Meta_Info metaserver;
126 
127 #ifdef HAVE_CURL_CURL_H
128  size_t realsize = size * nmemb;
129  char *cp, *newline, *eq, inbuf[CURL_MAX_WRITE_SIZE * 2 + 1], *leftover;
130 
131  leftover = (char*) data;
132 
133  if (realsize > CURL_MAX_WRITE_SIZE) {
134  LOG(LOG_CRITICAL, "common::metaserver2_writer", "Function called with more data than allowed!");
135  }
136 
137  /* This memcpy here is to just give us a null terminated character
138  * array - easier to do with than having to check lengths as well as other
139  * values. Also, it makes it easier to deal with unprocessed data from
140  * the last call.
141  */
142  memcpy(inbuf, leftover, strlen(leftover));
143  memcpy(inbuf + strlen(leftover), ptr, realsize);
144  inbuf[strlen(leftover) + realsize] = 0;
145  leftover[0] = 0;
146 
147  for (cp = inbuf; cp != NULL && *cp != 0; cp = newline) {
148  newline = strchr(cp, '\n');
149  if (newline) {
150  *newline = 0;
151  newline++;
152  } else {
153  /* If we didn't get a newline, then this is the
154  * end of the block of data for this call - store
155  * away the extra for the next call.
156  */
157  strncpy(leftover, cp, CURL_MAX_WRITE_SIZE - 1);
158  leftover[CURL_MAX_WRITE_SIZE - 1] = 0;
159  break;
160  }
161 
162  eq = strchr(cp, '=');
163  if (eq) {
164  *eq = 0;
165  eq++;
166  }
167 
168  if (!strcmp(cp, "START_SERVER_DATA")) {
169  /* Clear out all data - MS2 doesn't necessarily use all the
170  * fields, so blank out any that we are not using.
171  */
172  memset(&metaserver, 0, sizeof (Meta_Info));
173  } else if (!strcmp(cp, "END_SERVER_DATA")) {
174  char buf[MS_LARGE_BUF];
175 
176  // If server is running custom port, add it to server name.
177  if (metaserver.port != EPORT) {
178  snprintf(buf, sizeof(buf), "%s:%d",
179  metaserver.hostname, metaserver.port);
180  } else {
181  snprintf(buf, sizeof(buf), "%s", metaserver.hostname);
182  }
183 
184  callback(buf, metaserver.idle_time, metaserver.num_players,
185  metaserver.version, metaserver.text_comment,
186  ms_check_version(&metaserver));
187  } else {
188  /* If we get here, these should be variable=value pairs.
189  * if we don't have a value, can't do anything, and
190  * report an error. This would normally be incorrect
191  * data from the server.
192  */
193  if (!eq) {
194  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s", cp);
195  continue;
196  }
197  if (!strcmp(cp, "hostname")) {
198  strncpy(metaserver.hostname, eq, sizeof (metaserver.hostname));
199  } else if (!strcmp(cp, "port")) {
200  metaserver.port = atoi(eq);
201  } else if (!strcmp(cp, "html_comment")) {
202  strncpy(metaserver.html_comment, eq, sizeof (metaserver.html_comment));
203  } else if (!strcmp(cp, "text_comment")) {
204  strncpy(metaserver.text_comment, eq, sizeof (metaserver.text_comment));
205  } else if (!strcmp(cp, "archbase")) {
206  strncpy(metaserver.archbase, eq, sizeof (metaserver.archbase));
207  } else if (!strcmp(cp, "mapbase")) {
208  strncpy(metaserver.mapbase, eq, sizeof (metaserver.mapbase));
209  } else if (!strcmp(cp, "codebase")) {
210  strncpy(metaserver.codebase, eq, sizeof (metaserver.codebase));
211  } else if (!strcmp(cp, "flags")) {
212  strncpy(metaserver.flags, eq, sizeof (metaserver.flags));
213  } else if (!strcmp(cp, "version")) {
214  strncpy(metaserver.version, eq, sizeof (metaserver.version));
215  } else if (!strcmp(cp, "num_players")) {
216  metaserver.num_players = atoi(eq);
217  } else if (!strcmp(cp, "in_bytes")) {
218  metaserver.in_bytes = atoi(eq);
219  } else if (!strcmp(cp, "out_bytes")) {
220  metaserver.out_bytes = atoi(eq);
221  } else if (!strcmp(cp, "uptime")) {
222  metaserver.uptime = atoi(eq);
223  } else if (!strcmp(cp, "sc_version")) {
224  metaserver.sc_version = atoi(eq);
225  } else if (!strcmp(cp, "cs_version")) {
226  metaserver.cs_version = atoi(eq);
227  } else if (!strcmp(cp, "last_update")) {
228  /* MS2 reports update time as when it last got an update,
229  * where as we want actual elapsed time since last update.
230  * So do the conversion. Second check is because of clock
231  * skew - my clock may be fast, and we don't want negative times.
232  */
233  metaserver.idle_time = time(NULL) - atoi(eq);
234  if (metaserver.idle_time < 0) {
235  metaserver.idle_time = 0;
236  }
237  } else {
238  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s=%s", cp, eq);
239  }
240  }
241  }
242  return realsize;
243 #else
244  return 0;
245 #endif
246 }
247 
254 bool ms_fetch_server(const char *metaserver2) {
255 #ifdef HAVE_CURL_CURL_H
256  CURL *curl;
257  CURLcode res;
258  char leftover[CURL_MAX_WRITE_SIZE];
259 
260  curl = curl_easy_init();
261  if (!curl) {
262  return false;
263  }
264 
265  leftover[0] = 0;
266  curl_easy_setopt(curl, CURLOPT_URL, metaserver2);
267  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ms_writer);
268  curl_easy_setopt(curl, CURLOPT_WRITEDATA, leftover);
269  res = curl_easy_perform(curl);
270  curl_easy_cleanup(curl);
271 
272  if (res) {
273  return false;
274  } else {
275  return true;
276  }
277 #else
278  return true;
279 #endif
280 }
281 
288 void ms_fetch() {
289  const int count = sizeof(metaservers) / sizeof(char *);
290 
291  for (int i = 0; i < count; i++) {
293  }
294 }
295 
300 void ms_init() {
301 #ifdef HAVE_CURL_CURL_H
302  curl_global_init(CURL_GLOBAL_ALL);
303 #endif
304 }
#define MS_LARGE_BUF
Definition: metaserver.h:9
#define VERSION_CS
Definition: client.h:78
Fatal crash-worthy error.
Definition: client.h:457
int sc_version
Definition: metaserver.h:30
void ms_init()
Initialize metaserver client.
Definition: metaserver.c:300
guint32 in_bytes
Definition: metaserver.h:25
int port
Definition: metaserver.h:17
int cs_version
Definition: metaserver.h:31
static char * server
Definition: main.c:60
int uptime
Definition: metaserver.h:28
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:67
char codebase[MS_SMALL_BUF]
Definition: metaserver.h:22
char hostname[MS_LARGE_BUF]
Definition: metaserver.h:16
char text_comment[MS_LARGE_BUF]
Definition: metaserver.h:19
void ms_fetch()
Fetch a list of servers from the built-in official metaservers.
Definition: metaserver.c:288
#define EPORT
Definition: client.h:73
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:51
static ms_callback callback
Function to call when a new metaserver is available.
Definition: metaserver.c:45
Information about individual servers from the metaserver.
Definition: metaserver.h:15
char archbase[MS_SMALL_BUF]
Definition: metaserver.h:20
char flags[MS_SMALL_BUF]
Definition: metaserver.h:23
char html_comment[MS_LARGE_BUF]
Definition: metaserver.h:18
int num_players
Definition: metaserver.h:24
Warning that something definitely didn't work.
Definition: client.h:456
char mapbase[MS_SMALL_BUF]
Definition: metaserver.h:21
#define VERSION_SC
Definition: client.h:79
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:124
Includes various dependencies header files needed by most everything.
int idle_time
Definition: metaserver.h:27
bool ms_fetch_server(const char *metaserver2)
Fetch a list of servers from the given URL.
Definition: metaserver.c:254
char version[MS_SMALL_BUF]
Definition: metaserver.h:29
guint32 out_bytes
Definition: metaserver.h:26
static const char * metaservers[]
Definition: metaserver.c:38
void(* ms_callback)(char *, int, int, char *, char *, bool)
Definition: metaserver.h:6