Crossfire Server, Trunk  1.75.0
metaserver.cpp
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 
20 #include "global.h"
21 
22 #include <ctype.h>
23 #include <mutex>
24 #include <thread>
25 #include <system_error>
26 #include <time.h>
27 
28 #ifndef WIN32 /* ---win32 exclude unix header files */
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <netinet/in.h>
32 #include <netdb.h>
33 #include <arpa/inet.h>
34 
35 #endif /* end win32 */
36 
37 #include "metaserver2.h"
38 #include "version.h"
39 
40 #ifdef HAVE_LIBCURL
41 #include <curl/curl.h>
42 #include <curl/easy.h>
43 #endif
44 
46 static std::mutex ms2_info_mutex;
48 static std::timed_mutex ms2_signal;
49 
51  /* We could use socket_info.nconns, but that is not quite as accurate,
52  * as connections in the progress of being established, are listening
53  * but don't have a player, etc. The checks below are basically the
54  * same as for the who commands with the addition that WIZ, AFK, and BOT
55  * players are not counted.
56  */
57  char num_players = 0;
58  for (player *pl = first_player; pl != NULL; pl = pl->next) {
59  if (pl->ob->map == NULL)
60  continue;
61  if (pl->hidden)
62  continue;
63  if (QUERY_FLAG(pl->ob, FLAG_WIZ))
64  continue;
65  if (QUERY_FLAG(pl->ob, FLAG_AFK))
66  continue;
67  if (pl->state != ST_PLAYING && pl->state != ST_GET_PARTY_PASSWORD)
68  continue;
69  if (pl->socket->is_bot)
70  continue;
71  num_players++;
72  }
73  return num_players;
74 }
75 
82 void metaserver_update(void) {
83 #ifdef HAVE_LIBCURL
84  /* Everything inside the lock/unlock is related
85  * to metaserver2 synchronization.
86  */
87  ms2_info_mutex.lock();
89 #ifdef CS_LOGSTATS
93 #else
97 #endif
98  ms2_info_mutex.unlock();
99 #endif
100 }
101 
102 /*
103  * Start of metaserver2 logic
104  * Note: All static structures in this file should be treated as strictly
105  * private. The metaserver2 update logic runs in its own thread,
106  * so if something needs to modify these structures, proper locking
107  * is needed.
108  */
109 
111 static std::vector<std::string> metaservers;
112 
123  std::string hostname;
125  std::string html_comment;
126  std::string text_comment;
127  std::string archbase;
128  std::string mapbase;
129  std::string codebase;
130  std::string flags;
131  time_t last_read;
132 };
133 
136 
138 static std::thread metaserver_thread;
139 
142 
143 #ifdef HAVE_LIBCURL
144 
156 static size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data) {
157  (void)data;
158  size_t realsize = size*nmemb;
159  LOG(llevError, "Message from metaserver:\n%.*s\n", realsize, (const char*)ptr);
160  return realsize;
161 }
162 
163 static void metaserver2_build_form(struct curl_httppost **formpost) {
164  struct curl_httppost *lastptr = NULL;
165  char buf[MAX_BUF];
166 
167  /* First, fill in the form - note that everything has to be a string,
168  * so we convert as needed with snprintf.
169  * The order of fields here really isn't important.
170  * The string after CURLFORM_COPYNAME is the name of the POST variable
171  * as the
172  */
173  curl_formadd(formpost, &lastptr,
174  CURLFORM_COPYNAME, "hostname",
175  CURLFORM_COPYCONTENTS, local_info.hostname.c_str(),
176  CURLFORM_END);
177 
178  snprintf(buf, sizeof(buf), "%d", local_info.portnumber);
179  curl_formadd(formpost, &lastptr,
180  CURLFORM_COPYNAME, "port",
181  CURLFORM_COPYCONTENTS, buf,
182  CURLFORM_END);
183 
184  curl_formadd(formpost, &lastptr,
185  CURLFORM_COPYNAME, "html_comment",
186  CURLFORM_COPYCONTENTS, local_info.html_comment.c_str(),
187  CURLFORM_END);
188 
189  curl_formadd(formpost, &lastptr,
190  CURLFORM_COPYNAME, "text_comment",
191  CURLFORM_COPYCONTENTS, local_info.text_comment.c_str(),
192  CURLFORM_END);
193 
194  curl_formadd(formpost, &lastptr,
195  CURLFORM_COPYNAME, "archbase",
196  CURLFORM_COPYCONTENTS, local_info.archbase.c_str(),
197  CURLFORM_END);
198 
199  curl_formadd(formpost, &lastptr,
200  CURLFORM_COPYNAME, "mapbase",
201  CURLFORM_COPYCONTENTS, local_info.mapbase.c_str(),
202  CURLFORM_END);
203 
204  curl_formadd(formpost, &lastptr,
205  CURLFORM_COPYNAME, "codebase",
206  CURLFORM_COPYCONTENTS, local_info.codebase.c_str(),
207  CURLFORM_END);
208 
209  curl_formadd(formpost, &lastptr,
210  CURLFORM_COPYNAME, "flags",
211  CURLFORM_COPYCONTENTS, local_info.flags.c_str(),
212  CURLFORM_END);
213 
214  ms2_info_mutex.lock();
215 
216  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.num_players);
217  curl_formadd(formpost, &lastptr,
218  CURLFORM_COPYNAME, "num_players",
219  CURLFORM_COPYCONTENTS, buf,
220  CURLFORM_END);
221 
222  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.in_bytes);
223  curl_formadd(formpost, &lastptr,
224  CURLFORM_COPYNAME, "in_bytes",
225  CURLFORM_COPYCONTENTS, buf,
226  CURLFORM_END);
227 
228  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.out_bytes);
229  curl_formadd(formpost, &lastptr,
230  CURLFORM_COPYNAME, "out_bytes",
231  CURLFORM_COPYCONTENTS, buf,
232  CURLFORM_END);
233 
234  snprintf(buf, sizeof(buf), "%ld", (long)metaserver2_updateinfo.uptime);
235  curl_formadd(formpost, &lastptr,
236  CURLFORM_COPYNAME, "uptime",
237  CURLFORM_COPYCONTENTS, buf,
238  CURLFORM_END);
239 
240  ms2_info_mutex.unlock();
241 
242  /* Following few fields are global variables,
243  * but are really defines, so won't ever change.
244  */
245  curl_formadd(formpost, &lastptr,
246  CURLFORM_COPYNAME, "version",
247  CURLFORM_COPYCONTENTS, FULL_VERSION,
248  CURLFORM_END);
249 
250  snprintf(buf, sizeof(buf), "%d", VERSION_SC);
251  curl_formadd(formpost, &lastptr,
252  CURLFORM_COPYNAME, "sc_version",
253  CURLFORM_COPYCONTENTS, buf,
254  CURLFORM_END);
255 
256  snprintf(buf, sizeof(buf), "%d", VERSION_CS);
257  curl_formadd(formpost, &lastptr,
258  CURLFORM_COPYNAME, "cs_version",
259  CURLFORM_COPYCONTENTS, buf,
260  CURLFORM_END);
261 }
262 #endif
263 
268  char buf[MAX_BUF];
269  snprintf(buf, sizeof(buf), "%s/metaserver2", settings.confdir);
270 #ifndef WIN32
271  struct stat statbuf;
272  if (stat(buf, &statbuf) != 0)
273  return false;
274  return statbuf.st_mtime > local_info.last_read;
275 #else
276  return true; // TODO: implement
277 #endif
278 }
279 
281  // Initialize settings
284  local_info.hostname.clear();
285  local_info.html_comment.clear();
286  local_info.text_comment.clear();
287  local_info.archbase.clear();
288  local_info.mapbase.clear();
289  local_info.codebase.clear();
290  local_info.flags.clear();
291  metaservers.clear();
292 
293  /* Now load up the values from the file */
294  char buf[MAX_BUF], *cp, dummy[1];
295  dummy[0] = '\0';
296  snprintf(buf, sizeof(buf), "%s/metaserver2", settings.confdir);
297 
298  FILE *fp;
299  if ((fp = fopen(buf, "r")) == NULL) {
300  LOG(llevError, "failed to open %s: %s\n", buf, strerror(errno));
301  return;
302  }
303  while (fgets(buf, sizeof(buf), fp) != NULL) {
304  if (buf[0] == '#')
305  continue;
306  /* eliminate newline */
307  if ((cp = strrchr(buf, '\n')) != NULL)
308  *cp = '\0';
309 
310  /* Skip over empty lines */
311  if (buf[0] == 0)
312  continue;
313 
314  /* Find variable pairs */
315 
316  if ((cp = strpbrk(buf, " \t")) != NULL) {
317  while (isspace(*cp))
318  *cp++ = 0;
319  } else {
320  /* This makes it so we don't have to do NULL checks against
321  * cp everyplace
322  */
323  cp = dummy;
324  }
325 
326  if (!strcasecmp(buf, "metaserver2_notification")) {
327  if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
329  } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
331  } else {
332  LOG(llevError, "metaserver2: Unknown value for metaserver2_notification: %s\n", cp);
333  }
334  } else if (!strcasecmp(buf, "metaserver2_server")) {
335  if (*cp != 0) {
336  metaservers.push_back(cp);
337  } else {
338  LOG(llevError, "metaserver2: metaserver2_server must have a value.\n");
339  }
340  } else if (!strcasecmp(buf, "localhostname")) {
341  if (*cp != 0) {
342  local_info.hostname = cp;
343  } else {
344  LOG(llevError, "metaserver2: localhostname must have a value.\n");
345  }
346  } else if (!strcasecmp(buf, "portnumber")) {
347  if (*cp != 0) {
348  local_info.portnumber = atoi(cp);
349  } else {
350  LOG(llevError, "metaserver2: portnumber must have a value.\n");
351  }
352  /* For the following values, it is easier to make sure
353  * the pointers are set to something, even if it is a blank
354  * string, so don't care if there is data in the string or not.
355  */
356  } else if (!strcasecmp(buf, "html_comment")) {
358  } else if (!strcasecmp(buf, "text_comment")) {
360  } else if (!strcasecmp(buf, "archbase")) {
361  local_info.archbase = cp;
362  } else if (!strcasecmp(buf, "mapbase")) {
363  local_info.mapbase = cp;
364  } else if (!strcasecmp(buf, "codebase")) {
365  local_info.codebase = cp;
366  } else if (!strcasecmp(buf, "flags")) {
367  local_info.flags = cp;
368  } else {
369  LOG(llevError, "Unknown value in metaserver2 file: %s\n", buf);
370  }
371  }
372  fclose(fp);
373 
374  /* If no hostname is set, can't do updates */
375  if (local_info.hostname.empty())
377 
378 #ifndef HAVE_LIBCURL
379  if (local_info.notification) {
380  LOG(llevError, "metaserver2 file is set to do notification, but libcurl is not found.\n");
381  LOG(llevError, "Either fix your compilation, or turn off metaserver2 notification in \n");
382  LOG(llevError, "the %s/metaserver2 file.\n", settings.confdir);
383  LOG(llevError, "Exiting program.\n");
384  exit(1);
385  }
386 #endif
387 
388  local_info.last_read = time(NULL);
389 }
390 
391 #ifdef HAVE_LIBCURL
392 
397 static void metaserver2_updates(void) {
398  struct curl_httppost *formpost = NULL;
399  metaserver2_build_form(&formpost);
400 
401  for (auto hostname : metaservers) {
402  CURL *curl = curl_easy_init();
403  if (curl) {
404  curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30000);
405 
406  /* what URL that receives this POST */
407  curl_easy_setopt(curl, CURLOPT_URL, hostname.c_str());
408  curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
409 
410  /* Almost always, we will get HTTP data returned
411  * to us - instead of it going to stderr,
412  * we want to take care of it ourselves.
413  */
414  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer);
415 
416  char errbuf[CURL_ERROR_SIZE];
417  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
418 
419  CURLcode res = curl_easy_perform(curl);
420  if (res) {
421  LOG(llevError, "metaserver update failed: %s\n", errbuf);
422  }
423 
424  /* always cleanup */
425  curl_easy_cleanup(curl);
426  } else {
427  LOG(llevError, "metaserver: could not initialize curl\n");
428  }
429  }
430  /* then cleanup the formpost chain */
431  curl_formfree(formpost);
432 }
433 
438 void metaserver2_thread() {
439  while (true) {
441  metaserver2_updates();
442  if (ms2_signal.try_lock_for(std::chrono::seconds(60)))
443  break;
445  LOG(llevInfo, "re-reading metaserver config\n");
447  }
448  }
449  ms2_signal.unlock();
450 }
451 #endif
452 
462 int metaserver2_init(void) {
464 #ifdef HAVE_LIBCURL
465  curl_global_init(CURL_GLOBAL_ALL);
466  ms2_signal.lock();
467  try {
468  metaserver_thread = std::thread(metaserver2_thread);
469 #ifndef WIN32
470  pthread_setname_np(metaserver_thread.native_handle(), "metaserver");
471 #endif
472  }
473  catch (const std::system_error &err) {
474  LOG(llevError, "metaserver2_init: failed to create thread, code %d, what %s\n", err.code().value(), err.what());
475  /* Effectively true - we're not going to update the metaserver */
477  }
478 #endif
479  return local_info.notification;
480 }
481 
486 #ifdef HAVE_LIBCURL
487  ms2_signal.unlock();
488  if (metaserver_thread.joinable()) {
489  metaserver_thread.join();
490  }
491 #endif
492 }
player::next
player * next
Pointer to next player, NULL if this is last.
Definition: player.h:108
global.h
first_player
player * first_player
First player.
Definition: init.cpp:106
settings
struct Settings settings
Global settings.
Definition: init.cpp:139
CS_Stats::ibytes
int ibytes
ibytes, obytes are bytes in, out.
Definition: newclient.h:699
llevError
@ llevError
Problems requiring server admin to fix.
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:82
player
One player.
Definition: player.h:107
ms2_signal
static std::timed_mutex ms2_signal
When this mutex becomes unlocked, make the metaserver thread exit.
Definition: metaserver.cpp:48
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:371
LocalMeta2Info::notification
int notification
If true, do updates to metaservers.
Definition: metaserver.cpp:122
time
same as sound ncom command like but with extra the client want tick commands so it knows animation timing the client wants to be informed of pickup mode changes Mode will be sent when the player successfully logs and afterward any time the value is but over time
Definition: protocol.txt:416
FALSE
#define FALSE
Definition: compat.h:14
FULL_VERSION
#define FULL_VERSION
Definition: version.h:4
FLAG_WIZ
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:218
FLAG_AFK
#define FLAG_AFK
Player is AFK.
Definition: define.h:355
ST_PLAYING
#define ST_PLAYING
Usual state.
Definition: define.h:525
metaserver_update
void metaserver_update(void)
Updates our info in the metaserver Note that this is used for both metaserver1 and metaserver2 - for ...
Definition: metaserver.cpp:82
Settings::csport
uint16_t csport
Port for new client/server.
Definition: global.h:247
LocalMeta2Info::mapbase
std::string mapbase
and server.
Definition: metaserver.cpp:128
buf
StringBuffer * buf
Definition: readable.cpp:1564
version.h
LocalMeta2Info::last_read
time_t last_read
When config was last read.
Definition: metaserver.cpp:131
LocalMeta2Info::codebase
std::string codebase
Definition: metaserver.cpp:129
CS_Stats::time_start
time_t time_start
Definition: newclient.h:702
metaserver2_init
int metaserver2_init(void)
Initialize metaserver reporting.
Definition: metaserver.cpp:462
MetaServer2_UpdateInfo::num_players
int num_players
Number of players.
Definition: metaserver2.h:32
LocalMeta2Info::text_comment
std::string text_comment
text comment to send to metaservers.
Definition: metaserver.cpp:126
local_info
static LocalMeta2Info local_info
Non volatile information on the server.
Definition: metaserver.cpp:135
LocalMeta2Info::portnumber
int portnumber
Portnumber of this server.
Definition: metaserver.cpp:124
metaserver_thread
static std::thread metaserver_thread
Metaserver thread, if notifications are enabled.
Definition: metaserver.cpp:138
Settings::confdir
const char * confdir
Configuration files.
Definition: global.h:252
LocalMeta2Info::archbase
std::string archbase
Different sources for arches, maps.
Definition: metaserver.cpp:127
VERSION_SC
#define VERSION_SC
Definition: newserver.h:155
MetaServer2_UpdateInfo::out_bytes
int out_bytes
Number of bytes sent.
Definition: metaserver2.h:34
metaserver2_exit
void metaserver2_exit()
Stop metaserver updates.
Definition: metaserver.cpp:485
CS_Stats::obytes
int obytes
Definition: newclient.h:700
seconds
long seconds(void)
Return wall clock time in seconds.
Definition: time.cpp:348
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
metaserver2_updateinfo
MetaServer2_UpdateInfo metaserver2_updateinfo
Statistics on players and such sent to the metaserver2.
Definition: metaserver.cpp:141
cst_tot
CS_Stats cst_tot
metaservers
static std::vector< std::string > metaservers
Metaservers to send information to, list of hostnames.
Definition: metaserver.cpp:111
llevInfo
@ llevInfo
Information.
Definition: logger.h:14
ms2_info_mutex
static std::mutex ms2_info_mutex
Mutex to protect access to metaserver2_updateinfo.
Definition: metaserver.cpp:46
MetaServer2_UpdateInfo::uptime
time_t uptime
How long server has been up.
Definition: metaserver2.h:35
metaserver2_load_config
void metaserver2_load_config()
Definition: metaserver.cpp:280
MetaServer2_UpdateInfo
Structure containing information sent to the metaserver2.
Definition: metaserver2.h:31
LocalMeta2Info
LocalMeta2Info basically holds all the non server metaserver2 information that we read from the metas...
Definition: metaserver.cpp:121
LocalMeta2Info::flags
std::string flags
Short flags to send to metaserver.
Definition: metaserver.cpp:130
metaserver2.h
data
====Textual A command containing textual data has data fields separated by one ASCII space character. word::A sequence of ASCII characters that does not contain the space or nul character. This is to distinguish it from the _string_, which may contain space characters. Not to be confused with a machine word. int::A _word_ containing the textual representation of an integer. Not to be confused with any of the binary integers in the following section. Otherwise known as the "string value of integer data". Must be parsed, e.g. using `atoi()` to get the actual integer value. string::A sequence of ASCII characters. This must only appear at the end of a command, since spaces are used to separate fields of a textual message.=====Binary All multi-byte integers are transmitted in network byte order(MSB first). int8::1-byte(8-bit) integer int16::2-byte(16-bit) integer int32::4-byte(32-bit) integer lstring::A length-prefixed string, which consists of an `int8` followed by that many bytes of the actual string. This is used to transmit a string(that may contain spaces) in the middle of binary data. l2string::Like _lstring_, but is prefixed with an `int16` to support longer strings Implementation Notes ~~~~~~~~~~~~~~~~~~~~ - Typical implementations read two bytes to determine the length of the subsequent read for the actual message, then read and parse the data from each message according to the commands described below. To send a message, the sender builds the message in a buffer, counts the length of the message, sends the length, and finally sends the actual message. TIP:Incorrectly transmitting or receiving the `length` field can lead to apparent "no response" issues as the client or server blocks to read the entire length of the message. - Since the protocol is highly interactive, it may be useful to set `TCP_NODELAY` on both the client and server. - If you are using a language with a buffered output stream, remember to flush the stream after a complete message. - If the connection is lost(which will also happen if the output buffer overflowing), the player is saved and the server cleans up. This does open up some abuses, but there is no perfect solution here. - The server only reads data from the socket if the player has an action. This isn 't really good, since many of the commands below might not be actual commands for the player. The alternative is to look at the data, and if it is a player command and there isn 't time, store it away to be processed later. But this increases complexity, in that the server must start buffering the commands. Fortunately, for now, there are few such client commands. Commands -------- In the documentation below, `S->C` represents a message to the client from the server, and `C->S` represents a message to the server from the client. Commands are documented in a brief format like:C->S:version< csval >[scval[vinfo]] Fields are enclosed like `< this >`. Optional fields are denoted like `[this]`. Spaces that appear in the command are literal, i.e. the<< _version > > command above uses spaces to separate its fields, but the command below does not:C->S:accountlogin< name >< password > As described in<< _messages > >, if a command contains data, then the command is separated from the data by a literal space. Many of the commands below refer to 'object tags'. Whenever the server creates an object, it creates a unique tag for that object(starting at 1 when the server is first run, and ever increasing.) Tags are unique, but are not consistent between runs. Thus, the client can not store tags when it exits and hope to re-use them when it joins the server at a later time - tags are only valid for the current connection. The protocol commands are broken into various sections which based somewhat on what the commands are for(ie, item related commands, map commands, image commands, etc.) In this way, all the commands related to similar functionality is in the same place. Initialization ~~~~~~~~~~~~~~ version ^^^^^^^ C->S:version< csval >[scval[vinfo]] S->C:version< csval >[scval[vinfo]] Used by the client and server to exchange which version of the Crossfire protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval::int, version level of C->S communications scval::int, version level of S->C communications vinfo::string, that is purely for informative that general client/server info(ie, javaclient, x11client, winclient, sinix server, etc). It is purely of interest of server admins who can see what type of clients people are using.=====Version ID If a new command is added to the protocol in the C->S direction, then the version number in csval will get increased. Likewise, the same is true for the scval. The version are currently integers, in the form ABCD. A=1, and will likely for quite a while. This will only really change if needed from rollover of B. B represents major protocol changes - if B mismatches, the clients will be totally unusable. Such an example would be change of map or item sending commands(either new commands or new format.) C represents more minor but still significant changes - clients might still work together, but some features that used to work may now fail due to the mismatch. An example may be a change in the meaning of some field in some command - providing the field is the same size, it still should be decoded properly, but the meaning won 't be processed properly. D represents very minor changes or new commands. Things should work no worse if D does not match, however if they do match, some new features might be included. An example of the would be the C->S mark command to mark items. Server not understanding this just means that the server can not process it, and will ignore it.=====Handling As far as the client is concerned, its _scval_ must be at least equal to the server, and its _csval_ should not be newer than the server. The server does not care about the version command it receives right now - all it currently does is log mismatches. In theory, the server should keep track of what the client has, and adjust the commands it sends respectively in the S->C direction. The server is resilant enough that it won 't crash with a version mismatch(however, client may end up sending commands that the server just ignores). It is really up to the client to enforce versioning and quit if the versions don 't match. NOTE:Since all packets have the length as the first 2 bytes, all that either the client or server needs to be able to do is look at the first string and see if it understands it. If not, it knows how many bytes it can skip. As such, exact version matches should not be necessary for proper operation - however, both the client and server needs to be coded to handle such cases.=====History _scval_ and _vinfo_ were added in version 1020. Before then, there was only one version sent in the version command. NOTE:For the most part, this has been obsoleted by the setup command which always return status and whether it understood the command or not. However there are still some cases where using this versioning is useful - an example it the addition of the requestinfo/replyinfo commands - the client wants to wait for acknowledge of all the replyinfo commands it has issued before sending the addme command. However, if the server doesn 't understand these options, the client will never get a response. With the versioning, the client can look at the version and know if it should wait for a response or if the server will never send back. setup ^^^^^ C->S, S->C:setup< option1 >< value1 >< option2 >< value2 > ... Sent by the client to request protocol option changes. This can be at any point during the life of a connection, but usually sent at least once right after the<< _version > > command. The server responds with a message in the same format confirming what configuration options were set. The server only sends a setup command in response to one from the client. The sc_version should be updated in the server if commands have been obsoleted such that old clients may not be able to play. option::word, name of configuration option value::word, value of configuration option. May need further parsing according to the setup options below=====Setup Options There are really 2 set of setup commands here:. Those that control preferences of the client(how big is the map, what faceset to use, etc). . Those that describe capabilities of the client(client supports this protocol command or that) .Setup Options[options="autowidth,header"]|===========================|Command|Description|beat|Ask the server to enable heartbeat support. When heartbeat is enabled, the client must send the server a command every three seconds. If no commands need to be sent, use the `beat` no-op command. Clients that do not contact the server within the interval are assumed to have a temporary connection failure.|bot(0/1 value)|If set to 1, the client will not be considered a player when updating information to the metaserver. This is to avoid having a server with many bots appear more crowded than others.|darkness(0/1 value)|If set to 1(default), the server will send darkness information in the map protocol commands. If 0, the server will not include darkness, thus saving a minor amount of bandwidth. Since the client is free to ignore the darkness information, this does not allow the client to cheat. In the case of the old 'map' protocol command, turning darkness off will result in the masking faces not getting sent to the client.|extended_stats(0/1 value)|If set to 1, the server will send the CS_STAT_RACE_xxx and CS_STAT_BASE_xxx values too, so the client can display various status related to statistics. Default is 0.|facecache(0/1)|Determines if the client is caching images(1) or wants the images sent to it without caching them(0). Default is 0. This replaces the setfacemode command.|faceset(8 bit)|Faceset the client wishes to use. If the faceset is not valid, the server returns the faceset the client will be using(default 0).|loginmethod(8 bit)|Client sends this to server to note login support. This is basically used as a subset of the csversion/scversion to find out what level of login support the server and client support. Current defined values:0:no advanced support - only legacy login method 1:account based login(described more below) 2:new character creation support This list may grow - for example, advanced character creation could become a feature.|map2cmd:(1)|This indicates client support for the map2 protocol command. See the map2 protocol details above for the main differences. Obsolete:This is the only supported mode now, but many clients use it as a sanity check for protocol versions, so the server still replies. It doesn 't do anything with the data|mapsize(int x) X(int y)|Sets the map size to x X y. Note the spaces here are only for clarity - there should be no spaces when actually sent(it should be 11x11 or 25x25). The default map size unless changed is 11x11. The minimum map size the server will allow is 9x9(no technical reason this could be smaller, but I don 't think the game would be smaller). The maximum map size supported in the current protocol is 63x63. However, each server can have its maximum map size sent to most any value. If the client sends an invalid mapsize command or a mapsize of 0x0, the server will respond with a mapsize that is the maximum size the server supports. Thus, if the client wants to know the maximum map size, it can just do a 'mapsize 0x0' or 'mapsize' and it will get the maximum size back. The server will constrain the provided mapsize x &y to the configured minumum and maximums. For example, if the maximum map size is 25x25, the minimum map size is 9x9, and the client sends a 31x7 mapsize request, the mapsize will be set to 25x9 and the server will send back a mapsize 25x9 setup command. When the values are valid, the server will send back a mapsize XxY setup command. Note that this is from its parsed values, so it may not match stringwise with what the client sent, but will match 0 wise. For example, the client may send a 'mapsize 025X025' command, in which case the server will respond with a 'mapsize 25x25' command - the data is functionally the same. The server will send an updated map view when this command is sent.|notifications(int value)|Value indicating what notifications the client accepts. It is incremental, a value means "all notifications till this level". The following levels are supported:1:quest-related notifications("addquest" and "updquest") 2:knowledge-related notifications("addknowledge") 3:character status flags(overloaded, blind,...)|num_look_objects(int value)|The maximum number of objects shown in the ground view. If more objects are present, fake objects are created for selecting the previous/next group of items. Defaults to 50 if not set. The server may adjust the given value to a suitable one data
Definition: protocol.txt:379
strcasecmp
int strcasecmp(const char *s1, const char *s2)
LocalMeta2Info::hostname
std::string hostname
Hostname of this server.
Definition: metaserver.cpp:123
VERSION_CS
#define VERSION_CS
Version >= 1023 understand setup cmd.
Definition: newserver.h:154
metaserver2_config_modified
bool metaserver2_config_modified()
Return if the metaserver2 file has been modified since last load.
Definition: metaserver.cpp:267
TRUE
#define TRUE
Definition: compat.h:11
MetaServer2_UpdateInfo::in_bytes
int in_bytes
Number of bytes received.
Definition: metaserver2.h:33
count_players
int count_players()
Definition: metaserver.cpp:50
ST_GET_PARTY_PASSWORD
#define ST_GET_PARTY_PASSWORD
Player tried to join a password-protected party.
Definition: define.h:533
LocalMeta2Info::html_comment
std::string html_comment
html comment to send to metaservers.
Definition: metaserver.cpp:125