Crossfire Server, Trunk  1.75.0
loop.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 
25 #include "global.h"
26 
27 #include <assert.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #ifndef WIN32 /* ---win32 exclude unix headers */
35 #include <arpa/inet.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <netinet/in.h>
39 #include <netdb.h>
40 #else
41 #include <winsock2.h>
42 #endif /* end win32 */
43 
44 #include "image.h"
45 #include "newserver.h"
46 #include "sockproto.h"
47 #include "sproto.h"
48 
49 extern uint32_t last_time;
50 
51 /*****************************************************************************
52  * Start of command dispatch area.
53  * The commands here are protocol commands.
54  ****************************************************************************/
55 
56 /* Either keep this near the start or end of the file so it is
57  * at least reasonablye easy to find.
58  * There are really 2 commands - those which are sent/received
59  * before player joins, and those happen after the player has joined.
60  * As such, we have function types that might be called, so
61  * we end up having 2 tables.
62  */
63 
65 typedef void (*func_uint8_int_ns)(char *, int, socket_struct *);
66 
69  const char *cmdname;
71 };
72 
74 typedef void (*func_uint8_int_pl)(char *, int, player *);
77  const char *cmdname;
79  const uint8_t flag;
80 };
81 
97 static const struct player_cmd_mapping player_commands[] = {
98  { "examine", examine_cmd, 1 },
99  { "apply", apply_cmd, 1 },
100  { "move", move_cmd, 1 },
101  { "reply", reply_cmd, 0 },
102  { "ncom", (func_uint8_int_pl)new_player_cmd, 1 },
103  { "lookat", look_at_cmd, 1 },
104  { "lock", (func_uint8_int_pl)lock_item_cmd, 1 },
105  { "mark", (func_uint8_int_pl)mark_item_cmd, 1 },
106  { "inscribe", inscribe_scroll_cmd, 0 },
107  { NULL, NULL, 0 } /* terminator */
108 };
109 
111 static const struct client_cmd_mapping client_commands[] = {
112  { "addme", add_me_cmd },
113  { "askface", send_face_cmd }, /* Added: phil */
114  { "beat", NULL },
115  { "requestinfo", request_info_cmd },
116  { "setup", set_up_cmd },
117  { "version", version_cmd },
118  { "asksmooth", ask_smooth_cmd }, /*Added: tchize (smoothing technologies)*/
119  { "accountlogin", account_login_cmd },
120  { "accountnew", account_new_cmd },
121  { "accountaddplayer", account_add_player_cmd },
122  { "accountplay", account_play_cmd },
123  { "accountpw", account_password },
124  { "createplayer", create_player_cmd },
125  { NULL, NULL } /* terminator (I, II & III)*/
126 };
127 
136 void request_info_cmd(char *buf, int len, socket_struct *ns) {
137  char *params = NULL, *cp;
138  /* No match */
139  SockList sl;
140 
141  if (len <= 0 || !buf) {
142  LOG(llevDebug, "IP '%s' sent bogus request_info_cmd information\n", ns->host);
143  return;
144  }
145 
146  /* Set up replyinfo before we modify any of the buffers - this is used
147  * if we don't find a match.
148  */
149  SockList_Init(&sl);
150  SockList_AddString(&sl, "replyinfo ");
151  SockList_AddString(&sl, buf);
152 
153  /* find the first space, make it null, and update the
154  * params pointer.
155  */
156  for (cp = buf; *cp != '\0'; cp++)
157  if (*cp == ' ') {
158  *cp = '\0';
159  params = cp+1;
160  break;
161  }
162  if (!strcmp(buf, "image_info"))
163  send_image_info(ns);
164  else if (!strcmp(buf, "image_sums"))
165  send_image_sums(ns, params);
166  else if (!strcmp(buf, "skill_info"))
167  send_skill_info(ns, params);
168  else if (!strcmp(buf, "skill_extra"))
169  send_skill_extra(ns, params);
170  else if (!strcmp(buf, "spell_paths"))
171  send_spell_paths(ns);
172  else if (!strcmp(buf, "exp_table"))
173  send_exp_table(ns);
174  else if (!strcmp(buf, "race_list"))
175  send_race_list(ns);
176  else if (!strcmp(buf, "race_info"))
177  send_race_info(ns, params);
178  else if (!strcmp(buf, "class_list"))
179  send_class_list(ns);
180  else if (!strcmp(buf, "class_info"))
181  send_class_info(ns, params);
182  else if (!strcmp(buf, "rules"))
183  send_file(ns, "rules");
184  else if (!strcmp(buf, "motd"))
185  send_file(ns, "motd");
186  else if (!strcmp(buf, "news"))
187  send_file(ns, "news");
188  else if (!strcmp(buf,"newcharinfo"))
189  send_new_char_info(ns);
190  else if (!strcmp(buf,"startingmap"))
191  send_map_info(ns);
192  else if (!strcmp(buf, "knowledge_info"))
194  else
195  Send_With_Handling(ns, &sl);
196  SockList_Term(&sl);
197 }
198 
208 static int
209 handle_cmd(socket_struct *ns, player *pl, char *cmd, char *data, int len) {
210  /* Fuzz testing indicated a way to get a null command here
211  * --> make an empty command, but have a length.
212  * So, if we get here with a null command, log it and exit the function.
213  * Neila Hawkins 2020-01-16
214  */
215  if (cmd == NULL) {
216  LOG(llevDebug, "%s: missing command. Sending garbage?\n", ns->host);
217  return 0;
218  }
219  for (int i = 0; client_commands[i].cmdname != NULL; i++) {
220  if (strcmp(cmd, client_commands[i].cmdname) == 0) {
221  if (client_commands[i].cmdproc != NULL) {
222  client_commands[i].cmdproc(data, len, ns);
223  }
224  return 0;
225  }
226  }
227  /* Player must be in the playing state or the flag on the
228  * the command must be zero for the user to use the command -
229  * otherwise, a player cam save, be in the play_again state, and
230  * the map they were on getsswapped out, yet things that try to look
231  * at the map causes a crash. If the command is valid, but
232  * one they can't use, we still swallow it up.
233  */
234  if (pl) {
235  for (int i = 0; player_commands[i].cmdname != NULL; i++) {
236  if (strcmp(cmd, player_commands[i].cmdname) == 0) {
237  if (pl->state == ST_PLAYING || player_commands[i].flag == 0) {
238  player_commands[i].cmdproc(data, len, pl);
239  }
240  return 1;
241  }
242  }
243  }
244  LOG(llevDebug, "%s: invalid command '%s'\n", ns->host, cmd);
245  return 0;
246 }
247 
258  /* Loop through this - maybe we have several complete packets here. */
259  /* Command_count is used to limit the number of requests from
260  * clients that have not logged in - we do not want an unauthenticated
261  * connection to spew us with hundreds of requests. As such,
262  * this counter is only increased in the case of socket level commands.
263  * Note that this also has the effect of throttling down face and other
264  * socket commands from the client. As such, if we have a player attached,
265  * we will process more of these, as getting a fair number when entering
266  * a map may not be uncommon.
267  */
268  int command_count = 0;
269  while (command_count < 5 || (pl && command_count < 25)) {
270  if (pl && pl->state == ST_PLAYING && pl->ob != NULL && pl->ob->speed_left < 0) {
271  // Skip processing players with no turns left.
272  return false;
273  }
274 
275  int status = SockList_ReadPacket(ns->fd, &ns->inbuf, sizeof(ns->inbuf.buf)-1);
276  if (status != 1) {
277  if (status < 0) {
278  ns->status = Ns_Dead;
279  }
280  return false;
281  }
282 
283  /* Since we have a full packet, reset last tick time. */
284  ns->last_tick = 0;
285 
287  assert(ns->inbuf.len >= 2);
288  char *data;
289  char *cmd = strtok_r((char *)ns->inbuf.buf + 2, " ", &data);
290 
291  int got_player_cmd;
292  if (data != NULL) {
293  int rem = ns->inbuf.len - ((unsigned char *)data - ns->inbuf.buf);
294  got_player_cmd = handle_cmd(ns, pl, cmd, data, rem);
295  } else {
296  got_player_cmd = handle_cmd(ns, pl, cmd, NULL, 0);
297  }
298 
300  if (got_player_cmd) {
301  return true;
302  }
303 
304  command_count += 1;
305  /* Evil case, and not a nice workaround, but well...
306  * If we receive eg an accountplay command, the socket is copied
307  * to the player structure, and its faces_sent is set to NULL.
308  * This leads to issues when processing the next commands in the queue,
309  * especially if related to faces...
310  * So get out of here in this case, which we detect because faces_sent is NULL.
311  */
312  if (ns->faces_sent == NULL) {
313  command_count = 6;
314  }
315  }
316  return false;
317 }
318 
319 /*****************************************************************************
320  *
321  * Low level socket looping - select calls and watchdog udp packet
322  * sending.
323  *
324  ******************************************************************************/
325 
326 #ifdef WATCHDOG
327 
333 void watchdog(void) {
334  static int fd = -1;
335  static struct sockaddr_in insock;
336 
337  if (fd == -1) {
338  struct protoent *protoent;
339 
340  if ((protoent = getprotobyname("udp")) == NULL
341  || (fd = socket(PF_INET, SOCK_DGRAM, protoent->p_proto)) == -1) {
342  return;
343  }
344  insock.sin_family = AF_INET;
345  insock.sin_port = htons((unsigned short)13325);
346  insock.sin_addr.s_addr = inet_addr("127.0.0.1");
347  }
348  char buf[MAX_BUF];
349  snprintf(buf, sizeof(buf), "%d\n", pticks);
350  sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&insock, sizeof(insock));
351 }
352 #endif
353 
362 static int is_fd_valid(int fd) {
363 #ifndef WIN32
364  return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
365 #else
366  return 1;
367 #endif
368 }
369 
374 static void new_connection(int listen_fd) {
375  int newsocknum = -1, j;
376 #ifdef HAVE_GETNAMEINFO
377  struct sockaddr_storage addr;
378 #else
379  struct sockaddr_in addr;
380 #endif
381  socklen_t addrlen = sizeof(addr);
382 
383 #ifdef ESRV_DEBUG
384  LOG(llevDebug, "do_server: New Connection\n");
385 #endif
386 
387  for (j = 0; j < socket_info.allocated_sockets; j++)
388  if (init_sockets[j].status == Ns_Avail) {
389  newsocknum = j;
390  break;
391  }
392 
393  if (newsocknum == -1) {
394  /* If this is the case, all sockets currently in used */
395  init_sockets = static_cast<socket_struct *>(realloc(init_sockets, sizeof(socket_struct)*(socket_info.allocated_sockets+1)));
396  if (!init_sockets)
398  newsocknum = socket_info.allocated_sockets;
400  memset(&init_sockets[newsocknum], 0, sizeof(*init_sockets));
401  init_sockets[newsocknum].listen = NULL;
403  init_sockets[newsocknum].faces_sent = static_cast<uint8_t *>(calloc(get_faces_count(), sizeof(*init_sockets[newsocknum].faces_sent)));
404  if (!init_sockets[newsocknum].faces_sent)
406  init_sockets[newsocknum].status = Ns_Avail;
407  }
408 
409  if (newsocknum < 0) {
410  LOG(llevError, "FATAL: didn't allocate a newsocket?? alloc = %d, newsocknum = %d", socket_info.allocated_sockets, newsocknum);
412  }
413 
414  init_sockets[newsocknum].fd = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
415  if (init_sockets[newsocknum].fd == -1) {
416  LOG(llevError, "accept failed: %s\n", strerror(errno));
417  } else {
418  char buf[MAX_BUF];
419 #ifndef HAVE_GETNAMEINFO
420  long ip;
421 #endif
422  socket_struct *ns;
423 
424  ns = &init_sockets[newsocknum];
425 
426 #ifdef HAVE_GETNAMEINFO
427  getnameinfo((struct sockaddr *) &addr, addrlen, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST);
428 #else
429  ip = ntohl(addr.sin_addr.s_addr);
430  snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld", (ip>>24)&255, (ip>>16)&255, (ip>>8)&255, ip&255);
431 #endif
432 
433  if (checkbanned(NULL, buf)) {
434  LOG(llevInfo, "Banned host tried to connect: [%s]\n", buf);
435  close(init_sockets[newsocknum].fd);
436  init_sockets[newsocknum].fd = -1;
437  } else {
438  init_connection(ns, buf);
439  }
440  }
441 }
442 
448 bool connection_alive(const socket_struct *socket) {
449  // If the client doesn't send heartbeats, assume it's connected.
450  if (!socket->heartbeat) {
451  return true;
452  }
453 
454  // If a client message was received recently, it's connected.
455  if (socket->last_tick < tick_length(BEAT_INTERVAL + 1)) {
456  return true;
457  }
458 
459  return false;
460 }
461 
465 static void send_updates(player *pl) {
466  /* Update the players stats once per tick. More efficient than
467  * sending them whenever they change, and probably just as useful
468  */
469  esrv_update_stats(pl);
470  if (pl->last_weight != -1 && pl->last_weight != WEIGHT(pl->ob)) {
471  esrv_update_item(UPD_WEIGHT, pl->ob, pl->ob);
472  if (pl->last_weight != WEIGHT(pl->ob))
473  LOG(llevError, "esrv_update_item(UPD_WEIGHT) did not set player weight: is %lu, should be %lu\n", (unsigned long)pl->last_weight, (unsigned long)WEIGHT(pl->ob));
474  }
475  /* draw_client_map does sanity checking that map is
476  * valid, so don't do it here.
477  */
478  draw_client_map(pl->ob);
479  if (pl->socket->update_look)
480  esrv_draw_look(pl->ob);
481  if (pl->socket->update_inventory) {
482  if (pl->ob->container != NULL)
483  esrv_send_inventory(pl->ob, pl->ob->container);
484  pl->socket->update_inventory = 0;
485  }
486 }
487 
502  for (int i = 0; i < socket_info.allocated_sockets; i++) {
503  if (!is_fd_valid(init_sockets[i].fd)) {
505  // prevent a memory leak in pre-player clients with faces sent
506  FREE_AND_CLEAR(init_sockets[i].faces_sent);
507  }
508  }
509 
510  for (player *pl = first_player; pl != NULL; pl = pl->next) {
511  if (!is_fd_valid(pl->socket->fd)) {
512  pl->socket->status = Ns_Dead;
513  }
514  }
515 }
516 
524 void do_server(void) {
525  fd_set tmp_read, tmp_exceptions;
526  FD_ZERO(&tmp_read);
527  FD_ZERO(&tmp_exceptions);
528 
529  for (int i = 0; i < socket_info.allocated_sockets; i++) {
530  if (init_sockets[i].status == Ns_Dead) {
531  LOG(llevInfo, "Disconnected from %s\n", init_sockets[i].host);
532  if (init_sockets[i].listen) {
533  /* try to reopen the listening socket */
535  } else {
538  }
539  } else if (init_sockets[i].status != Ns_Avail) {
540  FD_SET((uint32_t)init_sockets[i].fd, &tmp_read);
541  FD_SET((uint32_t)init_sockets[i].fd, &tmp_exceptions);
542  }
543  }
544 
545  /* Go through the players. Let the loop set the next pl value,
546  * since we may remove some
547  */
548  player *pl, *next;
549  for (pl = first_player; pl != NULL; ) {
550  if (pl->socket->status == Ns_Dead) {
551  player *npl = pl->next;
552  save_player(pl->ob, 0);
553  leave(pl, 1);
554  final_free_player(pl);
555  pl = npl;
556  } else {
557  FD_SET((uint32_t)pl->socket->fd, &tmp_read);
558  FD_SET((uint32_t)pl->socket->fd, &tmp_exceptions);
559  pl = pl->next;
560  }
561  }
562 
563  long sleep_time = get_sleep_remaining();
564  if (sleep_time < 0) {
565  LOG(llevInfo, "skipping time (over by %ld ms)\n", -sleep_time/1000);
566  jump_time();
567 #ifdef CS_LOGSTATS
569 #endif
570  }
571 
572  // Log information about last tick. This can't be in log_time() because
573  // CS_Stat is in socket/, and time is in common/.
574 #ifdef CS_LOGSTATS
577  cst_lst.ticks++;
578 #endif
579 
580  // Since we don't report cumulative tick time information, we don't need to
581  // compute it for cst_tot. Those statistics are available from the
582  // process_*_utime variables anyway.
583 
584  while (sleep_time > 0) {
585  socket_info.timeout.tv_sec = 0;
586  socket_info.timeout.tv_usec = sleep_time;
587  int pollret = select(socket_info.max_filedescriptor, &tmp_read, NULL,
588  &tmp_exceptions, &socket_info.timeout);
589  if (pollret == -1) {
590  if (errno == EINTR) {
591  // ignore
592  return;
593  } else if (errno == EBADF) {
594  check_all_fds();
595  }
596  LOG(llevError, "select failed: %s\n", strerror(errno));
597  return;
598  } else if (!pollret) {
599  return;
600  }
601 
602  /* Check for any exceptions/input on the sockets */
603  for (int i = 0; i < socket_info.allocated_sockets; i++) {
604  /* listen sockets can stay in status Ns_Dead */
605  if (init_sockets[i].status != Ns_Add) {
606  continue;
607  }
608  if (FD_ISSET(init_sockets[i].fd, &tmp_exceptions)) {
611  continue;
612  }
613  if (FD_ISSET(init_sockets[i].fd, &tmp_read)) {
614  if (init_sockets[i].listen)
616  else
617  handle_client(&init_sockets[i], NULL);
618  }
619  }
620 
621  /* This does roughly the same thing, but for the players now */
622  for (pl = first_player; pl != NULL; pl = next) {
623  next = pl->next;
624  if (pl->socket->status == Ns_Dead)
625  continue;
626 
627  if (FD_ISSET(pl->socket->fd, &tmp_exceptions)) {
628  save_player(pl->ob, 0);
629  leave(pl, 1);
630  final_free_player(pl);
631  } else {
632  bool keep_processing = handle_client(pl->socket, pl);
633  if (!keep_processing) {
634  FD_CLR(pl->socket->fd, &tmp_read);
635  }
636 
637  /* There seems to be rare cases where next points to a removed/freed player.
638  * My belief is that this player does something (shout, move, whatever)
639  * that causes data to be sent to the next player on the list, but
640  * that player is defunct, so the socket codes removes that player.
641  * End result is that next now points at the removed player, and
642  * that has garbage data so we crash. So update the next pointer
643  * while pl is still valid. MSW 2007-04-21
644  */
645  next = pl->next;
646 
647 
648  /* If the player has left the game, then the socket status
649  * will be set to this be the leave function. We don't
650  * need to call leave again, as it has already been called
651  * once.
652  */
653  if (pl->socket->status == Ns_Dead) {
654  save_player(pl->ob, 0);
655  leave(pl, 1);
656  final_free_player(pl);
657  } else {
658  // Send updates that may have resulted from processing the
659  // latest command. This lets the client get a response to a
660  // command without waiting for the current tick to finish
661  // inside this select() loop. Note that we will call
662  // send_updates() again later in process players(),
663  send_updates(pl);
664  }
665  }
666  }
667  sleep_time = get_sleep_remaining();
668  }
669 }
670 
675  player *pl, *next;
676  for (pl = first_player; pl != NULL; pl = next) {
677  next = pl->next;
678  send_updates(pl);
679  /* Increment time since last contact only if logged in. */
680  if (pl->state == ST_PLAYING) {
681  pl->socket->last_tick++;
682 
683  if (!connection_alive(pl->socket)) {
684  // TODO: Handle a lost client connection.
685  LOG(llevDebug, "Lost client connection!\n");
686  }
687 
688  if (pl->socket->tick)
689  send_tick(pl);
690  }
691  }
692 }
request_info_cmd
void request_info_cmd(char *buf, int len, socket_struct *ns)
request_info_cmd is sort of a meta command.
Definition: loop.cpp:136
socket_struct::tick
uint32_t tick
Client wishes to get tick commands.
Definition: newserver.h:106
send_image_info
void send_image_info(socket_struct *ns)
Sends the number of images, checksum of the face file, and the image_info file information.
Definition: image.cpp:113
player::next
player * next
Pointer to next player, NULL if this is last.
Definition: player.h:106
global.h
player_cmd_mapping::cmdname
const char * cmdname
Command name.
Definition: loop.cpp:77
send_image_sums
void send_image_sums(socket_struct *ns, char *params)
Sends requested face information.
Definition: image.cpp:138
first_player
player * first_player
First player.
Definition: init.cpp:106
reply_cmd
void reply_cmd(char *buf, int len, player *pl)
This is a reply to a previous query.
Definition: request.cpp:589
socket_struct::heartbeat
bool heartbeat
Client will send hearbeats.
Definition: newserver.h:112
CS_Stats::total_ticktime
unsigned long total_ticktime
Definition: newclient.h:709
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
init_connection
void init_connection(socket_struct *ns, const char *from_ip)
Initializes a connection.
Definition: init.cpp:96
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
init_sockets
socket_struct * init_sockets
Established connections for clients not yet playing.
Definition: init.cpp:66
player
One player.
Definition: player.h:105
sockproto.h
client_commands
static const struct client_cmd_mapping client_commands[]
Commands sent directly by client, when connecting or when needed.
Definition: loop.cpp:111
socket_struct
Socket structure, represents a client-server connection.
Definition: newserver.h:89
Socket_Info::allocated_sockets
int allocated_sockets
Number of allocated items in init_sockets.
Definition: newserver.h:144
esrv_send_inventory
void esrv_send_inventory(object *pl, object *op)
Sends inventory of a container.
Definition: item.cpp:316
UPD_WEIGHT
#define UPD_WEIGHT
Definition: newclient.h:319
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Adds a string without length.
Definition: lowlevel.cpp:157
player::ob
object * ob
The object representing the player.
Definition: player.h:177
object::speed_left
float speed_left
How much speed is left to spend this round.
Definition: object.h:338
socket_info
Socket_Info socket_info
Socket information.
Definition: init.cpp:52
socket_struct::update_inventory
uint32_t update_inventory
If true, we need to send the inventory list.
Definition: newserver.h:105
mark_item_cmd
void mark_item_cmd(uint8_t *data, int len, player *pl)
Client wants to mark some object.
Definition: item.cpp:767
send_updates
static void send_updates(player *pl)
Send updated stats, map, look, and inventory to the player.
Definition: loop.cpp:465
Socket_Info::max_filedescriptor
int max_filedescriptor
max filedescriptor on the system.
Definition: newserver.h:143
send_new_char_info
void send_new_char_info(socket_struct *ns)
Sends information related to creating a new character to the client.
Definition: requestinfo.cpp:520
send_map_info
void send_map_info(socket_struct *ns)
Send information on available start maps.
Definition: requestinfo.cpp:441
send_exp_table
void send_exp_table(socket_struct *ns)
This sends the experience table the sever is using.
Definition: requestinfo.cpp:158
socket_struct::listen
struct listen_info * listen
Definition: newserver.h:92
is_fd_valid
static int is_fd_valid(int fd)
Checks if file descriptor is valid.
Definition: loop.cpp:362
SEE_LAST_ERROR
@ SEE_LAST_ERROR
Definition: define.h:52
socket_struct::inbuf
SockList inbuf
If we get an incomplete packet, this is used to hold the data.
Definition: newserver.h:99
CS_Stats::ticks
unsigned long ticks
Definition: newclient.h:706
pticks
uint32_t pticks
Number of ticks since time reset.
Definition: time.cpp:47
buf
StringBuffer * buf
Definition: readable.cpp:1565
MAX
#define MAX(x, y)
Definition: compat.h:24
socket_struct::update_look
uint32_t update_look
If true, we need to send the look window.
Definition: newserver.h:104
Ns_Dead
@ Ns_Dead
Definition: newserver.h:67
account_password
void account_password(char *buf, int len, socket_struct *ns)
Handles the account password change.
Definition: request.cpp:3052
send_skill_info
void send_skill_info(socket_struct *ns, char *params)
This sends the skill number to name mapping.
Definition: requestinfo.cpp:71
account_login_cmd
void account_login_cmd(char *buf, int len, socket_struct *ns)
Handles the account login.
Definition: request.cpp:2185
look_at_cmd
void look_at_cmd(char *buf, int len, player *pl)
Client wants to look at some object.
Definition: item.cpp:869
knowledge_send_info
void knowledge_send_info(socket_struct *ns)
Send the reply_info for 'knowledge_info'.
Definition: knowledge.cpp:1374
Ns_Avail
@ Ns_Avail
Definition: newserver.h:65
final_free_player
void final_free_player(player *pl)
Sends the 'goodbye' command to the player, and closes connection.
Definition: init.cpp:453
socklen_t
#define socklen_t
Definition: win32.h:17
player_cmd_mapping::flag
const uint8_t flag
If set, the player must be in the ST_PLAYING state for this command to be available.
Definition: loop.cpp:79
update_players
void update_players()
Send updates to players.
Definition: loop.cpp:674
func_uint8_int_ns
void(* func_uint8_int_ns)(char *, int, socket_struct *)
Prototype for functions the client sends without player interaction.
Definition: loop.cpp:65
esrv_update_stats
void esrv_update_stats(player *pl)
Sends a statistics update.
Definition: request.cpp:866
check_all_fds
void check_all_fds()
Check all file descriptors and mark sockets with invalid ones as dead.
Definition: loop.cpp:501
jump_time
void jump_time()
Definition: time.cpp:198
socket_struct::host
char * host
Which host it is connected from (ip address).
Definition: newserver.h:100
client_cmd_mapping
Definition of a function the client sends without player interaction.
Definition: loop.cpp:68
send_tick
void send_tick(player *pl)
Definition: request.cpp:2003
leave
void leave(player *pl, int draw_exit)
Player logs out, or was disconnected.
Definition: server.cpp:1303
ask_smooth_cmd
void ask_smooth_cmd(char *buf, int len, socket_struct *ns)
Tells client the picture it has to use to smooth a picture number given as argument.
Definition: request.cpp:503
account_add_player_cmd
void account_add_player_cmd(char *buf, int len, socket_struct *ns)
Handle accountaddplayer from server (add a character to this account).
Definition: request.cpp:2398
sproto.h
create_player_cmd
void create_player_cmd(char *buf, int len, socket_struct *ns)
We have received a createplayer command.
Definition: request.cpp:2632
new_player_cmd
void new_player_cmd(uint8_t *buf, int len, player *pl)
This handles the commands issued by the player (ie, north, fire, cast, etc.).
Definition: request.cpp:527
get_sleep_remaining
long get_sleep_remaining()
Definition: time.cpp:189
account_play_cmd
void account_play_cmd(char *buf, int len, socket_struct *ns)
We have received an accountplay command.
Definition: request.cpp:2527
SockList_Init
void SockList_Init(SockList *sl)
Initializes the SockList instance.
Definition: lowlevel.cpp:55
do_server
void do_server(void)
This checks the sockets for input and exceptions, does the right thing.
Definition: loop.cpp:524
SockList::len
size_t len
Definition: newclient.h:689
image.h
client_cmd_mapping::cmdname
const char * cmdname
Command name.
Definition: loop.cpp:69
move_cmd
void move_cmd(char *buf, int len, player *pl)
Moves an object (typically, container to inventory).
Definition: request.cpp:708
fatal
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.cpp:590
send_class_info
void send_class_info(socket_struct *ns, char *params)
Send information on the specified class.
Definition: requestinfo.cpp:413
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
player::last_weight
int32_t last_weight
Last weight as sent to client; -1 means do not send weight.
Definition: player.h:158
Ns_Add
@ Ns_Add
Definition: newserver.h:66
checkbanned
int checkbanned(const char *login, const char *host)
Check if a player and/or host is banned.
Definition: ban.cpp:32
version_cmd
void version_cmd(char *buf, int len, socket_struct *ns)
Client tells its version.
Definition: request.cpp:651
tick_length
unsigned int tick_length(float seconds)
Calculate the number of ticks that correspond to real time.
Definition: time.cpp:382
new_connection
static void new_connection(int listen_fd)
Handle a new connection from a client.
Definition: loop.cpp:374
SockList_Term
void SockList_Term(SockList *sl)
Frees all resources allocated by a SockList instance.
Definition: lowlevel.cpp:65
send_race_list
void send_race_list(socket_struct *ns)
Send the list of player races to the client.
Definition: requestinfo.cpp:326
handle_client
bool handle_client(socket_struct *ns, player *pl)
Handle commands from a client.
Definition: loop.cpp:257
ST_PLAYING
#define ST_PLAYING
Usual state.
Definition: define.h:535
free_newsocket
void free_newsocket(socket_struct *ns)
Frees a socket.
Definition: init.cpp:418
llevInfo
@ llevInfo
Information.
Definition: logger.h:12
inscribe_scroll_cmd
void inscribe_scroll_cmd(char *buf, int len, player *pl)
Definition: item.cpp:964
CS_Stats::max_ticktime
unsigned long max_ticktime
Definition: newclient.h:708
send_class_list
void send_class_list(socket_struct *ns)
Sends the list of classes to the client.
Definition: requestinfo.cpp:392
FREE_AND_CLEAR
#define FREE_AND_CLEAR(xyz)
Free the pointer and then set it to NULL.
Definition: global.h:195
socket_struct::last_tick
uint32_t last_tick
Number of ticks since last communication.
Definition: newserver.h:130
Socket_Info::timeout
struct timeval timeout
Timeout for select.
Definition: newserver.h:142
newserver.h
BEAT_INTERVAL
#define BEAT_INTERVAL
Interval between heartbeat requests.
Definition: config.h:636
socket_struct::status
enum Sock_Status status
Definition: newserver.h:90
player::state
uint8_t state
Input state of the player (name, password, etc).
Definition: player.h:131
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Updates object *op for player *pl.
Definition: main.cpp:359
esrv_draw_look
void esrv_draw_look(object *pl)
Send the look window.
Definition: item.cpp:193
send_file
void send_file(socket_struct *ns, const char *file)
Sends the desired file to the client.
Definition: requestinfo.cpp:479
examine_cmd
void examine_cmd(char *buf, int len, player *pl)
Client wants to examine some object.
Definition: item.cpp:646
socket_struct::faces_sent_len
size_t faces_sent_len
This is the number of elements allocated in faces_sent[].
Definition: newserver.h:95
WEIGHT
#define WEIGHT(op)
Returns the weight of the given object.
Definition: define.h:645
handle_cmd
static int handle_cmd(socket_struct *ns, player *pl, char *cmd, char *data, int len)
Handle a command, either directly sent by the client, or sent for a player's action.
Definition: loop.cpp:209
SockList_ResetRead
void SockList_ResetRead(SockList *sl)
Resets the length of the stored data for reading.
Definition: lowlevel.cpp:83
send_skill_extra
void send_skill_extra(socket_struct *ns, char *params)
Send extra skill information.
Definition: requestinfo.cpp:102
send_spell_paths
void send_spell_paths(socket_struct *ns)
This sends the spell path to name mapping.
Definition: requestinfo.cpp:132
account_new_cmd
void account_new_cmd(char *buf, int len, socket_struct *ns)
Handles the account creation This function shares a fair amount of the same logic as account_login_cm...
Definition: request.cpp:2284
watchdog
void watchdog(void)
socket_struct::faces_sent
uint8_t * faces_sent
This is a bitmap on sent face status.
Definition: newserver.h:96
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
lock_item_cmd
void lock_item_cmd(uint8_t *data, int len, player *pl)
Client wants to apply some object.
Definition: item.cpp:708
save_player
int save_player(object *op, int flag)
Saves a player to disk.
Definition: login.cpp:230
last_time
uint32_t last_time
Definition: time.cpp:50
object::container
object * container
Current container being used.
Definition: object.h:299
socket_struct::fd
int fd
Definition: newserver.h:91
player_cmd_mapping
Definition of a function called in reaction to player's action.
Definition: loop.cpp:76
player::socket
socket_struct * socket
Socket information for this player.
Definition: player.h:107
player_commands
static const struct player_cmd_mapping player_commands[]
Dispatch tables for the server.
Definition: loop.cpp:97
get_faces_count
size_t get_faces_count()
Definition: assets.cpp:292
CS_Stats::ticks_overtime
unsigned long ticks_overtime
Definition: newclient.h:707
SockList_ReadPacket
int SockList_ReadPacket(int fd, SockList *sl, int len)
This reads from fd and puts the data in sl.
Definition: lowlevel.cpp:275
set_up_cmd
void set_up_cmd(char *buf, int len, socket_struct *ns)
This is the Setup cmd - easy first implementation.
Definition: request.cpp:143
cst_lst
CS_Stats cst_lst
Definition: newclient.h:712
SockList::buf
unsigned char buf[MAXSOCKBUF]
Definition: newclient.h:690
send_race_info
void send_race_info(socket_struct *ns, char *params)
Sends information on specified race to the client.
Definition: requestinfo.cpp:347
client_cmd_mapping::cmdproc
const func_uint8_int_ns cmdproc
Function to call.
Definition: loop.cpp:70
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
player_cmd_mapping::cmdproc
const func_uint8_int_pl cmdproc
Function to call.
Definition: loop.cpp:78
add_me_cmd
void add_me_cmd(char *buf, int len, socket_struct *ns)
The client has requested to be added to the game.
Definition: request.cpp:415
draw_client_map
void draw_client_map(object *pl)
Draws client map.
Definition: request.cpp:1647
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Calls Write_To_Socket to send data to the client.
Definition: lowlevel.cpp:447
SockList
Contains the base information we use to make up a packet we want to send.
Definition: newclient.h:684
apply_cmd
void apply_cmd(char *buf, int len, player *pl)
Client wants to apply some object.
Definition: item.cpp:668
func_uint8_int_pl
void(* func_uint8_int_pl)(char *, int, player *)
Prototype for functions used to handle player actions.
Definition: loop.cpp:74
SockList_NullTerminate
void SockList_NullTerminate(SockList *sl)
Adds a NUL byte without changing the length.
Definition: lowlevel.cpp:237
send_face_cmd
void send_face_cmd(char *buff, int len, socket_struct *ns)
Client has requested pixmap that it somehow missed getting.
Definition: image.cpp:45
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
init_listening_socket
void init_listening_socket(socket_struct *ns)
This opens *ns for listening to connections.
Definition: init.cpp:192
connection_alive
bool connection_alive(const socket_struct *socket)
Check whether the given socket's connection is alive or not.
Definition: loop.cpp:448