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 
27 #ifndef WIN32 /* ---win32 exclude unix header files */
28 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <arpa/inet.h>
32 
33 #endif /* end win32 */
34 
35 #include "metaserver2.h"
36 #include "version.h"
37 
38 #ifdef HAVE_LIBCURL
39 #include <curl/curl.h>
40 #include <curl/easy.h>
41 #endif
42 
44 static std::mutex ms2_info_mutex;
46 static std::timed_mutex ms2_signal;
47 
49  /* We could use socket_info.nconns, but that is not quite as accurate,
50  * as connections in the progress of being established, are listening
51  * but don't have a player, etc. The checks below are basically the
52  * same as for the who commands with the addition that WIZ, AFK, and BOT
53  * players are not counted.
54  */
55  char num_players = 0;
56  for (player *pl = first_player; pl != NULL; pl = pl->next) {
57  if (pl->ob->map == NULL)
58  continue;
59  if (pl->hidden)
60  continue;
61  if (QUERY_FLAG(pl->ob, FLAG_WIZ))
62  continue;
63  if (QUERY_FLAG(pl->ob, FLAG_AFK))
64  continue;
65  if (pl->state != ST_PLAYING && pl->state != ST_GET_PARTY_PASSWORD)
66  continue;
67  if (pl->socket->is_bot)
68  continue;
69  num_players++;
70  }
71  return num_players;
72 }
73 
80 void metaserver_update(void) {
81 #ifdef HAVE_LIBCURL
82  /* Everything inside the lock/unlock is related
83  * to metaserver2 synchronization.
84  */
85  ms2_info_mutex.lock();
87 #ifdef CS_LOGSTATS
91 #else
95 #endif
96  ms2_info_mutex.unlock();
97 #endif
98 }
99 
100 /*
101  * Start of metaserver2 logic
102  * Note: All static structures in this file should be treated as strictly
103  * private. The metaserver2 update logic runs in its own thread,
104  * so if something needs to modify these structures, proper locking
105  * is needed.
106  */
107 
109 static std::vector<std::string> metaservers;
110 
121  char *hostname;
123  char *html_comment;
124  char *text_comment;
125  char *archbase;
126  char *mapbase;
127  char *codebase;
128  char *flags;
129 };
130 
133 
135 static std::thread metaserver_thread;
136 
139 
140 #ifdef HAVE_LIBCURL
141 
153 static size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data) {
154  (void)data;
155  size_t realsize = size*nmemb;
156  LOG(llevError, "Message from metaserver:\n%s\n", (const char*)ptr);
157  return realsize;
158 }
159 
160 static void metaserver2_build_form(struct curl_httppost **formpost) {
161  struct curl_httppost *lastptr = NULL;
162  char buf[MAX_BUF];
163 
164  /* First, fill in the form - note that everything has to be a string,
165  * so we convert as needed with snprintf.
166  * The order of fields here really isn't important.
167  * The string after CURLFORM_COPYNAME is the name of the POST variable
168  * as the
169  */
170  curl_formadd(formpost, &lastptr,
171  CURLFORM_COPYNAME, "hostname",
172  CURLFORM_COPYCONTENTS, local_info.hostname,
173  CURLFORM_END);
174 
175  snprintf(buf, sizeof(buf), "%d", local_info.portnumber);
176  curl_formadd(formpost, &lastptr,
177  CURLFORM_COPYNAME, "port",
178  CURLFORM_COPYCONTENTS, buf,
179  CURLFORM_END);
180 
181  curl_formadd(formpost, &lastptr,
182  CURLFORM_COPYNAME, "html_comment",
183  CURLFORM_COPYCONTENTS, local_info.html_comment,
184  CURLFORM_END);
185 
186  curl_formadd(formpost, &lastptr,
187  CURLFORM_COPYNAME, "text_comment",
188  CURLFORM_COPYCONTENTS, local_info.text_comment,
189  CURLFORM_END);
190 
191  curl_formadd(formpost, &lastptr,
192  CURLFORM_COPYNAME, "archbase",
193  CURLFORM_COPYCONTENTS, local_info.archbase,
194  CURLFORM_END);
195 
196  curl_formadd(formpost, &lastptr,
197  CURLFORM_COPYNAME, "mapbase",
198  CURLFORM_COPYCONTENTS, local_info.mapbase,
199  CURLFORM_END);
200 
201  curl_formadd(formpost, &lastptr,
202  CURLFORM_COPYNAME, "codebase",
203  CURLFORM_COPYCONTENTS, local_info.codebase,
204  CURLFORM_END);
205 
206  curl_formadd(formpost, &lastptr,
207  CURLFORM_COPYNAME, "flags",
208  CURLFORM_COPYCONTENTS, local_info.flags,
209  CURLFORM_END);
210 
211  ms2_info_mutex.lock();
212 
213  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.num_players);
214  curl_formadd(formpost, &lastptr,
215  CURLFORM_COPYNAME, "num_players",
216  CURLFORM_COPYCONTENTS, buf,
217  CURLFORM_END);
218 
219  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.in_bytes);
220  curl_formadd(formpost, &lastptr,
221  CURLFORM_COPYNAME, "in_bytes",
222  CURLFORM_COPYCONTENTS, buf,
223  CURLFORM_END);
224 
225  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.out_bytes);
226  curl_formadd(formpost, &lastptr,
227  CURLFORM_COPYNAME, "out_bytes",
228  CURLFORM_COPYCONTENTS, buf,
229  CURLFORM_END);
230 
231  snprintf(buf, sizeof(buf), "%ld", (long)metaserver2_updateinfo.uptime);
232  curl_formadd(formpost, &lastptr,
233  CURLFORM_COPYNAME, "uptime",
234  CURLFORM_COPYCONTENTS, buf,
235  CURLFORM_END);
236 
237  ms2_info_mutex.unlock();
238 
239  /* Following few fields are global variables,
240  * but are really defines, so won't ever change.
241  */
242  curl_formadd(formpost, &lastptr,
243  CURLFORM_COPYNAME, "version",
244  CURLFORM_COPYCONTENTS, FULL_VERSION,
245  CURLFORM_END);
246 
247  snprintf(buf, sizeof(buf), "%d", VERSION_SC);
248  curl_formadd(formpost, &lastptr,
249  CURLFORM_COPYNAME, "sc_version",
250  CURLFORM_COPYCONTENTS, buf,
251  CURLFORM_END);
252 
253  snprintf(buf, sizeof(buf), "%d", VERSION_CS);
254  curl_formadd(formpost, &lastptr,
255  CURLFORM_COPYNAME, "cs_version",
256  CURLFORM_COPYCONTENTS, buf,
257  CURLFORM_END);
258 }
259 #endif
260 
266 static void metaserver2_updates(void) {
267 #ifdef HAVE_LIBCURL
268  struct curl_httppost *formpost = NULL;
269  metaserver2_build_form(&formpost);
270 
271  for (auto hostname : metaservers) {
272  CURL *curl = curl_easy_init();
273  if (curl) {
274  curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30000);
275 
276  /* what URL that receives this POST */
277  curl_easy_setopt(curl, CURLOPT_URL, hostname.c_str());
278  curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
279 
280  /* Almost always, we will get HTTP data returned
281  * to us - instead of it going to stderr,
282  * we want to take care of it ourselves.
283  */
284  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer);
285 
286  char errbuf[CURL_ERROR_SIZE];
287  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
288 
289  CURLcode res = curl_easy_perform(curl);
290  if (res) {
291  LOG(llevError, "metaserver update failed: %s\n", errbuf);
292  }
293 
294  /* always cleanup */
295  curl_easy_cleanup(curl);
296  } else {
297  LOG(llevError, "metaserver: could not initialize curl\n");
298  }
299  }
300  /* then cleanup the formpost chain */
301  curl_formfree(formpost);
302 #endif
303 }
304 
311  do {
313  } while (!ms2_signal.try_lock_for(std::chrono::seconds(60)));
314  ms2_signal.unlock();
315 }
316 
331 int metaserver2_init(void) {
332  FILE *fp;
333  char buf[MAX_BUF], *cp, dummy[1];
334 
335  dummy[0] = '\0';
336 
337 #ifdef HAVE_LIBCURL
338  static int has_init = 0;
339  if (!has_init) {
340  memset(&local_info, 0, sizeof(LocalMeta2Info));
341  memset(&metaserver2_updateinfo, 0, sizeof(MetaServer2_UpdateInfo));
342 
344  curl_global_init(CURL_GLOBAL_ALL);
345  } else {
347  if (local_info.hostname)
353  if (local_info.archbase)
355  if (local_info.mapbase)
357  if (local_info.codebase)
359  if (local_info.flags)
361  metaservers.clear();
362  }
363 #endif
364 
365  /* Now load up the values from the file */
366  snprintf(buf, sizeof(buf), "%s/metaserver2", settings.confdir);
367 
368  if ((fp = fopen(buf, "r")) == NULL) {
369  LOG(llevError, "Warning: No metaserver2 file found\n");
370  return 0;
371  }
372  while (fgets(buf, sizeof(buf), fp) != NULL) {
373  if (buf[0] == '#')
374  continue;
375  /* eliminate newline */
376  if ((cp = strrchr(buf, '\n')) != NULL)
377  *cp = '\0';
378 
379  /* Skip over empty lines */
380  if (buf[0] == 0)
381  continue;
382 
383  /* Find variable pairs */
384 
385  if ((cp = strpbrk(buf, " \t")) != NULL) {
386  while (isspace(*cp))
387  *cp++ = 0;
388  } else {
389  /* This makes it so we don't have to do NULL checks against
390  * cp everyplace
391  */
392  cp = dummy;
393  }
394 
395  if (!strcasecmp(buf, "metaserver2_notification")) {
396  if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
398  } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
400  } else {
401  LOG(llevError, "metaserver2: Unknown value for metaserver2_notification: %s\n", cp);
402  }
403  } else if (!strcasecmp(buf, "metaserver2_server")) {
404  if (*cp != 0) {
405  metaservers.push_back(cp);
406  } else {
407  LOG(llevError, "metaserver2: metaserver2_server must have a value.\n");
408  }
409  } else if (!strcasecmp(buf, "localhostname")) {
410  if (*cp != 0) {
412  } else {
413  LOG(llevError, "metaserver2: localhostname must have a value.\n");
414  }
415  } else if (!strcasecmp(buf, "portnumber")) {
416  if (*cp != 0) {
417  local_info.portnumber = atoi(cp);
418  } else {
419  LOG(llevError, "metaserver2: portnumber must have a value.\n");
420  }
421  /* For the following values, it is easier to make sure
422  * the pointers are set to something, even if it is a blank
423  * string, so don't care if there is data in the string or not.
424  */
425  } else if (!strcasecmp(buf, "html_comment")) {
426  local_info.html_comment = strdup(cp);
427  } else if (!strcasecmp(buf, "text_comment")) {
428  local_info.text_comment = strdup(cp);
429  } else if (!strcasecmp(buf, "archbase")) {
430  local_info.archbase = strdup(cp);
431  } else if (!strcasecmp(buf, "mapbase")) {
432  local_info.mapbase = strdup(cp);
433  } else if (!strcasecmp(buf, "codebase")) {
434  local_info.codebase = strdup(cp);
435  } else if (!strcasecmp(buf, "flags")) {
436  local_info.flags = strdup(cp);
437  } else {
438  LOG(llevError, "Unknown value in metaserver2 file: %s\n", buf);
439  }
440  }
441  fclose(fp);
442 
443  /* If no hostname is set, can't do updates */
444  if (!local_info.hostname)
446 
447 #ifndef HAVE_LIBCURL
448  if (local_info.notification) {
449  LOG(llevError, "metaserver2 file is set to do notification, but libcurl is not found.\n");
450  LOG(llevError, "Either fix your compilation, or turn off metaserver2 notification in \n");
451  LOG(llevError, "the %s/metaserver2 file.\n", settings.confdir);
452  LOG(llevError, "Exiting program.\n");
453  exit(1);
454  }
455 #endif
456 
457  if (local_info.notification) {
458  /* As noted above, it is much easier for the rest of the code
459  * to not have to check for null pointers. So we do that
460  * here, and anything that is null, we just allocate
461  * an empty string.
462  */
464  local_info.html_comment = strdup("");
466  local_info.text_comment = strdup("");
467  if (!local_info.archbase)
468  local_info.archbase = strdup("");
469  if (!local_info.mapbase)
470  local_info.mapbase = strdup("");
471  if (!local_info.codebase)
472  local_info.codebase = strdup("");
473  if (!local_info.flags)
474  local_info.flags = strdup("");
475 
476  ms2_signal.lock();
477  try {
478  metaserver_thread = std::thread(metaserver2_thread);
479  }
480  catch (const std::system_error &err) {
481  LOG(llevError, "metaserver2_init: failed to create thread, code %d, what %s\n", err.code().value(), err.what());
482  /* Effectively true - we're not going to update the metaserver */
484  }
485  }
486  return local_info.notification;
487 }
488 
493  ms2_signal.unlock();
494  if (metaserver_thread.joinable()) {
495  metaserver_thread.join();
496  }
497 }
player::next
player * next
Pointer to next player, NULL if this is last.
Definition: player.h:106
global.h
first_player
player * first_player
First player.
Definition: init.cpp:106
settings
struct Settings settings
Server settings.
Definition: init.cpp:139
CS_Stats::ibytes
int ibytes
ibytes, obytes are bytes in, out.
Definition: newclient.h:698
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
player
One player.
Definition: player.h:105
LocalMeta2Info::hostname
char * hostname
Hostname of this server.
Definition: metaserver.cpp:121
strdup_local
#define strdup_local
Definition: compat.h:29
ms2_signal
static std::timed_mutex ms2_signal
Mutex to signal the thread should stop.
Definition: metaserver.cpp:46
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
LocalMeta2Info::notification
int notification
If true, do updates to metaservers.
Definition: metaserver.cpp:120
FALSE
#define FALSE
Definition: compat.h:14
FULL_VERSION
#define FULL_VERSION
Definition: version.h:4
LocalMeta2Info::html_comment
char * html_comment
html comment to send to metaservers.
Definition: metaserver.cpp:123
time
non standard information is not specified or uptime this means how long since the executable has been started A particular host may have been running a server for quite a long time
Definition: arch-handbook.txt:206
LocalMeta2Info::mapbase
char * mapbase
and server.
Definition: metaserver.cpp:126
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:80
Settings::csport
uint16_t csport
Port for new client/server.
Definition: global.h:244
buf
StringBuffer * buf
Definition: readable.cpp:1565
version.h
LocalMeta2Info::flags
char * flags
Short flags to send to metaserver.
Definition: metaserver.cpp:128
metaserver2_thread
void metaserver2_thread()
Repeatedly send updates to the metaserver, stopping when ms2_signal is acquired.
Definition: metaserver.cpp:310
CS_Stats::time_start
time_t time_start
Definition: newclient.h:701
metaserver2_init
int metaserver2_init(void)
This initializes the metaserver2 logic - it reads the metaserver2 file, storing the values away.
Definition: metaserver.cpp:331
MetaServer2_UpdateInfo::num_players
int num_players
Number of players.
Definition: metaserver2.h:32
local_info
static LocalMeta2Info local_info
Non volatile information on the server.
Definition: metaserver.cpp:132
LocalMeta2Info::portnumber
int portnumber
Portnumber of this server.
Definition: metaserver.cpp:122
metaserver_thread
static std::thread metaserver_thread
Metaserver thread, if notifications are enabled.
Definition: metaserver.cpp:135
FLAG_AFK
#define FLAG_AFK
Player is AFK.
Definition: define.h:368
Settings::confdir
const char * confdir
Configuration files.
Definition: global.h:249
VERSION_SC
#define VERSION_SC
Definition: newserver.h:150
LocalMeta2Info::text_comment
char * text_comment
text comment to send to metaservers.
Definition: metaserver.cpp:124
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:492
CS_Stats::obytes
int obytes
Definition: newclient.h:699
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:138
cst_tot
CS_Stats cst_tot
ST_PLAYING
#define ST_PLAYING
Usual state.
Definition: define.h:535
metaservers
static std::vector< std::string > metaservers
Metaservers to send information to, list of hostnames.
Definition: metaserver.cpp:109
FLAG_WIZ
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:231
ms2_info_mutex
static std::mutex ms2_info_mutex
Mutex to protect access to metaserver2_updateinfo.
Definition: metaserver.cpp:44
MetaServer2_UpdateInfo::uptime
time_t uptime
How long server has been up.
Definition: metaserver2.h:35
metaserver2_updates
static void metaserver2_updates(void)
This sends an update to the various metaservers.
Definition: metaserver.cpp:266
FREE_AND_CLEAR
#define FREE_AND_CLEAR(xyz)
Free the pointer and then set it to NULL.
Definition: global.h:195
MetaServer2_UpdateInfo
Structure containing information sent to the metaserver2.
Definition: metaserver2.h:31
num_players
non standard information is not specified num_players
Definition: arch-handbook.txt:200
LocalMeta2Info
LocalMeta2Info basically holds all the non server metaserver2 information that we read from the metas...
Definition: metaserver.cpp:119
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
LocalMeta2Info::archbase
char * archbase
Different sources for arches, maps.
Definition: metaserver.cpp:125
strcasecmp
int strcasecmp(const char *s1, const char *s2)
ST_GET_PARTY_PASSWORD
#define ST_GET_PARTY_PASSWORD
Player tried to join a password-protected party.
Definition: define.h:543
VERSION_CS
#define VERSION_CS
Version >= 1023 understand setup cmd.
Definition: newserver.h:149
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:48
LocalMeta2Info::codebase
char * codebase
Definition: metaserver.cpp:127