Crossfire Client, Trunk
commands.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 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 
55 int mapupdatesent = 0;
56 
57 #include "client.h"
58 
59 #include <assert.h>
60 #include <ctype.h>
61 #include <errno.h>
62 
63 #include "external.h"
64 #include "mapdata.h"
65 
66 /* In general, the data from the server should not do bad
67  * things like this, but checking for it makes it easier
68  * to find bugs. Often this is called within a loop
69  * that of iterating over the length of the buffer, hence
70  * the break. Note that this may not prevent crashes,
71  * but at least we generate a message.
72  * Note that curpos & buflen may be string pointers or
73  * may be integers - as long as both are the same
74  * (both integers or both char *) it will work.
75  */
76 #define ASSERT_LEN(function, curpos, buflen) \
77  if (curpos > buflen) { \
78  LOG(LOG_WARNING, function, "Data goes beyond length of buffer (%d>%d)", curpos, buflen); \
79  break; \
80 }
81 
82 char *news=NULL, *motd=NULL, *rules=NULL;
83 
85 static int spellmon_level = 0;
86 
87 int num_races = 0; /* Number of different races server has */
88 int used_races = 0; /* How many races we have filled in */
89 
90 int num_classes = 0; /* Same as race data above, but for classes */
91 int used_classes = 0;
92 
93 int stat_points; /* Number of stat points for new characters */
94 int stat_min; /* Minimum stat for new characters */
95 int stat_maximum; /* Maximum stat for new characters */
96 int starting_map_number = 0; /* Number of starting maps */
97 
100 
101 /* Best I can tell, none of this stat information is stored anyplace
102  * else in the server - MSW 2010-07-28
103  */
104 
105 #define NUM_STATS 7
106 
107 static const char *const short_stat_name[NUM_STATS] = {
108  "Str", "Dex", "Con",
109  "Wis", "Cha", "Int",
110  "Pow"
111 };
112 
113 /* Note that the label_cs and label_rs will be in this same
114  * order, eg, label_cs[0] will be strength, label_cs[1] will
115  * be con. However, this order can be changed, so it should
116  * not be assumed that label_cs[1] will always be con.
117  */
119  {"str", CS_STAT_STR, 0},
120  {"con", CS_STAT_CON, 1},
121  {"dex", CS_STAT_DEX, 2},
122  {"int", CS_STAT_INT, 3},
123  {"wis", CS_STAT_WIS, 4},
124  {"pow", CS_STAT_POW, 5},
125  {"cha", CS_STAT_CHA, 6}
126 };
127 
135 {
136  int i;
137 
138  if (!starting_map_info) {
139  return;
140  }
141 
142  /* Because we are going free the array storage itself, there is no reason
143  * to clear the data[i].. values.
144  */
145  for (i=0; i<starting_map_number; i++) {
146  if (starting_map_info[i].arch_name) {
147  free(starting_map_info[i].arch_name);
148  }
149  if (starting_map_info[i].public_name) {
150  free(starting_map_info[i].public_name);
151  }
152  if (starting_map_info[i].description) {
153  free(starting_map_info[i].description);
154  }
155  }
156 
157  free(starting_map_info);
158  starting_map_info=NULL;
160 }
161 
172 static void get_starting_map_info(unsigned char *data, int len) {
173  int pos, type, length, map_entry=-1;
174  char *cp;
175 
176  pos = 0;
177  while (pos < len) {
178  type = data[pos];
179  pos++;
180 
181  /* Right now, all the data is length prefixed strings, so
182  * the only real difference is where we store the data
183  */
184 
185  length = GetShort_String(data + pos);
186  pos += 2;
187 
188  if ((length+pos) > len) {
189  LOG(LOG_WARNING, "common::get_starting_map_info",
190  "Length of data is greater than buffer (%d>%d)", length + pos, len);
191  return;
192  }
193 
194  cp = g_malloc(length+1);
195  strncpy(cp, (char *)data + pos, length);
196  cp[length] = 0;
197 
198  pos += length;
199 
200  /* If it is the arch name, it is a new entry, so we allocate
201  * space and clear it. This isn't most efficient, but at
202  * the same time, I don't see there being many maps.
203  * Note: If g_realloc is given a null pointer (which starting_map_info
204  * will be after free or first load), g_realloc just acts as g_malloc.
205  */
206  if (type == INFO_MAP_ARCH_NAME) {
207  map_entry++;
209  (map_entry + 1) * sizeof(Starting_Map_Info));
210 
211  if (starting_map_info == NULL) {
212  LOG(LOG_ERROR, "get_starting_map_info",
213  "Could not allocate memory: %s", strerror(errno));
214  exit(EXIT_FAILURE);
215  }
216 
217  memset(&starting_map_info[map_entry], 0, sizeof(Starting_Map_Info));
218  starting_map_info[map_entry].arch_name = cp;
219  } else if (type == INFO_MAP_NAME) {
220  starting_map_info[map_entry].public_name = cp;
221  } else if (type == INFO_MAP_DESCRIPTION) {
222  starting_map_info[map_entry].description = cp;
223  } else {
224  /* Could be this is old client - but we can skip over
225  * this bad data so long as the length byte is valid.
226  */
227  LOG(LOG_WARNING, "common::get_starting_map_info",
228  "Unknown type: %d\n", type);
229  }
230  }
231  starting_map_number = map_entry;
233 }
234 
247 static void get_new_char_info(unsigned char *data, int len) {
248  int olen=0, llen;
249 
250  /* We reset these values - if the user is switching between
251  * servers before restarting the client, these may have
252  * different values.
253  */
254  stat_points = 0;
255  stat_min = 0;
256  stat_maximum = 0;
257 
258  while (olen < len) {
259  char datatype, *cp;
260 
261  /* Where this line ends in the total buffer */
262  llen = olen + GetChar_String(data + olen);
263 
264  /* By protocol convention, this should already be NULL,
265  * but we ensure it is. If the server has not included the
266  * null byte, we are overwriting some real data here, but
267  * the client will probably get an error at that point -
268  * if the server is not following the protocol, we really
269  * can't trust any of the data we get from it.
270  */
271  data[llen] = 0;
272 
273  if (llen > len) {
274  LOG(LOG_WARNING, "common::get_new_char_info",
275  "Length of line is greater than buffer (%d>%d)", llen, len);
276  return;
277  }
278  olen++;
279  datatype = GetChar_String(data+olen); /* Type value */
280  olen++;
281  /* First skip all the spaces */
282  while (olen <= len) {
283  if (!isspace(data[olen])) {
284  break;
285  }
286  olen++;
287  }
288  if (olen > len) {
289  LOG(LOG_WARNING, "common::get_new_char_info",
290  "Overran length of buffer (%d>%d)", olen, len);
291  return;
292  }
293 
294  cp = (char *)data + olen;
295  /* Go until we find another space */
296  while (olen <= len) {
297  if (isspace(data[olen])) {
298  break;
299  }
300  olen++;
301  }
302  data[olen] = 0; /* Null terminate the string */
303  olen++;
304  if (olen > len) {
305  LOG(LOG_WARNING, "common::get_new_char_info",
306  "Overran length of buffer (%d>%d)", olen, len);
307  return;
308  }
309  /* At this point, cp points to the string portion (variable name)
310  * of the line, with data+olen is the start of the next string
311  * (variable value).
312  */
313  if (!g_ascii_strcasecmp(cp,"points")) {
314  stat_points = atoi((char *)data + olen);
315  olen = llen + 1;
316  continue;
317  } else if (!g_ascii_strcasecmp(cp,"statrange")) {
318  if (sscanf((char *)data + olen, "%d %d", &stat_min, &stat_maximum) != 2) {
319  LOG(LOG_WARNING, "common::get_new_char_info",
320  "Unable to process statrange line (%s)", data + olen);
321  }
322  /* Either way, we go onto the next line */
323  olen = llen + 1;
324  continue;
325  } else if (!g_ascii_strcasecmp(cp,"statname")) {
326  /* The checking we do here is somewhat basic:
327  * 1) That we understand all the stat names that the server sends us
328  * 2) That we get the correct number of stats.
329  * Note that if the server sends us the same stat name twice, eg
330  * Str Str Dex Con ..., that will screw up this logic, but to a
331  * great extent, we have to trust that server is sending us correct
332  * information - sending the same stat twice does not follow that.
333  */
334  int i, matches=0;
335 
336  while (olen < llen) {
337  for (i=0; i < NUM_STATS; i++) {
338  if (!g_ascii_strncasecmp((char *)data + olen,
339  short_stat_name[i], strlen(short_stat_name[i]))) {
340  matches++;
341  olen += strlen(short_stat_name[i]) + 1;
342  break;
343  }
344  }
345  if (i == NUM_STATS) {
346  LOG(LOG_WARNING, "common::get_new_char_info",
347  "Unable to find matching stat name (%s)", data + olen);
348  break;
349  }
350  }
351  if (matches != NUM_STATS) {
352  LOG(LOG_WARNING, "common::get_new_char_info",
353  "Did not get correct number of stats (%d!=%d)", matches, NUM_STATS);
354  }
355  olen = llen + 1;
356  continue;
357  } else if (!g_ascii_strcasecmp(cp,"race") || !g_ascii_strcasecmp(cp,"class")) {
358  if (g_ascii_strcasecmp((char *)data + olen, "requestinfo")) {
359  LOG(LOG_WARNING, "common::get_new_char_info",
360  "Got unexpected value for %s: %s", cp, data+olen);
361  }
362  olen = llen + 1;
363  continue;
364  } else if (!g_ascii_strcasecmp(cp,"startingmap")) {
365  if (g_ascii_strcasecmp((char *)data + olen, "requestinfo")) {
366  LOG(LOG_WARNING, "common::get_new_char_info",
367  "Got unexpected value for %s: %s", cp, data+olen);
368  } else {
369  cs_print_string(csocket.fd, "requestinfo startingmap");
371  }
372  olen = llen + 1;
373  continue;
374  } else {
375  if (datatype == 'V' || datatype == 'R') {
376  LOG(LOG_WARNING, "common::get_new_char_info",
377  "Got unsupported string from server, type %c, value %s", datatype, cp);
378  /* pop up error here */
379  } else {
380  /* pop up warning here */
381  }
382  olen = llen + 1;
383  }
384  }
385  if (stat_min == 0 || stat_maximum == 0 || stat_points == 0) {
386  /* this needs to be handled better, but I'm not sure how -
387  * we could fall back to legacy character creation mode,
388  * but that will go away at some point - in a sense, if the
389  * server is not sending us values, that is a broken/non comformant
390  * server - best we could perhaps do is throw up a window saying
391  * this client is not compatible with the server.
392  */
393  LOG(LOG_ERROR, "common::get_new_char_info",
394  "Processed all newcharinfo yet have 0 value: stat_min=%d, stat_maximum=%d, stat_points=%d",
396  } else {
398  }
399 }
400 
401 
405 static int rc_compar(const Race_Class_Info *a, const Race_Class_Info *b)
406 {
407  return g_ascii_strcasecmp(a->public_name, b->public_name);
408 }
409 
420 void free_all_race_class_info(Race_Class_Info *data, int num_entries)
421 {
422  int i;
423 
424  /* Because we are going free the array storage itself, there is no reason
425  * to clear the data[i].. values.
426  */
427  for (i=0; i<num_entries; i++) {
428  int j;
429 
430  if (data[i].arch_name) {
431  free(data[i].arch_name);
432  }
433  if (data[i].public_name) {
434  free(data[i].public_name);
435  }
436  if (data[i].description) {
437  free(data[i].description);
438  }
439 
440  for (j=0; j<data[i].num_rc_choice; j++) {
441  int k;
442 
443  for (k=0; k<data[i].rc_choice[j].num_values; k++) {
444  free(data[i].rc_choice[j].value_arch[k]);
445  free(data[i].rc_choice[j].value_desc[k]);
446  }
447  free(data[i].rc_choice[j].value_arch);
448  free(data[i].rc_choice[j].value_desc);
449  free(data[i].rc_choice[j].choice_name);
450  free(data[i].rc_choice[j].choice_desc);
451  }
452  }
453 
454  free(data);
455  data=NULL;
456 }
457 
469 static void process_race_class_info(unsigned char *data, int len, Race_Class_Info *rci) {
470  char *cp = (char *)data, *nl;
471 
472  /* First thing is to process the remaining bit of the requestinfo line,
473  * which is the archetype name for this race/class
474  */
475  nl = strchr(cp, '\n');
476  if (nl) {
477  *nl=0;
478  rci->arch_name = g_strdup(cp);
479  cp = nl+1;
480  } else {
481  LOG(LOG_WARNING, "common::process_race_class_info", "Did not find archetype name");
482  return;
483  }
484 
485  /* Now we process the rest of the data - we look for a word the describes
486  * the data to follow. cp is a pointer to the data we are processing. nl
487  * is used to store temporary values.
488  */
489  do {
490  nl = strchr(cp, ' ');
491  /* If we did not find a space, may just mean we have reached the end
492  * of the data - could be a stray character, etc
493  */
494  if (!nl) {
495  break;
496  }
497 
498  if (nl) {
499  *nl = 0;
500  nl++;
501  }
502  if (!strcmp(cp, "name")) {
503  /* We get a name. The string is not NULL terminated, but the
504  * length is transmitted. So get the length, allocate a string
505  * large enough for that + NULL terminator, and copy string in,
506  * making sure to put terminator in place. also make sure we
507  * update cp beyond this block of data.
508  */
509  int namelen;
510 
511  namelen = GetChar_String((unsigned char *)nl);
512  ASSERT_LEN("common::process_race_class_info",
513  (unsigned char *)nl + namelen, data + len);
514  nl++;
515  rci->public_name = g_malloc(namelen+1);
516  strncpy(rci->public_name, nl, namelen);
517  rci->public_name[namelen] = 0;
518  cp = nl + namelen;
519  } else if (!strcmp(cp, "stats")) {
520  cp = nl;
521  /* This loop goes through the stat values - *cp points to the stat
522  * value - if 0, no more stats, hence the check here.
523  */
524  while (cp < (char *)data + len && *cp != 0) {
525  int i;
526 
527  for (i=0; i < NUM_NEW_CHAR_STATS; i++)
528  if (stat_mapping[i].cs_value == *cp) {
529  break;
530  }
531 
532  if (i == NUM_NEW_CHAR_STATS) {
533  /* Just return with what we have */
534  LOG(LOG_WARNING, "common::process_race_class_info",
535  "Unknown stat value: %d", cp);
536  return;
537  }
538  rci->stat_adj[stat_mapping[i].rc_offset] =
539  GetShort_String((unsigned char *)cp + 1);
540  cp += 3;
541  }
542  cp++; /* Skip over 0 terminator */
543  } else if (!strcmp(cp, "msg")) {
544  /* This is really exactly same as name processing above, except
545  * length is 2 bytes in this case.
546  */
547  int msglen;
548 
549  msglen = GetShort_String((unsigned char *)nl);
550  ASSERT_LEN("common::process_race_class_info",
551  (unsigned char *)nl + msglen, data + len);
552  nl+=2;
553  rci->description = g_malloc(msglen+1);
554  strncpy(rci->description, nl, msglen);
555  rci->description[msglen] = 0;
556  cp = nl + msglen;
557  } else if (!strcmp(cp, "choice")) {
558  int oc = rci->num_rc_choice, clen;
559 
560  rci->num_rc_choice++;
561  /* rc_choice may be null, but g_realloc still works there */
562  rci->rc_choice = g_realloc(rci->rc_choice, sizeof(struct RC_Choice) * rci->num_rc_choice);
563  memset(&rci->rc_choice[oc], 0, sizeof(struct RC_Choice));
564 
565  cp = nl;
566 
567  /* First is the coice string we return */
568  clen = GetChar_String((unsigned char *)cp);
569  cp++;
570  ASSERT_LEN("common::process_race_class_info",
571  (unsigned char *)cp + clen, data + len);
572  rci->rc_choice[oc].choice_name = g_malloc(clen+1);
573  strncpy(rci->rc_choice[oc].choice_name, cp, clen);
574  rci->rc_choice[oc].choice_name[clen] = 0;
575  cp += clen;
576 
577  /* Next is the description */
578  clen = GetChar_String((unsigned char *)cp);
579  cp++;
580  ASSERT_LEN("common::process_race_class_info",
581  (unsigned char *)cp + clen, data + len);
582  rci->rc_choice[oc].choice_desc = g_malloc(clen+1);
583  strncpy(rci->rc_choice[oc].choice_desc, cp, clen);
584  rci->rc_choice[oc].choice_desc[clen] = 0;
585  cp += clen;
586 
587  /* Now is a series of archetype/description pairs */
588  while (1) {
589  int vn;
590 
591  clen = GetChar_String((unsigned char *)cp);
592  cp++;
593  if (!clen) {
594  break; /* 0 length is end of data */
595  }
596  vn = rci->rc_choice[oc].num_values;
597  rci->rc_choice[oc].num_values++;
598  rci->rc_choice[oc].value_arch = g_realloc(rci->rc_choice[oc].value_arch,
599  sizeof(char*) * rci->rc_choice[oc].num_values);
600  rci->rc_choice[oc].value_desc = g_realloc(rci->rc_choice[oc].value_desc,
601  sizeof(char*) * rci->rc_choice[oc].num_values);
602 
603  ASSERT_LEN("common::process_race_class_info",
604  (unsigned char *)cp + clen, data + len);
605  rci->rc_choice[oc].value_arch[vn] = g_malloc(clen+1);
606  strncpy(rci->rc_choice[oc].value_arch[vn], cp, clen);
607  rci->rc_choice[oc].value_arch[vn][clen] = 0;
608  cp += clen;
609 
610  clen = GetChar_String((unsigned char *)cp);
611  cp++;
612  ASSERT_LEN("common::process_race_class_info",
613  (unsigned char *)cp + clen, data + len);
614  rci->rc_choice[oc].value_desc[vn] = g_malloc(clen+1);
615  strncpy(rci->rc_choice[oc].value_desc[vn], cp, clen);
616  rci->rc_choice[oc].value_desc[vn][clen] = 0;
617  cp += clen;
618  }
619  } else {
620  /* Got some keyword we did not understand. Because we do not know
621  * about it, we do not know how to skip it over - the data could
622  * very well contain spaces or other markers we look for.
623  */
624  LOG(LOG_WARNING, "common::process_race_class_info", "Got unknown keyword: %s", cp);
625  break;
626  }
627  } while ((unsigned char *)cp < data + len);
628 
629  /* The display code expects all of these to have a description -
630  * rather than add checks there for NULL values, simpler to
631  * just set things to an empty value.
632  */
633  if (!rci->description) {
634  rci->description = g_strdup("");
635  }
636 
637 }
638 
648 static void get_race_info(unsigned char *data, int len) {
649  /* This should not happen - the client is only requesting race info for
650  * races it has received - and it knows how many of those it has.
651  */
652  if (used_races >= num_races) {
653  LOG(LOG_ERROR, "common::get_race_info",
654  "used races exceed num races, %d>=%d", used_races, num_races);
655  return;
656  }
657 
659  used_races++;
660 
661  if (used_races == num_races) {
662  qsort(races, used_races, sizeof(Race_Class_Info),
663  (int (*)(const void *, const void *))rc_compar);
664 
666  }
667 }
668 
679 static void get_class_info(unsigned char *data, int len) {
680  /* This should not happen - the client is only requesting race info for
681  * classes it has received - and it knows how many of those it has.
682  */
683  if (used_classes >= num_classes) {
684  LOG(LOG_ERROR, "common::get_race_info",
685  "used classes exceed num classes, %d>=%d", used_classes, num_classes);
686  return;
687  }
688 
690  used_classes++;
691 
692  if (used_classes == num_classes) {
693  qsort(classes, used_classes, sizeof(Race_Class_Info),
694  (int (*)(const void *, const void *))rc_compar);
695 
697  }
698 }
699 
705 static void get_exp_info(const unsigned char *data, int len)
706 {
707  int pos, level;
708 
709  if (len < 2) {
710  LOG(LOG_ERROR, "common::get_exp_info", "no max level info from server provided");
711  return;
712  }
713 
715  pos = 2;
716  exp_table = calloc(exp_table_max, sizeof(guint64));
717  for (level = 1; level <= exp_table_max && pos < len; level++) {
718  exp_table[level] = GetInt64_String(data+pos);
719  pos += 8;
720  }
721  if (level != exp_table_max) {
722  LOG(LOG_ERROR, "common::get_exp_info",
723  "Incomplete table sent - got %d entries, wanted %d", level, exp_table_max);
724  }
725 }
726 
732 static void get_skill_info(char *data, int len)
733 {
734  char *cp, *nl, *sn;
735  int val;
736 
737  cp = data;
738  do {
739  nl = strchr(cp, '\n');
740  if (nl) {
741  *nl = 0;
742  nl++;
743  }
744  sn = strchr(cp, ':');
745  if (!sn) {
746  LOG(LOG_WARNING, "common::get_skill_info", "corrupt line: /%s/", cp);
747  return;
748  }
749 
750  *sn = 0;
751  sn++;
752  val = atoi(cp);
753  val -= CS_STAT_SKILLINFO;
754 
755  /* skill_names[MAX_SKILL] is the declaration, so check against that */
756  if (val < 0 || val >= MAX_SKILL) {
757  LOG(LOG_WARNING, "common::get_skill_info", "invalid skill number %d", val);
758  return;
759  }
760 
761  free(skill_names[val]);
762  skill_names[val] = g_strdup(sn);
763  cp = nl;
764  } while (cp < data+len);
765 }
766 
774 void ReplyInfoCmd(unsigned char *buf, int len) {
775  unsigned char *cp;
776  int i;
777 
778  /* Covers a bug in the server in that it could send a replyinfo with no
779  * parameters
780  */
781  if (!buf) {
782  return;
783  }
784 
785  for (i = 0; i < len; i++) {
786  /* Either a space or newline represents a break */
787  if (*(buf+i) == ' ' || *(buf+i) == '\n') {
788  break;
789  }
790  }
791  if (i >= len) {
792  /* Don't print buf, as it may contain binary data */
793  /* Downgrade this to DEBUG - if the client issued an unsupported
794  * requestinfo info to the server, we'll end up here - this could be
795  * normal behaviour
796  */
797  LOG(LOG_DEBUG, "common::ReplyInfoCmd", "Never found a space in the replyinfo");
798  return;
799  }
800 
801  /* Null out the space and put cp beyond it */
802  cp = buf+i;
803  *cp++ = '\0';
804  if (!strcmp((char*)buf, "image_info")) {
805  get_image_info(cp, len-i-1); /* Located in common/image.c */
806  } else if (!strcmp((char*)buf, "image_sums")) {
807  get_image_sums((char*)cp, len-i-1); /* Located in common/image.c */
808  } else if (!strcmp((char*)buf, "skill_info")) {
809  get_skill_info((char*)cp, len-i-1); /* Located in common/commands.c */
810  } else if (!strcmp((char*)buf, "exp_table")) {
811  get_exp_info(cp, len-i-1); /* Located in common/commands.c */
812  } else if (!strcmp((char*)buf, "motd")) {
813  if (motd) {
814  free((char*)motd);
815  }
816  motd = g_strdup((char *)cp);
818  } else if (!strcmp((char*)buf, "news")) {
819  if (news) {
820  free((char*)news);
821  }
822  news = g_strdup((char *)cp);
824  } else if (!strcmp((char*)buf, "rules")) {
825  if (rules) {
826  free((char*)rules);
827  }
828  rules = g_strdup((char *)cp);
830  } else if (!strcmp((char*)buf, "race_list")) {
831  unsigned char *cp1;
832  for (cp1=cp; *cp !=0; cp++) {
833  if (*cp == '|') {
834  *cp++ = '\0';
835  /* The first separator has no data, so only send request to
836  * server if this is not null.
837  */
838  if (*cp1!='\0') {
839  cs_print_string(csocket.fd, "requestinfo race_info %s", cp1);
840  num_races++;
841  }
842  cp1 = cp;
843  }
844  }
845  if (*cp1!='\0') {
846  cs_print_string(csocket.fd, "requestinfo race_info %s", cp1);
847  num_races++;
848  }
849  if (races) {
851  num_races=0;
852  used_races=0;
853  }
854  races = calloc(num_races, sizeof(Race_Class_Info));
855 
856  } else if (!strcmp((char*)buf, "class_list")) {
857  unsigned char *cp1;
858  for (cp1=cp; *cp !=0; cp++) {
859  if (*cp == '|') {
860  *cp++ = '\0';
861  /* The first separator has no data, so only send request to
862  * server if this is not null.
863  */
864  if (*cp1!='\0') {
865  cs_print_string(csocket.fd, "requestinfo class_info %s", cp1);
866  num_classes++;
867  }
868  cp1 = cp;
869  }
870  }
871  /* last race isn't followed by a | */
872  if (*cp1 != '\0') {
873  cs_print_string(csocket.fd, "requestinfo class_info %s", cp1);
874  num_classes++;
875  }
876  if (classes) {
878  num_classes=0;
879  used_classes=0;
880  }
881  classes = calloc(num_classes, sizeof(Race_Class_Info));
882  } else if (!strcmp((char*)buf, "race_info")) {
883  get_race_info(cp, len -i -1);
884  } else if (!strcmp((char*)buf, "class_info")) {
885  get_class_info(cp, len -i -1);
886  } else if (!strcmp((char*)buf, "newcharinfo")) {
887  get_new_char_info(cp, len -i -1);
888  } else if (!strcmp((char*)buf, "startingmap")) {
889  get_starting_map_info(cp, len -i -1);
890  }
891 }
892 
901 void SetupCmd(char *buf, int len)
902 {
903  int s;
904  char *cmd, *param;
905 
906  /* Process the setup commands.
907  * Syntax is setup <cmdname1> <parameter> <cmdname2> <parameter> ...
908  *
909  * The server sends the status of the cmd back, or a FALSE if the cmd is
910  * unknown. The client then must sort this out.
911  */
912 
913  LOG(LOG_DEBUG, "common::SetupCmd", "%s", buf);
914  for (s = 0; ; ) {
915  if (s >= len) { /* Ugly, but for secure...*/
916  break;
917  }
918 
919  cmd = &buf[s];
920 
921  /* Find the next space, and put a null there */
922  for (; buf[s] && buf[s] != ' '; s++)
923  ;
924  buf[s++] = 0;
925  while (buf[s] == ' ') {
926  s++;
927  }
928  if (s >= len) {
929  break;
930  }
931 
932  param = &buf[s];
933 
934  for (; buf[s] && buf[s] != ' '; s++)
935  ;
936  buf[s++] = 0;
937  while (s < len && buf[s] == ' ') {
938  s++;
939  }
940 
941  /* What is done with the returned data depends on what the server
942  * returns. In some cases the client may fall back to other methods,
943  * report an error, or try another setup command.
944  */
945  if (!strcmp(cmd, "sound2")) {
946  /* No parsing needed, but we don't want a warning about unknown
947  * setup option below.
948  */
949  } else if (!strcmp(cmd, "sound")) {
950  /* No, this should not be !strcmp()... */
951  } else if (!strcmp(cmd, "mapsize")) {
952  if (!g_ascii_strcasecmp(param, "false")) {
954  "Server only supports standard sized maps (11x11)");
955  /* Do this because we may have been playing on a big server
956  * before */
961  continue;
962  }
963 
964  // Parse map size returned by the server.
965  int x = atoi(param);
966  int y = 0;
967  for (char *cp = param; *cp != 0; cp++) {
968  if (*cp == 'x' || *cp == 'X') {
969  y = atoi(cp+1);
970  break;
971  }
972  }
973 
974  /* A size larger than what the server supports was requested.
975  * Reduce the size to server maximum, and re-send the setup
976  * command. Update our want sizes, and tell the player what is
977  * going on.
978  */
979  char tmpbuf[MAX_BUF];
981  if (want_config[CONFIG_MAPWIDTH] > x) {
983  }
984  if (want_config[CONFIG_MAPHEIGHT] > y) {
986  }
988  snprintf(tmpbuf, sizeof(tmpbuf), "Server supports a max mapsize of %d x %d - requesting a %d x %d mapsize",
991  tmpbuf);
992  } else if (want_config[CONFIG_MAPWIDTH] == x && want_config[CONFIG_MAPHEIGHT] == y) {
997  } else {
998  /* The request was not bigger than what server supports, and
999  * not the same size, so what is the problem? Tell the user
1000  * that something is wrong.
1001  */
1002  snprintf(tmpbuf, sizeof(tmpbuf), "Unable to set mapsize on server - we wanted %d x %d, server returned %d x %d",
1005  }
1006  } else if (!strcmp(cmd, "darkness")) {
1007  /* Older servers might not support this setup command.
1008  */
1009  if (!strcmp(param, "FALSE")) {
1010  LOG(LOG_WARNING, "common::SetupCmd", "Server returned FALSE for setup command %s", cmd);
1011  }
1012  } else if (!strcmp(cmd, "spellmon")) {
1013 
1014  /* Older servers might not support this setup command or all of
1015  * the extensions.
1016  *
1017  * Spellmon 2 was added to the protocol in January 2010 to send an
1018  * additional spell information string with casting requirements
1019  * including required items, if the spell needs arguments passed
1020  * (like text for rune of marking), etc.
1021  *
1022  * To use the new feature, "setup spellmon 1 spellmon 2" is sent,
1023  * and if "spellmon 1 spellmon FALSE" is returned then the server
1024  * doesn't accept 2 - sending spellmon 2 to a server that does not
1025  * support it is not problematic, so the spellmon 1 command will
1026  * still be handled correctly by the server. If the server sends
1027  * "spellmon 1 spellmon 2" then the extended mode is in effect.
1028  *
1029  * It is not particularly important for the player to know what
1030  * level of command is accepted by the server. The extra features
1031  * will simply not be functionally available.
1032  */
1033  if (!strcmp(param, "FALSE")) {
1034  LOG(LOG_INFO, "common::SetupCmd", "Server returned FALSE for a %s setup command", cmd);
1035  } else {
1036  spellmon_level = atoi(param);
1037  }
1038  } else if (!strcmp(cmd, "facecache")) {
1039  use_config[CONFIG_CACHE] = atoi(param);
1040  } else if (!strcmp(cmd, "faceset")) {
1041  if (!strcmp(param, "FALSE")) {
1043  "Server does not support other image sets, will use default");
1044  face_info.faceset = 0;
1045  }
1046  } else if (!strcmp(cmd, "map2cmd")) {
1047  if (!strcmp(param, "FALSE")) {
1049  "Server does not support map2cmd!");
1051  "This server is too old to support this client!");
1053  }
1054  } else if (!strcmp(cmd, "want_pickup")) {
1055  /* Nothing special to do as this is info pushed from server and
1056  * not having it isn't that bad.
1057  */
1058  } else if (!strcmp(cmd, "loginmethod")) {
1059  int method = atoi(param);
1060 
1061  /* If the server supports new login, start the process. Pass what
1062  * version the server supports so client can do appropriate
1063  * work
1064  */
1065  serverloginmethod = method;
1066  if (method) {
1067  start_login(method);
1068  }
1069  } else if (!strcmp(cmd, "newmapcmd")) {
1070  // if server doesn't support newmapcmd, too bad
1071  } else if (!strcmp(cmd, "tick")) {
1072  // Ticks drive the redraw loop via client_tick(), so we really
1073  // do need ticks. To support servers without ticks, add a timer
1074  // callback via g_timeout_add() that calls client_tick().
1075  if (!strcmp(param, "FALSE")) {
1077  "Server does not support tick!");
1079  "This server is too old to support this client!");
1081  }
1082  } else if (!strcmp(cmd, "extendedTextInfos")) {
1083  // Even though this is deprecated, old servers are sitll being
1084  // actively used. Request extended text info (drawextinfo).
1085  if (csocket.cs_version < 1023 && strcmp(param, "FALSE")) { /* server didn't send FALSE*/
1086  /* Server seems to accept extended text infos. Let's tell
1087  * it what extended text info we want
1088  */
1089  for (int i = 1; i < 20; i++) {
1090  char exttext[MAX_BUF];
1091  snprintf(exttext, sizeof(exttext), "toggleextendedtext %d", i);
1092  cs_print_string(csocket.fd, exttext);
1093  }
1094  }
1095  } else {
1096  LOG(LOG_INFO, "common::SetupCmd",
1097  "Got setup for a command we don't understand: %s %s",
1098  cmd, param);
1099  }
1100  }
1101 }
1102 
1111 void AddMeFail(char *data, int len)
1112 {
1113  (void)data; /* __UNUSED__ */
1114  (void)len; /* __UNUSED__ */
1115 
1116  LOG(LOG_INFO, "common::AddMeFail", "addme_failed received.");
1117  return;
1118 }
1119 
1127 void AddMeSuccess(char *data, int len)
1128 {
1129  (void)data; /* __UNUSED__ */
1130  (void)len; /* __UNUSED__ */
1131 
1133  show_main_client();
1134  LOG(LOG_DEBUG, "common::AddMeSuccess", "addme_success received.");
1135  return;
1136 }
1137 
1143 void GoodbyeCmd(char *data, int len)
1144 {
1145  (void)data; /* __UNUSED__ */
1146  (void)len; /* __UNUSED__ */
1147 
1148  /* This could probably be greatly improved - I am not sure if anything
1149  * needs to be saved here, but it should be possible to reconnect to the
1150  * server or a different server without having to rerun the client.
1151  */
1152  LOG(LOG_WARNING, "common::GoodbyeCmd", "Received goodbye command from server - exiting");
1153  exit(0);
1154 }
1155 
1157 
1163 void AnimCmd(unsigned char *data, int len)
1164 {
1165  short anum;
1166  int i, j;
1167 
1168  anum = GetShort_String(data);
1169  if (anum < 0 || anum > MAXANIM) {
1170  LOG(LOG_WARNING, "common::AnimCmd", "animation number invalid: %d", anum);
1171  return;
1172  }
1173 
1174  animations[anum].flags = GetShort_String(data+2);
1175  animations[anum].num_animations = (len-4)/2;
1176  if (animations[anum].num_animations < 1) {
1177  LOG(LOG_WARNING, "common::AnimCmd", "num animations invalid: %d",
1178  animations[anum].num_animations);
1179  return;
1180  }
1181  animations[anum].faces = g_malloc(sizeof(guint16)*animations[anum].num_animations);
1182  for (i = 4, j = 0; i < len; i += 2, j++) {
1183  animations[anum].faces[j] = GetShort_String(data+i);
1184  }
1185 
1186  if (j != animations[anum].num_animations) {
1187  LOG(LOG_WARNING, "common::AnimCmd",
1188  "Calculated animations does not equal stored animations? (%d!=%d)",
1189  j, animations[anum].num_animations);
1190  }
1191 
1192  animations[anum].speed = 0;
1193  animations[anum].speed_left = 0;
1194  animations[anum].phase = 0;
1195 
1196  LOG(LOG_DEBUG, "common::AnimCmd", "Received animation %d, %d faces", anum, animations[anum].num_animations);
1197 }
1198 
1208 void SmoothCmd(unsigned char *data, int len)
1209 {
1210  guint16 faceid;
1211  guint16 smoothing;
1212 
1213  /* len is unused. We should check that we don't have an invalid short
1214  * command. Hence, the compiler warning is valid.
1215  */
1216 
1217  faceid = GetShort_String(data);
1218  smoothing = GetShort_String(data+2);
1219  addsmooth(faceid, smoothing);
1220 }
1221 
1228 void DrawInfoCmd(char *data, int len)
1229 {
1230  int color = atoi(data);
1231  char *buf;
1232 
1233  (void)len; /* __UNUSED__ */
1234 
1235  buf = strchr(data, ' ');
1236  if (!buf) {
1237  LOG(LOG_WARNING, "common::DrawInfoCmd", "got no data");
1238  buf = "";
1239  } else {
1240  buf++;
1241  }
1243 }
1244 
1246 
1252 void setTextManager(int type, ExtTextManager callback)
1253 {
1254  TextManager *current = firstTextManager;
1255 
1256  while (current != NULL) {
1257  if (current->type == type) {
1258  current->callback = callback;
1259  return;
1260  }
1261  current = current->next;
1262  }
1263  current = g_malloc(sizeof(TextManager));
1264  current->type = type;
1265  current->callback = callback;
1266  current->next = firstTextManager;
1267  firstTextManager = current;
1268 }
1269 
1275 {
1276  TextManager *current = firstTextManager;
1277  while (current != NULL) {
1278  if (current->type == type) {
1279  return current->callback;
1280  }
1281  current = current->next;
1282  }
1283  return NULL;
1284 }
1285 
1292 void DrawExtInfoCmd(char *data, int len)
1293 {
1294  int color;
1295  int type, subtype;
1296  char *buf = data;
1297  int wordCount = 3;
1298  ExtTextManager fnct;
1299 
1300  while (wordCount > 0) {
1301  while (buf[0] == ' ') {
1302  buf++;
1303  }
1304  wordCount--;
1305  while (buf[0] != ' ') {
1306  if (buf[0] == '\0') {
1307  LOG(LOG_WARNING,
1308  "common::DrawExtInfoCmd", "Data is missing %d parameters %s",
1309  wordCount,
1310  data);
1311  return;
1312  } else {
1313  buf++;
1314  }
1315  }
1316  if (buf[0] == ' ') {
1317  buf++; /*remove trailing space to send clean data to callback */
1318  }
1319  }
1320  wordCount = sscanf(data, "%d %d %d", &color, &type, &subtype);
1321  if (wordCount != 3) {
1322  LOG(LOG_WARNING,
1323  "common::DrawExtInfoCmd", "Wrong parameters received. Could only parse %d out of 3 int in %s",
1324  wordCount,
1325  data);
1326  return;
1327  }
1328  fnct = getTextManager(type);
1329  if (fnct == NULL) {
1330  LOG(LOG_WARNING,
1331  "common::DrawExtInfoCmd", "Server send us a type %d but i can't find any callback for it",
1332  type);
1333  return;
1334  }
1335  fnct(color, type, subtype, buf);
1336 }
1337 
1344 void use_skill(int skill_id)
1345 {
1346  int i = 0;
1347  int next;
1348  int prev = last_used_skills[0];
1349 
1350  if(last_used_skills[0] == skill_id) {
1351  return;
1352  }
1353 
1354  do {
1355  next = last_used_skills[i+1];
1356  last_used_skills[i+1] = prev;
1357  prev = next;
1358  ++i;
1359  } while(next != skill_id && next >= 0);
1360  last_used_skills[0] = skill_id;
1361 }
1362 
1369 void StatsCmd(unsigned char *data, int len)
1370 {
1371  int i = 0, c, redraw = 0;
1372  gint64 last_exp;
1373 
1374  while (i < len) {
1375  c = data[i++];
1376  if (c >= CS_STAT_RESIST_START && c <= CS_STAT_RESIST_END) {
1378  i += 2;
1379  cpl.stats.resist_change = 1;
1380  } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) {
1381  /* We track to see if the exp has gone from 0 to some total value
1382  * - we do this because the draw logic currently only draws skills
1383  * where the player has exp. We need to communicate to the draw
1384  * function that it should draw all the players skills. Using
1385  * redraw is a little overkill, because a lot of the data may not
1386  * be changing. OTOH, such a transition should only happen
1387  * rarely, not not be a very big deal.
1388  */
1389  cpl.stats.skill_level[c-CS_STAT_SKILLINFO] = data[i++];
1390  last_exp = cpl.stats.skill_exp[c-CS_STAT_SKILLINFO];
1393  if (last_exp == 0 && cpl.stats.skill_exp[c-CS_STAT_SKILLINFO]) {
1394  redraw = 1;
1395  }
1396  i += 8;
1397  } else {
1398  switch (c) {
1399  case CS_STAT_HP:
1400  cpl.stats.hp = GetShort_String(data+i);
1401  i += 2;
1402  break;
1403  case CS_STAT_MAXHP:
1404  cpl.stats.maxhp = GetShort_String(data+i);
1405  i += 2;
1406  break;
1407  case CS_STAT_SP:
1408  cpl.stats.sp = GetShort_String(data+i);
1409  i += 2;
1410  break;
1411  case CS_STAT_MAXSP:
1412  cpl.stats.maxsp = GetShort_String(data+i);
1413  i += 2;
1414  break;
1415  case CS_STAT_GRACE:
1416  cpl.stats.grace = GetShort_String(data+i);
1417  i += 2;
1418  break;
1419  case CS_STAT_MAXGRACE:
1420  cpl.stats.maxgrace = GetShort_String(data+i);
1421  i += 2;
1422  break;
1423  case CS_STAT_STR:
1424  cpl.stats.Str = GetShort_String(data+i);
1425  i += 2;
1426  break;
1427  case CS_STAT_INT:
1428  cpl.stats.Int = GetShort_String(data+i);
1429  i += 2;
1430  break;
1431  case CS_STAT_POW:
1432  cpl.stats.Pow = GetShort_String(data+i);
1433  i += 2;
1434  break;
1435  case CS_STAT_WIS:
1436  cpl.stats.Wis = GetShort_String(data+i);
1437  i += 2;
1438  break;
1439  case CS_STAT_DEX:
1440  cpl.stats.Dex = GetShort_String(data+i);
1441  i += 2;
1442  break;
1443  case CS_STAT_CON:
1444  cpl.stats.Con = GetShort_String(data+i);
1445  i += 2;
1446  break;
1447  case CS_STAT_CHA:
1448  cpl.stats.Cha = GetShort_String(data+i);
1449  i += 2;
1450  break;
1451  case CS_STAT_EXP:
1452  cpl.stats.exp = GetInt_String(data+i);
1453  i += 4;
1454  break;
1455  case CS_STAT_EXP64:
1456  cpl.stats.exp = GetInt64_String(data+i);
1457  i += 8;
1458  break;
1459  case CS_STAT_LEVEL:
1460  cpl.stats.level = GetShort_String(data+i);
1461  i += 2;
1462  break;
1463  case CS_STAT_WC:
1464  cpl.stats.wc = GetShort_String(data+i);
1465  i += 2;
1466  break;
1467  case CS_STAT_AC:
1468  cpl.stats.ac = GetShort_String(data+i);
1469  i += 2;
1470  break;
1471  case CS_STAT_DAM:
1472  cpl.stats.dam = GetShort_String(data+i);
1473  i += 2;
1474  break;
1475  case CS_STAT_ARMOUR:
1476  cpl.stats.resists[0] = GetShort_String(data+i);
1477  i += 2;
1478  break;
1479  case CS_STAT_SPEED:
1480  cpl.stats.speed = GetInt_String(data+i);
1481  i += 4;
1482  break;
1483  case CS_STAT_FOOD:
1484  cpl.stats.food = GetShort_String(data+i);
1485  i += 2;
1486  break;
1487  case CS_STAT_WEAP_SP:
1488  cpl.stats.weapon_sp = GetInt_String(data+i);
1489  i += 4;
1490  break;
1491  case CS_STAT_SPELL_ATTUNE:
1492  cpl.stats.attuned = GetInt_String(data+i);
1493  i += 4;
1494  cpl.spells_updated = 1;
1495  break;
1496  case CS_STAT_SPELL_REPEL:
1497  cpl.stats.repelled = GetInt_String(data+i);
1498  i += 4;
1499  cpl.spells_updated = 1;
1500  break;
1501  case CS_STAT_SPELL_DENY:
1502  cpl.stats.denied = GetInt_String(data+i);
1503  i += 4;
1504  cpl.spells_updated = 1;
1505  break;
1506 
1507  case CS_STAT_FLAGS:
1508  cpl.stats.flags = GetShort_String(data+i);
1509  i += 2;
1510  break;
1511  case CS_STAT_WEIGHT_LIM:
1513  i += 4;
1514  /* Mark weight limit changes to update the client inventory window */
1515  cpl.ob->inv_updated = 1;
1516  break;
1517 
1518  case CS_STAT_RANGE: {
1519  int rlen = data[i++];
1520  strncpy(cpl.range, (const char*)data+i, rlen);
1521  cpl.range[rlen] = '\0';
1522  i += rlen;
1523  break;
1524  }
1525 
1526  case CS_STAT_TITLE: {
1527  int rlen = data[i++];
1528  strncpy(cpl.title, (const char*)data+i, rlen);
1529  cpl.title[rlen] = '\0';
1530  i += rlen;
1531  break;
1532  }
1533 
1534  default:
1535  LOG(LOG_WARNING, "common::StatsCmd", "Unknown stat number %d", c);
1536  break;
1537  }
1538  }
1539  }
1540 
1541  if (i > len) {
1542  LOG(LOG_WARNING, "common::StatsCmd", "got stats overflow, processed %d bytes out of %d", i, len);
1543  }
1544  draw_stats(redraw);
1546 #ifdef HAVE_LUA
1547  script_lua_stats();
1548 #endif
1549 }
1550 
1557 void handle_query(char *data, int len)
1558 {
1559  char *buf, *cp;
1560  guint8 flags = atoi(data);
1561 
1562  (void)len; /* __UNUSED__ */
1563 
1564  if (flags&CS_QUERY_HIDEINPUT) { /* No echo */
1565  cpl.no_echo = 1;
1566  } else {
1567  cpl.no_echo = 0;
1568  }
1569 
1570  /* Let the window system know this may have changed */
1571  x_set_echo();
1572 
1573  /* The actual text is optional */
1574  buf = strchr(data, ' ');
1575  if (buf) {
1576  buf++;
1577  }
1578 
1579  /* If we just get passed an empty string, why draw this? */
1580  if (buf) {
1581  cp = buf;
1582  while ((buf = strchr(buf, '\n')) != NULL) {
1583  *buf++ = '\0';
1584  draw_ext_info(
1586  cp = buf;
1587  }
1588  /* Yes/no - don't do anything with it now */
1589  if (flags&CS_QUERY_YESNO) {
1590  }
1591 
1592  /* One character response expected */
1593  if (flags&CS_QUERY_SINGLECHAR) {
1595  } else {
1597  }
1598 
1599  if (cp) {
1600  draw_prompt(cp);
1601  }
1602  }
1603 
1604  LOG(LOG_DEBUG, "common::handle_query", "Received query. Input state now %d", cpl.input_state);
1605 }
1606 
1613 void send_reply(const char *text)
1614 {
1615  cs_print_string(csocket.fd, "reply %s", text);
1616 
1617  /* Let the window system know that the (possibly hidden) query is over. */
1618  cpl.no_echo = 0;
1619  x_set_echo();
1620 }
1621 
1630 void PlayerCmd(unsigned char *data, int len)
1631 {
1632  char name[MAX_BUF];
1633  int tag, weight, face, i = 0, nlen;
1634 
1636  tag = GetInt_String(data);
1637  i += 4;
1638  weight = GetInt_String(data+i);
1639  i += 4;
1640  face = GetInt_String(data+i);
1641  i += 4;
1642  nlen = data[i++];
1643  memcpy(name, (const char*)data+i, nlen);
1644  name[nlen] = '\0';
1645  i += nlen;
1646 
1647  if (i != len) {
1648  LOG(LOG_WARNING, "common::PlayerCmd", "lengths do not match (%d!=%d)", len, i);
1649  }
1650  new_player(tag, name, weight, face);
1651 }
1652 
1658 {
1659  if (!op) {
1660  return;
1661  }
1662 
1663  if (op->open) {
1664  open_container(op);
1665  cpl.container = op;
1666  } else if (op->was_open) {
1667  close_container(op);
1668  cpl.container = NULL;
1669  }
1670 }
1671 
1678 void Item2Cmd(unsigned char *data, int len) {
1679  int weight, loc, tag, face, flags, pos = 0, nlen, anim, nrof, type;
1680  guint8 animspeed;
1681  char name[MAX_BUF];
1682 
1683  loc = GetInt_String(data);
1684  pos += 4;
1685 
1686  if (pos == len) {
1687  LOG(LOG_WARNING, "common::common_item_command", "Got location with no other data");
1688  return;
1689  } else if (loc < 0) { /* Delete following items */
1690  LOG(LOG_WARNING, "common::common_item_command", "Got location with negative value (%d)", loc);
1691  return;
1692  } else {
1693  while (pos < len) {
1694  tag = GetInt_String(data+pos);
1695  pos += 4;
1696  flags = GetInt_String(data+pos);
1697  pos += 4;
1698  weight = GetInt_String(data+pos);
1699  pos += 4;
1700  face = GetInt_String(data+pos);
1701  pos += 4;
1702  nlen = data[pos++];
1703  memcpy(name, (char*)data+pos, nlen);
1704  pos += nlen;
1705  name[nlen] = '\0';
1706  anim = GetShort_String(data+pos);
1707  pos += 2;
1708  animspeed = data[pos++];
1709  nrof = GetInt_String(data+pos);
1710  pos += 4;
1711  type = GetShort_String(data+pos);
1712  pos += 2;
1713  update_item(tag, loc, name, weight, face, flags, anim, animspeed, nrof, type);
1714  item_actions(locate_item(tag));
1715  }
1716  if (pos > len) {
1717  LOG(LOG_WARNING, "common::common_item_cmd", "Overread buffer: %d > %d", pos, len);
1718  }
1719  }
1720 }
1721 
1728 void UpdateItemCmd(unsigned char *data, int len)
1729 {
1730  int weight, loc, tag, face, sendflags, flags, pos = 0, nlen, anim;
1731  guint32 nrof;
1732  char name[MAX_BUF];
1733  item *ip;
1734  guint8 animspeed;
1735 
1736  sendflags = data[0];
1737  pos += 1;
1738  tag = GetInt_String(data+pos);
1739  pos += 4;
1740  ip = locate_item(tag);
1741  if (!ip) {
1742  /*
1743  fprintf(stderr, "Got update_item command for item we don't have (%d)\n", tag);
1744  */
1745  return;
1746  }
1747 
1748  /* Copy all of these so we can pass the values to update_item and don't
1749  * need to figure out which ones were modified by this function.
1750  */
1751  *name = '\0';
1752  loc = ip->env ? ip->env->tag : 0;
1753  weight = ip->weight*1000;
1754  face = ip->face;
1755  flags = ip->flagsval;
1756  anim = ip->animation_id;
1757  animspeed = ip->anim_speed;
1758  nrof = ip->nrof;
1759 
1760  if (sendflags&UPD_LOCATION) {
1761  loc = GetInt_String(data+pos);
1762  LOG(LOG_WARNING, "common::UpdateItemCmd", "Got tag of unknown object (%d) for new location", loc);
1763  pos += 4;
1764  }
1765  if (sendflags&UPD_FLAGS) {
1766  flags = GetInt_String(data+pos);
1767  pos += 4;
1768  }
1769  if (sendflags&UPD_WEIGHT) {
1770  weight = GetInt_String(data+pos);
1771  pos += 4;
1772  }
1773  if (sendflags&UPD_FACE) {
1774  face = GetInt_String(data+pos);
1775  pos += 4;
1776  }
1777  if (sendflags&UPD_NAME) {
1778  nlen = data[pos++];
1779  memcpy(name, (char*)data+pos, nlen);
1780  pos += nlen;
1781  name[nlen] = '\0';
1782  }
1783  if (pos > len) {
1784  LOG(LOG_WARNING, "common::UpdateItemCmd", "Overread buffer: %d > %d", pos, len);
1785  return; /* We have bad data, probably don't want to store it then */
1786  }
1787  if (sendflags&UPD_ANIM) {
1788  anim = GetShort_String(data+pos);
1789  pos += 2;
1790  }
1791  if (sendflags&UPD_ANIMSPEED) {
1792  animspeed = data[pos++];
1793  }
1794  if (sendflags&UPD_NROF) {
1795  nrof = (guint32)GetInt_String(data+pos);
1796  pos += 4;
1797  }
1798  /* update_item calls set_item_values which will then set the list redraw
1799  * flag, so we don't need to do an explicit redraw here. Actually,
1800  * calling update_item is a little bit of overkill, since we already
1801  * determined some of the values in this function.
1802  */
1803  update_item(tag, loc, name, weight, face, flags, anim, animspeed, nrof, ip->type);
1804  item_actions(locate_item(tag));
1805 }
1806 
1812 void DeleteItem(unsigned char *data, int len)
1813 {
1814  int pos = 0, tag;
1815 
1816  while (pos < len) {
1817  item *op;
1818 
1819  tag = GetInt_String(data+pos);
1820  pos += 4;
1821  op = locate_item(tag);
1822  if (op != NULL) {
1823  remove_item(op);
1824  } else {
1825  LOG(LOG_WARNING, "common::DeleteItem", "Cannot find tag %d", tag);
1826  }
1827  }
1828  if (pos > len) {
1829  LOG(LOG_WARNING, "common::DeleteItem", "Overread buffer: %d > %d", pos, len);
1830  }
1831 }
1832 
1838 void DeleteInventory(unsigned char *data, int len)
1839 {
1840  int tag;
1841  item *op;
1842 
1843  (void)len; /* __UNUSED__ */
1844 
1845  tag = atoi((const char*)data);
1846  op = locate_item(tag);
1847  if (op != NULL) {
1849  } else {
1850  LOG(LOG_WARNING, "common::DeleteInventory", "Invalid tag: %d", tag);
1851  }
1852 }
1853 
1857 static void rstrip(char buf[static 1], size_t len) {
1858  for (size_t i = len - 1; i > 0; i--) {
1859  if (buf[i] == '\n' || buf[i] == ' ') {
1860  buf[i] = '\0';
1861  } else {
1862  return;
1863  }
1864  }
1865 }
1866 
1867 /****************************************************************************/
1868 
1879 void AddspellCmd(unsigned char *data, int len)
1880 {
1881  guint8 nlen;
1882  guint16 mlen, pos = 0;
1883  Spell *newspell, *tmp;
1884 
1885  while (pos < len) {
1886  newspell = calloc(1, sizeof(Spell));
1887 
1888  /* Get standard spell information (spellmon 1)
1889  */
1890  newspell->tag = GetInt_String(data+pos);
1891  pos += 4;
1892  newspell->level = GetShort_String(data+pos);
1893  pos += 2;
1894  newspell->time = GetShort_String(data+pos);
1895  pos += 2;
1896  newspell->sp = GetShort_String(data+pos);
1897  pos += 2;
1898  newspell->grace = GetShort_String(data+pos);
1899  pos += 2;
1900  newspell->dam = GetShort_String(data+pos);
1901  pos += 2;
1902  newspell->skill_number = GetChar_String(data+pos);
1903  pos += 1;
1904  newspell->path = GetInt_String(data+pos);
1905  pos += 4;
1906  newspell->face = GetInt_String(data+pos);
1907  pos += 4;
1908  nlen = GetChar_String(data+pos);
1909  pos += 1;
1910  strncpy(newspell->name, (char*)data+pos, nlen);
1911  pos += nlen;
1912  newspell->name[nlen] = '\0'; /* To ensure we are null terminated */
1913  mlen = GetShort_String(data+pos);
1914  pos += 2;
1915  strncpy(newspell->message, (char*)data+pos, mlen);
1916  pos += mlen;
1917  newspell->message[mlen] = '\0'; /* To ensure we are null terminated */
1918  rstrip(newspell->message, mlen);
1919 
1920  if (spellmon_level < 2) {
1921 
1922  /* The server is not sending spellmon 2 extended information, so
1923  * initialize the spell data fields as unused/empty.
1924  */
1925  newspell->usage = 0;
1926  newspell->requirements[0] = '\0';
1927 
1928  } else if (pos < len) {
1929 
1930  /* The server is sending extended spell information (spellmon 2) so
1931  * process it.
1932  */
1933  newspell->usage = GetChar_String(data+pos);
1934  pos += 1;
1935  nlen = GetChar_String(data+pos);
1936  pos += 1;
1937  strncpy(newspell->requirements, (char*) data+pos, nlen);
1938  pos += nlen;
1939  newspell->requirements[nlen] = '\0'; /* Ensure null-termination */
1940  }
1941 
1942  /* Compute the derived spell information.
1943  */
1944  newspell->skill = skill_names[newspell->skill_number-CS_STAT_SKILLINFO];
1945 
1946  /* Add the spell to the player struct.
1947  */
1948  if (!cpl.spelldata) {
1949  cpl.spelldata = newspell;
1950  } else {
1951  for (tmp = cpl.spelldata; tmp->next; tmp = tmp->next)
1952  ;
1953  tmp->next = newspell;
1954  }
1955  /* Check to see if there are more spells to add.
1956  */
1957  }
1958  if (pos > len) {
1959  LOG(LOG_WARNING, "common::AddspellCmd", "Overread buffer: %d > %d", pos, len);
1960  }
1961  cpl.spells_updated = 1;
1962 }
1963 
1964 void UpdspellCmd(unsigned char *data, int len) {
1965  int flags, pos = 0;
1966  guint32 tag;
1967  Spell *tmp;
1968 
1969  if (!cpl.spelldata) {
1970  LOG(LOG_WARNING, "common::UpdspellCmd", "I know no spells to update");
1971  return;
1972  }
1973 
1974  flags = GetChar_String(data+pos);
1975  pos += 1;
1976  tag = GetInt_String(data+pos);
1977  pos += 4;
1978  for (tmp = cpl.spelldata; tmp && tmp->tag != tag; tmp = tmp->next)
1979  ;
1980  if (!tmp) {
1981  LOG(LOG_WARNING, "common::UpdspellCmd", "Invalid tag: %d", tag);
1982  return;
1983  }
1984  if (flags&UPD_SP_MANA) {
1985  tmp->sp = GetShort_String(data+pos);
1986  pos += 2;
1987  }
1988  if (flags&UPD_SP_GRACE) {
1989  tmp->grace = GetShort_String(data+pos);
1990  pos += 2;
1991  }
1992  if (flags&UPD_SP_DAMAGE) {
1993  tmp->dam = GetShort_String(data+pos);
1994  pos += 2;
1995  }
1996  if (pos > len) {
1997  LOG(LOG_WARNING, "common::UpdspellCmd", "Overread buffer: %d > %d", pos, len);
1998  }
1999  cpl.spells_updated = 1;
2000 }
2001 
2002 void DeleteSpell(unsigned char *data, int len) {
2003  guint32 tag;
2004  Spell *tmp, *target;
2005 
2006  if (!cpl.spelldata) {
2007  LOG(LOG_WARNING, "common::DeleteSpell", "I know no spells to delete");
2008  return;
2009  }
2010 
2011  tag = GetInt_String(data);
2012  /* Special case: the first spell is the one removed */
2013  if (cpl.spelldata->tag == tag) {
2014  target = cpl.spelldata;
2015  if (target->next) {
2016  cpl.spelldata = target->next;
2017  } else {
2018  cpl.spelldata = NULL;
2019  }
2020  free(target);
2021  return;
2022  }
2023 
2024  for (tmp = cpl.spelldata; tmp->next && tmp->next->tag != tag; tmp = tmp->next)
2025  ;
2026  if (!tmp->next) {
2027  LOG(LOG_WARNING, "common::DeleteSpell", "Invalid tag: %d", tag);
2028  return;
2029  }
2030  target = tmp->next;
2031  if (target->next) {
2032  tmp->next = target->next;
2033  } else {
2034  tmp->next = NULL;
2035  }
2036  free(target);
2037  cpl.spells_updated = 1;
2038 }
2039 
2040 /****************************************************************************/
2041  /* EndOf SCSpellCommands
2044  */
2045 
2056 void NewmapCmd(unsigned char *data, int len)
2057 {
2058  (void)data; /* __UNUSED__ */
2059  (void)len; /* __UNUSED__ */
2060 
2061  mapdata_newmap();
2062 }
2063 
2064 /* This is the common processing block for the map1 and map1a protocol
2065  * commands. The map1a mieks minor extensions and are easy to deal with
2066  * inline (in fact, this code doesn't even care what rev is - just certain
2067  * bits will only bet set when using the map1a command. rev is 0 for map1, 1
2068  * for map1a. It conceivable that there could be future revisions.
2069  */
2070 
2071 /* NUM_LAYERS should only be used for the map1{a} which only has a few layers.
2072  * Map2 has 10 layers. However, some of the map1 logic requires this to be
2073  * set right.
2074  */
2075 #define NUM_LAYERS (MAP1_LAYERS-1)
2076 
2082 void Map2Cmd(unsigned char *data, int len)
2083 {
2084  int mask, x, y, pos = 0, space_len, value;
2085  guint8 type;
2086 
2087  /* Not really using map1 protocol, but some draw logic differs from the
2088  * original draw logic, and map2 is closest.
2089  */
2090  while (pos < len) {
2091  mask = GetShort_String(data+pos);
2092  pos += 2;
2093  x = ((mask>>10)&0x3f)-MAP2_COORD_OFFSET;
2094  y = ((mask>>4)&0x3f)-MAP2_COORD_OFFSET;
2095 
2096  /* This is a scroll then. Go back and fetch another coordinate */
2097  if (mask&0x1) {
2098  mapdata_scroll(x, y);
2099  continue;
2100  }
2101 
2102  if (x<0) {
2103  LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative x!");
2104  x = 0;
2105  } else if (x >= MAX_VIEW) {
2106  LOG(LOG_WARNING, "commands.c::Map2Cmd", "got x >= MAX_VIEW!");
2107  x = MAX_VIEW - 1;
2108  }
2109 
2110  if (y<0) {
2111  LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative y!");
2112  y = 0;
2113  } else if (y >= MAX_VIEW) {
2114  LOG(LOG_WARNING, "commands.c::Map2Cmd", "got y >= MAX_VIEW!");
2115  y = MAX_VIEW - 1;
2116  }
2117 
2118  assert(0 <= x && x < MAX_VIEW);
2119  assert(0 <= y && y < MAX_VIEW);
2120  /* Clearing old cell data as needed (was in mapdata_set_face_layer()
2121  * before however that caused darkness to only work if sent after the
2122  * layers).
2123  */
2124  mapdata_clear_old(x, y);
2125 
2126  if (debug_protocol)
2127  LOG(LOG_INFO, "Map2Cmd", "(%d, %d) ", x, y);
2128 
2129  bool first_label = true;
2130 
2131  /* Inner loop is for the data on the space itself */
2132  while (pos < len) {
2133  type = data[pos++];
2134  /* type == 255 means nothing more for this space */
2135  if (type == 255) {
2137  break;
2138  }
2139  space_len = type>>5;
2140  type &= 0x1f;
2141  /* Clear the space */
2142  if (type == MAP2_TYPE_CLEAR) {
2143  mapdata_clear_space(x, y);
2144  if (debug_protocol)
2145  LOG(LOG_INFO, "Map2Cmd", " clear");
2146  continue;
2147  } else if (type == MAP2_TYPE_DARKNESS) {
2148  value = data[pos++];
2149  mapdata_set_darkness(x, y, value);
2150  if (debug_protocol)
2151  LOG(LOG_INFO, "Map2Cmd", " darkness %d", value);
2152  continue;
2153  } else if (type == MAP2_TYPE_LABEL) {
2154  if (space_len != 7) {
2155  // protocol error
2156  abort();
2157  }
2158  unsigned int lenp2 = data[pos++];
2159  unsigned char subtype = data[pos++];
2160  unsigned int len = data[pos++];
2161  char buf[256];
2162  strncpy(buf, (const char *)(data+pos), len);
2163  buf[len] = '\0';
2164  if (debug_protocol)
2165  LOG(LOG_INFO, "Map2Cmd", " label %d %s (%d)", subtype, buf, first_label);
2166  if (first_label) {
2167  int px = pl_pos.x + x;
2168  int py = pl_pos.y + y;
2169  mapdata_clear_label(px, py);
2170  }
2171  mapdata_add_label(x, y, subtype, buf);
2172  first_label = false;
2173  pos += len;
2174  } else if (type >= MAP2_LAYER_START && type < MAP2_LAYER_START+MAXLAYERS) {
2175  int layer, opt;
2176 
2177  /* This is face information for a layer. */
2178  layer = type&0xf;
2179 
2180  if (layer < 0) {
2181  LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative layer!");
2182  layer = 0;
2183  } else if (layer >= MAXLAYERS) {
2184  LOG(LOG_WARNING, "commands.c::Map2Cmd", "got layer >= MAXLAYERS!");
2185  layer = MAXLAYERS - 1;
2186  }
2187  assert(0 <= layer && layer < MAXLAYERS);
2188 
2189  /* This is the face */
2190  value = GetShort_String(data+pos);
2191  pos += 2;
2192  if (!(value&FACE_IS_ANIM)) {
2193  mapdata_set_face_layer(x, y, value, layer);
2194  if (debug_protocol)
2195  LOG(LOG_INFO, "Map2Cmd", " layer %d face %d", layer, value);
2196  }
2197 
2198  if (space_len > 2) {
2199  opt = data[pos++];
2200  if (value&FACE_IS_ANIM) {
2201  /* Animation speed */
2202  mapdata_set_anim_layer(x, y, value, opt, layer);
2203  } else {
2204  /* Smooth info */
2205  mapdata_set_smooth(x, y, opt, layer);
2206  }
2207  }
2208  /* Currently, if 4 bytes, must be a smooth byte */
2209  if (space_len > 3) {
2210  opt = data[pos++];
2211  mapdata_set_smooth(x, y, opt, layer);
2212  }
2213  continue;
2214  } else {
2215  // unhandled type, we're still responsible for checking the length
2216  if (space_len != 7) {
2217  pos += space_len;
2218  } else {
2219  int len = data[pos++];
2220  pos += len;
2221  }
2222  }
2223  } /* while pos<len inner loop for space */
2224  } /* While pos<len outer loop */
2225  mapupdatesent = 0;
2226  display_map_doneupdate(FALSE, FALSE);
2227 }
2228 
2235 void map_scrollCmd(char *data, int len)
2236 {
2237  int dx, dy;
2238  char *buf;
2239 
2240  (void)len; /* __UNUSED__ */
2241 
2242  dx = atoi(data);
2243  buf = strchr(data, ' ');
2244  if (!buf) {
2245  LOG(LOG_WARNING, "common::map_scrollCmd", "Got short packet.");
2246  return;
2247  }
2248  buf++;
2249  dy = atoi(buf);
2250 
2251  mapdata_scroll(dx, dy);
2252  display_map_doneupdate(FALSE, TRUE);
2253 }
2254 
2265 int ExtSmooth(unsigned char *data, int len, int x, int y, int layer)
2266 {
2267  static int dx[8] = { 0, 1, 1, 1, 0, -1, -1, -1, };
2268  static int dy[8] = { -1, -1, 0, 1, 1, 1, 0, -1, };
2269  int i, rx, ry;
2270  int newsm;
2271 
2272  if (len < 1) {
2273  return 0;
2274  }
2275 
2276  x += pl_pos.x;
2277  y += pl_pos.y;
2278  newsm = GetChar_String(data);
2279 
2280  if (mapdata_cell(x, y)->smooth[layer] != newsm) {
2281  for (i = 0; i < 8; i++) {
2282  rx = x+dx[i];
2283  ry = y+dy[i];
2284  if (!mapdata_contains(rx, ry)) {
2285  continue;
2286  }
2287  mapdata_cell(x, y)->need_resmooth = 1;
2288  }
2289  }
2290  mapdata_cell(x, y)->smooth[layer] = newsm;
2291  return 1;/*Cause smooth infos only use 1 byte*/
2292 }
2293 
2303 void MapExtendedCmd(unsigned char *data, int len)
2304 {
2305  int mask, x, y, pos = 0, layer;
2306  int noredraw = 0;
2307  int hassmooth = 0;
2308  int entrysize;
2309  int startpackentry;
2310 
2311  mapupdatesent = 1;
2312  mask = GetChar_String(data+pos);
2313  pos += 1;
2314  if (mask&EMI_NOREDRAW) {
2315  noredraw = 1;
2316  }
2317  if (mask&EMI_SMOOTH) {
2318  hassmooth = 1;
2319  }
2320  while (mask&EMI_HASMOREBITS) {
2321  /*There may be bits we ignore about*/
2322  mask = GetChar_String(data+pos);
2323  pos += 1;
2324  }
2325  entrysize = GetChar_String(data+pos);
2326  pos = pos+1;
2327 
2328  while (pos+entrysize+2 <= len) {
2329  mask = GetShort_String(data+pos);
2330  pos += 2;
2331  x = (mask>>10)&0x3f;
2332  y = (mask>>4)&0x3f;
2333  for (layer = NUM_LAYERS; layer >= 0; layer--) {
2334  if (mask&(1<<layer)) {
2335  /*handle an entry*/
2336  if (pos+entrysize > len) { /*erroneous packet*/
2337  break;
2338  }
2339  startpackentry = pos;
2340  /* If you had extended infos to the server, this is where, in
2341  * the client, you may add your code
2342  */
2343  if (hassmooth) {
2344  pos = pos+ExtSmooth(data+pos, len-pos, x, y, NUM_LAYERS-layer);
2345  }
2346  /* Continue with other if you add new extended infos to server
2347  *
2348  * Now point to the next data
2349  */
2350  pos = startpackentry+entrysize;
2351  }
2352  }
2353  }
2354  if (!noredraw) {
2355  display_map_doneupdate(FALSE, FALSE);
2356  mapupdatesent = 0;
2357  }
2358 }
2359 
2365 void MagicMapCmd(unsigned char *data, int len)
2366 {
2367  unsigned char *cp;
2368  int i;
2369 
2370  /* First, extract the size/position information. */
2371  if (sscanf((const char*)data, "%hd %hd %hd %hd", &cpl.mmapx, &cpl.mmapy, &cpl.pmapx, &cpl.pmapy) != 4) {
2372  LOG(LOG_WARNING, "common::MagicMapCmd", "Was not able to properly extract magic map size, pos");
2373  return;
2374  }
2375 
2376  if (cpl.mmapx == 0 || cpl.mmapy == 0) {
2377  LOG(LOG_WARNING, "common::MagicMapCmd", "empty map");
2378  return;
2379  }
2380 
2381  /* Now we need to find the start of the actual data. There are 4 space
2382  * characters we need to skip over.
2383  */
2384  for (cp = data, i = 0; i < 4 && cp < data+len; cp++) {
2385  if (*cp == ' ') {
2386  i++;
2387  }
2388  }
2389  if (i != 4) {
2390  LOG(LOG_WARNING, "common::MagicMapCmd", "Was unable to find start of magic map data");
2391  return;
2392  }
2393  i = len-(cp-data); /* This should be the number of bytes left */
2394  if (i != cpl.mmapx*cpl.mmapy) {
2395  LOG(LOG_WARNING, "common::MagicMapCmd", "Magic map size mismatch. Have %d bytes, should have %d",
2396  i, cpl.mmapx*cpl.mmapy);
2397  return;
2398  }
2399  free(cpl.magicmap);
2400  cpl.magicmap = g_malloc(cpl.mmapx*cpl.mmapy);
2401  /* Order the server puts it in should be just fine. Note that the only
2402  * requirement that this works is that magicmap by 8 bits, being that is
2403  * the size specified in the protocol and what the server sends us.
2404  */
2405  memcpy(cpl.magicmap, cp, cpl.mmapx*cpl.mmapy);
2406  draw_magic_map();
2407 }
2408  /* EndOf SCMapCommands
2411  */
2412 
2418 void SinkCmd(unsigned char *data, int len)
2419 {
2420 }
2421 
2429 void TickCmd(guint8 *data, int len)
2430 {
2431  /* Up to the specific client to decide what to do */
2432  client_tick(GetInt_String(data));
2433 }
2434 
2443 void PickupCmd(guint8 *data, int len)
2444 {
2445  guint32 pickup = GetInt_String(data);
2446  client_pickup(pickup);
2447 }
2448 
2457 void FailureCmd(char *buf, int len)
2458 {
2459  char *cp;
2460 
2461  /* The format of the buffer is 'command error message'. We need to
2462  * extract the failed command, and then pass in the error message to the
2463  * appropriate handler. So find the space, set it to null. in that way,
2464  * buf is now just the failure command, and cp is the message.
2465  */
2466  cp = strchr(buf,' ');
2467  if (!cp) {
2468  return;
2469  }
2470 
2471  *cp = 0;
2472  cp++;
2473 
2474  if (!strcmp(buf,"accountlogin")) {
2476  } else if (!strcmp(buf,"accountnew")) {
2478  } else if (!strcmp(buf,"accountaddplayer")) {
2480  } else if (!strcmp(buf,"createplayer")) {
2482  } else if (!strcmp(buf, "accountpw")) {
2484  } else if (!strcmp(buf, "accountplay")) {
2485  // This creates a dialog that says the failure message.
2486  // It should suffice for what we want here anyway.
2488  } else
2489  /* This really is an error - if this happens it menas the server
2490  * failed to process a request that the client made - the client
2491  * should be able to handle failures for all request types it makes.
2492  * But this is also a problem in that it means that the server is
2493  * waiting for a correct response, and if we do not display anything,
2494  * the player is unlikely to know this.
2495  */
2496  LOG(LOG_ERROR, "common::FailureCmd", "Got a failure response we can not handle: %s:%s",
2497  buf, cp);
2498 }
2499 
2503 void AccountPlayersCmd(char *buf, int len) {
2504  int level, pos, faceno;
2505  guint8 flen;
2506  char name[MAX_BUF], class[MAX_BUF], race[MAX_BUF],
2507  face[MAX_BUF], party[MAX_BUF], map[MAX_BUF];
2508 
2509  /* This is called first so it can clear out the existing data store.
2510  */
2512 
2513  level=0;
2514  name[0]=0;
2515  class[0]=0;
2516  race[0]=0;
2517  face[0]=0;
2518  party[0]=0;
2519  map[0]=0;
2520  faceno=0;
2521 
2522  pos=1;
2523  while (pos < len) {
2524  flen = buf[pos];
2525  /* flen == 0 is to note that we got end of character data */
2526  if (flen == 0) {
2527  update_character_choose(name, class, race, face, party, map, level, faceno);
2528  /* Blank all the values - it is no sure thing that the next
2529  * character will fill all these in.
2530  */
2531  level=0;
2532  name[0]=0;
2533  class[0]=0;
2534  race[0]=0;
2535  face[0]=0;
2536  party[0]=0;
2537  map[0]=0;
2538  faceno=0;
2539  pos++;
2540  continue;
2541  }
2542  pos++;
2543  if ((pos +flen) > len) { // flen always < MAX_BUF by virtue of being 8 bits
2544  LOG(LOG_ERROR,"commands.c:AccountPlayerCmd", "data overran buffer");
2545  return;
2546  }
2547  switch (buf[pos]) {
2548  case ACL_NAME:
2549  strncpy(name, buf + pos +1, flen-1);
2550  name[flen-1] = 0;
2551  break;
2552 
2553  case ACL_CLASS:
2554  strncpy(class, buf + pos +1, flen-1);
2555  class[flen-1] = 0;
2556  break;
2557 
2558  case ACL_RACE:
2559  strncpy(race, buf + pos +1, flen-1);
2560  race[flen-1] = 0;
2561  break;
2562 
2563  case ACL_FACE:
2564  strncpy(face, buf + pos +1, flen-1);
2565  face[flen-1] = 0;
2566  break;
2567 
2568  case ACL_PARTY:
2569  strncpy(party, buf + pos +1, flen-1);
2570  party[flen-1] = 0;
2571  break;
2572 
2573  case ACL_MAP:
2574  strncpy(map, buf + pos +1, flen-1);
2575  map[flen-1] = 0;
2576  break;
2577 
2578  case ACL_LEVEL:
2579  level = GetShort_String((unsigned char *)buf + pos + 1);
2580  break;
2581  case ACL_FACE_NUM:
2582  faceno = GetShort_String((unsigned char *)buf + pos + 1);
2583  break;
2584  }
2585  pos += flen;
2586  }
2587 }
2588 
DeleteSpell
void DeleteSpell(unsigned char *data, int len)
Definition: commands.c:2002
Player_Struct::stats
Stats stats
Player stats.
Definition: client.h:350
Stat_struct::skill_level
gint16 skill_level[MAX_SKILL]
Level of known skills.
Definition: client.h:290
mapdata_cell
struct MapCell * mapdata_cell(const int x, const int y)
Get the stored map cell at the given map coordinate.
Definition: mapdata.c:139
CS_NUM_SKILLS
#define CS_NUM_SKILLS
CS_NUM_SKILLS does not match how many skills there really are - instead, it is used as a range of val...
Definition: newclient.h:178
LOG_INFO
@ LOG_INFO
Minor, non-harmful issues.
Definition: client.h:437
redraw
static gboolean redraw(gpointer data)
Redraw the map.
Definition: main.c:137
UPD_FACE
#define UPD_FACE
Definition: newclient.h:290
Race_Class_Info::public_name
char * public_name
Definition: client.h:598
TextManager::type
int type
Definition: client.h:48
MSG_TYPE_CLIENT
#define MSG_TYPE_CLIENT
Client originated Messages.
Definition: newclient.h:390
Stat_struct::Str
gint8 Str
Strength.
Definition: client.h:253
CS_STAT_GRACE
#define CS_STAT_GRACE
Definition: newclient.h:112
Spell_struct::message
char message[10000]
This is huge, the packets can't be much bigger than this anyway.
Definition: client.h:298
item_actions
void item_actions(item *op)
Definition: commands.c:1657
TickCmd
void TickCmd(guint8 *data, int len)
Got a tick from the server.
Definition: commands.c:2429
mapdata_clear_space
void mapdata_clear_space(int x, int y)
Definition: mapdata.c:674
ACL_FACE_NUM
#define ACL_FACE_NUM
Definition: newclient.h:206
GetShort_String
short GetShort_String(const unsigned char *data)
Definition: newsocket.c:180
LOG_WARNING
@ LOG_WARNING
Warning that something might not work.
Definition: client.h:438
item_struct::animation_id
guint16 animation_id
Definition: item.h:63
client_tick
void client_tick(guint32 tick)
Called whenever the server sends a tick command.
Definition: main.c:194
ExtTextManager
void(* ExtTextManager)(int flag, int type, int subtype, char *message)
Definition: client.h:45
races
Race_Class_Info * races
Definition: commands.c:98
Stat_Mapping
The usage of the stat_mapping is to simplify the code and make it easier to expand.
Definition: client.h:571
Spell_struct::tag
guint32 tag
Unique ID number for a spell so updspell etc can operate on it.
Definition: client.h:300
Stat_struct::skill_exp
gint64 skill_exp[MAX_SKILL]
Experience points for skills.
Definition: client.h:291
CS_QUERY_YESNO
#define CS_QUERY_YESNO
Yes/no question.
Definition: newclient.h:69
Player_Struct::title
char title[MAX_BUF]
Title of character.
Definition: client.h:352
MAP2_COORD_OFFSET
#define MAP2_COORD_OFFSET
How much the x,y coordinates in the map2 are off from actual upper left corner.
Definition: newclient.h:32
CS_STAT_DAM
#define CS_STAT_DAM
Definition: newclient.h:104
rules
char * rules
Definition: commands.c:82
RC_Choice
For classes & races, the server can present some number of choices, eg, the character gets to choose ...
Definition: client.h:588
setTextManager
void setTextManager(int type, ExtTextManager callback)
Definition: commands.c:1252
ACL_RACE
#define ACL_RACE
Definition: newclient.h:201
PlayerPosition::x
int x
Definition: client.h:528
EMI_NOREDRAW
#define EMI_NOREDRAW
< Take extended information into account but do not redraw.
Definition: newclient.h:346
Player_Struct::input_state
Input_State input_state
What the input state is.
Definition: client.h:342
client_disconnect
void client_disconnect()
Closes the connection to the server.
Definition: client.c:175
ClientSocket::fd
GSocketConnection * fd
Definition: client.h:124
GoodbyeCmd
void GoodbyeCmd(char *data, int len)
Definition: commands.c:1143
CS_STAT_HP
#define CS_STAT_HP
Definition: newclient.h:90
get_race_info
static void get_race_info(unsigned char *data, int len)
This is a little wrapper function that does some bounds checking and then calls process_race_info() t...
Definition: commands.c:648
item_struct::flagsval
guint32 flagsval
Definition: item.h:81
free_all_race_class_info
void free_all_race_class_info(Race_Class_Info *data, int num_entries)
This function clears the data from the Race_Class_Info array.
Definition: commands.c:420
DeleteItem
void DeleteItem(unsigned char *data, int len)
Definition: commands.c:1812
Stat_struct::Con
gint8 Con
Constitution.
Definition: client.h:255
item_struct::env
struct item_struct * env
Definition: item.h:53
Starting_Map_Info::public_name
char * public_name
Definition: client.h:607
external.h
handle_query
void handle_query(char *data, int len)
Prompts the user for input.
Definition: commands.c:1557
draw_stats
void draw_stats(int redraw)
Draws the stats window.
Definition: stats.c:554
RC_Choice::value_desc
char ** value_desc
Definition: client.h:593
mapdata_set_face_layer
void mapdata_set_face_layer(int x, int y, gint16 face, int layer)
Definition: mapdata.c:875
UPD_WEIGHT
#define UPD_WEIGHT
Definition: newclient.h:289
Animations::phase
guint8 phase
Definition: client.h:107
Stat_struct::food
gint16 food
Quantity food in stomach.
Definition: client.h:270
CS_STAT_SPELL_DENY
#define CS_STAT_SPELL_DENY
Definition: newclient.h:119
mapdata_clear_label
void mapdata_clear_label(int px, int py)
Definition: mapdata.c:830
RC_Choice::value_arch
char ** value_arch
Definition: client.h:592
RC_Choice::choice_desc
char * choice_desc
Definition: client.h:590
starting_map_number
int starting_map_number
Definition: commands.c:96
CS_STAT_WIS
#define CS_STAT_WIS
Definition: newclient.h:96
remove_item_inventory
void remove_item_inventory(item *op)
Definition: item.c:344
Stat_struct::ac
gint8 ac
Armour Class.
Definition: client.h:261
CS_STAT_SPEED
#define CS_STAT_SPEED
Definition: newclient.h:106
UPD_LOCATION
#define UPD_LOCATION
Definition: newclient.h:287
mapdata_newmap
void mapdata_newmap(void)
Clears the map view.
Definition: mapdata.c:1028
CS_STAT_INT
#define CS_STAT_INT
Definition: newclient.h:95
EMI_SMOOTH
#define EMI_SMOOTH
Data about smoothing.
Definition: newclient.h:350
Reply_One
@ Reply_One
Definition: client.h:146
CS_STAT_MAXSP
#define CS_STAT_MAXSP
Definition: newclient.h:93
starting_map_update_info
void starting_map_update_info()
We have gotten starting map information from the server - now update the combo boxes.
Definition: create_char.c:753
hide_all_login_windows
void hide_all_login_windows(void)
Hides all the login related windows.
Definition: account.c:96
Stat_Mapping::rc_offset
guint8 rc_offset
Definition: client.h:574
Stat_struct::weapon_sp
gint32 weapon_sp
Weapon speed (displayed in client as a float)
Definition: client.h:277
Animations::num_animations
guint8 num_animations
Number of animations.
Definition: client.h:101
mapupdatesent
int mapupdatesent
Definition: commands.c:55
motd
char * motd
Definition: commands.c:82
CS_QUERY_SINGLECHAR
#define CS_QUERY_SINGLECHAR
Single character response expected.
Definition: newclient.h:70
Stat_struct::hp
gint16 hp
Hit Points.
Definition: client.h:263
face_info
Face_Information face_info
Definition: image.c:169
get_exp_info
static void get_exp_info(const unsigned char *data, int len)
Definition: commands.c:705
Stat_struct::maxgrace
gint16 maxgrace
Maximum spell points.
Definition: client.h:268
CS_STAT_SP
#define CS_STAT_SP
Definition: newclient.h:92
RC_Choice::num_values
int num_values
Definition: client.h:591
new_player
void new_player(long tag, char *name, long weight, long face)
Initialize player object using information from the server.
Definition: player.c:54
ACL_NAME
#define ACL_NAME
Definition: newclient.h:199
GetInt_String
int GetInt_String(const unsigned char *data)
The reverse of SockList_AddInt, but on strings instead.
Definition: newsocket.c:150
rc_compar
static int rc_compar(const Race_Class_Info *a, const Race_Class_Info *b)
Used for bsearch searching.
Definition: commands.c:405
mapdata_set_smooth
void mapdata_set_smooth(int x, int y, guint8 smooth, int layer)
Definition: mapdata.c:778
Stat_struct::exp
gint64 exp
Experience.
Definition: client.h:269
NDI_RED
#define NDI_RED
Definition: newclient.h:227
script_lua_stats
void script_lua_stats(void)
update_item
void update_item(int tag, int loc, char *name, int weight, int face, int flags, int anim, int animspeed, guint32 nrof, int type)
Definition: item.c:579
mapdata_set_check_space
void mapdata_set_check_space(int x, int y)
Definition: mapdata.c:707
display_map_doneupdate
void display_map_doneupdate(int redraw, int notice)
This is called after the map has been all digested.
Definition: map.c:671
ACL_CLASS
#define ACL_CLASS
Definition: newclient.h:200
UPD_NROF
#define UPD_NROF
Definition: newclient.h:294
client_pickup
void client_pickup(guint32 pickup)
We get pickup information from server, update our status.
Definition: pickup.c:630
DrawExtInfoCmd
void DrawExtInfoCmd(char *data, int len)
We must extract color, type, subtype and dispatch to callback.
Definition: commands.c:1292
MSG_TYPE_CLIENT_COMMAND
#define MSG_TYPE_CLIENT_COMMAND
DrawInfoCmd()
Definition: newclient.h:635
new_char_window_update_info
void new_char_window_update_info()
We have gotten some new information from the server, so we need to update the information - race/clas...
Definition: create_char.c:633
Spell_struct::skill_number
guint8 skill_number
The index in the skill arrays, plus CS_STAT_SKILLINFO.
Definition: client.h:310
start_login
void start_login(int method)
Starts the login process.
Definition: account.c:1263
ReplyInfoCmd
void ReplyInfoCmd(unsigned char *buf, int len)
Handles the response from a 'requestinfo' command.
Definition: commands.c:774
CS_STAT_EXP64
#define CS_STAT_EXP64
Definition: newclient.h:116
item_struct::nrof
guint32 nrof
Definition: item.h:60
Spell_struct::requirements
char requirements[256]
Spellmon 2 data.
Definition: client.h:328
AddMeFail
void AddMeFail(char *data, int len)
Handles when the server says we can't be added.
Definition: commands.c:1111
UPD_SP_MANA
#define UPD_SP_MANA
updspell command flag value.
Definition: newclient.h:297
PlayerCmd
void PlayerCmd(unsigned char *data, int len)
Gets the player information.
Definition: commands.c:1630
Animations::speed
guint8 speed
Definition: client.h:105
Stat_Mapping::cs_value
guint8 cs_value
Definition: client.h:573
num_classes
int num_classes
Definition: commands.c:90
Stat_struct::resists
gint16 resists[30]
Resistant values.
Definition: client.h:288
TextManager::next
struct TextManager * next
Definition: client.h:50
get_starting_map_info
static void get_starting_map_info(unsigned char *data, int len)
This processes the replyinfo starting_map_info.
Definition: commands.c:172
used_classes
int used_classes
Definition: commands.c:91
CS_STAT_RESIST_START
#define CS_STAT_RESIST_START
Start of resistances (inclusive)
Definition: newclient.h:144
SmoothCmd
void SmoothCmd(unsigned char *data, int len)
Receives the smooth mapping from the server.
Definition: commands.c:1208
ExtSmooth
int ExtSmooth(unsigned char *data, int len, int x, int y, int layer)
Extract smoothing infos from an extendedmapinfo packet part data is located at the beginning of the s...
Definition: commands.c:2265
exp_table_max
guint16 exp_table_max
Definition: client.c:64
Stat_struct::flags
guint16 flags
Contains fire on/run on flags.
Definition: client.h:287
mapdata_clear_old
void mapdata_clear_old(int x, int y)
Prepare a map cell, which may contain old fog of war data, for new visible map data.
Definition: mapdata.c:844
TextManager::callback
ExtTextManager callback
Definition: client.h:49
ACL_MAP
#define ACL_MAP
Definition: newclient.h:205
PlayerPosition::y
int y
Definition: client.h:529
MapExtendedCmd
void MapExtendedCmd(unsigned char *data, int len)
Handle MapExtended command Warning! if you add commands to extended, take care that the 'layer' argum...
Definition: commands.c:2303
Player_Struct::mmapx
guint16 mmapx
Definition: client.h:361
SinkCmd
void SinkCmd(unsigned char *data, int len)
Definition: commands.c:2418
account_change_password_failure
void account_change_password_failure(char *message)
Definition: account.c:1123
Reply_Many
@ Reply_Many
Definition: client.h:146
UPD_SP_DAMAGE
#define UPD_SP_DAMAGE
updspell command flag value.
Definition: newclient.h:299
Stat_struct::grace
gint16 grace
Spell points for using prayers.
Definition: client.h:267
TextManager
Definition: client.h:47
NewmapCmd
void NewmapCmd(unsigned char *data, int len)
Definition: commands.c:2056
open_container
void open_container(item *op)
Definition: inventory.c:643
Stat_struct::resist_change
guint32 resist_change
Resistant value change flag.
Definition: client.h:289
Spell_struct
Definition: client.h:295
CS_STAT_ARMOUR
#define CS_STAT_ARMOUR
Definition: newclient.h:105
Starting_Map_Info::arch_name
char * arch_name
Definition: client.h:606
Stat_struct::Int
gint8 Int
Intelligence.
Definition: client.h:258
exp_table
guint64 * exp_table
Definition: client.c:65
Spell_struct::next
struct Spell_struct * next
Definition: client.h:296
draw_magic_map
void draw_magic_map(void)
Definition: magicmap.c:24
CS_STAT_SPELL_REPEL
#define CS_STAT_SPELL_REPEL
Definition: newclient.h:118
mapdata.h
close_container
void close_container(item *op)
Open and close_container are now no-ops - since these are now drawn inline as treestores,...
Definition: inventory.c:636
last_used_skills
int last_used_skills[MAX_SKILL+1]
maps position to skill id with trailing zero as stop mark.
Definition: client.c:55
NUM_NEW_CHAR_STATS
#define NUM_NEW_CHAR_STATS
Definition: client.h:563
get_image_info
void get_image_info(guint8 *data, int len)
Takes the data from a replyinfo image_info and breaks it down.
Definition: image.c:706
CS_STAT_LEVEL
#define CS_STAT_LEVEL
Definition: newclient.h:101
FailureCmd
void FailureCmd(char *buf, int len)
Handles a failure return from the server.
Definition: commands.c:2457
Stat_struct::sp
gint16 sp
Spell points for casting spells.
Definition: client.h:265
Spell_struct::face
gint32 face
A face ID that may be used to show a graphic representation of the spell.
Definition: client.h:320
CS_STAT_CON
#define CS_STAT_CON
Definition: newclient.h:98
CONFIG_CACHE
#define CONFIG_CACHE
Definition: client.h:187
ACL_PARTY
#define ACL_PARTY
Definition: newclient.h:204
Player_Struct::container
item * container
open container
Definition: client.h:340
CS_STAT_CHA
#define CS_STAT_CHA
Definition: newclient.h:99
Stat_struct::speed
gint32 speed
Speed (is displayed as a float)
Definition: client.h:276
MAX_BUF
#define MAX_BUF
Definition: client.h:40
Stat_struct::level
gint8 level
Experience level.
Definition: client.h:262
Stat_struct::maxhp
gint16 maxhp
Maximum hit points.
Definition: client.h:264
Race_Class_Info::rc_choice
struct RC_Choice * rc_choice
Definition: client.h:602
free_all_starting_map_info
void free_all_starting_map_info()
This function clears the data from the Race_Class_Info array.
Definition: commands.c:134
MSG_TYPE_CLIENT_SERVER
#define MSG_TYPE_CLIENT_SERVER
Server configuration issues.
Definition: newclient.h:634
Spell_struct::name
char name[256]
One length byte plus data
Definition: client.h:297
debug_protocol
bool debug_protocol
Definition: main.c:65
item_struct::open
guint16 open
Definition: item.h:74
Race_Class_Info::description
char * description
Definition: client.h:599
item_struct::face
gint16 face
Definition: item.h:62
MAP2_LAYER_START
#define MAP2_LAYER_START
Definition: newclient.h:66
Player_Struct::ob
item * ob
Player object.
Definition: client.h:337
Map2Cmd
void Map2Cmd(unsigned char *data, int len)
Definition: commands.c:2082
Player_Struct::pmapx
guint16 pmapx
Definition: client.h:362
num_races
int num_races
Definition: commands.c:87
account_login_failure
void account_login_failure(char *message)
Handles a failure from the server - pretty basic - just throw up the message and let the user try aga...
Definition: account.c:868
MAP2_TYPE_LABEL
#define MAP2_TYPE_LABEL
Definition: newclient.h:44
news
char * news
Definition: commands.c:82
CS_STAT_AC
#define CS_STAT_AC
Definition: newclient.h:103
Player_Struct::range
char range[MAX_BUF]
Range attack chosen.
Definition: client.h:353
PickupCmd
void PickupCmd(guint8 *data, int len)
Server gives us current player's pickup.
Definition: commands.c:2443
Stat_struct::maxsp
gint16 maxsp
Maximum spell points.
Definition: client.h:266
get_image_sums
void get_image_sums(char *data, int len)
This gets a block of checksums from the server.
Definition: image.c:802
CS_STAT_WEAP_SP
#define CS_STAT_WEAP_SP
Definition: newclient.h:108
Spell_struct::skill
char * skill
Pointer to the skill name, derived from the skill number.
Definition: client.h:314
mapdata_set_anim_layer
void mapdata_set_anim_layer(int x, int y, guint16 anim, guint8 anim_speed, int layer)
Definition: mapdata.c:904
UPD_FLAGS
#define UPD_FLAGS
Definition: newclient.h:288
ASSERT_LEN
#define ASSERT_LEN(function, curpos, buflen)
Definition: commands.c:76
stat_maximum
int stat_maximum
Definition: commands.c:95
CS_STAT_SKILLINFO
#define CS_STAT_SKILLINFO
CS_STAT_SKILLINFO is used as the starting index point.
Definition: newclient.h:171
MAX_SKILL
#define MAX_SKILL
How many skill types server supports/client will get sent to it.
Definition: client.h:84
map_scrollCmd
void map_scrollCmd(char *data, int len)
Scrolls the map on the client by some amount.
Definition: commands.c:2235
MapCell::smooth
guint8 smooth[MAXLAYERS]
Definition: mapdata.h:67
LOG
void LOG(LogLevel level, const char *origin, const char *format,...)
Log messages of a certain importance to stderr.
Definition: misc.c:111
firstTextManager
TextManager * firstTextManager
Definition: commands.c:1245
MAP2_TYPE_DARKNESS
#define MAP2_TYPE_DARKNESS
Definition: newclient.h:43
cs_print_string
int cs_print_string(GSocketConnection *fd, const char *str,...)
Send a printf-formatted packet to the socket.
Definition: newsocket.c:251
send_reply
void send_reply(const char *text)
Sends a reply to the server.
Definition: commands.c:1613
create_new_character_failure
void create_new_character_failure(char *message)
Pop up a dialog window with the error from the server.
Definition: account.c:142
addsmooth
void addsmooth(guint16 face, guint16 smooth_face)
Referenced from common/commands.c.
Definition: image.c:251
DrawInfoCmd
void DrawInfoCmd(char *data, int len)
Draws a string in the info window.
Definition: commands.c:1228
Race_Class_Info::num_rc_choice
int num_rc_choice
Definition: client.h:601
INFO_MAP_ARCH_NAME
#define INFO_MAP_ARCH_NAME
Definitions for the requestion/replyinfo map data.
Definition: newclient.h:647
starting_map_info
Starting_Map_Info * starting_map_info
Definition: commands.c:99
want_config
gint16 want_config[CONFIG_NUMS]
Definition: init.c:44
draw_ext_info
void draw_ext_info(int orig_color, int type, int subtype, const char *message)
A message processor that accepts messages along with meta information color and type.
Definition: info.c:915
csocket
ClientSocket csocket
Definition: client.c:70
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:224
Race_Class_Info::stat_adj
gint8 stat_adj[NUM_NEW_CHAR_STATS]
Definition: client.h:600
CS_STAT_FOOD
#define CS_STAT_FOOD
Definition: newclient.h:107
update_character_choose
void update_character_choose(const char *name, const char *class, const char *race, const char *face, const char *party, const char *map, int level, int faceno)
This gets data and adds it to the list store.
Definition: account.c:547
RC_Choice::choice_name
char * choice_name
Definition: client.h:589
Race_Class_Info
Definition: client.h:596
NUM_LAYERS
#define NUM_LAYERS
Definition: commands.c:2075
Player_Struct::magicmap
guint8 * magicmap
Magic map data.
Definition: client.h:364
CS_STAT_WEIGHT_LIM
#define CS_STAT_WEIGHT_LIM
Definition: newclient.h:115
get_skill_info
static void get_skill_info(char *data, int len)
Definition: commands.c:732
StatsCmd
void StatsCmd(unsigned char *data, int len)
Updates the local copy of the stats and displays it.
Definition: commands.c:1369
Player_Struct::no_echo
guint32 no_echo
If TRUE, don't echo keystrokes.
Definition: client.h:359
CS_STAT_WC
#define CS_STAT_WC
Definition: newclient.h:102
Stat_struct::wc
gint8 wc
Weapon Class.
Definition: client.h:260
UpdspellCmd
void UpdspellCmd(unsigned char *data, int len)
Definition: commands.c:1964
Spell_struct::path
guint32 path
The bitmask of paths this spell belongs to.
Definition: client.h:316
account_add_character_failure
void account_add_character_failure(char *message)
Handles a failure from the server - pretty basic - just throw up the message and let the user try aga...
Definition: account.c:282
CS_STAT_MAXGRACE
#define CS_STAT_MAXGRACE
Definition: newclient.h:113
MAP2_TYPE_CLEAR
#define MAP2_TYPE_CLEAR
Definition: newclient.h:42
UPD_ANIMSPEED
#define UPD_ANIMSPEED
Definition: newclient.h:293
stat_mapping
struct Stat_Mapping stat_mapping[NUM_NEW_CHAR_STATS]
Definition: commands.c:118
Animations::speed_left
guint8 speed_left
Definition: client.h:106
CS_STAT_STR
#define CS_STAT_STR
Definition: newclient.h:94
Stat_struct::dam
gint16 dam
How much damage this object does for each hit.
Definition: client.h:273
item_struct::anim_speed
guint8 anim_speed
Definition: item.h:64
item_struct::was_open
guint16 was_open
Definition: item.h:75
draw_prompt
void draw_prompt(const char *str)
Draws a prompt.
Definition: keys.c:1719
CS_STAT_SPELL_ATTUNE
#define CS_STAT_SPELL_ATTUNE
Definition: newclient.h:117
Spell_struct::usage
guint8 usage
Spellmon 2 data.
Definition: client.h:323
choose_character_init
void choose_character_init(void)
Called when we get the accountplayers command from the server (indirectly via AccountPlayersCmd).
Definition: account.c:421
item_struct
Definition: item.h:50
x_set_echo
void x_set_echo(void)
Definition: keys.c:1710
CS_STAT_EXP
#define CS_STAT_EXP
No longer used.
Definition: newclient.h:100
mapdata_contains
bool mapdata_contains(int x, int y)
Determine whether the map data contains the given cell.
Definition: mapdata.c:146
item_struct::type
guint16 type
Definition: item.h:82
MapCell::need_resmooth
guint8 need_resmooth
Definition: mapdata.h:70
animations
Animations animations[MAXANIM]
Definition: commands.c:1156
map
static item * map
Definition: item.c:27
item_struct::tag
gint32 tag
Definition: item.h:59
Player_Struct::pmapy
guint16 pmapy
Where the player is on the magic map.
Definition: client.h:362
resize_map_window
void resize_map_window(int x, int y)
Resize_map_window is a NOOP for the time being - not sure if it will in fact need to do something,...
Definition: map.c:511
remove_item
void remove_item(item *op)
Definition: item.c:309
LOG_ERROR
@ LOG_ERROR
Warning that something definitely didn't work.
Definition: client.h:439
Animations::faces
guint16 * faces
Definition: client.h:108
process_race_class_info
static void process_race_class_info(unsigned char *data, int len, Race_Class_Info *rci)
This extracts the data from a replyinfo race_info/class_info request.
Definition: commands.c:469
Animations
Definition: client.h:99
locate_item
item * locate_item(gint32 tag)
Definition: item.c:278
get_class_info
static void get_class_info(unsigned char *data, int len)
This is a little wrapper function that does some bounds checking and then calls process_race_info() t...
Definition: commands.c:679
CONFIG_MAPWIDTH
#define CONFIG_MAPWIDTH
Definition: client.h:201
cpl
Client_Player cpl
Player object.
Definition: client.c:69
stat_min
int stat_min
Definition: commands.c:94
mapdata_scroll
void mapdata_scroll(int dx, int dy)
Scrolls the map view.
Definition: mapdata.c:955
Stat_struct::Pow
gint8 Pow
Power.
Definition: client.h:259
NUM_STATS
#define NUM_STATS
Definition: commands.c:105
Item2Cmd
void Item2Cmd(unsigned char *data, int len)
Parses the data sent to us from the server.
Definition: commands.c:1678
INFO_MOTD
#define INFO_MOTD
Definition: client.h:628
ClientSocket::cs_version
int cs_version
Definition: client.h:125
AddMeSuccess
void AddMeSuccess(char *data, int len)
This is really a throwaway command - there really isn't any reason to send addme_success commands.
Definition: commands.c:1127
MagicMapCmd
void MagicMapCmd(unsigned char *data, int len)
Definition: commands.c:2365
reset_player_data
void reset_player_data()
Reset player experience data.
Definition: init.c:234
Starting_Map_Info::description
char * description
Definition: client.h:608
Animations::flags
guint16 flags
Definition: client.h:100
Spell_struct::sp
guint16 sp
Mana per cast; may be zero.
Definition: client.h:304
Stat_struct::repelled
guint32 repelled
Spell paths to which the player is repelled.
Definition: client.h:283
AddspellCmd
void AddspellCmd(unsigned char *data, int len)
Definition: commands.c:1879
mapdata_set_size
void mapdata_set_size(int viewx, int viewy)
Initializes the module.
Definition: mapdata.c:654
mapdata_add_label
void mapdata_add_label(int x, int y, int subtype, const char *label)
Definition: mapdata.c:806
account_creation_failure
void account_creation_failure(char *message)
Handles a failure from the server - pretty basic - just throw up the message and let the user try aga...
Definition: account.c:692
cfmaplog.matches
matches
Definition: cfmaplog.py:354
show_main_client
void show_main_client(void)
Show main client window.
Definition: main.c:486
Face_Information_struct::faceset
guint8 faceset
Definition: client.h:409
Spell_struct::dam
guint16 dam
Damage done by spell though the meaning is spell dependent and actual damage may depend on how the sp...
Definition: client.h:306
ACL_LEVEL
#define ACL_LEVEL
Definition: newclient.h:202
UPD_NAME
#define UPD_NAME
Definition: newclient.h:291
CS_STAT_TITLE
#define CS_STAT_TITLE
Definition: newclient.h:110
Spell_struct::level
guint16 level
The casting level of the spell.
Definition: client.h:302
getTextManager
static ExtTextManager getTextManager(int type)
Definition: commands.c:1274
item_struct::inv_updated
guint16 inv_updated
Definition: item.h:77
Stat_struct::weight_limit
guint32 weight_limit
Carrying weight limit.
Definition: client.h:292
UPD_SP_GRACE
#define UPD_SP_GRACE
updspell command flag value.
Definition: newclient.h:298
CS_STAT_POW
#define CS_STAT_POW
Definition: newclient.h:111
spellmon_level
static int spellmon_level
Keeps track of what spellmon command is supported by the server.
Definition: commands.c:85
mapdata_set_darkness
void mapdata_set_darkness(int x, int y, int darkness)
Definition: mapdata.c:755
pl_pos
PlayerPosition pl_pos
Position of the player on the internal map.
Definition: mapdata.c:30
AnimCmd
void AnimCmd(unsigned char *data, int len)
Definition: commands.c:1163
use_config
gint16 use_config[CONFIG_NUMS]
Definition: client.h:245
AccountPlayersCmd
void AccountPlayersCmd(char *buf, int len)
This handles the accountplayers command.
Definition: commands.c:2503
EMI_HASMOREBITS
#define EMI_HASMOREBITS
Indicates the bitfield continue un next byte There may be several on contiguous bytes.
Definition: newclient.h:356
MSG_TYPE_CLIENT_QUERY
#define MSG_TYPE_CLIENT_QUERY
handle_query() and prompts
Definition: newclient.h:636
Stat_struct::denied
guint32 denied
Spell paths denied to the player.
Definition: client.h:286
DeleteInventory
void DeleteInventory(unsigned char *data, int len)
Definition: commands.c:1838
MAXLAYERS
#define MAXLAYERS
The protocol supports 10 layers, so set MAXLAYERS accordingly.
Definition: mapdata.h:6
Stat_struct::Cha
gint8 Cha
Charisma.
Definition: client.h:257
Stat_struct::Wis
gint8 Wis
Wisdom.
Definition: client.h:256
UPD_ANIM
#define UPD_ANIM
Definition: newclient.h:292
CS_STAT_MAXHP
#define CS_STAT_MAXHP
Definition: newclient.h:91
Player_Struct::mmapy
guint16 mmapy
size of magic map
Definition: client.h:361
Spell_struct::time
guint16 time
Casting time in server ticks.
Definition: client.h:303
MAXANIM
#define MAXANIM
Definition: client.h:86
draw_message_window
void draw_message_window(int redraw)
Updates the stats pane - hp, sp, etc labels.
Definition: stats.c:459
Spell_struct::grace
guint16 grace
Grace per cast; may be zero.
Definition: client.h:305
CS_STAT_RANGE
#define CS_STAT_RANGE
Definition: newclient.h:109
item_struct::weight
float weight
Definition: item.h:61
stat_points
int stat_points
Definition: commands.c:93
CONFIG_MAPHEIGHT
#define CONFIG_MAPHEIGHT
Definition: client.h:202
CS_STAT_DEX
#define CS_STAT_DEX
Definition: newclient.h:97
MAX_VIEW
#define MAX_VIEW
Maximum size of view area a server could support.
Definition: mapdata.h:11
INFO_MAP_NAME
#define INFO_MAP_NAME
Proper name of this entry.
Definition: newclient.h:648
update_login_info
void update_login_info(int type)
This is called from ReplyInfoCmd when it gets a response from news/motd/rules.
Definition: account.c:1174
GetInt64_String
gint64 GetInt64_String(const unsigned char *data)
The reverse of SockList_AddInt, but on strings instead.
Definition: newsocket.c:162
GetChar_String
char GetChar_String(const unsigned char *data)
Definition: newsocket.c:138
Stat_struct::attuned
guint32 attuned
Spell paths to which the player is attuned.
Definition: client.h:280
short_stat_name
static const char *const short_stat_name[NUM_STATS]
Short name of stats.
Definition: commands.c:107
used_races
int used_races
Definition: commands.c:88
SetupCmd
void SetupCmd(char *buf, int len)
Received a response to a setup from the server.
Definition: commands.c:901
Player_Struct::spelldata
Spell * spelldata
List of spells known.
Definition: client.h:351
serverloginmethod
int serverloginmethod
Definition: client.c:62
use_skill
void use_skill(int skill_id)
Maintain the last_used_skills LRU list for displaying the recently used skills first.
Definition: commands.c:1344
CS_STAT_RESIST_END
#define CS_STAT_RESIST_END
End of resistances (inclusive)
Definition: newclient.h:145
Player_Struct::spells_updated
guint32 spells_updated
Whether or not spells updated.
Definition: client.h:354
skill_names
char * skill_names[MAX_SKILL]
Definition: client.c:50
LOG_DEBUG
@ LOG_DEBUG
Useful debugging information.
Definition: client.h:436
classes
Race_Class_Info * classes
Definition: commands.c:98
FACE_IS_ANIM
#define FACE_IS_ANIM
Definition: newclient.h:319
get_new_char_info
static void get_new_char_info(unsigned char *data, int len)
This is process the newcharinfo requestinfo.
Definition: commands.c:247
INFO_MAP_DESCRIPTION
#define INFO_MAP_DESCRIPTION
Description of this map.
Definition: newclient.h:649
client_mapsize
void client_mapsize(int width, int height)
Ask the server for the given map size.
Definition: client.c:171
client.h
UpdateItemCmd
void UpdateItemCmd(unsigned char *data, int len)
Updates some attributes of an item.
Definition: commands.c:1728
Stat_struct::Dex
gint8 Dex
Dexterity.
Definition: client.h:254
ACL_FACE
#define ACL_FACE
Definition: newclient.h:203
set_weight_limit
void set_weight_limit(guint32 wlim)
No reason to divide by 1000 everytime we do the display, so do it once and store it here.
Definition: inventory.c:682
INFO_NEWS
#define INFO_NEWS
Definition: client.h:627
Starting_Map_Info
Definition: client.h:605
Race_Class_Info::arch_name
char * arch_name
Definition: client.h:597
INFO_RULES
#define INFO_RULES
Definition: client.h:629
CS_QUERY_HIDEINPUT
#define CS_QUERY_HIDEINPUT
Hide input being entered.
Definition: newclient.h:71
rstrip
static void rstrip(char buf[static 1], size_t len)
Remove trailing newlines from the given C string in-place.
Definition: commands.c:1857
CS_STAT_FLAGS
#define CS_STAT_FLAGS
Definition: newclient.h:114