Crossfire Server, Trunk
request.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 
42 #include "global.h"
43 
44 #include <assert.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/time.h>
48 #include <unistd.h>
49 
50 /* This block is basically taken from socket.c - I assume if it works there,
51  * it should work here.
52  */
53 #ifndef WIN32 /* ---win32 exclude unix headers */
54 #include <sys/types.h>
55 #include <netinet/in.h>
56 #include <netinet/tcp.h>
57 #include <netdb.h>
58 typedef int ssop_t;
59 #else
60 #include <winsock2.h>
61 typedef char ssop_t;
62 #endif /* win32 */
63 
64 #include "commands.h"
65 #include "living.h"
66 #include "newserver.h"
67 #include "shared/newclient.h"
68 #include "sounds.h"
69 #include "sproto.h"
70 
71 #define VALIDCHAR_MSG "The first character must be alphanumeric and the last cannot be a space. None of these characters are allowed: :;/\\["
72 
73 static bool map2_coord_valid(int x) {
74  return x >= -MAP2_COORD_OFFSET && x<= 63 - MAP2_COORD_OFFSET;
75 }
76 
86 static uint16_t MAP2_COORD_ENCODE(int x, int y, int flags) {
87  assert(map2_coord_valid(x));
88  assert(map2_coord_valid(y));
89  assert(flags >= 0 && flags <= 15);
90  return ((x+MAP2_COORD_OFFSET)&0x3f)<<10 | ((y+MAP2_COORD_OFFSET)&0x3f)<<4 | (flags&0x0f);
91 }
92 
99 static const short atnr_cs_stat[NROFATTACKS] = {
104  CS_STAT_RES_DRAIN, -1 /* weaponmagic */,
108  CS_STAT_RES_FEAR, -1 /* Cancellation */,
110  -1 /* Chaos */, -1 /* Counterspell */,
111  -1 /* Godpower */, CS_STAT_RES_HOLYWORD,
113  -1, /* Internal */
114  -1, /* life stealing */
115  -1 /* Disease - not fully done yet */
116 };
117 
119 void set_up_cmd(char *buf, int len, socket_struct *ns) {
120  int s = 0;
121  char *cmd, *param;
122  SockList sl;
123 
124  if (len <= 0 || !buf) {
125  LOG(llevDebug, "IP '%s' sent bogus set_up_cmd information\n", ns->host);
126  return;
127  }
128 
129  /* run through the cmds of setup
130  * syntax is setup <cmdname1> <parameter> <cmdname2> <parameter> ...
131  *
132  * we send the status of the cmd back, or a FALSE is the cmd
133  * is the server unknown
134  * The client then must sort this out
135  */
136 
137  LOG(llevDebug, "setup: %s\n", buf);
138  SockList_Init(&sl);
139  SockList_AddString(&sl, "setup");
140  while (s < len) {
141  cmd = &buf[s];
142 
143  /* find the next space, and put a null there */
144  for (; buf[s] && buf[s] != ' '; s++)
145  ;
146  if (s >= len)
147  break;
148  buf[s++] = 0;
149 
150  while (buf[s] == ' ')
151  s++;
152  if (s >= len)
153  break;
154  param = &buf[s];
155 
156  for (; buf[s] && buf[s] != ' '; s++)
157  ;
158  buf[s++] = 0;
159 
160  while (s < len && buf[s] == ' ')
161  s++;
162 
163  SockList_AddPrintf(&sl, " %s ", cmd);
164 
165  if (!strcmp(cmd, "sound2")) {
166  ns->sound = atoi(param)&(SND_EFFECTS|SND_MUSIC|SND_MUTE);
168  } else if (!strcmp(cmd, "spellmon")) {
169  int monitor_spells;
170 
171  monitor_spells = atoi(param);
172  if (monitor_spells < 0 || monitor_spells > 2) {
173  SockList_AddString(&sl, "FALSE");
174  } else {
175  ns->monitor_spells = monitor_spells;
176  SockList_AddPrintf(&sl, "%d", monitor_spells);
177  }
178  } else if (!strcmp(cmd, "darkness")) {
179  int darkness;
180 
181  darkness = atoi(param);
182  if (darkness != 0 && darkness != 1) {
183  SockList_AddString(&sl, "FALSE");
184  } else {
185  ns->darkness = darkness;
186  SockList_AddPrintf(&sl, "%d", darkness);
187  }
188  } else if (!strcmp(cmd, "map2cmd")) {
189  int map2cmd;
190 
191  map2cmd = atoi(param);
192  if (map2cmd != 1) {
193  SockList_AddString(&sl, "FALSE");
194  } else {
195  SockList_AddString(&sl, "1");
196  }
197  } else if (!strcmp(cmd, "facecache")) {
198  int facecache;
199 
200  facecache = atoi(param);
201  if (facecache != 0 && facecache != 1) {
202  SockList_AddString(&sl, "FALSE");
203  } else {
204  ns->facecache = facecache;
205  SockList_AddPrintf(&sl, "%d", facecache);
206  }
207  } else if (!strcmp(cmd, "faceset")) {
208  int q = atoi(param);
209 
210  if (is_valid_faceset(q))
211  ns->faceset = q;
212  SockList_AddPrintf(&sl, "%d", ns->faceset);
213  } else if (!strcmp(cmd, "mapsize")) {
214  int x, y, n;
215 
216  if (sscanf(param, "%dx%d%n", &x, &y, &n) != 2 || n != (int)strlen(param)) {
217  /* mapsize command is invalid, return maximum map size */
218  x = 0;
219  y = 0;
220  }
221 
222  /* Return the maximum map size if the requested x/y is 0, as per
223  * protocol.txt. Note this does not actually change the map size.
224  */
225  if ((x <= 0) && (y <= 0)) {
227  } else {
228  player *pl;
229 
230  /* Constrain the provided map size to what is defined in config.h */
231  if (x < MAP_CLIENT_X_MINIMUM)
233  if (y < MAP_CLIENT_Y_MINIMUM)
235  if (x > MAP_CLIENT_X)
236  x = MAP_CLIENT_X;
237  if (y > MAP_CLIENT_Y)
238  y = MAP_CLIENT_Y;
239 
240  ns->mapx = x;
241  ns->mapy = y;
242 
243  /* better to send back what we are really using and not the
244  * param as given to us in case it gets parsed differently.
245  */
246  SockList_AddPrintf(&sl, "%dx%d", x, y);
247 
248  /* need to update the los, else the view jumps */
249  pl = find_player_socket(ns);
250  if (pl)
251  update_los(pl->ob);
252 
253  /* Client and server need to resynchronize on data - treating it as
254  * a new map is best way to go.
255  */
256  map_newmap_cmd(ns);
257  }
258  } else if (!strcmp(cmd, "tick")) {
259  int tick;
260 
261  tick = atoi(param);
262  if (tick != 0 && tick != 1) {
263  SockList_AddString(&sl, "FALSE");
264  } else {
265  ns->tick = tick;
266  SockList_AddPrintf(&sl, "%d", tick);
267  }
268  } else if (!strcmp(cmd, "bot")) {
269  int is_bot;
270 
271  is_bot = atoi(param);
272  if (is_bot != 0 && is_bot != 1) {
273  SockList_AddString(&sl, "FALSE");
274  } else {
275  ns->is_bot = is_bot;
276  SockList_AddPrintf(&sl, "%d", is_bot);
277  }
278  } else if (!strcmp(cmd, "want_pickup")) {
279  int want_pickup;
280 
281  want_pickup = atoi(param);
282  if (want_pickup != 0 && want_pickup != 1) {
283  SockList_AddString(&sl, "FALSE");
284  } else {
285  ns->want_pickup = want_pickup;
286  SockList_AddPrintf(&sl, "%d", want_pickup);
287  }
288  } else if (!strcmp(cmd, "num_look_objects")) {
289  int tmp;
290  player *pl;
291 
292  tmp = atoi(param);
293  if (tmp < MIN_NUM_LOOK_OBJECTS) {
295  } else if (tmp > MAX_NUM_LOOK_OBJECTS) {
297  }
298  ns->num_look_objects = (uint8_t)tmp;
299  SockList_AddPrintf(&sl, "%d", tmp);
300 
301  pl = find_player_socket(ns);
302  if (pl && pl->ob) {
303  ns->update_look = 1;
304  esrv_draw_look(pl->ob);
305  }
306  } else if (!strcmp(cmd, "extended_stats")) {
307  int extended_stats;
308 
309  extended_stats = atoi(param);
310  if (extended_stats != 0 && extended_stats != 1) {
311  SockList_AddString(&sl, "FALSE");
312  } else {
313  ns->extended_stats = extended_stats;
314  SockList_AddPrintf(&sl, "%d", extended_stats);
315  }
316  } else if (!strcmp(cmd, "beat")) {
317  int use_beat = atoi(param);
318 
319  if (use_beat != 0 && use_beat != 1) {
320  SockList_AddString(&sl, "FALSE");
321  } else {
322  ns->heartbeat = use_beat ? true : false;
323 
324  // Send setup command with standard beat interval.
325  SockList_AddPrintf(&sl, "%d", BEAT_INTERVAL);
326  }
327  } else if (!strcmp(cmd, "loginmethod")) {
328  int loginmethod;
329 
330  loginmethod = atoi(param);
331 
332  /* Only support basic login right now */
333  if (loginmethod > 2) loginmethod=2;
334 
335  ns->login_method = loginmethod;
336  SockList_AddPrintf(&sl, "%d", loginmethod);
337 
338  } else if (!strcmp(cmd, "notifications")) {
339  int notifications;
340 
341  notifications = atoi(param);
342 
343  ns->notifications = MIN(notifications, 3);
344  SockList_AddPrintf(&sl, "%d", ns->notifications);
345 
346  } else if (!strcmp(cmd, "newmapcmd")) {
347  /* newmapcmd is deprecated (now standard part), but some
348  * clients still use this setup option, and if the server
349  * doesn't respond, erroneously report that the client is
350  * too old. Since it is always on, regardless of what is
351  * request, send back one.
352  */
353  SockList_AddString(&sl, "1");
354  } else if (!strcmp(cmd, "extendedTextInfos")) {
355  /* like newmapcmd above, extendedTextInfos is
356  * obsolete, but we respond for the same reason as we do
357  * in newmapcmd
358  */
359  SockList_AddString(&sl, "1");
360  } else if (!strcmp(cmd, "itemcmd")) {
361  /* like newmapcmd above, itemcmd is
362  * obsolete, but we respond for the same reason as we do
363  * in newmapcmd
364  */
365  SockList_AddString(&sl, "2");
366  } else if (!strcmp(cmd, "exp64")) {
367  /* like newmapcmd above, exp64 is
368  * obsolete, but we respond for the same reason as we do
369  * in newmapcmd
370  */
371  SockList_AddString(&sl, "1");
372  } else {
373  /* Didn't get a setup command we understood -
374  * report a failure to the client.
375  */
376  SockList_AddString(&sl, "FALSE");
377  }
378  } /* for processing all the setup commands */
379  Send_With_Handling(ns, &sl);
380  SockList_Term(&sl);
381 }
382 
391 void add_me_cmd(char *buf, int len, socket_struct *ns) {
392  Settings oldsettings;
393  SockList sl;
394  (void)buf;
395  (void)len;
396 
397  oldsettings = settings;
398  if (ns->status != Ns_Add) {
399  SockList_Init(&sl);
400  SockList_AddString(&sl, "addme_failed");
401  Send_With_Handling(ns, &sl);
402  SockList_Term(&sl);
403  } else if (find_player_socket(ns) == NULL) {
404  /* if there is already a player for this socket (add_me was already called),
405  * just ignore, else weird issues. */
406 
407  add_player(ns, 0);
408  /* Basically, the add_player copies the socket structure into
409  * the player structure, so this one (which is from init_sockets)
410  * is not needed anymore. The write below should still work,
411  * as the stuff in ns is still relevant.
412  */
413  SockList_Init(&sl);
414  SockList_AddString(&sl, "addme_success");
415  Send_With_Handling(ns, &sl);
416  SockList_Term(&sl);
417  if (ns->sc_version < 1027 || ns->cs_version < 1023) {
419  "Warning: Your client is too old to receive map data. Please update to a new client at: "
420  "https://sourceforge.net/projects/crossfire/");
421  }
422  }
423  settings = oldsettings;
424 }
425 
435 static void send_smooth(socket_struct *ns, const Face *face) {
436  const Face *smoothface;
437  SockList sl;
438 
439  // A malicious client can send bogus asksmooth commands that don't
440  // translate to any face. Catch those here before the message below
441  // in order to avoid a segfault.
442  if (!face) {
443  LOG(llevError, "Tried to smooth null face.\n");
444  return;
445  }
446 
447  // Try to find a smoothing face, or the default smoothing face. If this
448  // fails, set NS_FACESENT_SMOOTH so we don't try to send it again.
449  //
450  // Failures are usually due to map makers changing the face of a ground
451  // tile, but forgetting to unset smoothlevel.
452  if (!find_smooth(face, &smoothface)
453  && !find_smooth(smooth_face, &smoothface)) {
454  LOG(llevInfo,
455  "Could not smooth face %s. "
456  "Check that this face has a smoothing pixmap, or remove its smoothlevel.\n",
457  face->name);
458  ns->faces_sent[face->number] |= NS_FACESENT_SMOOTH;
459  return;
460  }
461 
462  if (!(ns->faces_sent[smoothface->number]&NS_FACESENT_FACE))
463  esrv_send_face(ns, smoothface, 0);
464 
465  ns->faces_sent[face->number] |= NS_FACESENT_SMOOTH;
466 
467  SockList_Init(&sl);
468  SockList_AddString(&sl, "smooth ");
469  SockList_AddShort(&sl, face->number);
470  SockList_AddShort(&sl, smoothface->number);
471  Send_With_Handling(ns, &sl);
472  SockList_Term(&sl);
473 }
474 
479 void ask_smooth_cmd(char *buf, int len, socket_struct *ns) {
480  uint16_t facenid;
481 
482  if (len <= 0 || !buf) {
483  LOG(llevDebug, "IP '%s' sent bogus ask_smooth_cmd information\n", ns->host);
484  return;
485  }
486 
487  facenid = atoi(buf);
488  send_smooth(ns, get_face_by_id(facenid));
489 }
490 
503 void new_player_cmd(uint8_t *buf, int len, player *pl) {
504  int time, repeat;
505  short packet;
506  char command[MAX_BUF];
507  SockList sl;
508 
509  if (len < 7) {
510  LOG(llevDebug, "Corrupt ncom command - not long enough - discarding\n");
511  return;
512  }
513 
514  packet = GetShort_String(buf);
515  repeat = GetInt_String(buf+2);
516  /* -1 is special - no repeat, but don't update */
517  if (repeat != -1) {
518  pl->count = repeat;
519  }
520  if (len-4 >= MAX_BUF)
521  len = MAX_BUF-5;
522 
523  strncpy(command, (char *)buf+6, len-4);
524  command[len-4] = '\0';
525 
526  /* The following should never happen with a proper or honest client.
527  * Therefore, the error message doesn't have to be too clear - if
528  * someone is playing with a hacked/non working client, this gives them
529  * an idea of the problem, but they deserve what they get
530  */
531  if (pl->state != ST_PLAYING) {
533  "You can not issue commands - state is not ST_PLAYING (%s)",
534  buf);
535  return;
536  }
537 
538  /* This should not happen anymore. */
539  if (pl->ob->speed_left < 0) {
540  LOG(llevError, "Player %s (%s) has negative time - shouldn't do command.\n",
541  pl->ob->name ? pl->ob->name : "(unnamed)", pl->socket->account_name ? pl->socket->account_name : "(no account)");
542  }
543  ssop_t tmp = 1;
544  if (setsockopt(pl->socket->fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp)))
545  LOG(llevError, "send_tick: Unable to turn off TCP_NODELAY\n");
546  command_execute(pl->ob, command);
547  /* Perhaps something better should be done with a left over count.
548  * Cleaning up the input should probably be done first - all actions
549  * for the command that issued the count should be done before
550  * any other commands.
551  */
552  pl->count = 0;
553 
554  /* Send confirmation of command execution now */
555  SockList_Init(&sl);
556  SockList_AddString(&sl, "comc ");
557  SockList_AddShort(&sl, packet);
558  if (FABS(pl->ob->speed) < 0.001)
559  time = MAX_TIME*100;
560  else
561  time = (int)(MAX_TIME/FABS(pl->ob->speed));
562  SockList_AddInt(&sl, time);
563  Send_With_Handling(pl->socket, &sl);
564  SockList_Term(&sl);
565  tmp = 0;
566  if (setsockopt(pl->socket->fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp)))
567  LOG(llevError, "send_tick: Unable to turn on TCP_NODELAY\n");
568 }
569 
571 void reply_cmd(char *buf, int len, player *pl) {
572 
573  if (len <= 0 || !buf) {
574  LOG(llevDebug, "Player '%s' sent bogus reply_cmd information\n", pl->ob->name);
575  return;
576  }
577 
578  /* this avoids any hacking here */
579 
580  switch (pl->state) {
581  case ST_PLAYING:
582  LOG(llevError, "Got reply message with ST_PLAYING input state\n");
583  break;
584 
585  case ST_PLAY_AGAIN:
586  /* We can check this for return value (2==quit). Maybe we
587  * should, and do something appropriate?
588  */
589  receive_play_again(pl->ob, buf[0]);
590  break;
591 
592  case ST_ROLL_STAT:
593  key_roll_stat(pl->ob, buf[0]);
594  break;
595 
596  case ST_CHANGE_CLASS:
597 
598  key_change_class(pl->ob, buf[0]);
599  break;
600 
601  case ST_CONFIRM_QUIT:
602  key_confirm_quit(pl->ob, buf[0]);
603  break;
604 
605  case ST_GET_NAME:
606  receive_player_name(pl->ob, buf);
607  break;
608 
609  case ST_GET_PASSWORD:
610  case ST_CONFIRM_PASSWORD:
615  break;
616 
617  case ST_GET_PARTY_PASSWORD: /* Get password for party */
619  break;
620 
621  default:
622  LOG(llevError, "Unknown input state: %d\n", pl->state);
623  }
624 }
625 
633 void version_cmd(char *buf, int len, socket_struct *ns) {
634  char *rest = NULL;
635  char *cs_str = strtok_r(buf, " ", &rest);
636  char *sc_str = strtok_r(NULL, " ", &rest);
637  (void)len;
638 
639  if (cs_str == NULL || sc_str == NULL) {
640  LOG(llevError, "%s: sent invalid version string\n", ns->host);
641  return;
642  } else {
643  ns->cs_version = atoi(cs_str);
644  ns->sc_version = atoi(sc_str);
645  }
646 
647 #ifdef ESRV_DEBUG
648  if (VERSION_CS != ns->cs_version) {
649  LOG(llevDebug, "CS: csversion mismatch (%d,%d)\n", VERSION_CS, ns->cs_version);
650  }
651 
652  if (VERSION_SC != ns->sc_version) {
653  LOG(llevDebug, "CS: scversion mismatch (%d,%d)\n", VERSION_SC, ns->sc_version);
654  }
655 #endif
656 
657  if (rest != NULL) {
658  LOG(llevInfo, "Connection from %s (%s), CS %d, SC %d\n",
659  ns->host, rest, ns->cs_version, ns->sc_version);
660  }
661 }
662 
670  SockList sl;
671 
672  /* If getting a newmap command, this scroll information
673  * is no longer relevant.
674  */
675  ns->map_scroll_x = 0;
676  ns->map_scroll_y = 0;
677 
678 
679  memset(&ns->lastmap, 0, sizeof(ns->lastmap));
680  SockList_Init(&sl);
681  SockList_AddString(&sl, "newmap");
682  Send_With_Handling(ns, &sl);
683  SockList_Term(&sl);
684 }
685 
690 void move_cmd(char *buf, int len, player *pl) {
691  int vals[3], i;
692 
693  if (len <= 0 || !buf) {
694  LOG(llevDebug, "Player '%s' sent bogus move_cmd information\n", pl->ob->name);
695  return;
696  }
697 
698  /* A little funky here. We only cycle for 2 records, because
699  * we obviously am not going to find a space after the third
700  * record. Perhaps we should just replace this with a
701  * sscanf?
702  */
703  for (i = 0; i < 2; i++) {
704  vals[i] = atoi(buf);
705  if (!(buf = strchr(buf, ' '))) {
706  LOG(llevError, "Incomplete move command: %s\n", buf);
707  return;
708  }
709  buf++;
710  }
711  vals[2] = atoi(buf);
712 
713 /* LOG(llevDebug, "Move item %d (nrof=%d) to %d.\n", vals[1], vals[2], vals[0]);*/
714  esrv_move_object(pl->ob, vals[0], vals[1], vals[2]);
715 }
716 
717 /***************************************************************************
718  *
719  * Start of commands the server sends to the client.
720  *
721  ***************************************************************************
722  */
723 
728 void send_query(socket_struct *ns, uint8_t flags, const char *text) {
729  SockList sl;
730 
731  SockList_Init(&sl);
732  SockList_AddPrintf(&sl, "query %d %s", flags, text ? text : "");
733  Send_With_Handling(ns, &sl);
734  SockList_Term(&sl);
735 }
736 
737 #define AddIfInt64(Old, New, sl, Type) \
738  if (Old != New) { \
739  Old = New; \
740  SockList_AddChar(sl, Type); \
741  SockList_AddInt64(sl, New); \
742  }
743 
744 #define AddIfInt(Old, New, sl, Type) \
745  if (Old != New) { \
746  Old = New; \
747  SockList_AddChar(sl, Type); \
748  SockList_AddInt(sl, New); \
749  }
750 
751 #define AddIfShort(Old, New, sl, Type) \
752  if (Old != New) { \
753  Old = New; \
754  SockList_AddChar(sl, Type); \
755  SockList_AddShort(sl, New); \
756  }
757 
758 #define AddIfFloat(Old, New, sl, Type) \
759  if (Old != New) { \
760  Old = New; \
761  SockList_AddChar(sl, Type); \
762  SockList_AddInt(sl, (long)(New*FLOAT_MULTI));\
763  }
764 
765 #define AddIfString(Old, New, sl, Type) \
766  if (Old == NULL || strcmp(Old, New)) { \
767  free(Old); \
768  Old = strdup_local(New); \
769  SockList_AddChar(sl, Type); \
770  SockList_AddLen8Data(sl, New, strlen(New)); \
771  }
772 
778 static uint8_t is_perfect(const player *pl) {
779  const int16_t until = MIN(11, pl->ob->level);
780  for (int16_t i = 1; i < until; i++) {
781  if (pl->levhp[i] < 9 || pl->levsp[i] < 6 || pl->levgrace[i] < 3) {
782  return 0;
783  }
784  }
785  return 1;
786 }
787 
793 static void send_extra_stats(SockList *sl, player *pl) {
794  uint32_t uflags = 0;
795  const char *god = "none";
796 
797  if (pl->socket->notifications < 3) {
798  return;
799  }
800 
801  if (!pl->peaceful) {
802  uflags |= CF_HOSTILE;
803  }
804  if (!is_perfect(pl)) {
805  uflags |= CF_NOT_PERFECT;
806  }
807 
808 #define FIF(F, C) \
809  if (QUERY_FLAG(pl->ob, F)) { \
810  uflags |= C; \
811  }
813  FIF(FLAG_CONFUSED, CF_CONFUSED); // If confused by an item
817 
818  FOR_INV_PREPARE(pl->ob, item) {
819  if (item->type == DISEASE && !QUERY_FLAG(item, FLAG_STARTEQUIP)) {
820  uflags |= CF_DISEASED;
821  }
822  if (strcmp(item->arch->name, "poisoning") == 0) {
823  uflags |= CF_POISONED;
824  }
825  if (strcmp(item->arch->name, "blindness") == 0) {
826  uflags |= CF_BLIND;
827  }
828  if (item->type == FORCE && strcmp(item->name, "confusion") == 0) {
829  uflags |= CF_CONFUSED;
830  }
831  if (item->type == SKILL && item->subtype == SK_PRAYING && item->title) {
832  god = item->title;
833  }
834  }
835  FOR_INV_FINISH();
836 
837  AddIfInt(pl->last_character_flags, uflags, sl, CS_STAT_CHARACTER_FLAGS);
838  AddIfFloat(pl->last_character_load, pl->character_load, sl, CS_STAT_OVERLOAD);
839  AddIfString(pl->socket->stats.god, god, sl, CS_STAT_GOD_NAME);
840  AddIfShort(pl->last_item_power, pl->item_power, sl, CS_STAT_ITEM_POWER);
841 }
842 
850  SockList sl;
851  char buf[MAX_BUF];
852  uint16_t flags;
853  uint8_t s;
854 
855  SockList_Init(&sl);
856  SockList_AddString(&sl, "stats ");
857 
858  if (pl->ob != NULL) {
859  AddIfShort(pl->last_stats.hp, pl->ob->stats.hp, &sl, CS_STAT_HP);
860  AddIfShort(pl->last_stats.maxhp, pl->ob->stats.maxhp, &sl, CS_STAT_MAXHP);
861  AddIfShort(pl->last_stats.sp, pl->ob->stats.sp, &sl, CS_STAT_SP);
862  AddIfShort(pl->last_stats.maxsp, pl->ob->stats.maxsp, &sl, CS_STAT_MAXSP);
863  AddIfShort(pl->last_stats.grace, pl->ob->stats.grace, &sl, CS_STAT_GRACE);
864  AddIfShort(pl->last_stats.maxgrace, pl->ob->stats.maxgrace, &sl, CS_STAT_MAXGRACE);
865  AddIfShort(pl->last_stats.Str, pl->ob->stats.Str, &sl, CS_STAT_STR);
866  AddIfShort(pl->last_stats.Int, pl->ob->stats.Int, &sl, CS_STAT_INT);
867  AddIfShort(pl->last_stats.Pow, pl->ob->stats.Pow, &sl, CS_STAT_POW);
868  AddIfShort(pl->last_stats.Wis, pl->ob->stats.Wis, &sl, CS_STAT_WIS);
869  AddIfShort(pl->last_stats.Dex, pl->ob->stats.Dex, &sl, CS_STAT_DEX);
870  AddIfShort(pl->last_stats.Con, pl->ob->stats.Con, &sl, CS_STAT_CON);
871  AddIfShort(pl->last_stats.Cha, pl->ob->stats.Cha, &sl, CS_STAT_CHA);
872  }
873  if (pl->socket->extended_stats) {
874  int16_t golem_hp, golem_maxhp;
875  AddIfShort(pl->last_orig_stats.Str, pl->orig_stats.Str, &sl, CS_STAT_BASE_STR);
876  AddIfShort(pl->last_orig_stats.Int, pl->orig_stats.Int, &sl, CS_STAT_BASE_INT);
877  AddIfShort(pl->last_orig_stats.Pow, pl->orig_stats.Pow, &sl, CS_STAT_BASE_POW);
878  AddIfShort(pl->last_orig_stats.Wis, pl->orig_stats.Wis, &sl, CS_STAT_BASE_WIS);
879  AddIfShort(pl->last_orig_stats.Dex, pl->orig_stats.Dex, &sl, CS_STAT_BASE_DEX);
880  AddIfShort(pl->last_orig_stats.Con, pl->orig_stats.Con, &sl, CS_STAT_BASE_CON);
881  AddIfShort(pl->last_orig_stats.Cha, pl->orig_stats.Cha, &sl, CS_STAT_BASE_CHA);
882  if (pl->ob != NULL) {
883  AddIfShort(pl->last_race_stats.Str, 20 + pl->ob->arch->clone.stats.Str, &sl, CS_STAT_RACE_STR);
884  AddIfShort(pl->last_race_stats.Int, 20 + pl->ob->arch->clone.stats.Int, &sl, CS_STAT_RACE_INT);
885  AddIfShort(pl->last_race_stats.Pow, 20 + pl->ob->arch->clone.stats.Pow, &sl, CS_STAT_RACE_POW);
886  AddIfShort(pl->last_race_stats.Wis, 20 + pl->ob->arch->clone.stats.Wis, &sl, CS_STAT_RACE_WIS);
887  AddIfShort(pl->last_race_stats.Dex, 20 + pl->ob->arch->clone.stats.Dex, &sl, CS_STAT_RACE_DEX);
888  AddIfShort(pl->last_race_stats.Con, 20 + pl->ob->arch->clone.stats.Con, &sl, CS_STAT_RACE_CON);
889  AddIfShort(pl->last_race_stats.Cha, 20 + pl->ob->arch->clone.stats.Cha, &sl, CS_STAT_RACE_CHA);
890  AddIfShort(pl->last_applied_stats.Str, pl->applied_stats.Str, &sl, CS_STAT_APPLIED_STR);
891  AddIfShort(pl->last_applied_stats.Int, pl->applied_stats.Int, &sl, CS_STAT_APPLIED_INT);
892  AddIfShort(pl->last_applied_stats.Pow, pl->applied_stats.Pow, &sl, CS_STAT_APPLIED_POW);
893  AddIfShort(pl->last_applied_stats.Wis, pl->applied_stats.Wis, &sl, CS_STAT_APPLIED_WIS);
894  AddIfShort(pl->last_applied_stats.Dex, pl->applied_stats.Dex, &sl, CS_STAT_APPLIED_DEX);
895  AddIfShort(pl->last_applied_stats.Con, pl->applied_stats.Con, &sl, CS_STAT_APPLIED_CON);
896  AddIfShort(pl->last_applied_stats.Cha, pl->applied_stats.Cha, &sl, CS_STAT_APPLIED_CHA);
897  }
898  if (pl->ranges[range_golem]) {
899  object *golem = pl->ranges[range_golem];
900  if (QUERY_FLAG(golem, FLAG_REMOVED) || golem->count != pl->golem_count || QUERY_FLAG(golem, FLAG_FREED)) {
901  golem_hp = 0;
902  golem_maxhp = 0;
903  } else {
904  golem_hp = golem->stats.hp;
905  golem_maxhp = golem->stats.maxhp;
906  }
907  } else {
908  golem_hp = 0;
909  golem_maxhp = 0;
910  }
911  /* send first the maxhp, so the client can set up the display */
912  AddIfShort(pl->last_golem_maxhp, golem_maxhp, &sl, CS_STAT_GOLEM_MAXHP);
913  AddIfShort(pl->last_golem_hp, golem_hp, &sl, CS_STAT_GOLEM_HP);
914  }
915 
916  for (s = 0; s < MAX_SKILLS; s++) {
917  if (pl->last_skill_ob[s]) {
918  // Skill objects can be removed without updating last_skill_ob.
919  // Clean them up here if that's the case.
920  if (QUERY_FLAG(pl->last_skill_ob[s], FLAG_REMOVED)) {
921  LOG(llevDebug, "pruning removed object from last_skill_ob\n");
922  pl->last_skill_ob[s] = NULL;
923  continue;
924  }
925 
926  if (pl->last_skill_exp[s] != pl->last_skill_ob[s]->stats.exp) {
927  /* Always send along the level if exp changes. This
928  * is only 1 extra byte, but keeps processing simpler.
929  */
930  SockList_AddChar(&sl, (char)(get_skill_client_code(pl->last_skill_ob[s]->name) + CS_STAT_SKILLINFO));
931  SockList_AddChar(&sl, (char)pl->last_skill_ob[s]->level);
932  SockList_AddInt64(&sl, pl->last_skill_ob[s]->stats.exp);
933  pl->last_skill_exp[s] = pl->last_skill_ob[s]->stats.exp;
934  }
935  }
936  }
937  AddIfInt64(pl->last_stats.exp, pl->ob->stats.exp, &sl, CS_STAT_EXP64);
938  AddIfShort(pl->last_level, (char)pl->ob->level, &sl, CS_STAT_LEVEL);
939  AddIfShort(pl->last_stats.wc, pl->ob->stats.wc, &sl, CS_STAT_WC);
940  AddIfShort(pl->last_stats.ac, pl->ob->stats.ac, &sl, CS_STAT_AC);
941  AddIfShort(pl->last_stats.dam, pl->ob->stats.dam, &sl, CS_STAT_DAM);
942  AddIfFloat(pl->last_speed, pl->ob->speed, &sl, CS_STAT_SPEED);
943  AddIfShort(pl->last_stats.food, pl->ob->stats.food, &sl, CS_STAT_FOOD);
944  AddIfFloat(pl->last_weapon_sp, pl->ob->weapon_speed, &sl, CS_STAT_WEAP_SP);
945  AddIfInt(pl->last_weight_limit, (int32_t)get_weight_limit(pl->ob->stats.Str), &sl, CS_STAT_WEIGHT_LIM);
946  flags = 0;
947  if (pl->fire_on)
948  flags |= SF_FIREON;
949  if (pl->run_on)
950  flags |= SF_RUNON;
951 
952  AddIfShort(pl->last_flags, flags, &sl, CS_STAT_FLAGS);
953  if (pl->socket->sc_version < 1025) {
954  AddIfShort(pl->last_resist[ATNR_PHYSICAL], pl->ob->resist[ATNR_PHYSICAL], &sl, CS_STAT_ARMOUR);
955  } else {
956  int i;
957 
958  for (i = 0; i < NROFATTACKS; i++) {
959  /* Skip ones we won't send */
960  if (atnr_cs_stat[i] == -1)
961  continue;
962  AddIfShort(pl->last_resist[i], pl->ob->resist[i], &sl, (char)atnr_cs_stat[i]);
963  }
964  }
965  if (pl->socket->monitor_spells) {
966  AddIfInt(pl->last_path_attuned, pl->ob->path_attuned, &sl, CS_STAT_SPELL_ATTUNE);
967  AddIfInt(pl->last_path_repelled, pl->ob->path_repelled, &sl, CS_STAT_SPELL_REPEL);
968  AddIfInt(pl->last_path_denied, pl->ob->path_denied, &sl, CS_STAT_SPELL_DENY);
969  }
970  /* we want to use the new fire & run system in new client */
971  rangetostring(pl->ob, buf, sizeof(buf));
972  AddIfString(pl->socket->stats.range, buf, &sl, CS_STAT_RANGE);
973  set_title(pl->ob, buf, sizeof(buf));
974  AddIfString(pl->socket->stats.title, buf, &sl, CS_STAT_TITLE);
975 
976  send_extra_stats(&sl, pl);
977 
978  /* Only send it away if we have some actual data - 2 bytes for length, 6 for "stats ". */
979  if (sl.len > 8) {
980 #ifdef ESRV_DEBUG
981  LOG(llevDebug, "Sending stats command, %d bytes long.\n", sl.len);
982 #endif
983  Send_With_Handling(pl->socket, &sl);
984  }
985  SockList_Term(&sl);
986 }
987 
991 void esrv_new_player(player *pl, uint32_t weight) {
992  SockList sl;
993 
994  pl->last_weight = weight;
995 
996  if (!(pl->socket->faces_sent[pl->ob->face->number]&NS_FACESENT_FACE))
997  esrv_send_face(pl->socket, pl->ob->face, 0);
998 
999  SockList_Init(&sl);
1000  SockList_AddString(&sl, "player ");
1001  SockList_AddInt(&sl, pl->ob->count);
1002  SockList_AddInt(&sl, weight);
1003  SockList_AddInt(&sl, pl->ob->face->number);
1004  SockList_AddLen8Data(&sl, pl->ob->name, strlen(pl->ob->name));
1005 
1006  Send_With_Handling(pl->socket, &sl);
1007  SockList_Term(&sl);
1009 }
1010 
1022  SockList sl;
1023  int i;
1024 
1025  if (anim == NULL) {
1026  LOG(llevError, "esrv_send_anim NULL??\n");
1027  return;
1028  }
1029 
1030  SockList_Init(&sl);
1031  SockList_AddString(&sl, "anim ");
1032  SockList_AddShort(&sl, anim->num);
1033  SockList_AddShort(&sl, 0); /* flags - not used right now */
1034  /* Build up the list of faces. Also, send any information (ie, the
1035  * the face itself) down to the client.
1036  */
1037  for (i = 0; i < anim->num_animations; i++) {
1038  if (!(ns->faces_sent[anim->faces[i]->number]&NS_FACESENT_FACE))
1039  esrv_send_face(ns, anim->faces[i], 0);
1040  /* flags - not used right now */
1041  SockList_AddShort(&sl, anim->faces[i]->number);
1042  }
1043  Send_With_Handling(ns, &sl);
1044  SockList_Term(&sl);
1045  ns->anims_sent[anim->num] = 1;
1046 }
1047 
1048 /****************************************************************************
1049  *
1050  * Start of map related commands.
1051  *
1052  ****************************************************************************/
1053 
1055 static void map_clearcell(struct map_cell_struct *cell, int face, int darkness) {
1056  cell->darkness = darkness;
1057  memset(cell->faces, face, sizeof(cell->faces));
1058 }
1059 
1060 #define MAX_HEAD_POS MAX(MAX_CLIENT_X, MAX_CLIENT_Y)
1061 
1069 static const object *heads[MAX_HEAD_POS][MAX_HEAD_POS][MAP_LAYERS];
1070 
1071 /****************************************************************************
1072  * This block is for map2 drawing related commands.
1073  * Note that the map2 still uses other functions.
1074  *
1075  ***************************************************************************/
1076 
1096 static int map2_add_ob(int ax, int ay, int layer, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int is_head) {
1097  uint8_t nlayer, smoothlevel = 0;
1098  const object *head;
1099 
1100  assert(ob != NULL);
1101 
1102  head = HEAD(ob);
1103  const Face *face = ob->face;
1104 
1105  /* This is a multipart object, and we are not at the lower
1106  * right corner. So we need to store away the lower right corner.
1107  */
1108  if (!is_head && (head->arch->tail_x || head->arch->tail_y)
1109  && (head->arch->tail_x != ob->arch->clone.x || head->arch->tail_y != ob->arch->clone.y)) {
1110  int bx, by, l;
1111 
1112  /* Basically figure out where the offset is from where we
1113  * are right now. the ob->arch->clone.{x,y} values hold
1114  * the offset that this current piece is from the head,
1115  * and the tail is where the tail is from the head.
1116  * Note that bx and by will equal sx and sy if we are
1117  * already working on the bottom right corner. If ob is
1118  * the head, the clone values will be zero, so the right
1119  * thing will still happen.
1120  */
1121  bx = ax+head->arch->tail_x-ob->arch->clone.x;
1122  by = ay+head->arch->tail_y-ob->arch->clone.y;
1123 
1124  /* I don't think this can ever happen, but better to check
1125  * for it just in case.
1126  */
1127  if (bx < ax || by < ay) {
1128  LOG(llevError, "map2_add_ob: bx (%d) or by (%d) is less than ax (%d) or ay (%d)\n", bx, by, ax, ay);
1129  face = NULL;
1130  }
1131  /* the target position must be within +/-1 of our current
1132  * layer as the layers are defined. We are basically checking
1133  * to see if we have already stored this object away.
1134  */
1135  for (l = layer-1; l <= layer+1; l++) {
1136  if (l < 0 || l >= MAP_LAYERS)
1137  continue;
1138  if (heads[by][bx][l] == head)
1139  break;
1140  }
1141  /* Didn't find it. So we need to store it away. Try to store it
1142  * on our original layer, and then move up a layer.
1143  */
1144  if (l == layer+2) {
1145  if (!heads[by][bx][layer])
1146  heads[by][bx][layer] = head;
1147  else if (layer+1 < MAP_LAYERS && !heads[by][bx][layer+1])
1148  heads[by][bx][layer+1] = head;
1149  }
1150  return 0;
1151  /* Ok - All done storing away the head for future use */
1152  } else {
1153  uint16_t face_num = face ? face->number : 0;
1154  (*has_obj)++;
1157  face_num = (ob->animation ? ob->animation->num : 0)|(1<<15);
1159  face_num |= ANIM_SYNC;
1161  face_num |= ANIM_RANDOM;
1162  }
1163  /* Since face_num includes the bits for the animation tag,
1164  * and we will store that away in the faces[] array, below
1165  * check works fine _except_ for the case where animation
1166  * speed chances.
1167  */
1168  if (ns->lastmap.cells[ax][ay].faces[layer] != face_num) {
1169  uint8_t len, anim_speed = 0, i;
1170 
1171  /* This block takes care of sending the actual face
1172  * to the client. */
1173  ns->lastmap.cells[ax][ay].faces[layer] = face_num;
1174 
1175  /* Now form the data packet */
1176  nlayer = MAP2_LAYER_START+layer;
1177 
1178  len = 2;
1179 
1180  if (ob->map && !MAP_NOSMOOTH(ob->map)) {
1181  smoothlevel = ob->smoothlevel;
1182  if (smoothlevel)
1183  len++;
1184  }
1185 
1188  len++;
1189  /* 1/0.004 == 250, so this is a good cap for an
1190  * upper limit */
1191  if (ob->anim_speed)
1192  anim_speed = ob->anim_speed;
1193  else if (FABS(ob->speed) < 0.004)
1194  anim_speed = 255;
1195  else if (FABS(ob->speed) >= 1.0)
1196  anim_speed = 1;
1197  else
1198  anim_speed = (int)(1.0/FABS(ob->speed));
1199 
1200  if (ob->animation && !ns->anims_sent[ob->animation->num])
1201  esrv_send_animation(ns, ob->animation);
1202 
1203  /* If smoothing, need to send smoothing information
1204  * for all faces in the animation sequence. Since
1205  * smoothlevel is an object attribute,
1206  * it applies to all faces.
1207  */
1208  if (smoothlevel) {
1209  for (i = 0; i < NUM_ANIMATIONS(ob); i++) {
1210  if (!(ns->faces_sent[ob->animation->faces[i]->number]&NS_FACESENT_SMOOTH))
1211  send_smooth(ns, ob->animation->faces[i]);
1212  }
1213  }
1214  } else if (!(ns->faces_sent[face_num]&NS_FACESENT_FACE)) {
1215  esrv_send_face(ns, face, 0);
1216  }
1217 
1218  if (smoothlevel
1219  && !(ns->faces_sent[ob->face->number]&NS_FACESENT_SMOOTH))
1220  send_smooth(ns, ob->face);
1221 
1222  /* Length of packet */
1223  nlayer |= len<<5;
1224 
1225  SockList_AddChar(sl, nlayer);
1226  SockList_AddShort(sl, face_num);
1227  if (anim_speed)
1228  SockList_AddChar(sl, anim_speed);
1229  if (smoothlevel)
1230  SockList_AddChar(sl, smoothlevel);
1231  return 1;
1232  } /* Face is different */
1233  }
1234  return 0;
1235 }
1236 
1237 /* This function is used see if a layer needs to be cleared.
1238  * It updates the socklist, and returns 1 if the update is
1239  * needed, 0 otherwise.
1240  */
1241 static int map2_delete_layer(int ax, int ay, int layer, SockList *sl, socket_struct *ns) {
1242  int nlayer;
1243 
1244  if (ns->lastmap.cells[ax][ay].faces[layer] != 0) {
1245  /* Now form the data packet */
1246  nlayer = 0x10+layer+(2<<5);
1247  SockList_AddChar(sl, nlayer);
1248  SockList_AddShort(sl, 0);
1249  ns->lastmap.cells[ax][ay].faces[layer] = 0;
1250  return 1;
1251  }
1252  return 0;
1253 }
1254 
1266 static int check_probe(int ax, int ay, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int *alive_layer) {
1267  int got_one = 0, poisoned = 0, diseased = 0;
1268  char name[60];
1269  int value, granularity;
1270  const object *probe;
1271 
1272  /* send hp bar if needed */
1273  if ((*alive_layer) != -1 || ob->head || !CAN_PROBE(ob))
1274  return 0;
1275 
1276  if (settings.always_show_hp == 2) {
1277  /* global hp bars are enabled */
1278  granularity = 30;
1279  } else if (settings.always_show_hp == 1 && ob->stats.hp < ob->stats.maxhp) {
1280  granularity = 30;
1281  } else {
1282  /* only give hp bars to monsters that have been probed */
1283  if (!QUERY_FLAG(ob, FLAG_PROBE)) {
1284  return 0;
1285  }
1286  probe = object_find_by_type_and_name(ob, FORCE, "probe_force");
1287  if (probe == NULL || probe->level < 15) {
1288  /* if probe is not null, this is an error, but well */
1289  return 0;
1290  }
1291 
1292  granularity = (probe->level - 14) / 3;
1293  if (granularity <= 0)
1294  granularity = 1;
1295  else if (granularity > 30)
1296  granularity = 30;
1297  }
1298 
1299  if (ob->stats.maxhp > 0) {
1300  value = (ob->stats.hp * granularity) / (ob->stats.maxhp);
1301 
1302  if (value < 0)
1303  value = 0;
1304  else if (value > granularity)
1305  value = granularity;
1306  } else
1307  value = 30;
1308 
1309  value = (value * 30) / granularity;
1310 
1311  if (object_present_in_ob(POISONING, ob) != NULL)
1312  poisoned = 1;
1313  if (object_present_in_ob(DISEASE, ob) != NULL)
1314  diseased = 1;
1315 
1316  if (value > 0) {
1317  archetype *dummy;
1318 
1319  snprintf(name, sizeof(name), "hpbar%s%s%s_%d",
1320  poisoned ? "_poisoned" : "",
1321  diseased ? "_diseased" : "",
1322  (!poisoned && !diseased) ? "_standard" : "",
1323  value);
1324  dummy = try_find_archetype(name);
1325  if (dummy != NULL) {
1326  got_one += map2_add_ob(ax, ay, MAP_LAYER_FLY2, &dummy->clone, sl, ns, has_obj, 0);
1327  (*alive_layer) = MAP_LAYER_FLY2;
1328  }
1329  }
1330 
1331  return got_one;
1332 }
1333 
1334 static int annotate_ob(int ax, int ay, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int *alive_layer) {
1335  int got_one = check_probe(ax, ay, ob, sl, ns, has_obj, alive_layer);
1337  if (ob->msg != NULL || object_find_by_arch_name(ob, "npc_dialog")) {
1338  archetype *dummy = try_find_archetype("speechbubble");
1339  if (dummy != NULL) {
1340  got_one += map2_add_ob(ax, ay, MAP_LAYER_FLY2, &dummy->clone, sl, ns, has_obj, 0);
1341  (*alive_layer) = MAP_LAYER_FLY2;
1342  }
1343  }
1344  }
1345  return got_one;
1346 }
1347 
1348 /*
1349  * This function is used to check a space (ax, ay) whose only
1350  * data we may care about are any heads. Basically, this
1351  * space is out of direct view. This is only used with the
1352  * Map2 protocol.
1353  *
1354  * @param ax
1355  * viewport relative x-coordinate
1356  * @param ay
1357  * viewport relative y-coordinate
1358  * @param sl
1359  * the reply to append to
1360  * @param ns
1361  * the client socket
1362  */
1363 static void check_space_for_heads(int ax, int ay, SockList *sl, socket_struct *ns) {
1364  int layer, got_one = 0, del_one = 0, oldlen, has_obj = 0;
1365  uint16_t coord;
1366 
1367  coord = MAP2_COORD_ENCODE(ax, ay, 0);
1368  oldlen = sl->len;
1369  SockList_AddShort(sl, coord);
1370 
1371  for (layer = 0; layer < MAP_LAYERS; layer++) {
1372  const object *head;
1373 
1374  head = heads[ay][ax][layer];
1375  if (head) {
1376  /* in this context, got_one should always increase
1377  * because heads should always point to data to really send.
1378  */
1379  got_one += map2_add_ob(ax, ay, layer, head, sl, ns, &has_obj, 1);
1380  } else {
1381  del_one += map2_delete_layer(ax, ay, layer, sl, ns);
1382  }
1383  }
1384  /* Note - if/when lighting information is added, some code is
1385  * needed here - lighting sources that are out of sight may still
1386  * extend into the viewable area.
1387  */
1388 
1389  /* If nothing to do for this space, we
1390  * can erase the coordinate bytes
1391  */
1392  if (!del_one && !got_one) {
1393  sl->len = oldlen;
1394  } else if (del_one && !has_obj) {
1395  /* If we're only deleting faces and not adding, and there
1396  * are not any faces on the space we care about,
1397  * more efficient
1398  * to send 0 as the type/len field.
1399  */
1400  sl->len = oldlen+2; /* 2 bytes for coordinate */
1401  SockList_AddChar(sl, MAP2_TYPE_CLEAR); /* Clear byte */
1402  SockList_AddChar(sl, 255); /* Termination byte */
1403  // Reduce defreferences by passing the inner array offset instead of address of value
1404  map_clearcell(ns->lastmap.cells[ax] + ay, 0, 0);
1405  } else {
1406  SockList_AddChar(sl, 255); /* Termination byte */
1407  }
1408 }
1409 
1410 static void draw_client_map2(object *pl) {
1411  int x, y, ax, ay, darkness, min_x, max_x, min_y, max_y, oldlen, layer;
1412  size_t startlen;
1413  int16_t nx, ny;
1414  SockList sl;
1415  uint16_t coord;
1416  mapstruct *m;
1417  // Dereference once. It should not change in the middle of processing.
1418  player *plyr = pl->contr;
1419 
1420  SockList_Init(&sl);
1421  SockList_AddString(&sl, "map2 ");
1422  startlen = sl.len;
1423 
1424  /* Handle map scroll */
1425  if (plyr->socket->map_scroll_x || plyr->socket->map_scroll_y) {
1427  plyr->socket->map_scroll_x = 0;
1428  plyr->socket->map_scroll_y = 0;
1429  SockList_AddShort(&sl, coord);
1430  }
1431 
1432  /* Init data to zero */
1433  memset(heads, 0, sizeof(heads));
1434 
1435  /* We could do this logic as conditionals in the if statement,
1436  * but that started to get a bit messy to look at.
1437  */
1438  min_x = pl->x-plyr->socket->mapx/2;
1439  min_y = pl->y-plyr->socket->mapy/2;
1440  max_x = pl->x+(plyr->socket->mapx+1)/2+MAX_HEAD_OFFSET;
1441  max_y = pl->y+(plyr->socket->mapy+1)/2+MAX_HEAD_OFFSET;
1442 
1443  /* x, y are the real map locations. ax, ay are viewport relative
1444  * locations.
1445  */
1446  ay = 0;
1447  for (y = min_y; y < max_y; y++, ay++) {
1448  ax = 0;
1449  for (x = min_x; x < max_x; x++, ax++) {
1450  /* If this space is out of the normal viewable area,
1451  * we only check the heads value. This is used to
1452  * handle big images - if they extend to a viewable
1453  * portion, we need to send just the lower right corner.
1454  */
1455  if (ax >= plyr->socket->mapx || ay >= plyr->socket->mapy) {
1456  check_space_for_heads(ax, ay, &sl, plyr->socket);
1457  } else {
1458  /* This space is within the viewport of the client. Due
1459  * to walls or darkness, it may still not be visible.
1460  */
1461 
1462  /* Meaning of darkness (see defines in LOS_* in los.c):
1463  * LOS_NO_DARKNESS (0) - object is in plain sight, full brightness.
1464  * 1 - MAX_DARKNESS - how dark the space is - higher
1465  * value is darker space. If level is at max darkness,
1466  * you can't see the space (too dark)
1467  * LOS_BLOCKED (100) - space is blocked from sight.
1468  */
1469  darkness = plyr->blocked_los[ax][ay];
1470 
1471  /* If the coordinates are not valid, or it is too
1472  * dark to see, we tell the client as such
1473  */
1474  nx = x;
1475  ny = y;
1476  m = get_map_from_coord(pl->map, &nx, &ny);
1477  coord = MAP2_COORD_ENCODE(ax, ay, 0);
1478 
1479  if (!m) {
1480  /* space is out of map. Update space and clear
1481  * values if this hasn't already been done.
1482  * If the space is out of the map, it shouldn't
1483  * have a head.
1484  */
1485  if (plyr->socket->lastmap.cells[ax][ay].darkness != 0) {
1486  SockList_AddShort(&sl, coord);
1488  SockList_AddChar(&sl, 255); /* Termination byte */
1489  // Reduce dereferences by passing the inner array offset instead of address of value
1490  map_clearcell(plyr->socket->lastmap.cells[ax] + ay, 0, 0);
1491  }
1492  } else if (darkness >= MAX_LIGHT_RADII) {
1493  /* This block deals with spaces that are not
1494  * visible due to darkness or walls. Still
1495  * need to send the heads for this space.
1496  */
1497  check_space_for_heads(ax, ay, &sl, plyr->socket);
1498  } else {
1499  int have_darkness = 0, has_obj = 0, got_one = 0, del_one = 0, g1, alive_layer = -1, old_got;
1500 
1501  /* In this block, the space is visible. */
1502 
1503  /* Rather than try to figure out what everything
1504  * that we might need to send is, then form the
1505  * packet after that, we presume that we will in
1506  * fact form a packet, and update the bits by what
1507  * we do actually send. If we send nothing, we
1508  * just back out sl.len to the old value, and no
1509  * harm is done.
1510  * I think this is simpler than doing a bunch of
1511  * checks to see what if anything we need to send,
1512  * setting the bits, then doing those checks again
1513  * to add the real data.
1514  */
1515 
1516  oldlen = sl.len;
1517  SockList_AddShort(&sl, coord);
1518 
1519  /* Darkness changed */
1520  if (plyr->socket->lastmap.cells[ax][ay].darkness != darkness
1521  && plyr->socket->darkness) {
1522  plyr->socket->lastmap.cells[ax][ay].darkness = darkness;
1523  /* Darkness tag & length (length=1) */
1524  SockList_AddChar(&sl, MAP2_TYPE_DARKNESS|(1<<5));
1525 
1526  /* Convert darkness level (0-MAX_DARKNESS) to a value
1527  * from 1 (dark) to 255 (bright) for the client.
1528  * Darkness values of 0 (completely dark) are not sent,
1529  * as the space is completely dark.
1530  */
1531  SockList_AddChar(&sl, 255-darkness*(256/MAX_LIGHT_RADII));
1532  have_darkness = 1;
1533  }
1534 
1535  for (layer = 0; layer < MAP_LAYERS; layer++) {
1536  const object *ob = GET_MAP_FACE_OBJ(m, nx, ny, layer);
1537 
1538  /* Special case: send player itself if invisible */
1539  if (!ob
1540  && x == pl->x
1541  && y == pl->y
1542  && (pl->invisible&(pl->invisible < 50 ? 4 : 1))
1543  && (layer == MAP_LAYER_LIVING1 || layer == MAP_LAYER_LIVING2))
1544  ob = pl;
1545 
1546  if (ob) {
1547  g1 = has_obj;
1548  old_got = got_one;
1549  got_one += map2_add_ob(ax, ay, layer, ob, &sl, plyr->socket, &has_obj, 0);
1550 
1551  /* if we added the face, or it is a monster's head, check probe spell */
1552  if (got_one != old_got || (ob->head == NULL && ob->more))
1553  got_one += annotate_ob(ax, ay, ob, &sl, plyr->socket, &has_obj, &alive_layer);
1554 
1555  /* If we are just storing away the head
1556  * for future use, then effectively this
1557  * space/layer is blank, and we should clear
1558  * it if needed.
1559  */
1560  if (g1 == has_obj) {
1561  del_one += map2_delete_layer(ax, ay, layer, &sl, plyr->socket);
1562  } else if (ob->head == NULL) {
1563  /* for single-part items */
1564  got_one += annotate_ob(ax, ay, ob, &sl, plyr->socket, &has_obj, &alive_layer);
1565  }
1566  } else {
1567  if (layer != alive_layer)
1568  del_one += map2_delete_layer(ax, ay, layer, &sl, plyr->socket);
1569  }
1570  }
1571  /* If nothing to do for this space, we
1572  * can erase the coordinate bytes
1573  */
1574  if (!del_one && !got_one && !have_darkness) {
1575  sl.len = oldlen;
1576  } else if (del_one && !has_obj) {
1577  /* If we're only deleting faces and don't
1578  * have any objs we care about, just clear
1579  * space. Note it is possible we may have
1580  * darkness, but if there is nothing on the
1581  * space, darkness isn't all that interesting
1582  * - we can send it when an object shows up.
1583  */
1584  sl.len = oldlen+2; /* 2 bytes for coordinate */
1586  SockList_AddChar(&sl, 255); /* Termination byte */
1587  // Reduce dereferences by passing the inner array offset instead of address of value
1588  map_clearcell(plyr->socket->lastmap.cells[ax] + ay, 0, 0);
1589  } else {
1590  SockList_AddChar(&sl, 255); /* Termination byte */
1591  }
1592  }
1593  } /* else this is a viewable space */
1594  } /* for x loop */
1595  } /* for y loop */
1596 
1597  /* Only send this if there are in fact some differences. */
1598  if (sl.len > startlen) {
1599  Send_With_Handling(plyr->socket, &sl);
1600  }
1601  SockList_Term(&sl);
1602 }
1603 
1607 void draw_client_map(object *pl) {
1608  int i, j;
1609  int16_t ax, ay;
1610  int mflags;
1611  mapstruct *m, *pm;
1612  int min_x, min_y, max_x, max_y;
1613 
1614  if (pl->type != PLAYER) {
1615  LOG(llevError, "draw_client_map called with non player/non eric-server\n");
1616  return;
1617  }
1618 
1619  if (pl->contr->transport) {
1620  pm = pl->contr->transport->map;
1621  } else
1622  pm = pl->map;
1623 
1624  /* If player is just joining the game, he isn't here yet, so
1625  * the map can get swapped out. If so, don't try to send them
1626  * a map. All will be OK once they really log in.
1627  */
1628  if (pm == NULL || pm->in_memory != MAP_IN_MEMORY)
1629  return;
1630 
1631  /*
1632  * This block just makes sure all the spaces are properly
1633  * updated in terms of what they look like.
1634  */
1635  min_x = pl->x-pl->contr->socket->mapx/2;
1636  min_y = pl->y-pl->contr->socket->mapy/2;
1637  max_x = pl->x+(pl->contr->socket->mapx+1)/2;
1638  max_y = pl->y+(pl->contr->socket->mapy+1)/2;
1639  for (j = min_y; j < max_y; j++) {
1640  for (i = min_x; i < max_x; i++) {
1641  ax = i;
1642  ay = j;
1643  m = pm;
1644  mflags = get_map_flags(m, &m, ax, ay, &ax, &ay);
1645  if (mflags&P_OUT_OF_MAP)
1646  continue;
1647  if (mflags&P_NEED_UPDATE)
1648  update_position(m, ax, ay);
1649  /* If a map is visible to the player, we don't want
1650  * to swap it out just to reload it. This should
1651  * really call something like swap_map, but this is
1652  * much more efficient and 'good enough'
1653  */
1654  if (mflags&P_NEW_MAP)
1655  m->timeout = 50;
1656  }
1657  }
1658 
1659  /* do LOS after calls to update_position */
1660  if (pl->contr->do_los) {
1661  update_los(pl);
1662  pl->contr->do_los = 0;
1663  }
1664 
1666 }
1667 
1668 void esrv_map_scroll(socket_struct *ns, int dx, int dy) {
1669  struct Map newmap;
1670  int x, y, mx, my;
1671 
1672  ns->map_scroll_x += dx;
1673  ns->map_scroll_y += dy;
1674 
1675  mx = ns->mapx+MAX_HEAD_OFFSET;
1676  my = ns->mapy+MAX_HEAD_OFFSET;
1677 
1678  /* the x and y here are coordinates for the new map, i.e. if we moved
1679  * (dx,dy), newmap[x][y] = oldmap[x-dx][y-dy]. For this reason,
1680  * if the destination x or y coordinate is outside the viewable
1681  * area, we clear the values - otherwise, the old values
1682  * are preserved, and the check_head thinks it needs to clear them.
1683  */
1684  for (x = 0; x < mx; x++) {
1685  for (y = 0; y < my; y++) {
1686  if (x >= ns->mapx || y >= ns->mapy) {
1687  /* clear cells outside the viewable area */
1688  memset(&newmap.cells[x][y], 0, sizeof(newmap.cells[x][y]));
1689  } else if (x+dx < 0 || x+dx >= ns->mapx || y+dy < 0 || y+dy >= ns->mapy) {
1690  /* clear newly visible tiles within the viewable area */
1691  memset(&newmap.cells[x][y], 0, sizeof(newmap.cells[x][y]));
1692  } else {
1693  memcpy(&newmap.cells[x][y], &ns->lastmap.cells[x+dx][y+dy], sizeof(newmap.cells[x][y]));
1694  }
1695  }
1696  }
1697 
1698  memcpy(&ns->lastmap, &newmap, sizeof(ns->lastmap));
1699 }
1700 
1706 void send_plugin_custom_message(object *pl, char *buf) {
1707  SockList sl;
1708 
1709  SockList_Init(&sl);
1710  SockList_AddString(&sl, buf);
1711  Send_With_Handling(pl->contr->socket, &sl);
1712  SockList_Term(&sl);
1713 }
1714 
1721  SockList sl;
1722  int flags = 0;
1723  client_spell *spell_info;
1724 
1725  if (!pl->socket || !pl->socket->monitor_spells)
1726  return;
1727 
1728  /* Handles problem at login, where this is called from fix_object
1729  * before we have had a chance to send spells to the player. It does seem
1730  * to me that there should never be a case where update_spells is called
1731  * before add_spells has been called. Add_spells() will update the
1732  * spell_state to non null.
1733  */
1734  if (!pl->spell_state)
1735  return;
1736 
1737  FOR_INV_PREPARE(pl->ob, spell) {
1738  if (spell->type == SPELL) {
1739  spell_info = get_client_spell_state(pl, spell);
1740  /* check if we need to update it*/
1741  if (spell_info->last_sp != SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA)) {
1742  spell_info->last_sp = SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA);
1743  flags |= UPD_SP_MANA;
1744  }
1745  if (spell_info->last_grace != SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE)) {
1747  flags |= UPD_SP_GRACE;
1748  }
1749  if (spell_info->last_dam != spell->stats.dam+SP_level_dam_adjust(pl->ob, spell)) {
1750  spell_info->last_dam = spell->stats.dam+SP_level_dam_adjust(pl->ob, spell);
1751  flags |= UPD_SP_DAMAGE;
1752  }
1753  if (flags != 0) {
1754  SockList_Init(&sl);
1755  SockList_AddString(&sl, "updspell ");
1756  SockList_AddChar(&sl, flags);
1757  SockList_AddInt(&sl, spell->count);
1758  if (flags&UPD_SP_MANA)
1759  SockList_AddShort(&sl, spell_info->last_sp);
1760  if (flags&UPD_SP_GRACE)
1761  SockList_AddShort(&sl, spell_info->last_grace);
1762  if (flags&UPD_SP_DAMAGE)
1763  SockList_AddShort(&sl, spell_info->last_dam);
1764  flags = 0;
1765  Send_With_Handling(pl->socket, &sl);
1766  SockList_Term(&sl);
1767  }
1768  }
1769  } FOR_INV_FINISH();
1770 }
1771 
1773  SockList sl;
1774 
1775  if (!pl || !spell || spell->env != pl->ob) {
1776  LOG(llevError, "Invalid call to esrv_remove_spell\n");
1777  return;
1778  }
1779  if (!pl->socket->monitor_spells)
1780  return;
1781 
1782  SockList_Init(&sl);
1783  SockList_AddString(&sl, "delspell ");
1784  SockList_AddInt(&sl, spell->count);
1785  Send_With_Handling(pl->socket, &sl);
1786  SockList_Term(&sl);
1787 }
1788 
1796  SockList sl;
1797 
1798  if (!pl->socket->want_pickup)
1799  return;
1800  SockList_Init(&sl);
1801  SockList_AddString(&sl, "pickup ");
1802  SockList_AddInt(&sl, pl->mode);
1803  Send_With_Handling(pl->socket, &sl);
1804  SockList_Term(&sl);
1805 }
1806 
1815 static int spell_client_use(const object *spell) {
1816  switch (spell->type)
1817  {
1818  case SP_RAISE_DEAD:
1819  case SP_MAKE_MARK:
1820  return 3;
1821  case SP_RUNE:
1822  if (!spell->other_arch)
1823  return 1;
1824  break;
1825  case SP_CREATE_FOOD:
1826  case SP_CREATE_MISSILE:
1827  return 2;
1828  case SP_SUMMON_MONSTER:
1829  if (spell->randomitems != NULL)
1830  return 2;
1831  /* break; */// If add conditins below, use this break statement
1832  }
1833  // This is not in the switch statement so that it supports fallthrough logic
1834  // on the few spell types that have additional conditions attached.
1835  return 0;
1836 }
1837 
1839 static void append_spell(player *pl, SockList *sl, object *spell) {
1840  client_spell *spell_info;
1841  int len, i, skill = 0;
1842 
1843  if (!spell->name) {
1844  LOG(llevError, "item number %d is a spell with no name.\n", spell->count);
1845  return;
1846  }
1847 
1848  if (spell->face && !(pl->socket->faces_sent[spell->face->number]&NS_FACESENT_FACE))
1849  esrv_send_face(pl->socket, spell->face, 0);
1850 
1851  spell_info = get_client_spell_state(pl, spell);
1852  SockList_AddInt(sl, spell->count);
1853  SockList_AddShort(sl, spell->level);
1854  SockList_AddShort(sl, spell->casting_time);
1855  /* store costs and damage in the object struct, to compare to later */
1856  spell_info->last_sp = SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA);
1858  spell_info->last_dam = spell->stats.dam+SP_level_dam_adjust(pl->ob, spell);
1859  /* send the current values */
1860  SockList_AddShort(sl, spell_info->last_sp);
1861  SockList_AddShort(sl, spell_info->last_grace);
1862  SockList_AddShort(sl, spell_info->last_dam);
1863 
1864  /* figure out which skill it uses, if it uses one */
1865  if (spell->skill) {
1866  for (i = 0; i < MAX_SKILLS && skill_names[i]; i++)
1867  if (!strcmp(spell->skill, skill_names[i])) {
1869  break;
1870  }
1871  }
1872  SockList_AddChar(sl, skill);
1873 
1874  SockList_AddInt(sl, spell->path_attuned);
1875  SockList_AddInt(sl, spell->face ? spell->face->number : 0);
1876  SockList_AddLen8Data(sl, spell->name, strlen(spell->name));
1877 
1878  if (!spell->msg) {
1879  SockList_AddShort(sl, 0);
1880  } else {
1881  len = strlen(spell->msg);
1882  SockList_AddShort(sl, len);
1883  SockList_AddData(sl, spell->msg, len);
1884  }
1885 
1886  /* Extended spell information available if the client wants it.
1887  */
1888  if (pl->socket->monitor_spells >= 2) {
1889  /* spellmon 2
1890  */
1891  sstring req = object_get_value(spell, "casting_requirements");
1892 
1893  SockList_AddChar(sl, spell_client_use(spell)); /* Usage code */
1894 
1895  if (req) { /* Requirements */
1896  SockList_AddLen8Data(sl, req, strlen(req));
1897  } else {
1898  SockList_AddChar(sl, 0);
1899  }
1900  /* end spellmon 2
1901  */
1902  }
1903 }
1904 
1909 void esrv_add_spells(player *pl, object *spell) {
1910  SockList sl;
1911  size_t size;
1912  sstring value;
1913 
1914  if (!pl) {
1915  LOG(llevError, "esrv_add_spells, tried to add a spell to a NULL player\n");
1916  return;
1917  }
1918 
1919  if (!pl->socket->monitor_spells)
1920  return;
1921 
1922  SockList_Init(&sl);
1923  SockList_AddString(&sl, "addspell ");
1924  if (!spell) {
1925  FOR_INV_PREPARE(pl->ob, spell) {
1926  if (spell->type != SPELL)
1927  continue;
1928  /* Were we to simply keep appending data here, we could
1929  * exceed the SockList buffer if the player has enough spells
1930  * to add. We know that append_spell will always append
1931  * 23 data bytes, plus 3 length bytes and 2 strings
1932  * (because that is the spec) so we need to check that
1933  * the length of those 2 strings, plus the 26 bytes,
1934  * won't take us over the length limit for the socket.
1935  * If it does, we need to send what we already have,
1936  * and restart packet formation.
1937  */
1938  size = 26+strlen(spell->name)+(spell->msg ? strlen(spell->msg) : 0);
1939  if (pl->socket->monitor_spells >= 2) {
1941  value = object_get_value(spell, "casting_requirements");
1942  size += 2 + (value ? strlen(value) : 0);
1943  }
1944  if (SockList_Avail(&sl) < size) {
1945  Send_With_Handling(pl->socket, &sl);
1946  SockList_Reset(&sl);
1947  SockList_AddString(&sl, "addspell ");
1948  }
1949  append_spell(pl, &sl, spell);
1950  } FOR_INV_FINISH();
1951  } else if (spell->type != SPELL) {
1952  LOG(llevError, "Asked to send a non-spell object as a spell\n");
1953  return;
1954  } else
1955  append_spell(pl, &sl, spell);
1956  /* finally, we can send the packet */
1957  Send_With_Handling(pl->socket, &sl);
1958  SockList_Term(&sl);
1959 }
1960 
1961 /* sends a 'tick' information to the client.
1962  * We also take the opportunity to toggle TCP_NODELAY -
1963  * this forces the data in the socket to be flushed sooner to the
1964  * client - otherwise, the OS tries to wait for full packets
1965  * and will this hold sending the data for some amount of time,
1966  * which thus adds some additional latency.
1967  */
1969  SockList sl;
1970  ssop_t tmp;
1971 
1972  SockList_Init(&sl);
1973  SockList_AddString(&sl, "tick ");
1974  SockList_AddInt(&sl, pticks);
1975  tmp = 1;
1976  if (setsockopt(pl->socket->fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp)))
1977  LOG(llevError, "send_tick: Unable to turn on TCP_NODELAY\n");
1978 
1979  Send_With_Handling(pl->socket, &sl);
1980  tmp = 0;
1981  if (setsockopt(pl->socket->fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp)))
1982  LOG(llevError, "send_tick: Unable to turn off TCP_NODELAY\n");
1983  SockList_Term(&sl);
1984 }
1985 
1998 static void add_char_field(SockList *sl, int type, const char *data)
1999 {
2000  int len;
2001 
2002  len = strlen(data);
2003 
2004  if (len) {
2005  /* one extra for length for the type byte */
2006  SockList_AddChar(sl, len+1);
2007  SockList_AddChar(sl, type);
2008  SockList_AddString(sl, data);
2009  }
2010 }
2011 
2033 {
2034  SockList sl;
2035  int num_chars;
2036  linked_char *extra;
2037 
2038  if (ns->account_chars) {
2040  }
2042 
2043  num_chars = 0;
2044  extra = account_get_additional_chars(ns->account_name, ns->account_chars, &num_chars);
2045 
2046  SockList_Init(&sl);
2047  SockList_AddString(&sl, "accountplayers ");
2048 
2049  SockList_AddChar(&sl, static_cast<char>(ns->account_chars->chars.size() + num_chars));
2050 
2051  /* Now add real character data */
2052  for (auto acn : ns->account_chars->chars) {
2053  uint16_t faceno = 0;
2054 
2055  /* Ignore a dead character. They don't need to show up. */
2056  if (acn->isDead) {
2057  continue;
2058  }
2059 
2060  add_char_field(&sl, ACL_NAME, acn->name);
2061  add_char_field(&sl, ACL_CLASS, acn->character_class);
2062  add_char_field(&sl, ACL_RACE, acn->race);
2063  add_char_field(&sl, ACL_FACE, acn->face);
2064  if (acn->face[0] != 0 ) {
2065  const Face *face = try_find_face(acn->face, NULL);
2066 
2067  if (face != NULL) {
2068  if (!(ns->faces_sent[face->number]&NS_FACESENT_FACE)) {
2069  esrv_send_face(ns, face, 0);
2070  }
2071  faceno = face->number;
2072  }
2073  }
2074 
2075  add_char_field(&sl, ACL_PARTY, acn->party);
2076  add_char_field(&sl, ACL_MAP, acn->map);
2077  SockList_AddChar(&sl, 3);
2079  SockList_AddShort(&sl, acn->level);
2080  if (faceno) {
2081  SockList_AddChar(&sl, 3);
2083  SockList_AddShort(&sl, faceno);
2084  }
2085 
2086  SockList_AddChar(&sl, 0);
2087  }
2088  /* Now for any characters where we just have the name */
2089  for (linked_char *e = extra; e != NULL; e = e->next) {
2090  add_char_field(&sl, ACL_NAME, e->name);
2091  SockList_AddChar(&sl, 0);
2092  }
2093 
2094  Send_With_Handling(ns, &sl);
2095  SockList_Term(&sl);
2096 
2097  if (extra) {
2098  free_charlinks(extra);
2099  }
2100 }
2101 
2123 static int decode_name_password(const char *buf, int *len, char *name, char *password)
2124 {
2125  int nlen, plen;
2126 
2127  if (*len < 2) {
2128  return 1;
2129  }
2130 
2131  nlen = (unsigned char)buf[0];
2132  if (nlen >= MAX_BUF || nlen > *len-2) {
2133  return 1;
2134  }
2135  memcpy(name, buf+1, nlen);
2136  name[nlen] = 0;
2137 
2138  plen = (unsigned char)buf[nlen+1];
2139  if (plen >= MAX_BUF || plen > *len-2-nlen) {
2140  return 2;
2141  }
2142  memcpy(password, buf+2+nlen, plen);
2143  password[plen] = 0;
2144 
2145  *len = nlen+plen+2;
2146 
2147  return 0;
2148 }
2159 void account_login_cmd(char *buf, int len, socket_struct *ns) {
2160  char name[MAX_BUF], password[MAX_BUF];
2161  int status;
2162  SockList sl;
2163 
2164  if (len <= 0 || !buf) {
2165  LOG(llevDebug, "IP '%s' sent bogus add_player_cmd information\n", ns->host);
2166  return;
2167  }
2168 
2169  SockList_Init(&sl);
2170 
2171  status = decode_name_password(buf, &len, name, password);
2172 
2173  if (status == 1) {
2174  SockList_AddString(&sl, "failure accountlogin Name is too long");
2175  Send_With_Handling(ns, &sl);
2176  SockList_Term(&sl);
2177  return;
2178  }
2179  if (status == 2) {
2180  SockList_AddString(&sl, "failure accountlogin Password is too long");
2181  Send_With_Handling(ns, &sl);
2182  SockList_Term(&sl);
2183  return;
2184  }
2185 
2186  if (!account_exists(name)) {
2187  SockList_AddString(&sl, "failure accountlogin No such account name exists on this server");
2188  Send_With_Handling(ns, &sl);
2189  SockList_Term(&sl);
2190  return;
2191  }
2192 
2193  if (account_login(name, password)) {
2194  LOG(llevInfo, "Account login for '%s' from %s\n", name, ns->host);
2195 
2196  if (ns->account_name) free(ns->account_name);
2197  /* We want to store away official name so we do not
2198  * have any case sensitivity issues on the files.
2199  * because we have already checked password, we
2200  * know that account_exists should never return NULL in
2201  * this case.
2202  */
2204 
2206  } else {
2207  LOG(llevInfo, "Failed account login for '%s' from %s\n", name, ns->host);
2208  SockList_AddString(&sl, "failure accountlogin Incorrect password for account");
2209  Send_With_Handling(ns, &sl);
2210  SockList_Term(&sl);
2211  }
2212 }
2213 
2222 static int account_block_create(const socket_struct *ns) {
2223  /* Check if account creation is blocked. */
2225  /* Account creation is allowed for everyone. */
2226  return 0;
2227  }
2228 
2229  /* Has the trusted host been defined? */
2230  if(settings.account_trusted_host == NULL) {
2231  /* No, allocate it and set it to localhost now. */
2233  }
2234 
2235  /* Return false if the client connected from the trusted host. */
2236  if(strcmp(ns->host, settings.account_trusted_host) == 0){
2237  return 0;
2238  }
2239 
2240  /*
2241  * If we are here, then we are blocking account create and we do
2242  * not trust this client's IP address.
2243  */
2244  return 1;
2245 }
2246 
2258 void account_new_cmd(char *buf, int len, socket_struct *ns) {
2259  char name[MAX_BUF], password[MAX_BUF];
2260  int status;
2261  SockList sl;
2262 
2263  if (len <= 0 || !buf) {
2264  LOG(llevDebug, "IP '%s' sent bogus add_player_cmd information\n", ns->host);
2265  return;
2266  }
2267 
2268  SockList_Init(&sl);
2269 
2270  status = decode_name_password(buf, &len, name, password);
2271 
2272  if (account_block_create(ns)) {
2273  LOG(llevInfo, "Account create blocked from %s\n", ns->host);
2274  SockList_AddString(&sl, "failure accountnew Account creation is disabled");
2275  Send_With_Handling(ns, &sl);
2276  SockList_Term(&sl);
2277  return;
2278  }
2279 
2280  if (status == 1) {
2281  SockList_AddString(&sl, "failure accountnew Name is too long");
2282  Send_With_Handling(ns, &sl);
2283  SockList_Term(&sl);
2284  return;
2285  }
2286  if (status == 2) {
2287  SockList_AddString(&sl, "failure accountnew Password is too long");
2288  Send_With_Handling(ns, &sl);
2289  SockList_Term(&sl);
2290  return;
2291  }
2292  /*The minimum length isn't exactly required, but in the current implementation,
2293  * client will send the same password for character for which there is a
2294  * 2 character minimum size. Thus an account with a one character password
2295  * won't be able to create a character. */
2296  if (strlen(name)<settings.min_name) {
2297  SockList_AddString(&sl, "failure accountnew Name is too short");
2298  Send_With_Handling(ns, &sl);
2299  SockList_Term(&sl);
2300  return;
2301  }
2302  if (strlen(password)<2) {
2303  SockList_AddString(&sl, "failure accountnew Password is too short");
2304  Send_With_Handling(ns, &sl);
2305  SockList_Term(&sl);
2306  return;
2307  }
2308 
2309  if (account_exists(name)) {
2310  SockList_AddString(&sl, "failure accountnew That account already exists on this server");
2311  Send_With_Handling(ns, &sl);
2312  SockList_Term(&sl);
2313  return;
2314  }
2315 
2317  if (status == 1) {
2318  SockList_AddString(&sl,
2319  "failure accountnew Choose a different account name. " VALIDCHAR_MSG);
2320  Send_With_Handling(ns, &sl);
2321  SockList_Term(&sl);
2322  return;
2323  }
2324 
2325  if (status == 2) {
2326  SockList_AddString(&sl,
2327  "failure accountnew That account name is too long");
2328  Send_With_Handling(ns, &sl);
2329  SockList_Term(&sl);
2330  return;
2331  }
2332 
2333  status = account_check_string(password);
2334  if (status == 1) {
2335  SockList_AddString(&sl,
2336  "failure accountnew Choose a different password. " VALIDCHAR_MSG);
2337  Send_With_Handling(ns, &sl);
2338  SockList_Term(&sl);
2339  return;
2340  }
2341 
2342  if (status == 2) {
2343  SockList_AddString(&sl,
2344  "failure accountnew That password is too long");
2345  Send_With_Handling(ns, &sl);
2346  SockList_Term(&sl);
2347  return;
2348  }
2349 
2350  /* If we got here, we passed all checks - so now add it */
2351  if (ns->account_name) free(ns->account_name);
2353  account_new(name, password);
2354  /* save account information */
2355  accounts_save();
2357 }
2358 
2372 void account_add_player_cmd(char *buf, int len, socket_struct *ns) {
2373  char name[MAX_BUF], password[MAX_BUF];
2374  int status, force, nlen;
2375  SockList sl;
2376  const char *cp;
2377 
2378  if (len <= 0 || !buf) {
2379  LOG(llevDebug, "IP '%s' sent bogus add_player_cmd information\n", ns->host);
2380  return;
2381  }
2382 
2383  SockList_Init(&sl);
2384 
2385  if (ns->account_name == NULL) {
2386  SockList_AddString(&sl, "failure accountaddplayer Not logged in");
2387  Send_With_Handling(ns, &sl);
2388  SockList_Term(&sl);
2389  return;
2390  }
2391 
2392  force = buf[0];
2393  nlen = len - 1;
2394  status = decode_name_password(buf+1, &nlen, name, password);
2395  if (status == 1) {
2396  SockList_AddString(&sl, "failure accountaddplayer Name is too long");
2397  Send_With_Handling(ns, &sl);
2398  SockList_Term(&sl);
2399  return;
2400  }
2401  if (status == 2) {
2402  SockList_AddString(&sl, "failure accountaddplayer Password is too long");
2403  Send_With_Handling(ns, &sl);
2404  SockList_Term(&sl);
2405  return;
2406  }
2407 
2408  status = verify_player(name, password);
2409  if (status) {
2410  /* From a security standpoint, telling random folks if it
2411  * it as wrong password makes it easier to hack. However,
2412  * it is fairly easy to determine what characters exist on a server
2413  * (either by trying to create a new one and see if the name is in
2414  * in use, or just looking at the high score file), so this
2415  * really does not make things much less secure
2416  */
2417  if (status == 1)
2418  SockList_AddString(&sl, "failure accountaddplayer 0 The character does not exist.");
2419  else
2420  SockList_AddString(&sl, "failure accountaddplayer 0 That password is incorrect.");
2421 
2422  Send_With_Handling(ns, &sl);
2423  SockList_Term(&sl);
2424  return;
2425  }
2426  /* Check to see if this character is associated with an account.
2427  */
2429  if (cp) {
2430  if (!strcmp(cp, ns->account_name)) {
2431  SockList_AddString(&sl, "failure accountaddplayer 0 That character is already connected to this account.");
2432  Send_With_Handling(ns, &sl);
2433  SockList_Term(&sl);
2434  return;
2435  } else {
2436  if (!force) {
2437  SockList_AddString(&sl, "failure accountaddplayer 1 That character is already connected to a different account.");
2438  Send_With_Handling(ns, &sl);
2439  SockList_Term(&sl);
2440  return;
2441  } else if (account_is_logged_in(cp)) {
2442  /* We could be clever and try to handle this case, but it is
2443  * trickier. If the character is logged in, it has to
2444  * be logged out. And the socket holds some data which
2445  * needs to be cleaned up. Since it should be fairly
2446  * uncommon that users need to do this, just disallowing
2447  * it makes things a lot simpler.
2448  */
2449  SockList_AddString(&sl, "failure accountaddplayer 0 That character is already connected to a different account which is currently logged in.");
2450  Send_With_Handling(ns, &sl);
2451  SockList_Term(&sl);
2452  return;
2453  }
2454  }
2455  }
2456  /* If we have gotten this far, the name/password provided is OK,
2457  * and the character is not associated with a different account (or
2458  * force is true). Now try to add the character to this account.
2459  */
2461 
2462  /* This should never happen, but check for it just in case -
2463  * if we were able to log in, the account should exist. but
2464  * if this fails, need to give the user some clue.
2465  */
2466  if (status==1) {
2467  SockList_AddString(&sl, "failure accountaddplayer 0 Could not find your account.");
2468  Send_With_Handling(ns, &sl);
2469  SockList_Term(&sl);
2470  return;
2471  } else if (status == 2) {
2472  SockList_AddString(&sl, "failure accountaddplayer 0 You have reached the maximum number of characters allowed per account.");
2473  Send_With_Handling(ns, &sl);
2474  SockList_Term(&sl);
2475  return;
2476  }
2477 
2478  /* If cp is set, then this character used to belong to a different
2479  * account. Remove it now.
2480  */
2481  if (cp) {
2482  Account_Chars *chars;
2483 
2485  chars = account_char_load(cp);
2486  account_char_remove(chars, name);
2487  account_char_save(chars);
2488  account_char_free(chars);
2489  }
2490 
2492 
2493  /* store data so nothing is lost in case of crash */
2495 }
2496 
2501 void account_play_cmd(char *buf, int len, socket_struct *ns)
2502 {
2503  char **chars;
2504  int i;
2505  SockList sl;
2506  player *pl;
2507 
2508  if (len <= 0 || !buf) {
2509  LOG(llevDebug, "IP '%s' sent bogus account_play_cmd information\n", ns->host);
2510  return;
2511  }
2512 
2513  SockList_Init(&sl);
2514 
2515  if (ns->status != Ns_Add) {
2516  SockList_AddString(&sl, "failure accountplay Not allowed right now");
2517  Send_With_Handling(ns, &sl);
2518  SockList_Term(&sl);
2519  return;
2520  }
2521 
2522  if (!buf[0]) {
2523  SockList_AddString(&sl, "failure accountplay Malformed character name");
2524  Send_With_Handling(ns, &sl);
2525  SockList_Term(&sl);
2526  return;
2527  }
2528 
2529  if (ns->account_name == NULL) {
2530  SockList_AddString(&sl, "failure accountplay Not logged in");
2531  Send_With_Handling(ns, &sl);
2532  SockList_Term(&sl);
2533  return;
2534  }
2535 
2536  /* Make sure a client is not trying to spoof us here */
2538 
2539  for (i=0; chars[i]; i++) {
2540  if (strcmp(chars[i], buf) == 0)
2541  break;
2542  }
2543  if (!chars[i]) {
2544  SockList_AddPrintf(&sl,
2545  "failure accountplay Character %s is not associated with account %s",
2546  buf, ns->account_name);
2547  Send_With_Handling(ns, &sl);
2548  SockList_Term(&sl);
2549  return;
2550  }
2551 
2552  for (pl = first_player; pl; pl = pl->next) {
2553  if (pl->ob && pl->socket != ns && strcmp(buf, pl->ob->name) == 0 && pl->state != ST_PLAY_AGAIN && pl->state != ST_GET_NAME) {
2554  SockList_AddPrintf(&sl,
2555  "failure accountplay Character %s is already playing",
2556  buf);
2557  Send_With_Handling(ns, &sl);
2558  SockList_Term(&sl);
2559  return;
2560  }
2561  }
2562 
2563  /* from a protocol standpoint, accountplay can be used
2564  * before there is a player structure (first login) or after
2565  * (character has logged in and is changing characters).
2566  * Checkthe sockets for that second case - if so,
2567  * we don't need to make a new player object, etc.
2568  */
2569  for (pl=first_player; pl; pl=pl->next) {
2570  if (pl->socket == ns) {
2571  /* The player still in the socket must be saved first. */
2572  save_player(pl->ob, 0);
2573  break;
2574  }
2575  }
2576 
2577  /* Some of this logic is from add_player()
2578  * we just don't use add_player() as it does some other work
2579  * we don't really want to do.
2580  */
2581  if (!pl) {
2582  pl = get_player(NULL);
2583  set_player_socket(pl, ns);
2584  ns->status = Ns_Avail;
2585  ns->account_chars = NULL;
2586  SockList_ResetRead(&pl->socket->inbuf);
2587  } else {
2588  pl->state = ST_PLAYING;
2589  }
2590 
2591  pl->ob->name = add_string(buf);
2592  check_login(pl->ob, NULL);
2593 
2594  SockList_AddString(&sl, "addme_success");
2595  Send_With_Handling(ns, &sl);
2596  SockList_Term(&sl);
2597 }
2598 
2599 #define MAX_CHOICES 100
2600 
2606 void create_player_cmd(char *buf, int len, socket_struct *ns)
2607 {
2608  char name[MAX_BUF], password[MAX_BUF], *choices[MAX_CHOICES];
2609  int status, nlen, choice_num=0, i;
2610  SockList sl;
2611  player *pl;
2612  archetype *map=NULL, *race_a=NULL, *class_a=NULL;
2613  living new_stats;
2614 
2615  if (len <= 0 || !buf) {
2616  LOG(llevDebug, "IP '%s' sent bogus create_player_cmd information\n", ns->host);
2617  return;
2618  }
2619 
2620  SockList_Init(&sl);
2621 
2622  if (ns->status != Ns_Add) {
2623  SockList_AddString(&sl, "failure createplayer Not allowed right now");
2624  Send_With_Handling(ns, &sl);
2625  SockList_Term(&sl);
2626  return;
2627  }
2628 
2629  nlen = len;
2630  status = decode_name_password(buf, &nlen, name, password);
2631  if (status == 1) {
2632  SockList_AddString(&sl, "failure createplayer Name is too long");
2633  Send_With_Handling(ns, &sl);
2634  SockList_Term(&sl);
2635  return;
2636  }
2637 
2638  /* Minimum character name limit (if set) */
2639  if (strlen(name)<settings.min_name) {
2640  SockList_AddString(&sl, "failure createplayer Name is too short");
2641  Send_With_Handling(ns, &sl);
2642  SockList_Term(&sl);
2643  return;
2644  }
2645 
2646  if (playername_ok(name) == 0) {
2647  SockList_AddString(&sl, "failure createplayer Player name contains invalid characters");
2648  Send_With_Handling(ns, &sl);
2649  SockList_Term(&sl);
2650  return;
2651  }
2652 
2653  /* 2 characters minimum for password */
2654  if (strlen(password)<2) {
2655  SockList_AddString(&sl, "failure createplayer Password is too short");
2656  Send_With_Handling(ns, &sl);
2657  SockList_Term(&sl);
2658  return;
2659  }
2660 
2662  if (status == 2) {
2663  SockList_AddString(&sl, "failure createplayer Password is too long");
2664  Send_With_Handling(ns, &sl);
2665  SockList_Term(&sl);
2666  return;
2667  }
2668 
2669  /* This is a fairly ugly solution - we're truncating the password.
2670  * however, the password information for characters is really
2671  * a legacy issue - when every character is associated with
2672  * an account, legacy login (character name/password) will get
2673  * removed, at which point the only use for password might be
2674  * to move characters from one account to another, but not sure
2675  * if that is something we want to allow.
2676  */
2677  if (strlen(password)>17)
2678  password[16] = 0;
2679 
2680  /* We just can't call check_name(), since that uses draw_info() to
2681  * report status. We are also more permissive on names, so we use
2682  * account_check_string() - if that is safe for account, also safe
2683  * for player names.
2684  */
2685  if (account_check_string(name)) {
2686  SockList_AddString(&sl, "failure createplayer Choose a different character name. " VALIDCHAR_MSG);
2687  Send_With_Handling(ns, &sl);
2688  SockList_Term(&sl);
2689  return;
2690  }
2691 
2692  /* 1 means no such player, 0 is correct name/password (which in this
2693  * case, is an error), and 2 is incorrect password. Only way we
2694  * go onward is if there is no such player.
2695  */
2696  if (verify_player(name, password) != 1) {
2697  SockList_AddString(&sl, "failure createplayer That name is already in use");
2698  Send_With_Handling(ns, &sl);
2699  SockList_Term(&sl);
2700  return;
2701  }
2702 
2703  /* from a protocol standpoint, accountplay can be used
2704  * before there is a player structure (first login) or after
2705  * (character has logged in and is changing characters).
2706  * Check the sockets for that second case - if so,
2707  * we don't need to make a new player object, etc.
2708  */
2709  for (pl=first_player; pl; pl=pl->next)
2710  if (pl->socket == ns) {
2711  if (pl->ob->name) {
2712  if (!strcmp(pl->ob->name, name)) {
2713  /* For some reason not only the socket is the same but also
2714  * the player is already playing. If this happens at this
2715  * point let's assume the character never was able to apply
2716  * a bet of reality to make a correct first-time save.
2717  * So, for safety remove it and start over.
2718  */
2719  if (!QUERY_FLAG(pl->ob, FLAG_REMOVED))
2720  object_remove(pl->ob);
2721  }
2722  else {
2723  /* If this is a different player on the same socket, then we
2724  * need to make sure that the old one is not connected to the player
2725  *
2726  * This prevents bizarre scenarios where the player is on the map
2727  * multiple times (from multiple createplayer commands), and
2728  * only one of them is controlled by the player.
2729  */
2730  if (pl->ob->contr == pl) {
2731  if (!QUERY_FLAG(pl->ob, FLAG_REMOVED))
2732  object_remove(pl->ob);
2733  }
2734  }
2735  }
2736  break;
2737  }
2738 
2739  /* In this mode, we have additional data
2740  * Note that because there are a lot of failure cases in here
2741  * (where we end up not creating the new player), the code
2742  * to create the new player is done within this routine
2743  * after all checks pass. Note that all of the checks
2744  * done are done without using the player structure,
2745  * as pl may be null right now.
2746  */
2747  if (ns->login_method >= 2) {
2748  int i, j, stat_total=0;
2749  char *key, *value, *race=NULL, *class_name=NULL;
2750 
2751  /* By setting this to zero, then we can easily
2752  * check to see if all stats have been set.
2753  */
2754  memset(&new_stats, 0, sizeof(living));
2755 
2756  while (nlen < len) {
2757  i = buf[nlen]; /* Length of this line */
2758  /* Sanity check from client - don't want to loop
2759  * forever if there is a 0 length, and don't
2760  * want to read beyond size of packet.
2761  * Likewise, client should have sent
2762  * the string to us already null terminated,
2763  * but we will just make sure.
2764  */
2765  if ((i == 0) || (nlen + i > len)) break;
2766  buf[nlen + i] = 0;
2767 
2768  /* What we have are a series of lines -
2769  * 'key value' format. Find that space,
2770  * and null it out so we can do strcasecmp.
2771  * If no space, abort processing
2772  */
2773  key = buf + nlen + 1;
2774  value = strchr(key, ' ');
2775  if (!value) break;
2776  *value = 0;
2777  value++;
2778 
2779  if (!strcasecmp(key,"race")) race = value;
2780  else if (!strcasecmp(key,"class")) class_name = value;
2781  else if (!strcasecmp(key,"starting_map")) {
2783  if (!map || map->clone.type != MAP || map->clone.subtype !=MAP_TYPE_CHOICE) {
2784  SockList_AddString(&sl,
2785  "failure createplayer Invalid starting map");
2786  Send_With_Handling(ns, &sl);
2787  SockList_Term(&sl);
2788  return;
2789  }
2790  }
2791  else if (!strcasecmp(key,"choice")) {
2792  /* In general, MAX_CHOICES should be large enough
2793  * to always handle the choices from the client - of
2794  * course, the client could be broken and send us many
2795  * more choices than we should have, so handle that.
2796  */
2797  if (choice_num == MAX_CHOICES) {
2798  LOG(llevError,
2799  "Number of choices receive exceed max value: %d>%d\n",
2800  choice_num, MAX_CHOICES);
2801  } else {
2802  choices[choice_num] = value;
2803  choice_num++;
2804  }
2805  }
2806  else {
2807  /* Do stat processing here */
2808  for (j=0; j < NUM_STATS; j++) {
2809  if (!strcasecmp(key,short_stat_name[j])) {
2810  int val = atoi(value);
2811 
2812  set_attr_value(&new_stats, j, val);
2813  break;
2814  }
2815  }
2816  if (j >= NUM_STATS) {
2817  /* Bad clients could do this - we should at least report
2818  * it, and useful when trying to add new parameters.
2819  */
2820  LOG(llevError, "Got unknown key/value from client: %s %s\n", key, value);
2821  }
2822  }
2823  nlen += i + 1;
2824  }
2825  /* Do some sanity checking now. But checking the stat
2826  * values here, we will catch any 0 values since we do
2827  * a memset above. A properly behaving client should
2828  * never do any of these things, but we do not presume
2829  * clients will behave properly.
2830  */
2831  for (j=0; j<NUM_STATS; j++) {
2832  int val = get_attr_value(&new_stats, j);
2833 
2834  stat_total += val;
2835  if (val > settings.starting_stat_max ||
2836  val < settings.starting_stat_min) {
2837  SockList_AddPrintf(&sl,
2838  "failure createplayer Stat value is out of range - %d must be between %d and %d",
2840  Send_With_Handling(ns, &sl);
2841  SockList_Term(&sl);
2842  return;
2843  }
2844  }
2845  if (stat_total > settings.starting_stat_points) {
2846  SockList_AddPrintf(&sl,
2847  "failure createplayer Total allocated statistics is higher than allowed (%d>%d)",
2848  stat_total, settings.starting_stat_points);
2849  Send_With_Handling(ns, &sl);
2850  SockList_Term(&sl);
2851  return;
2852  }
2853 
2854  if (race)
2855  race_a = try_find_archetype(race);
2856 
2857  if (class_name)
2858  class_a = try_find_archetype(class_name);
2859 
2860  /* This should never happen with a properly behaving client, so the error message
2861  * doesn't have to be that great.
2862  */
2863  if (!race_a || race_a->clone.type != PLAYER || !class_a || class_a->clone.type != CLASS) {
2864  SockList_AddString(&sl,
2865  "failure createplayer Invalid or unknown race or class");
2866  Send_With_Handling(ns, &sl);
2867  SockList_Term(&sl);
2868  return;
2869  }
2870 
2871  /* At current time, only way this can fail is if the adjusted
2872  * stat is less than 1.
2873  */
2874  if (check_race_and_class(&new_stats, race_a, class_a)) {
2875  SockList_AddString(&sl,
2876  "failure createplayer Unable to apply race or class - statistic is out of bounds");
2877  Send_With_Handling(ns, &sl);
2878  SockList_Term(&sl);
2879  return;
2880  }
2881 
2882  if (!pl)
2884  // If we already have a player, we a replaying on the same connection.
2885  // Since add_player normally sets ns->status, we still need that to happen.
2886  else
2887  ns->status = Ns_Avail;
2888 
2889  // We need to copy the name in before apply_race_and_class() because it
2890  // tells the client our character name. If we don't update it, they get the old one.
2891  FREE_AND_COPY(pl->ob->name, name);
2892 
2893  apply_race_and_class(pl->ob, race_a, class_a, &new_stats);
2894 
2895  } else {
2896  /* In thise case, old login method */
2897  if (!pl)
2898  pl = add_player(ns, ADD_PLAYER_NEW);
2899  // If we already have a player, we a replaying on the same connection.
2900  // Since add_player normally sets ns->status, we still need that to happen.
2901  else
2902  ns->status = Ns_Avail;
2903 
2904  // Make sure to do this on both code branches.
2905  FREE_AND_COPY(pl->ob->name, name);
2906 /* already done by add_player
2907  roll_again(pl->ob);
2908  pl->state = ST_ROLL_STAT;
2909  set_first_map(pl->ob);*/
2910  }
2911 
2912  /* add_player does a lot of the work, but there are a few
2913  * things we need to update, like starting name and
2914  * password.
2915  * This is done before processing in login_method>2.
2916  * The character creation process it does when
2917  * applying the race/class will use this
2918  * name information.
2919  */
2920  FREE_AND_COPY(pl->ob->name_pl, name);
2921  pl->name_changed = 1;
2922  safe_strncpy(pl->password, newhash(password), sizeof(pl->password));
2923 
2924  SockList_AddString(&sl, "addme_success");
2925  Send_With_Handling(ns, &sl);
2926  SockList_Term(&sl);
2927 
2928  if (ns->login_method >= 2) {
2929  /* The client could have provided us a map - if so, map will be set
2930  * and we don't want to overwrite it
2931  */
2932  if (!map)
2934  assert(map); // Existence checked in init_dynamic()
2935 
2936  enter_exit(pl->ob, &map->clone);
2937 
2938  if (pl->ob->map == NULL) {
2939  LOG(llevError, "Couldn't put player %s on start map %s!", pl->ob->name, map->name);
2940  abort();
2941  }
2942 
2943  /* copy information to bed of reality information, in case the player dies */
2944  safe_strncpy(pl->savebed_map, pl->ob->map->path, sizeof(pl->savebed_map));
2945  pl->bed_x = pl->ob->x;
2946  pl->bed_y = pl->ob->y;
2947 
2949  }
2950 
2951  /* We insert any objects after we have put the player on the map -
2952  * this makes things safer, as certain objects may expect a normal
2953  * environment. Note that choice_num will only be set in the
2954  * loginmethod > 2, which also checks (and errors out) if the
2955  * race/class is not set, which is why explicit checking for
2956  * those is not need.
2957  */
2958  for (i=0; i < choice_num; i++) {
2959  char *choiceval;
2960  const char *value, *cp;
2961  archetype *arch;
2962  object *op;
2963 
2964  choiceval = strchr(choices[i], ' ');
2965  if (!choiceval) {
2966  LOG(llevError, "Choice does not specify value: %s\n", choices[i]);
2967  continue;
2968  }
2969  *choiceval=0;
2970  choiceval++;
2971  value = object_get_value(&race_a->clone, choices[i]);
2972  if (!value)
2973  value = object_get_value(&class_a->clone, choices[i]);
2974 
2975  if (!value) {
2976  LOG(llevError, "Choice not found in archetype: %s\n", choices[i]);
2977  continue;
2978  }
2979  cp = strstr(value, choiceval);
2980  if (!cp) {
2981  LOG(llevError, "Choice value not found in archetype: %s %s\n",
2982  choices[i], choiceval);
2983  continue;
2984  }
2985 
2986  /* Check to make sure that the matched string is an entire word,
2987  * and not a substring (eg, valid choice being great_sword but
2988  * we just get sword) - the space after the match should either be a
2989  * space or null, and space before match should also be a space
2990  * or the start of the string.
2991  */
2992  if ((cp[strlen(choiceval)] != ' ') && (cp[strlen(choiceval)] != 0) &&
2993  (cp != value) && (*(cp-1) != ' ')) {
2994 
2995  LOG(llevError, "Choice value matches substring but not entire word: %s substring %s\n",
2996  choiceval, value);
2997  continue;
2998  }
2999  arch = try_find_archetype(choiceval);
3000  if (!arch) {
3001  LOG(llevError, "Choice value can not find archetype %s\n", choiceval);
3002  continue;
3003  }
3004  op = arch_to_object(arch);
3005  op = object_insert_in_ob(op, pl->ob);
3007  ob_apply(op, pl->ob, 0);
3008  }
3009 
3010  LOG(llevInfo, "new character %s from %s\n", pl->ob->name, pl->ob->contr->socket->host);
3013  "%s has entered the game.", pl->ob->name);
3014 }
3015 
3026 void account_password(char *buf, int len, socket_struct *ns) {
3027  char old[MAX_BUF], change[MAX_BUF];
3028  int status;
3029  SockList sl;
3030 
3031  if (len <= 0 || !buf) {
3032  LOG(llevDebug, "IP '%s' sent bogus account_password_cmd information\n", ns->host);
3033  return;
3034  }
3035 
3036  SockList_Init(&sl);
3037 
3038  if (ns->account_name == NULL) {
3039  SockList_AddString(&sl, "failure accountpw Not logged in");
3040  Send_With_Handling(ns, &sl);
3041  SockList_Term(&sl);
3042  return;
3043  }
3044 
3045  status = decode_name_password(buf, &len, old, change);
3046  if (status == 1) {
3047  SockList_AddString(&sl, "failure accountpw Old password is too long");
3048  Send_With_Handling(ns, &sl);
3049  SockList_Term(&sl);
3050  return;
3051  }
3052  if (status == 2) {
3053  SockList_AddString(&sl, "failure accountpw New password is too long");
3054  Send_With_Handling(ns, &sl);
3055  SockList_Term(&sl);
3056  return;
3057  }
3058  /*The minimum length isn't exactly required, but in the current implementation,
3059  * client will send the same password for character for which there is a
3060  * 2 character minimum size. Thus an account with a one character password
3061  * won't be able to create a character. */
3062  if (strlen(change)<2) {
3063  SockList_AddString(&sl, "failure accountpw New password is too short");
3064  Send_With_Handling(ns, &sl);
3065  SockList_Term(&sl);
3066  return;
3067  }
3068 
3069  status = account_check_string(change);
3070  if (status == 1) {
3071  SockList_AddString(&sl,
3072  "failure accountpw Choose a different password. " VALIDCHAR_MSG);
3073  Send_With_Handling(ns, &sl);
3074  SockList_Term(&sl);
3075  return;
3076  }
3077 
3078  if (status == 2) {
3079  SockList_AddString(&sl,
3080  "failure accountpw That password is too long");
3081  Send_With_Handling(ns, &sl);
3082  SockList_Term(&sl);
3083  return;
3084  }
3085 
3086  status = account_change_password(ns->account_name, old, change);
3087  if (status != 0) {
3088  const char *error;
3089 
3090  if (status == 1) {
3091  error = "failure accountpw Illegal characters present";
3092  } else if (status == 2) {
3093  error = "failure accountpw Invalid account";
3094  } else {
3095  error = "failure accountpw Incorrect current password";
3096  }
3097 
3098  SockList_AddString(&sl, error);
3099  Send_With_Handling(ns, &sl);
3100  SockList_Term(&sl);
3101  return;
3102  }
3103 
3104  /* If we got here, we passed all checks, and password was changed */
3106 }
find_player_socket
player * find_player_socket(const socket_struct *ns)
Definition: player.cpp:123
ADD_PLAYER_NO_STATS_ROLL
#define ADD_PLAYER_NO_STATS_ROLL
Definition: player.h:247
SF_FIREON
#define SF_FIREON
Definition: newclient.h:178
CF_BLIND
#define CF_BLIND
Definition: newclient.h:189
account_add_player_cmd
void account_add_player_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:2372
CLASS
@ CLASS
Definition: object.h:143
CS_STAT_RES_DEPLETE
#define CS_STAT_RES_DEPLETE
Definition: newclient.h:151
MAP_CLIENT_X
#define MAP_CLIENT_X
Definition: config.h:237
CF_STEALTHY
#define CF_STEALTHY
Definition: newclient.h:194
socket_struct::tick
uint32_t tick
Definition: newserver.h:106
atnr_cs_stat
static const short atnr_cs_stat[NROFATTACKS]
Definition: request.cpp:99
PLAYER
@ PLAYER
Definition: object.h:112
MAP_CLIENT_X_MINIMUM
#define MAP_CLIENT_X_MINIMUM
Definition: config.h:245
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Definition: lowlevel.cpp:124
global.h
ANIM_SYNC
#define ANIM_SYNC
Definition: newclient.h:337
NS_FACESENT_FACE
#define NS_FACESENT_FACE
Definition: newserver.h:137
socket_struct::sc_version
uint32_t sc_version
Definition: newserver.h:113
first_player
player * first_player
Definition: init.cpp:106
ST_CHANGE_PASSWORD_OLD
#define ST_CHANGE_PASSWORD_OLD
Definition: define.h:550
settings
struct Settings settings
Definition: init.cpp:139
CS_STAT_RACE_CON
#define CS_STAT_RACE_CON
Definition: newclient.h:110
print_ext_msg
void print_ext_msg(socket_struct *ns, int color, uint8_t type, uint8_t subtype, const char *message)
Definition: info.cpp:62
banquet.l
l
Definition: banquet.py:164
CS_STAT_GRACE
#define CS_STAT_GRACE
Definition: newclient.h:98
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
find_smooth
int find_smooth(const Face *face, const Face **smoothed)
Definition: image.cpp:102
annotate_ob
static int annotate_ob(int ax, int ay, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int *alive_layer)
Definition: request.cpp:1334
MAP
@ MAP
Definition: object.h:130
living::maxhp
int16_t maxhp
Definition: living.h:41
socket_struct::heartbeat
bool heartbeat
Definition: newserver.h:112
SockList_AddInt64
void SockList_AddInt64(SockList *sl, uint64_t data)
Definition: lowlevel.cpp:137
ST_GET_PASSWORD
#define ST_GET_PASSWORD
Definition: define.h:547
ACL_FACE_NUM
#define ACL_FACE_NUM
Definition: newclient.h:213
FLAG_CONFUSED
#define FLAG_CONFUSED
Definition: define.h:311
CAN_PROBE
static bool CAN_PROBE(const object *ob)
Definition: object.h:607
llevError
@ llevError
Definition: logger.h:11
FABS
#define FABS(x)
Definition: define.h:22
FLAG_CLIENT_ANIM_RANDOM
#define FLAG_CLIENT_ANIM_RANDOM
Definition: define.h:241
Settings::account_trusted_host
char * account_trusted_host
Definition: global.h:329
send_account_players
void send_account_players(socket_struct *ns)
Definition: request.cpp:2032
CS_STAT_APPLIED_WIS
#define CS_STAT_APPLIED_WIS
Definition: newclient.h:122
ssop_t
int ssop_t
Definition: request.cpp:58
MSG_TYPE_ADMIN_PLAYER
#define MSG_TYPE_ADMIN_PLAYER
Definition: newclient.h:485
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
send_query
void send_query(socket_struct *ns, uint8_t flags, const char *text)
Definition: request.cpp:728
account_play_cmd
void account_play_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:2501
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
new_player_cmd
void new_player_cmd(uint8_t *buf, int len, player *pl)
Definition: request.cpp:503
client_spell
Definition: player.h:87
CS_STAT_RES_FEAR
#define CS_STAT_RES_FEAR
Definition: newclient.h:150
MAP2_COORD_OFFSET
#define MAP2_COORD_OFFSET
Definition: newclient.h:32
player
Definition: player.h:105
CS_STAT_DAM
#define CS_STAT_DAM
Definition: newclient.h:90
strdup_local
#define strdup_local
Definition: compat.h:29
ACL_RACE
#define ACL_RACE
Definition: newclient.h:208
diamondslots.x
x
Definition: diamondslots.py:15
FLAG_STARTEQUIP
#define FLAG_STARTEQUIP
Definition: define.h:268
CS_STAT_RES_FIRE
#define CS_STAT_RES_FIRE
Definition: newclient.h:139
n
based on the size of the this randomly makes land number of spaces randomly lower or higher The default is Note that this is run also based on the the altitude amount will likely be less So if you do something like l and n
Definition: land.6.txt:25
ask_smooth_cmd
void ask_smooth_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:479
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
newhash
char const * newhash(char const *password)
Definition: server.cpp:101
archininventory.arch
arch
DIALOGCHECK MINARGS 1 MAXARGS 1
Definition: archininventory.py:16
socket_struct::sound
uint32_t sound
Definition: newserver.h:111
account_get_additional_chars
linked_char * account_get_additional_chars(const char *account_name, const Account_Chars *chars, int *count)
Definition: account.cpp:551
CS_STAT_APPLIED_POW
#define CS_STAT_APPLIED_POW
Definition: newclient.h:126
CS_STAT_HP
#define CS_STAT_HP
Definition: newclient.h:76
socket_struct
Definition: newserver.h:89
socket_struct::mapx
uint8_t mapx
Definition: newserver.h:116
get_skill_client_code
int get_skill_client_code(const char *skill_name)
Definition: skill_util.cpp:116
ST_GET_NAME
#define ST_GET_NAME
Definition: define.h:546
map2_delete_layer
static int map2_delete_layer(int ax, int ay, int layer, SockList *sl, socket_struct *ns)
Definition: request.cpp:1241
Settings
Definition: global.h:240
update_position
void update_position(mapstruct *m, int x, int y)
Definition: map.cpp:2107
MAX_NUM_LOOK_OBJECTS
#define MAX_NUM_LOOK_OBJECTS
Definition: newserver.h:28
socket_struct::num_look_objects
uint8_t num_look_objects
Definition: newserver.h:122
object::arch
struct archetype * arch
Definition: object.h:422
command_execute
void command_execute(object *pl, char *command)
Definition: commands.cpp:455
AddIfInt64
#define AddIfInt64(Old, New, sl, Type)
Definition: request.cpp:737
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Definition: lowlevel.cpp:154
ring_occidental_mages.rest
rest
Definition: ring_occidental_mages.py:16
SockList::len
size_t len
Definition: newclient.h:675
MAX_TIME
#define MAX_TIME
Definition: config.h:254
CS_STAT_SPELL_DENY
#define CS_STAT_SPELL_DENY
Definition: newclient.h:105
account_get_players_for_account
char ** account_get_players_for_account(const char *account_name)
Definition: account.cpp:520
accounts_save
void accounts_save(void)
Definition: account.cpp:256
Settings::always_show_hp
uint8_t always_show_hp
Definition: global.h:273
CS_STAT_WIS
#define CS_STAT_WIS
Definition: newclient.h:82
Account_Chars
Definition: account_char.h:27
reply_cmd
void reply_cmd(char *buf, int len, player *pl)
Definition: request.cpp:571
CS_STAT_SPEED
#define CS_STAT_SPEED
Definition: newclient.h:92
socket_struct::extended_stats
uint32_t extended_stats
Definition: newserver.h:109
CS_STAT_INT
#define CS_STAT_INT
Definition: newclient.h:81
append_spell
static void append_spell(player *pl, SockList *sl, object *spell)
Definition: request.cpp:1839
guildjoin.ob
ob
Definition: guildjoin.py:42
SK_PRAYING
@ SK_PRAYING
Definition: skills.h:49
CS_STAT_MAXSP
#define CS_STAT_MAXSP
Definition: newclient.h:79
command
Crossfire Protocol most of the time after the actual code was already omit certain important and possibly make life miserable any new developer or curious player should be able to find most of the relevant information here If inconsistencies are found or this documentation proves to be consider the latest server side protocol code in the public source code repository as the authoritative reference Introduction If you were ever curious enough to telnet or netcat to a Crossfire chances are you were sorely disappointed While the protocol may seem to use plain text at it actually uses a mix of ASCII and binary data This handbook attempts to document various aspects of the Crossfire protocol As consult the README file to find out how to get in touch with helpful people via mailing and more History the communications plan was set to be a text based system It was up to the server and client to parse these messages and determine what to do These messages were assumed to be line per message At a reasonably early stage of Eric Anderson wrote a then the data itself you could send many data and after the other end could decode these commands This works fairly but I think the creation of numerous sub packets has some performance hit the eutl was not especially well so writing a client for a different platform became more Eric left to work on other products shortly after writing his which didn t really leave anyone with a full understanding of the socket code I have decided to remove the eutl dependency At least one advantage is that having this network related code directly in the client and server makes error handling a bit easier cleaner Packet Format the outside packet method the byte size for the size information is not included here Eutl originally used bytes for the size to bytes seems it makes a least some sense The actual data is something of the nature of the commands listed below It is a text command
Definition: protocol.txt:63
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
Settings::starting_stat_max
uint8_t starting_stat_max
Definition: global.h:321
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
MIN
#define MIN(x, y)
Definition: compat.h:21
CS_STAT_BASE_DEX
#define CS_STAT_BASE_DEX
Definition: newclient.h:116
CS_STAT_RES_MAG
#define CS_STAT_RES_MAG
Definition: newclient.h:138
FIF
#define FIF(F, C)
MAP_LAYERS
#define MAP_LAYERS
Definition: map.h:32
SKILL
@ SKILL
Definition: object.h:148
client_spell::last_sp
int16_t last_sp
Definition: player.h:89
socket_struct::is_bot
uint32_t is_bot
Definition: newserver.h:107
get_player
player * get_player(player *p)
Definition: player.cpp:285
send_extra_stats
static void send_extra_stats(SockList *sl, player *pl)
Definition: request.cpp:793
object::count
tag_t count
Definition: object.h:307
flags
static const flag_definition flags[]
Definition: gridarta-types-convert.cpp:101
Ice.tmp
int tmp
Definition: Ice.py:207
CS_STAT_SP
#define CS_STAT_SP
Definition: newclient.h:78
SP_CREATE_FOOD
#define SP_CREATE_FOOD
Definition: spells.h:96
ACL_NAME
#define ACL_NAME
Definition: newclient.h:206
CS_STAT_RES_HOLYWORD
#define CS_STAT_RES_HOLYWORD
Definition: newclient.h:153
CS_STAT_GOLEM_HP
#define CS_STAT_GOLEM_HP
Definition: newclient.h:127
CS_STAT_RES_CONF
#define CS_STAT_RES_CONF
Definition: newclient.h:142
NDI_RED
#define NDI_RED
Definition: newclient.h:234
MAX_HEAD_OFFSET
#define MAX_HEAD_OFFSET
Definition: newserver.h:42
account_link
int account_link(const char *account_name, const char *player_name)
Definition: account.cpp:445
ATNR_PHYSICAL
#define ATNR_PHYSICAL
Definition: attack.h:49
CS_STAT_TURN_UNDEAD
#define CS_STAT_TURN_UNDEAD
Definition: newclient.h:149
ACL_CLASS
#define ACL_CLASS
Definition: newclient.h:207
SP_CREATE_MISSILE
#define SP_CREATE_MISSILE
Definition: spells.h:113
CF_NOT_PERFECT
#define CF_NOT_PERFECT
Definition: newclient.h:192
NROFATTACKS
#define NROFATTACKS
Definition: attack.h:17
receive_play_again
void receive_play_again(object *op, char key)
Definition: player.cpp:956
set_player_socket
void set_player_socket(player *p, socket_struct *ns)
Definition: player.cpp:413
MSG_TYPE_COMMAND_ERROR
#define MSG_TYPE_COMMAND_ERROR
Definition: newclient.h:518
map_clearcell
static void map_clearcell(struct map_cell_struct *cell, int face, int darkness)
Definition: request.cpp:1055
CS_STAT_EXP64
#define CS_STAT_EXP64
Definition: newclient.h:102
map2_coord_valid
static bool map2_coord_valid(int x)
Definition: request.cpp:73
FLAG_PROBE
#define FLAG_PROBE
Definition: define.h:257
range_golem
@ range_golem
Definition: player.h:34
send_plugin_custom_message
void send_plugin_custom_message(object *pl, char *buf)
Definition: request.cpp:1706
SockList_Reset
void SockList_Reset(SockList *sl)
Definition: lowlevel.cpp:71
CS_STAT_CHARACTER_FLAGS
#define CS_STAT_CHARACTER_FLAGS
Definition: newclient.h:129
socket_struct::map_scroll_x
int8_t map_scroll_x
Definition: newserver.h:94
object_get_value
const char * object_get_value(const object *op, const char *const key)
Definition: object.cpp:4337
set_up_cmd
void set_up_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:119
UPD_SP_MANA
#define UPD_SP_MANA
Definition: newclient.h:313
FLAG_STEALTH
#define FLAG_STEALTH
Definition: define.h:312
heads
static const object * heads[MAX_HEAD_POS][MAX_HEAD_POS][MAP_LAYERS]
Definition: request.cpp:1069
pticks
uint32_t pticks
Definition: time.cpp:47
buf
StringBuffer * buf
Definition: readable.cpp:1552
key_confirm_quit
void key_confirm_quit(object *op, char key)
Definition: player.cpp:1592
CS_STAT_APPLIED_DEX
#define CS_STAT_APPLIED_DEX
Definition: newclient.h:123
add_player
player * add_player(socket_struct *ns, int flags)
Definition: player.cpp:460
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2848
POISONING
@ POISONING
Definition: object.h:223
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:393
CS_STAT_RACE_STR
#define CS_STAT_RACE_STR
Definition: newclient.h:106
CS_STAT_GOLEM_MAXHP
#define CS_STAT_GOLEM_MAXHP
Definition: newclient.h:128
SND_EFFECTS
#define SND_EFFECTS
Definition: sounds.h:12
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
ACL_MAP
#define ACL_MAP
Definition: newclient.h:212
linked_char
Definition: global.h:96
FLAG_ALIVE
#define FLAG_ALIVE
Definition: define.h:230
SockList
Definition: newclient.h:670
socket_struct::update_look
uint32_t update_look
Definition: newserver.h:104
SockList_Avail
size_t SockList_Avail(const SockList *sl)
Definition: lowlevel.cpp:243
GetShort_String
short GetShort_String(const unsigned char *data)
Definition: lowlevel.cpp:255
short_stat_name
const char *const short_stat_name[NUM_STATS]
Definition: living.cpp:194
SP_SUMMON_MONSTER
#define SP_SUMMON_MONSTER
Definition: spells.h:101
UPD_SP_DAMAGE
#define UPD_SP_DAMAGE
Definition: newclient.h:315
m
static event_registration m
Definition: citylife.cpp:425
AddIfShort
#define AddIfShort(Old, New, sl, Type)
Definition: request.cpp:751
socket_struct::account_chars
Account_Chars * account_chars
Definition: newserver.h:127
esrv_send_animation
void esrv_send_animation(socket_struct *ns, const Animations *anim)
Definition: request.cpp:1021
VALIDCHAR_MSG
#define VALIDCHAR_MSG
Definition: request.cpp:71
socket_struct::mapy
uint8_t mapy
Definition: newserver.h:116
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:127
Ns_Avail
@ Ns_Avail
Definition: newserver.h:65
CS_STAT_RACE_DEX
#define CS_STAT_RACE_DEX
Definition: newclient.h:109
CS_STAT_ARMOUR
#define CS_STAT_ARMOUR
Definition: newclient.h:91
positioning_system.coord
coord
Definition: positioning_system.py:26
item.q
q
Definition: item.py:32
set_title
void set_title(const object *pl, char *buf, size_t len)
Definition: info.cpp:334
map_cell_struct
Definition: newserver.h:31
MAP_CLIENT_Y
#define MAP_CLIENT_Y
Definition: config.h:238
CS_STAT_SPELL_REPEL
#define CS_STAT_SPELL_REPEL
Definition: newclient.h:104
disinfect.map
map
Definition: disinfect.py:4
CS_STAT_RACE_CHA
#define CS_STAT_RACE_CHA
Definition: newclient.h:111
P_NEW_MAP
#define P_NEW_MAP
Definition: map.h:249
CS_STAT_LEVEL
#define CS_STAT_LEVEL
Definition: newclient.h:87
CS_STAT_CON
#define CS_STAT_CON
Definition: newclient.h:84
account_new_cmd
void account_new_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:2258
account_login_cmd
void account_login_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:2159
playername_ok
int playername_ok(const char *cp)
Definition: player.cpp:257
esrv_send_face
void esrv_send_face(socket_struct *ns, const Face *face, int nocache)
Definition: image.cpp:72
client_spell::last_dam
int16_t last_dam
Definition: player.h:91
ACL_PARTY
#define ACL_PARTY
Definition: newclient.h:211
CS_STAT_CHA
#define CS_STAT_CHA
Definition: newclient.h:85
CS_STAT_APPLIED_CHA
#define CS_STAT_APPLIED_CHA
Definition: newclient.h:125
ST_CHANGE_PASSWORD_CONFIRM
#define ST_CHANGE_PASSWORD_CONFIRM
Definition: define.h:552
CS_STAT_RES_SLOW
#define CS_STAT_RES_SLOW
Definition: newclient.h:147
socket_struct::facecache
uint32_t facecache
Definition: newserver.h:102
check_race_and_class
int check_race_and_class(living *stats, archetype *race, archetype *opclass)
Definition: player.cpp:1429
player::blocked_los
int8_t blocked_los[MAP_CLIENT_X][MAP_CLIENT_Y]
Definition: player.h:178
check_login
void check_login(object *op, const char *password)
Definition: login.cpp:511
CS_STAT_BASE_POW
#define CS_STAT_BASE_POW
Definition: newclient.h:119
account_get_account_for_char
const char * account_get_account_for_char(const char *charname)
Definition: account.cpp:580
CS_STAT_RES_PHYS
#define CS_STAT_RES_PHYS
Definition: newclient.h:137
ST_PLAY_AGAIN
#define ST_PLAY_AGAIN
Definition: define.h:542
CS_STAT_GOD_NAME
#define CS_STAT_GOD_NAME
Definition: newclient.h:130
ADD_PLAYER_NO_MAP
#define ADD_PLAYER_NO_MAP
Definition: player.h:246
map_cell_struct::darkness
int darkness
Definition: newserver.h:33
account_char_free
void account_char_free(Account_Chars *chars)
Definition: account_char.cpp:345
archetype::clone
object clone
Definition: object.h:478
send_smooth
static void send_smooth(socket_struct *ns, const Face *face)
Definition: request.cpp:435
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
draw_client_map
void draw_client_map(object *pl)
Definition: request.cpp:1607
Face::name
sstring name
Definition: face.h:19
account_remove_player
int account_remove_player(const char *account_name, const char *player_name)
Definition: account.cpp:475
HEAD
#define HEAD(op)
Definition: object.h:598
MAX_HEAD_POS
#define MAX_HEAD_POS
Definition: request.cpp:1060
SockList_AddShort
void SockList_AddShort(SockList *sl, uint16_t data)
Definition: lowlevel.cpp:113
ANIM_RANDOM
#define ANIM_RANDOM
Definition: newclient.h:336
CS_STAT_BASE_CHA
#define CS_STAT_BASE_CHA
Definition: newclient.h:118
object_find_by_type_and_name
object * object_find_by_type_and_name(const object *who, int type, const char *name)
Definition: object.cpp:4099
SockList_AddChar
void SockList_AddChar(SockList *sl, unsigned char c)
Definition: lowlevel.cpp:103
set_attr_value
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.cpp:218
FLAG_FREED
#define FLAG_FREED
Definition: define.h:233
socket_struct::lastmap
struct Map lastmap
Definition: newserver.h:93
CF_POISONED
#define CF_POISONED
Definition: newclient.h:188
MAP2_LAYER_START
#define MAP2_LAYER_START
Definition: newclient.h:53
SPELL_GRACE
#define SPELL_GRACE
Definition: spells.h:59
CS_STAT_APPLIED_STR
#define CS_STAT_APPLIED_STR
Definition: newclient.h:120
socket_struct::host
char * host
Definition: newserver.h:100
Map
Definition: newserver.h:48
CS_STAT_RACE_POW
#define CS_STAT_RACE_POW
Definition: newclient.h:112
account_password
void account_password(char *buf, int len, socket_struct *ns)
Definition: request.cpp:3026
esrv_map_scroll
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.cpp:1668
GET_MAP_FACE_OBJ
#define GET_MAP_FACE_OBJ(M, X, Y, L)
Definition: map.h:183
socket_struct::account_name
char * account_name
Definition: newserver.h:126
CS_STAT_AC
#define CS_STAT_AC
Definition: newclient.h:89
FREE_AND_COPY
#define FREE_AND_COPY(sv, nv)
Definition: global.h:204
client_spell::last_grace
int16_t last_grace
Definition: player.h:90
check_space_for_heads
static void check_space_for_heads(int ax, int ay, SockList *sl, socket_struct *ns)
Definition: request.cpp:1363
CS_STAT_OVERLOAD
#define CS_STAT_OVERLOAD
Definition: newclient.h:131
CS_STAT_WEAP_SP
#define CS_STAT_WEAP_SP
Definition: newclient.h:94
socket_struct::monitor_spells
uint32_t monitor_spells
Definition: newserver.h:110
FLAG_PARALYZED
#define FLAG_PARALYZED
Definition: define.h:371
CS_STAT_APPLIED_CON
#define CS_STAT_APPLIED_CON
Definition: newclient.h:124
MAP_LAYER_LIVING1
#define MAP_LAYER_LIVING1
Definition: map.h:46
navar-midane_time.data
data
Definition: navar-midane_time.py:11
esrv_update_spells
void esrv_update_spells(player *pl)
Definition: request.cpp:1720
skill_names
const char * skill_names[MAX_SKILLS]
Definition: skill_util.cpp:59
map_cell_struct::faces
uint16_t faces[MAP_LAYERS]
Definition: newserver.h:32
socket_struct::faceset
uint8_t faceset
Definition: newserver.h:117
spell
with a maximum of six This is not so if you are wearing plate you receive no benefit Armour is additive with all the supplementry forms of which means that it lasts until the next semi permanent spell effect is cast upon the character spell
Definition: tome-of-magic.txt:44
CS_STAT_RES_GHOSTHIT
#define CS_STAT_RES_GHOSTHIT
Definition: newclient.h:145
linked_char::next
struct linked_char * next
Definition: global.h:98
CS_STAT_SKILLINFO
#define CS_STAT_SKILLINFO
Definition: newclient.h:161
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
free_charlinks
void free_charlinks(linked_char *lc)
Definition: utils.cpp:596
account_new
int account_new(const char *account_name, const char *account_password)
Definition: account.cpp:400
ST_CHANGE_PASSWORD_NEW
#define ST_CHANGE_PASSWORD_NEW
Definition: define.h:551
CS_STAT_RES_BLIND
#define CS_STAT_RES_BLIND
Definition: newclient.h:154
FLAG_UNAGGRESSIVE
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
CF_DISEASED
#define CF_DISEASED
Definition: newclient.h:191
MAP2_TYPE_DARKNESS
#define MAP2_TYPE_DARKNESS
Definition: newclient.h:43
socket_struct::map_scroll_y
int8_t map_scroll_y
Definition: newserver.h:94
MAP2_COORD_ENCODE
static uint16_t MAP2_COORD_ENCODE(int x, int y, int flags)
Definition: request.cpp:86
archetype
Definition: object.h:474
sproto.h
CS_STAT_RACE_INT
#define CS_STAT_RACE_INT
Definition: newclient.h:107
SND_MUTE
#define SND_MUTE
Definition: sounds.h:14
MAX_SKILLS
#define MAX_SKILLS
Definition: skills.h:70
get_map_from_coord
mapstruct * get_map_from_coord(mapstruct *m, int16_t *x, int16_t *y)
Definition: map.cpp:2359
VERSION_SC
#define VERSION_SC
Definition: newserver.h:150
animate.anim
string anim
Definition: animate.py:20
get_face_by_id
const Face * get_face_by_id(uint16_t id)
Definition: assets.cpp:315
Settings::account_block_create
uint8_t account_block_create
Definition: global.h:328
SP_level_spellpoint_cost
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.cpp:236
account_is_logged_in
int account_is_logged_in(const char *name)
Definition: account.cpp:605
MAP_TYPE_CHOICE
#define MAP_TYPE_CHOICE
Definition: map.h:59
SockList_Init
void SockList_Init(SockList *sl)
Definition: lowlevel.cpp:52
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
CS_STAT_BASE_WIS
#define CS_STAT_BASE_WIS
Definition: newclient.h:115
is_perfect
static uint8_t is_perfect(const player *pl)
Definition: request.cpp:778
weight
TIPS on SURVIVING Crossfire is populated with a wealth of different monsters These monsters can have varying immunities and attack types In some of them can be quite a bit smarter than others It will be important for new players to learn the abilities of different monsters and learn just how much it will take to kill them This section discusses how monsters can interact with players Most monsters in the game are out to mindlessly kill and destroy the players These monsters will help boost a player s after he kills them When fighting a large amount of monsters in a single attempt to find a narrower hallway so that you are not being attacked from all sides Charging into a room full of Beholders for instance would not be open the door and fight them one at a time For there are several maps designed for them Find these areas and clear them out All throughout these a player can find signs and books which they can read by stepping onto them and hitting A to apply the book sign These messages will help the player to learn the system One more always keep an eye on your food If your food drops to your character will soon so BE CAREFUL ! NPCs Non Player Character are special monsters which have intelligence Players may be able to interact with these monsters to help solve puzzles and find items of interest To speak with a monster you suspect to be a simply move to an adjacent square to them and push the double ie Enter your and press< Return > You can also use say if you feel like typing a little extra Other NPCs may not speak to but display intelligence with their movement Some monsters can be and may attack the nearest of your enemies Others can be in that they follow you around and help you in your quest to kill enemies and find treasure SPECIAL ITEMS There are many special items which can be found in of these the most important may be the signs all a player must do is apply the handle In the case of the player must move items over the button to hold it down Some of the larger buttons may need very large items to be moved onto before they can be activated Gates and locked but be for you could fall down into a pit full of ghosts or dragons and not be able to get back out Break away sometimes it may be worth a player s time to test the walls of a map for secret doors Fire such as missile weapons and spells you will notice them going up in smoke ! So be careful not to destroy valuable items Spellbooks sometimes a player can learn the other times they cannot There are many different types of books and scrolls out there Improve item have lower weight
Definition: survival-guide.txt:100
CS_STAT_FOOD
#define CS_STAT_FOOD
Definition: newclient.h:93
socket_struct::cs_version
uint32_t cs_version
Definition: newserver.h:113
living
Definition: living.h:35
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:248
CS_STAT_WEIGHT_LIM
#define CS_STAT_WEIGHT_LIM
Definition: newclient.h:101
MAX_BUF
#define MAX_BUF
Definition: define.h:35
Ns_Add
@ Ns_Add
Definition: newserver.h:66
CS_STAT_ITEM_POWER
#define CS_STAT_ITEM_POWER
Definition: newclient.h:132
CS_STAT_RES_POISON
#define CS_STAT_RES_POISON
Definition: newclient.h:146
receive_player_name
void receive_player_name(object *op, const char *name)
Definition: c_misc.cpp:1935
is_valid_faceset
int is_valid_faceset(int fsn)
Definition: image.cpp:117
CS_STAT_WC
#define CS_STAT_WC
Definition: newclient.h:88
ADD_PLAYER_NEW
#define ADD_PLAYER_NEW
Definition: player.h:245
object_present_in_ob
object * object_present_in_ob(uint8_t type, const object *op)
Definition: object.cpp:3159
get_weight_limit
uint32_t get_weight_limit(int stat)
Definition: living.cpp:2362
SockList_Term
void SockList_Term(SockList *sl)
Definition: lowlevel.cpp:62
AddIfInt
#define AddIfInt(Old, New, sl, Type)
Definition: request.cpp:744
probe
int probe(object *op, object *caster, object *spell_ob, int dir, int level)
Definition: spell_effect.cpp:699
FLAG_CLIENT_SENT
#define FLAG_CLIENT_SENT
Definition: define.h:346
CS_STAT_MAXGRACE
#define CS_STAT_MAXGRACE
Definition: newclient.h:99
key_roll_stat
void key_roll_stat(object *op, char key)
Definition: player.cpp:1213
MAP2_TYPE_CLEAR
#define MAP2_TYPE_CLEAR
Definition: newclient.h:42
FLAG_CLIENT_ANIM_SYNC
#define FLAG_CLIENT_ANIM_SYNC
Definition: define.h:240
ST_PLAYING
#define ST_PLAYING
Definition: define.h:541
create_player_cmd
void create_player_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:2606
CS_STAT_STR
#define CS_STAT_STR
Definition: newclient.h:80
apply_race_and_class
int apply_race_and_class(object *op, archetype *race, archetype *opclass, living *stats)
Definition: player.cpp:1479
CS_STAT_BASE_INT
#define CS_STAT_BASE_INT
Definition: newclient.h:114
sounds.h
FLAG_REMOVED
#define FLAG_REMOVED
Definition: define.h:232
ST_CHANGE_CLASS
#define ST_CHANGE_CLASS
Definition: define.h:544
CS_STAT_SPELL_ATTUNE
#define CS_STAT_SPELL_ATTUNE
Definition: newclient.h:103
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
llevInfo
@ llevInfo
Definition: logger.h:12
SockList_AddData
void SockList_AddData(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:164
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
esrv_move_object
void esrv_move_object(object *pl, tag_t to, tag_t tag, long nrof)
Definition: item.cpp:890
FLAG_FRIENDLY
#define FLAG_FRIENDLY
Definition: define.h:246
get_client_spell_state
client_spell * get_client_spell_state(player *pl, object *spell)
Definition: player.cpp:144
archetype::tail_x
int8_t tail_x
Definition: object.h:479
SP_level_dam_adjust
int SP_level_dam_adjust(const object *caster, const object *spob)
Definition: spell_util.cpp:287
socket_struct::want_pickup
uint32_t want_pickup
Definition: newserver.h:108
object_find_by_arch_name
object * object_find_by_arch_name(const object *who, const char *name)
Definition: object.cpp:4243
AddIfFloat
#define AddIfFloat(Old, New, sl, Type)
Definition: request.cpp:758
ST_CONFIRM_PASSWORD
#define ST_CONFIRM_PASSWORD
Definition: define.h:548
send_tick
void send_tick(player *pl)
Definition: request.cpp:1968
Settings::starting_stat_min
uint8_t starting_stat_min
Definition: global.h:320
map2_add_ob
static int map2_add_ob(int ax, int ay, int layer, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int is_head)
Definition: request.cpp:1096
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.cpp:299
account_block_create
static int account_block_create(const socket_struct *ns)
Definition: request.cpp:2222
item
Definition: item.py:1
DISEASE
@ DISEASE
Definition: object.h:249
P_NEED_UPDATE
#define P_NEED_UPDATE
Definition: map.h:238
newserver.h
mapstruct
Definition: map.h:314
enter_exit
void enter_exit(object *op, object *exit_ob)
Definition: server.cpp:734
SP_RUNE
#define SP_RUNE
Definition: spells.h:76
BEAT_INTERVAL
#define BEAT_INTERVAL
Definition: config.h:636
map_newmap_cmd
void map_newmap_cmd(socket_struct *ns)
Definition: request.cpp:669
esrv_add_spells
void esrv_add_spells(player *pl, object *spell)
Definition: request.cpp:1909
SND_MUSIC
#define SND_MUSIC
Definition: sounds.h:13
sstring
const typedef char * sstring
Definition: sstring.h:2
give.op
op
Definition: give.py:33
NDI_ALL
#define NDI_ALL
Definition: newclient.h:252
socket_struct::status
enum Sock_Status status
Definition: newserver.h:90
MAP_CLIENT_Y_MINIMUM
#define MAP_CLIENT_Y_MINIMUM
Definition: config.h:246
autojail.value
value
Definition: autojail.py:6
CS_STAT_RES_COLD
#define CS_STAT_RES_COLD
Definition: newclient.h:141
FLAG_AUTO_APPLY
#define FLAG_AUTO_APPLY
Definition: define.h:250
esrv_remove_spell
void esrv_remove_spell(player *pl, object *spell)
Definition: request.cpp:1772
Account_Chars::chars
std::vector< Account_Char * > chars
Definition: account_char.h:30
SockList_AddLen8Data
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:176
receive_player_password
void receive_player_password(object *op, const char *password)
Definition: c_misc.cpp:1955
mapstruct::in_memory
uint32_t in_memory
Definition: map.h:335
Settings::min_name
uint8_t min_name
Definition: global.h:331
Map::cells
struct map_cell_struct cells[MAX_CLIENT_X][MAX_CLIENT_Y]
Definition: newserver.h:49
add_me_cmd
void add_me_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:391
esrv_draw_look
void esrv_draw_look(object *pl)
Definition: item.cpp:193
version_cmd
void version_cmd(char *buf, int len, socket_struct *ns)
Definition: request.cpp:633
diamondslots.y
y
Definition: diamondslots.py:16
CF_CONFUSED
#define CF_CONFUSED
Definition: newclient.h:187
ob_apply
method_ret ob_apply(object *op, object *applier, int aflags)
Definition: ob_methods.cpp:44
NUM_ANIMATIONS
#define NUM_ANIMATIONS(ob)
Definition: global.h:171
SockList_ResetRead
void SockList_ResetRead(SockList *sl)
Definition: lowlevel.cpp:80
NDI_DK_ORANGE
#define NDI_DK_ORANGE
Definition: newclient.h:237
socket_struct::anims_sent
uint8_t anims_sent[MAXANIMNUM]
Definition: newserver.h:97
account_change_password
int account_change_password(const char *account_name, const char *current_password, const char *new_password)
Definition: account.cpp:628
socket_struct::faces_sent
uint8_t * faces_sent
Definition: newserver.h:96
CS_STAT_RES_DEATH
#define CS_STAT_RES_DEATH
Definition: newclient.h:152
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:229
GetInt_String
int GetInt_String(const unsigned char *data)
Definition: lowlevel.cpp:251
draw_client_map2
static void draw_client_map2(object *pl)
Definition: request.cpp:1410
strcasecmp
int strcasecmp(const char *s1, const char *s2)
castle_read.key
key
Definition: castle_read.py:64
make_face_from_files.int
int
Definition: make_face_from_files.py:32
ACL_LEVEL
#define ACL_LEVEL
Definition: newclient.h:209
MAP_LAYER_FLY2
#define MAP_LAYER_FLY2
Definition: map.h:49
Face
Definition: face.h:14
CS_STAT_TITLE
#define CS_STAT_TITLE
Definition: newclient.h:96
MAP_TYPE_DEFAULT
#define MAP_TYPE_DEFAULT
Definition: map.h:58
newclient.h
save_player
int save_player(object *op, int flag)
Definition: login.cpp:230
ST_GET_PARTY_PASSWORD
#define ST_GET_PARTY_PASSWORD
Definition: define.h:549
VERSION_CS
#define VERSION_CS
Definition: newserver.h:149
UPD_SP_GRACE
#define UPD_SP_GRACE
Definition: newclient.h:314
get_archetype_by_type_subtype
archetype * get_archetype_by_type_subtype(int type, int subtype)
Definition: arch.cpp:99
CS_STAT_POW
#define CS_STAT_POW
Definition: newclient.h:97
skill
skill
Definition: arch-handbook.txt:585
CF_WIZARD
#define CF_WIZARD
Definition: newclient.h:196
esrv_send_pickup
void esrv_send_pickup(player *pl)
Definition: request.cpp:1795
object_remove
void object_remove(object *op)
Definition: object.cpp:1828
player_set_state
void player_set_state(player *pl, uint8_t state)
Definition: player.cpp:4475
try_find_archetype
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:270
CS_STAT_RACE_WIS
#define CS_STAT_RACE_WIS
Definition: newclient.h:108
AddIfString
#define AddIfString(Old, New, sl, Type)
Definition: request.cpp:765
MAX_CHOICES
#define MAX_CHOICES
Definition: request.cpp:2599
try_find_face
const Face * try_find_face(const char *name, const Face *error)
Definition: assets.cpp:286
SP_RAISE_DEAD
#define SP_RAISE_DEAD
Definition: spells.h:75
MIN_NUM_LOOK_OBJECTS
#define MIN_NUM_LOOK_OBJECTS
Definition: newserver.h:16
SP_MAKE_MARK
#define SP_MAKE_MARK
Definition: spells.h:77
MAP_LAYER_LIVING2
#define MAP_LAYER_LIVING2
Definition: map.h:47
socket_struct::login_method
uint8_t login_method
Definition: newserver.h:128
archetype::tail_y
int8_t tail_y
Definition: object.h:479
check_probe
static int check_probe(int ax, int ay, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int *alive_layer)
Definition: request.cpp:1266
commands.h
ST_CONFIRM_QUIT
#define ST_CONFIRM_QUIT
Definition: define.h:545
CS_STAT_MAXHP
#define CS_STAT_MAXHP
Definition: newclient.h:77
move_cmd
void move_cmd(char *buf, int len, player *pl)
Definition: request.cpp:690
account_char_load
Account_Chars * account_char_load(const char *account_name)
Definition: account_char.cpp:135
key_change_class
void key_change_class(object *op, char key)
Definition: player.cpp:1289
receive_party_password
void receive_party_password(object *op, const char *password)
Definition: c_party.cpp:53
spell_client_use
static int spell_client_use(const object *spell)
Definition: request.cpp:1815
account_check_string
int account_check_string(const char *str)
Definition: account.cpp:361
esrv_update_stats
void esrv_update_stats(player *pl)
Definition: request.cpp:849
socket_struct::darkness
uint32_t darkness
Definition: newserver.h:103
FLAG_XRAYS
#define FLAG_XRAYS
Definition: define.h:300
player::socket
socket_struct * socket
Definition: player.h:107
socket_struct::notifications
uint16_t notifications
Definition: newserver.h:129
SF_RUNON
#define SF_RUNON
Definition: newclient.h:179
Animations
Definition: face.h:25
CS_STAT_RANGE
#define CS_STAT_RANGE
Definition: newclient.h:95
param
No space after the left no space before the right paren Comma right after the formal param
Definition: style-guide.txt:215
report.error
def error(pl)
Definition: report.py:43
object::stats
living stats
Definition: object.h:378
MSG_TYPE_ADMIN_VERSION
#define MSG_TYPE_ADMIN_VERSION
Definition: newclient.h:490
Face::number
uint16_t number
Definition: face.h:15
get_attr_value
int8_t get_attr_value(const living *stats, int attr)
Definition: living.cpp:313
update_los
void update_los(object *op)
Definition: los.cpp:509
CS_STAT_BASE_CON
#define CS_STAT_BASE_CON
Definition: newclient.h:117
add_char_field
static void add_char_field(SockList *sl, int type, const char *data)
Definition: request.cpp:1998
CS_STAT_DEX
#define CS_STAT_DEX
Definition: newclient.h:83
account_login
int account_login(const char *account_name, const char *account_password)
Definition: account.cpp:319
verify_player
int verify_player(const char *name, char *password)
Definition: login.cpp:111
ST_ROLL_STAT
#define ST_ROLL_STAT
Definition: define.h:543
SPELL
@ SPELL
Definition: object.h:219
CS_STAT_BASE_STR
#define CS_STAT_BASE_STR
Definition: newclient.h:113
smooth_face
const Face * smooth_face
Definition: image.cpp:36
esrv_new_player
void esrv_new_player(player *pl, uint32_t weight)
Definition: request.cpp:991
decode_name_password
static int decode_name_password(const char *buf, int *len, char *name, char *password)
Definition: request.cpp:2123
text
Crossfire Protocol most of the time after the actual code was already omit certain important and possibly make life miserable any new developer or curious player should be able to find most of the relevant information here If inconsistencies are found or this documentation proves to be consider the latest server side protocol code in the public source code repository as the authoritative reference Introduction If you were ever curious enough to telnet or netcat to a Crossfire chances are you were sorely disappointed While the protocol may seem to use plain text at it actually uses a mix of ASCII and binary data This handbook attempts to document various aspects of the Crossfire protocol As consult the README file to find out how to get in touch with helpful people via mailing and more History the communications plan was set to be a text based system It was up to the server and client to parse these messages and determine what to do These messages were assumed to be line per message At a reasonably early stage of Eric Anderson wrote a then the data itself you could send many data and after the other end could decode these commands This works fairly but I think the creation of numerous sub packets has some performance hit the eutl was not especially well so writing a client for a different platform became more Eric left to work on other products shortly after writing his which didn t really leave anyone with a full understanding of the socket code I have decided to remove the eutl dependency At least one advantage is that having this network related code directly in the client and server makes error handling a bit easier cleaner Packet Format the outside packet method the byte size for the size information is not included here Eutl originally used bytes for the size to bytes seems it makes a least some sense The actual data is something of the nature of the commands listed below It is a text followed by possible other data The remaining data can be binary it is up to the client and server to decode what it sent The commands as described below is just the data portion of the packet If writing a new remember that you must take into account the size of the packet There is no termination of other than knowing how long it should be For most everything that is sent is text This is more or less how things worked under except it packed the ints into bytes in a known order In some we handle ints as in they are sent as binary information How any command handles it is detailed below in the command description The S and C represent the direction of the we use MSB as well as any ints or shorts that get sent inside the packets All packets are defined to have at least one word of text
Definition: protocol.txt:84
account_char_save
void account_char_save(Account_Chars *chars)
Definition: account_char.cpp:158
CS_STAT_RES_DRAIN
#define CS_STAT_RES_DRAIN
Definition: newclient.h:144
Settings::starting_stat_points
uint8_t starting_stat_points
Definition: global.h:322
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
MAP_NOSMOOTH
#define MAP_NOSMOOTH(m)
Definition: map.h:85
living.h
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Definition: lowlevel.cpp:444
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:391
CF_HOSTILE
#define CF_HOSTILE
Definition: newclient.h:193
CF_PARALYZED
#define CF_PARALYZED
Definition: newclient.h:195
NUM_STATS
@ NUM_STATS
Definition: living.h:18
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
living::hp
int16_t hp
Definition: living.h:40
takeitem.status
status
Definition: takeitem.py:38
FORCE
@ FORCE
Definition: object.h:229
account_char_remove
void account_char_remove(Account_Chars *chars, const char *pl_name)
Definition: account_char.cpp:313
CS_STAT_APPLIED_INT
#define CS_STAT_APPLIED_INT
Definition: newclient.h:121
MAX_LIGHT_RADII
#define MAX_LIGHT_RADII
Definition: define.h:450
ACL_FACE
#define ACL_FACE
Definition: newclient.h:210
CF_XRAY
#define CF_XRAY
Definition: newclient.h:190
rangetostring
void rangetostring(const object *pl, char *obuf, size_t len)
Definition: info.cpp:264
llevDebug
@ llevDebug
Definition: logger.h:13
NS_FACESENT_SMOOTH
#define NS_FACESENT_SMOOTH
Definition: newserver.h:138
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
SockList_AddPrintf
void SockList_AddPrintf(SockList *sl, const char *format,...)
Definition: lowlevel.cpp:199
account_exists
const char * account_exists(const char *account_name)
Definition: account.cpp:297
CS_STAT_RES_PARA
#define CS_STAT_RES_PARA
Definition: newclient.h:148
CS_STAT_RES_ACID
#define CS_STAT_RES_ACID
Definition: newclient.h:143
SPELL_MANA
#define SPELL_MANA
Definition: spells.h:58
dragon_attune.force
force
Definition: dragon_attune.py:45
CS_STAT_RES_ELEC
#define CS_STAT_RES_ELEC
Definition: newclient.h:140
CS_STAT_FLAGS
#define CS_STAT_FLAGS
Definition: newclient.h:100