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
14 /**
15 * @file
16 * Handles server->client commands; See player.c for client->server commands.
17 *
18 * Not necessarily all commands are handled - some might be in other files
19 * (like init.c)
20 *
21 * This file contains most of the commands for the dispatch loop. Most of the
22 * functions are self-explanatory.
23 *
24 * pixmap/bitmap : receive the picture, and display it.
25 * drawinfo : draws a string in the info window.
26 * stats : updates the local copy of the stats and displays it.
27 * handle_query : prompts the user for input.
28 * send_reply : sends off the reply for the input.
29 * player : gets the player information.
30 * MapScroll : scrolls the map on the client by some amount.
31 * MapCmd : displays the map with layer packing or stack packing.
32 * packing/unpacking is best understood by looking at the server code
33 * (server/ericserver.c)
34 * stack packing: for every map entry that changed, we pack 1 byte for the
35 * x/y location, 1 byte for the count, and 2 bytes per face in the stack.
36 * layer packing is harder, but I seem to remember more efficient: first we
37 * pack in a list of all map cells that changed and are now empty. The end
38 * of this list is a 255, which is bigger that 121, the maximum packed map
39 * location.
40 * For each changed location we also pack in a list of all the faces and X/Y
41 * coordinates by layer, where the layer is the depth in the map. This
42 * essentially takes slices through the map rather than stacks.
43 * Then for each layer, (max is MAXMAPCELLFACES, a bad name) we start
44 * packing the layer into the message. First we pack in a face, then for
45 * each place on the layer with the same face, we pack in the x/y location.
46 * We mark the last x/y location with the high bit on (11*11 = 121 < 128).
47 * We then continue on with the next face, which is why the code marks the
48 * faces as -1 if they are finished. Finally we mark the last face in the
49 * layer again with the high bit, clearly limiting the total number of faces
50 * to 32767, the code comments it's 16384, I'm not clear why, but the second
51 * bit may be used somewhere else as well.
52 * The unpacking routines basically perform the opposite operations.
53 */
54
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
84 /** Keeps track of what spellmon command is supported by the server. */
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
98 Race_Class_Info *races=NULL, *classes=NULL;
99 Starting_Map_Info *starting_map_info = NULL;
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 /** Short name of stats. */
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 */
118 struct Stat_Mapping stat_mapping[NUM_NEW_CHAR_STATS] = {
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
128 /**
129 * This function clears the data from the Race_Class_Info array. Because the
130 * structure itself contains data that is allocated, some work needs to be
131 * done to clear that data.
132 *
133 */
free_all_starting_map_info()134 void free_all_starting_map_info()
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;
159 starting_map_number = 0;
160 }
161
162 /**
163 * This processes the replyinfo starting_map_info
164 *
165 * The data is a series of length prefixed lines.
166 *
167 * @param data
168 * data returned from server. Format is documented in protocol file.
169 * @param len
170 * length of data.
171 */
get_starting_map_info(unsigned char * data,int len)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++;
208 starting_map_info = g_realloc(starting_map_info,
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;
232 starting_map_update_info();
233 }
234
235 /**
236 * This is process the newcharinfo requestinfo.
237 * In some cases, it stores away the value, for others, it just
238 * makes sure we understand them.
239 *
240 * The data is a series of length prefixed lines.
241 *
242 * @param data
243 * data returned from server. Format is documented in protocol file.
244 * @param len
245 * length of data.
246 */
get_new_char_info(unsigned char * data,int len)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");
370 free_all_starting_map_info();
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",
395 stat_min, stat_maximum, stat_points);
396 } else {
397 new_char_window_update_info();
398 }
399 }
400
401
402 /**
403 * Used for bsearch searching.
404 */
rc_compar(const Race_Class_Info * a,const Race_Class_Info * b)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
410 /**
411 * This function clears the data from the Race_Class_Info array. Because the
412 * structure itself contains data that is allocated, some work needs to be
413 * done to clear that data.
414 *
415 * @param data
416 * array to clear
417 * @param num_entries
418 * size of the array.
419 */
free_all_race_class_info(Race_Class_Info * data,int num_entries)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
458 /**
459 * This extracts the data from a replyinfo race_info/class_info request. We
460 * only get this data if the client has made a requestinfo of this data.
461 *
462 * @param data
463 * data returned from server. Format is documented in protocol file.
464 * @param len
465 * length of data
466 * @param rci
467 * Where to store the data.
468 */
process_race_class_info(unsigned char * data,int len,Race_Class_Info * rci)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
639 /**
640 * This is a little wrapper function that does some bounds checking and then
641 * calls process_race_info() to do the bulk of the work.
642 *
643 * @param data
644 * data returned from server. Format is documented in protocol file.
645 * @param len
646 * length of data.
647 */
get_race_info(unsigned char * data,int len)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
658 process_race_class_info(data, len, &races[used_races]);
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
665 new_char_window_update_info();
666 }
667 }
668
669 /**
670 * This is a little wrapper function that does some bounds checking and then
671 * calls process_race_info() to do the bulk of the work. Pretty much
672 * identical to get_race_info() except this is for classes.
673 *
674 * @param data
675 * data returned from server. Format is documented in protocol file.
676 * @param len
677 * length of data.
678 */
get_class_info(unsigned char * data,int len)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
689 process_race_class_info(data, len, &classes[used_classes]);
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
696 new_char_window_update_info();
697 }
698 }
699
700 /**
701 *
702 * @param data
703 * @param len
704 */
get_exp_info(const unsigned char * data,int len)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
714 exp_table_max = GetShort_String(data);
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
727 /**
728 *
729 * @param data
730 * @param len
731 */
get_skill_info(char * data,int len)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
767 /**
768 * Handles the response from a 'requestinfo' command. This function doesn't
769 * do much itself other than dispatch to other functions.
770 *
771 * @param buf
772 * @param len
773 */
ReplyInfoCmd(unsigned char * buf,int len)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);
817 update_login_info(INFO_MOTD);
818 } else if (!strcmp((char*)buf, "news")) {
819 if (news) {
820 free((char*)news);
821 }
822 news = g_strdup((char *)cp);
823 update_login_info(INFO_NEWS);
824 } else if (!strcmp((char*)buf, "rules")) {
825 if (rules) {
826 free((char*)rules);
827 }
828 rules = g_strdup((char *)cp);
829 update_login_info(INFO_RULES);
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) {
850 free_all_race_class_info(races, num_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) {
877 free_all_race_class_info(classes, num_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
893 /**
894 * Received a response to a setup from the server. This function is basically
895 * the same as the server side function - we just do some different processing
896 * on the data.
897 *
898 * @param buf
899 * @param len
900 */
SetupCmd(char * buf,int len)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 int x, y = 0;
953 char *cp, tmpbuf[MAX_BUF];
954
955 if (!g_ascii_strcasecmp(param, "false")) {
956 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
957 "Server only supports standard sized maps (11x11)");
958 /* Do this because we may have been playing on a big server
959 * before */
960 use_config[CONFIG_MAPWIDTH] = 11;
961 use_config[CONFIG_MAPHEIGHT] = 11;
962 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
963 resize_map_window(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
964 continue;
965 }
966 x = atoi(param);
967 for (cp = param; *cp != 0; cp++) {
968 if (*cp == 'x' || *cp == 'X') {
969 y = atoi(cp+1);
970 break;
971 }
972 }
973 /* A size larger than what the server supports was requested.
974 * Reduce the size to server maximum, and re-send the setup
975 * command. Update our want sizes, and tell the player what is
976 * going on.
977 */
978 if (use_config[CONFIG_MAPWIDTH] > x || use_config[CONFIG_MAPHEIGHT] > y) {
979 if (use_config[CONFIG_MAPWIDTH] > x) {
980 use_config[CONFIG_MAPWIDTH] = x;
981 }
982 if (use_config[CONFIG_MAPHEIGHT] > y) {
983 use_config[CONFIG_MAPHEIGHT] = y;
984 }
985 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
986 cs_print_string(csocket.fd,
987 "setup mapsize %dx%d", use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
988 snprintf(tmpbuf, sizeof(tmpbuf), "Server supports a max mapsize of %d x %d - requesting a %d x %d mapsize",
989 x, y, use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
990 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
991 tmpbuf);
992 } else if (use_config[CONFIG_MAPWIDTH] == x && use_config[CONFIG_MAPHEIGHT] == y) {
993 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
994 resize_map_window(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
995 } else {
996 /* The request was not bigger than what server supports, and
997 * not the same size, so what is the problem? Tell the user
998 * that something is wrong.
999 */
1000 snprintf(tmpbuf, sizeof(tmpbuf), "Unable to set mapsize on server - we wanted %d x %d, server returned %d x %d",
1001 use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT], x, y);
1002 draw_ext_info(
1003 NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER, tmpbuf);
1004 }
1005 } else if (!strcmp(cmd, "darkness")) {
1006 /* Older servers might not support this setup command.
1007 */
1008 if (!strcmp(param, "FALSE")) {
1009 LOG(LOG_WARNING, "common::SetupCmd", "Server returned FALSE for setup command %s", cmd);
1010 }
1011 } else if (!strcmp(cmd, "spellmon")) {
1012
1013 /* Older servers might not support this setup command or all of
1014 * the extensions.
1015 *
1016 * Spellmon 2 was added to the protocol in January 2010 to send an
1017 * additional spell information string with casting requirements
1018 * including required items, if the spell needs arguments passed
1019 * (like text for rune of marking), etc.
1020 *
1021 * To use the new feature, "setup spellmon 1 spellmon 2" is sent,
1022 * and if "spellmon 1 spellmon FALSE" is returned then the server
1023 * doesn't accept 2 - sending spellmon 2 to a server that does not
1024 * support it is not problematic, so the spellmon 1 command will
1025 * still be handled correctly by the server. If the server sends
1026 * "spellmon 1 spellmon 2" then the extended mode is in effect.
1027 *
1028 * It is not particularly important for the player to know what
1029 * level of command is accepted by the server. The extra features
1030 * will simply not be functionally available.
1031 */
1032 if (!strcmp(param, "FALSE")) {
1033 LOG(LOG_INFO, "common::SetupCmd", "Server returned FALSE for a %s setup command", cmd);
1034 } else {
1035 spellmon_level = atoi(param);
1036 }
1037 } else if (!strcmp(cmd, "facecache")) {
1038 use_config[CONFIG_CACHE] = atoi(param);
1039 } else if (!strcmp(cmd, "faceset")) {
1040 if (!strcmp(param, "FALSE")) {
1041 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1042 "Server does not support other image sets, will use default");
1043 face_info.faceset = 0;
1044 }
1045 } else if (!strcmp(cmd, "map2cmd")) {
1046 if (!strcmp(param, "FALSE")) {
1047 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1048 "Server does not support map2cmd!");
1049 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1050 "This server is too old to support this client!");
1051 client_disconnect();
1052 }
1053 } else if (!strcmp(cmd, "want_pickup")) {
1054 /* Nothing special to do as this is info pushed from server and
1055 * not having it isn't that bad.
1056 */
1057 } else if (!strcmp(cmd, "loginmethod")) {
1058 int method = atoi(param);
1059
1060 /* If the server supports new login, start the process. Pass what
1061 * version the server supports so client can do appropriate
1062 * work
1063 */
1064 if (method) {
1065 start_login(method);
1066 }
1067 } else if (!strcmp(cmd, "newmapcmd")) {
1068 // if server doesn't support newmapcmd, too bad
1069 } else if (!strcmp(cmd, "tick")) {
1070 // Ticks drive the redraw loop via client_tick(), so we really
1071 // do need ticks. To support servers without ticks, add a timer
1072 // callback via g_timeout_add() that calls client_tick().
1073 if (!strcmp(param, "FALSE")) {
1074 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1075 "Server does not support tick!");
1076 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1077 "This server is too old to support this client!");
1078 client_disconnect();
1079 }
1080 } else if (!strcmp(cmd, "extendedTextInfos")) {
1081 // Even though this is deprecated, old servers are sitll being
1082 // actively used. Request extended text info (drawextinfo).
1083 if (csocket.cs_version < 1023 && strcmp(param, "FALSE")) { /* server didn't send FALSE*/
1084 /* Server seems to accept extended text infos. Let's tell
1085 * it what extended text info we want
1086 */
1087 for (int i = 1; i < 20; i++) {
1088 char exttext[MAX_BUF];
1089 snprintf(exttext, sizeof(exttext), "toggleextendedtext %d", i);
1090 cs_print_string(csocket.fd, exttext);
1091 }
1092 }
1093 } else {
1094 LOG(LOG_INFO, "common::SetupCmd",
1095 "Got setup for a command we don't understand: %s %s",
1096 cmd, param);
1097 }
1098 }
1099 }
1100
1101 /**
1102 * Handles when the server says we can't be added. In reality, we need to
1103 * close the connection and quit out, because the client is going to close us
1104 * down anyways.
1105 *
1106 * @param data
1107 * @param len
1108 */
AddMeFail(char * data,int len)1109 void AddMeFail(char *data, int len)
1110 {
1111 (void)data; /* __UNUSED__ */
1112 (void)len; /* __UNUSED__ */
1113
1114 LOG(LOG_INFO, "common::AddMeFail", "addme_failed received.");
1115 return;
1116 }
1117
1118 /**
1119 * This is really a throwaway command - there really isn't any reason to send
1120 * addme_success commands.
1121 *
1122 * @param data
1123 * @param len
1124 */
AddMeSuccess(char * data,int len)1125 void AddMeSuccess(char *data, int len)
1126 {
1127 (void)data; /* __UNUSED__ */
1128 (void)len; /* __UNUSED__ */
1129
1130 hide_all_login_windows();
1131 show_main_client();
1132 LOG(LOG_DEBUG, "common::AddMeSuccess", "addme_success received.");
1133 return;
1134 }
1135
1136 /**
1137 *
1138 * @param data
1139 * @param len
1140 */
GoodbyeCmd(char * data,int len)1141 void GoodbyeCmd(char *data, int len)
1142 {
1143 (void)data; /* __UNUSED__ */
1144 (void)len; /* __UNUSED__ */
1145
1146 /* This could probably be greatly improved - I am not sure if anything
1147 * needs to be saved here, but it should be possible to reconnect to the
1148 * server or a different server without having to rerun the client.
1149 */
1150 LOG(LOG_WARNING, "common::GoodbyeCmd", "Received goodbye command from server - exiting");
1151 exit(0);
1152 }
1153
1154 Animations animations[MAXANIM];
1155
1156 /**
1157 *
1158 * @param data
1159 * @param len
1160 */
AnimCmd(unsigned char * data,int len)1161 void AnimCmd(unsigned char *data, int len)
1162 {
1163 short anum;
1164 int i, j;
1165
1166 anum = GetShort_String(data);
1167 if (anum < 0 || anum > MAXANIM) {
1168 LOG(LOG_WARNING, "common::AnimCmd", "animation number invalid: %d", anum);
1169 return;
1170 }
1171
1172 animations[anum].flags = GetShort_String(data+2);
1173 animations[anum].num_animations = (len-4)/2;
1174 if (animations[anum].num_animations < 1) {
1175 LOG(LOG_WARNING, "common::AnimCmd", "num animations invalid: %d",
1176 animations[anum].num_animations);
1177 return;
1178 }
1179 animations[anum].faces = g_malloc(sizeof(guint16)*animations[anum].num_animations);
1180 for (i = 4, j = 0; i < len; i += 2, j++) {
1181 animations[anum].faces[j] = GetShort_String(data+i);
1182 }
1183
1184 if (j != animations[anum].num_animations) {
1185 LOG(LOG_WARNING, "common::AnimCmd",
1186 "Calculated animations does not equal stored animations? (%d!=%d)",
1187 j, animations[anum].num_animations);
1188 }
1189
1190 animations[anum].speed = 0;
1191 animations[anum].speed_left = 0;
1192 animations[anum].phase = 0;
1193
1194 LOG(LOG_DEBUG, "common::AnimCmd", "Received animation %d, %d faces", anum, animations[anum].num_animations);
1195 }
1196
1197 /**
1198 * Receives the smooth mapping from the server. Because this information is
1199 * reference a lot, the smoothing face is stored in the pixmap data - this
1200 * makes access much faster than searching an array of data for the face to
1201 * use.
1202 *
1203 * @param data
1204 * @param len
1205 */
SmoothCmd(unsigned char * data,int len)1206 void SmoothCmd(unsigned char *data, int len)
1207 {
1208 guint16 faceid;
1209 guint16 smoothing;
1210
1211 /* len is unused. We should check that we don't have an invalid short
1212 * command. Hence, the compiler warning is valid.
1213 */
1214
1215 faceid = GetShort_String(data);
1216 smoothing = GetShort_String(data+2);
1217 addsmooth(faceid, smoothing);
1218 }
1219
1220 /**
1221 * Draws a string in the info window.
1222 *
1223 * @param data
1224 * @param len
1225 */
DrawInfoCmd(char * data,int len)1226 void DrawInfoCmd(char *data, int len)
1227 {
1228 int color = atoi(data);
1229 char *buf;
1230
1231 (void)len; /* __UNUSED__ */
1232
1233 buf = strchr(data, ' ');
1234 if (!buf) {
1235 LOG(LOG_WARNING, "common::DrawInfoCmd", "got no data");
1236 buf = "";
1237 } else {
1238 buf++;
1239 }
1240 draw_ext_info(color, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_COMMAND, buf);
1241 }
1242
1243 TextManager *firstTextManager = NULL;
1244
1245 /**
1246 *
1247 * @param type
1248 * @param callback
1249 */
setTextManager(int type,ExtTextManager callback)1250 void setTextManager(int type, ExtTextManager callback)
1251 {
1252 TextManager *current = firstTextManager;
1253
1254 while (current != NULL) {
1255 if (current->type == type) {
1256 current->callback = callback;
1257 return;
1258 }
1259 current = current->next;
1260 }
1261 current = g_malloc(sizeof(TextManager));
1262 current->type = type;
1263 current->callback = callback;
1264 current->next = firstTextManager;
1265 firstTextManager = current;
1266 }
1267
1268 /**
1269 *
1270 * @param type
1271 */
getTextManager(int type)1272 static ExtTextManager getTextManager(int type)
1273 {
1274 TextManager *current = firstTextManager;
1275 while (current != NULL) {
1276 if (current->type == type) {
1277 return current->callback;
1278 }
1279 current = current->next;
1280 }
1281 return NULL;
1282 }
1283
1284 /**
1285 * We must extract color, type, subtype and dispatch to callback
1286 *
1287 * @param data
1288 * @param len
1289 */
DrawExtInfoCmd(char * data,int len)1290 void DrawExtInfoCmd(char *data, int len)
1291 {
1292 int color;
1293 int type, subtype;
1294 char *buf = data;
1295 int wordCount = 3;
1296 ExtTextManager fnct;
1297
1298 while (wordCount > 0) {
1299 while (buf[0] == ' ') {
1300 buf++;
1301 }
1302 wordCount--;
1303 while (buf[0] != ' ') {
1304 if (buf[0] == '\0') {
1305 LOG(LOG_WARNING,
1306 "common::DrawExtInfoCmd", "Data is missing %d parameters %s",
1307 wordCount,
1308 data);
1309 return;
1310 } else {
1311 buf++;
1312 }
1313 }
1314 if (buf[0] == ' ') {
1315 buf++; /*remove trailing space to send clean data to callback */
1316 }
1317 }
1318 wordCount = sscanf(data, "%d %d %d", &color, &type, &subtype);
1319 if (wordCount != 3) {
1320 LOG(LOG_WARNING,
1321 "common::DrawExtInfoCmd", "Wrong parameters received. Could only parse %d out of 3 int in %s",
1322 wordCount,
1323 data);
1324 return;
1325 }
1326 fnct = getTextManager(type);
1327 if (fnct == NULL) {
1328 LOG(LOG_WARNING,
1329 "common::DrawExtInfoCmd", "Server send us a type %d but i can't find any callback for it",
1330 type);
1331 return;
1332 }
1333 fnct(color, type, subtype, buf);
1334 }
1335
1336 /**
1337 * Maintain the last_used_skills LRU list for displaying the recently used
1338 * skills first.
1339 *
1340 * @param skill_id
1341 */
use_skill(int skill_id)1342 void use_skill(int skill_id)
1343 {
1344 int i = 0;
1345 int next;
1346 int prev = last_used_skills[0];
1347
1348 if(last_used_skills[0] == skill_id) {
1349 return;
1350 }
1351
1352 do {
1353 next = last_used_skills[i+1];
1354 last_used_skills[i+1] = prev;
1355 prev = next;
1356 ++i;
1357 } while(next != skill_id && next >= 0);
1358 last_used_skills[0] = skill_id;
1359 }
1360
1361 /**
1362 * Updates the local copy of the stats and displays it.
1363 *
1364 * @param data
1365 * @param len
1366 */
StatsCmd(unsigned char * data,int len)1367 void StatsCmd(unsigned char *data, int len)
1368 {
1369 int i = 0, c, redraw = 0;
1370 gint64 last_exp;
1371
1372 while (i < len) {
1373 c = data[i++];
1374 if (c >= CS_STAT_RESIST_START && c <= CS_STAT_RESIST_END) {
1375 cpl.stats.resists[c-CS_STAT_RESIST_START] = GetShort_String(data+i);
1376 i += 2;
1377 cpl.stats.resist_change = 1;
1378 } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) {
1379 /* We track to see if the exp has gone from 0 to some total value
1380 * - we do this because the draw logic currently only draws skills
1381 * where the player has exp. We need to communicate to the draw
1382 * function that it should draw all the players skills. Using
1383 * redraw is a little overkill, because a lot of the data may not
1384 * be changing. OTOH, such a transition should only happen
1385 * rarely, not not be a very big deal.
1386 */
1387 cpl.stats.skill_level[c-CS_STAT_SKILLINFO] = data[i++];
1388 last_exp = cpl.stats.skill_exp[c-CS_STAT_SKILLINFO];
1389 cpl.stats.skill_exp[c-CS_STAT_SKILLINFO] = GetInt64_String(data+i);
1390 use_skill(c-CS_STAT_SKILLINFO);
1391 if (last_exp == 0 && cpl.stats.skill_exp[c-CS_STAT_SKILLINFO]) {
1392 redraw = 1;
1393 }
1394 i += 8;
1395 } else {
1396 switch (c) {
1397 case CS_STAT_HP:
1398 cpl.stats.hp = GetShort_String(data+i);
1399 i += 2;
1400 break;
1401 case CS_STAT_MAXHP:
1402 cpl.stats.maxhp = GetShort_String(data+i);
1403 i += 2;
1404 break;
1405 case CS_STAT_SP:
1406 cpl.stats.sp = GetShort_String(data+i);
1407 i += 2;
1408 break;
1409 case CS_STAT_MAXSP:
1410 cpl.stats.maxsp = GetShort_String(data+i);
1411 i += 2;
1412 break;
1413 case CS_STAT_GRACE:
1414 cpl.stats.grace = GetShort_String(data+i);
1415 i += 2;
1416 break;
1417 case CS_STAT_MAXGRACE:
1418 cpl.stats.maxgrace = GetShort_String(data+i);
1419 i += 2;
1420 break;
1421 case CS_STAT_STR:
1422 cpl.stats.Str = GetShort_String(data+i);
1423 i += 2;
1424 break;
1425 case CS_STAT_INT:
1426 cpl.stats.Int = GetShort_String(data+i);
1427 i += 2;
1428 break;
1429 case CS_STAT_POW:
1430 cpl.stats.Pow = GetShort_String(data+i);
1431 i += 2;
1432 break;
1433 case CS_STAT_WIS:
1434 cpl.stats.Wis = GetShort_String(data+i);
1435 i += 2;
1436 break;
1437 case CS_STAT_DEX:
1438 cpl.stats.Dex = GetShort_String(data+i);
1439 i += 2;
1440 break;
1441 case CS_STAT_CON:
1442 cpl.stats.Con = GetShort_String(data+i);
1443 i += 2;
1444 break;
1445 case CS_STAT_CHA:
1446 cpl.stats.Cha = GetShort_String(data+i);
1447 i += 2;
1448 break;
1449 case CS_STAT_EXP:
1450 cpl.stats.exp = GetInt_String(data+i);
1451 i += 4;
1452 break;
1453 case CS_STAT_EXP64:
1454 cpl.stats.exp = GetInt64_String(data+i);
1455 i += 8;
1456 break;
1457 case CS_STAT_LEVEL:
1458 cpl.stats.level = GetShort_String(data+i);
1459 i += 2;
1460 break;
1461 case CS_STAT_WC:
1462 cpl.stats.wc = GetShort_String(data+i);
1463 i += 2;
1464 break;
1465 case CS_STAT_AC:
1466 cpl.stats.ac = GetShort_String(data+i);
1467 i += 2;
1468 break;
1469 case CS_STAT_DAM:
1470 cpl.stats.dam = GetShort_String(data+i);
1471 i += 2;
1472 break;
1473 case CS_STAT_ARMOUR:
1474 cpl.stats.resists[0] = GetShort_String(data+i);
1475 i += 2;
1476 break;
1477 case CS_STAT_SPEED:
1478 cpl.stats.speed = GetInt_String(data+i);
1479 i += 4;
1480 break;
1481 case CS_STAT_FOOD:
1482 cpl.stats.food = GetShort_String(data+i);
1483 i += 2;
1484 break;
1485 case CS_STAT_WEAP_SP:
1486 cpl.stats.weapon_sp = GetInt_String(data+i);
1487 i += 4;
1488 break;
1489 case CS_STAT_SPELL_ATTUNE:
1490 cpl.stats.attuned = GetInt_String(data+i);
1491 i += 4;
1492 cpl.spells_updated = 1;
1493 break;
1494 case CS_STAT_SPELL_REPEL:
1495 cpl.stats.repelled = GetInt_String(data+i);
1496 i += 4;
1497 cpl.spells_updated = 1;
1498 break;
1499 case CS_STAT_SPELL_DENY:
1500 cpl.stats.denied = GetInt_String(data+i);
1501 i += 4;
1502 cpl.spells_updated = 1;
1503 break;
1504
1505 case CS_STAT_FLAGS:
1506 cpl.stats.flags = GetShort_String(data+i);
1507 i += 2;
1508 break;
1509 case CS_STAT_WEIGHT_LIM:
1510 set_weight_limit(cpl.stats.weight_limit = GetInt_String(data+i));
1511 i += 4;
1512 /* Mark weight limit changes to update the client inventory window */
1513 cpl.ob->inv_updated = 1;
1514 break;
1515
1516 case CS_STAT_RANGE: {
1517 int rlen = data[i++];
1518 strncpy(cpl.range, (const char*)data+i, rlen);
1519 cpl.range[rlen] = '\0';
1520 i += rlen;
1521 break;
1522 }
1523
1524 case CS_STAT_TITLE: {
1525 int rlen = data[i++];
1526 strncpy(cpl.title, (const char*)data+i, rlen);
1527 cpl.title[rlen] = '\0';
1528 i += rlen;
1529 break;
1530 }
1531
1532 default:
1533 LOG(LOG_WARNING, "common::StatsCmd", "Unknown stat number %d", c);
1534 break;
1535 }
1536 }
1537 }
1538
1539 if (i > len) {
1540 LOG(LOG_WARNING, "common::StatsCmd", "got stats overflow, processed %d bytes out of %d", i, len);
1541 }
1542 draw_stats(redraw);
1543 draw_message_window(0);
1544 #ifdef HAVE_LUA
1545 script_lua_stats();
1546 #endif
1547 }
1548
1549 /**
1550 * Prompts the user for input.
1551 *
1552 * @param data
1553 * @param len
1554 */
handle_query(char * data,int len)1555 void handle_query(char *data, int len)
1556 {
1557 char *buf, *cp;
1558 guint8 flags = atoi(data);
1559
1560 (void)len; /* __UNUSED__ */
1561
1562 if (flags&CS_QUERY_HIDEINPUT) { /* No echo */
1563 cpl.no_echo = 1;
1564 } else {
1565 cpl.no_echo = 0;
1566 }
1567
1568 /* Let the window system know this may have changed */
1569 x_set_echo();
1570
1571 /* The actual text is optional */
1572 buf = strchr(data, ' ');
1573 if (buf) {
1574 buf++;
1575 }
1576
1577 /* If we just get passed an empty string, why draw this? */
1578 if (buf) {
1579 cp = buf;
1580 while ((buf = strchr(buf, '\n')) != NULL) {
1581 *buf++ = '\0';
1582 draw_ext_info(
1583 NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, cp);
1584 cp = buf;
1585 }
1586 /* Yes/no - don't do anything with it now */
1587 if (flags&CS_QUERY_YESNO) {
1588 }
1589
1590 /* One character response expected */
1591 if (flags&CS_QUERY_SINGLECHAR) {
1592 cpl.input_state = Reply_One;
1593 } else {
1594 cpl.input_state = Reply_Many;
1595 }
1596
1597 if (cp) {
1598 draw_prompt(cp);
1599 }
1600 }
1601
1602 LOG(LOG_DEBUG, "common::handle_query", "Received query. Input state now %d", cpl.input_state);
1603 }
1604
1605 /**
1606 * Sends a reply to the server. This function basically just packs the stuff
1607 * up.
1608 *
1609 * @param text contains the null terminated string of text to send.
1610 */
send_reply(const char * text)1611 void send_reply(const char *text)
1612 {
1613 cs_print_string(csocket.fd, "reply %s", text);
1614
1615 /* Let the window system know that the (possibly hidden) query is over. */
1616 cpl.no_echo = 0;
1617 x_set_echo();
1618 }
1619
1620 /**
1621 * Gets the player information. This function copies relevant data from the
1622 * archetype to the object. Only copies data that was not set in the object
1623 * structure.
1624 *
1625 * @param data
1626 * @param len
1627 */
PlayerCmd(unsigned char * data,int len)1628 void PlayerCmd(unsigned char *data, int len)
1629 {
1630 char name[MAX_BUF];
1631 int tag, weight, face, i = 0, nlen;
1632
1633 reset_player_data();
1634 tag = GetInt_String(data);
1635 i += 4;
1636 weight = GetInt_String(data+i);
1637 i += 4;
1638 face = GetInt_String(data+i);
1639 i += 4;
1640 nlen = data[i++];
1641 memcpy(name, (const char*)data+i, nlen);
1642 name[nlen] = '\0';
1643 i += nlen;
1644
1645 if (i != len) {
1646 LOG(LOG_WARNING, "common::PlayerCmd", "lengths do not match (%d!=%d)", len, i);
1647 }
1648 new_player(tag, name, weight, face);
1649 }
1650
1651 /**
1652 *
1653 * @param op
1654 */
item_actions(item * op)1655 void item_actions(item *op)
1656 {
1657 if (!op) {
1658 return;
1659 }
1660
1661 if (op->open) {
1662 open_container(op);
1663 cpl.container = op;
1664 } else if (op->was_open) {
1665 close_container(op);
1666 cpl.container = NULL;
1667 }
1668 }
1669
1670 /**
1671 * Parses the data sent to us from the server. revision is what item command
1672 * the data came from - newer ones have addition fields.
1673 * @param data
1674 * @param len
1675 */
Item2Cmd(unsigned char * data,int len)1676 void Item2Cmd(unsigned char *data, int len) {
1677 int weight, loc, tag, face, flags, pos = 0, nlen, anim, nrof, type;
1678 guint8 animspeed;
1679 char name[MAX_BUF];
1680
1681 loc = GetInt_String(data);
1682 pos += 4;
1683
1684 if (pos == len) {
1685 LOG(LOG_WARNING, "common::common_item_command", "Got location with no other data");
1686 return;
1687 } else if (loc < 0) { /* Delete following items */
1688 LOG(LOG_WARNING, "common::common_item_command", "Got location with negative value (%d)", loc);
1689 return;
1690 } else {
1691 while (pos < len) {
1692 tag = GetInt_String(data+pos);
1693 pos += 4;
1694 flags = GetInt_String(data+pos);
1695 pos += 4;
1696 weight = GetInt_String(data+pos);
1697 pos += 4;
1698 face = GetInt_String(data+pos);
1699 pos += 4;
1700 nlen = data[pos++];
1701 memcpy(name, (char*)data+pos, nlen);
1702 pos += nlen;
1703 name[nlen] = '\0';
1704 anim = GetShort_String(data+pos);
1705 pos += 2;
1706 animspeed = data[pos++];
1707 nrof = GetInt_String(data+pos);
1708 pos += 4;
1709 type = GetShort_String(data+pos);
1710 pos += 2;
1711 update_item(tag, loc, name, weight, face, flags, anim, animspeed, nrof, type);
1712 item_actions(locate_item(tag));
1713 }
1714 if (pos > len) {
1715 LOG(LOG_WARNING, "common::common_item_cmd", "Overread buffer: %d > %d", pos, len);
1716 }
1717 }
1718 }
1719
1720 /**
1721 * Updates some attributes of an item
1722 *
1723 * @param data
1724 * @param len
1725 */
UpdateItemCmd(unsigned char * data,int len)1726 void UpdateItemCmd(unsigned char *data, int len)
1727 {
1728 int weight, loc, tag, face, sendflags, flags, pos = 0, nlen, anim;
1729 guint32 nrof;
1730 char name[MAX_BUF];
1731 item *ip;
1732 guint8 animspeed;
1733
1734 sendflags = data[0];
1735 pos += 1;
1736 tag = GetInt_String(data+pos);
1737 pos += 4;
1738 ip = locate_item(tag);
1739 if (!ip) {
1740 /*
1741 fprintf(stderr, "Got update_item command for item we don't have (%d)\n", tag);
1742 */
1743 return;
1744 }
1745
1746 /* Copy all of these so we can pass the values to update_item and don't
1747 * need to figure out which ones were modified by this function.
1748 */
1749 *name = '\0';
1750 loc = ip->env ? ip->env->tag : 0;
1751 weight = ip->weight*1000;
1752 face = ip->face;
1753 flags = ip->flagsval;
1754 anim = ip->animation_id;
1755 animspeed = ip->anim_speed;
1756 nrof = ip->nrof;
1757
1758 if (sendflags&UPD_LOCATION) {
1759 loc = GetInt_String(data+pos);
1760 LOG(LOG_WARNING, "common::UpdateItemCmd", "Got tag of unknown object (%d) for new location", loc);
1761 pos += 4;
1762 }
1763 if (sendflags&UPD_FLAGS) {
1764 flags = GetInt_String(data+pos);
1765 pos += 4;
1766 }
1767 if (sendflags&UPD_WEIGHT) {
1768 weight = GetInt_String(data+pos);
1769 pos += 4;
1770 }
1771 if (sendflags&UPD_FACE) {
1772 face = GetInt_String(data+pos);
1773 pos += 4;
1774 }
1775 if (sendflags&UPD_NAME) {
1776 nlen = data[pos++];
1777 memcpy(name, (char*)data+pos, nlen);
1778 pos += nlen;
1779 name[nlen] = '\0';
1780 }
1781 if (pos > len) {
1782 LOG(LOG_WARNING, "common::UpdateItemCmd", "Overread buffer: %d > %d", pos, len);
1783 return; /* We have bad data, probably don't want to store it then */
1784 }
1785 if (sendflags&UPD_ANIM) {
1786 anim = GetShort_String(data+pos);
1787 pos += 2;
1788 }
1789 if (sendflags&UPD_ANIMSPEED) {
1790 animspeed = data[pos++];
1791 }
1792 if (sendflags&UPD_NROF) {
1793 nrof = (guint32)GetInt_String(data+pos);
1794 pos += 4;
1795 }
1796 /* update_item calls set_item_values which will then set the list redraw
1797 * flag, so we don't need to do an explicit redraw here. Actually,
1798 * calling update_item is a little bit of overkill, since we already
1799 * determined some of the values in this function.
1800 */
1801 update_item(tag, loc, name, weight, face, flags, anim, animspeed, nrof, ip->type);
1802 item_actions(locate_item(tag));
1803 }
1804
1805 /**
1806 *
1807 * @param data
1808 * @param len
1809 */
DeleteItem(unsigned char * data,int len)1810 void DeleteItem(unsigned char *data, int len)
1811 {
1812 int pos = 0, tag;
1813
1814 while (pos < len) {
1815 item *op;
1816
1817 tag = GetInt_String(data+pos);
1818 pos += 4;
1819 op = locate_item(tag);
1820 if (op != NULL) {
1821 remove_item(op);
1822 } else {
1823 LOG(LOG_WARNING, "common::DeleteItem", "Cannot find tag %d", tag);
1824 }
1825 }
1826 if (pos > len) {
1827 LOG(LOG_WARNING, "common::DeleteItem", "Overread buffer: %d > %d", pos, len);
1828 }
1829 }
1830
1831 /**
1832 *
1833 * @param data
1834 * @param len
1835 */
DeleteInventory(unsigned char * data,int len)1836 void DeleteInventory(unsigned char *data, int len)
1837 {
1838 int tag;
1839 item *op;
1840
1841 (void)len; /* __UNUSED__ */
1842
1843 tag = atoi((const char*)data);
1844 op = locate_item(tag);
1845 if (op != NULL) {
1846 remove_item_inventory(op);
1847 } else {
1848 LOG(LOG_WARNING, "common::DeleteInventory", "Invalid tag: %d", tag);
1849 }
1850 }
1851
1852 /**
1853 * Remove trailing newlines from the given C string in-place.
1854 */
rstrip(char buf[static1],size_t len)1855 static void rstrip(char buf[static 1], size_t len) {
1856 for (size_t i = len - 1; i > 0; i--) {
1857 if (buf[i] == '\n' || buf[i] == ' ') {
1858 buf[i] = '\0';
1859 } else {
1860 return;
1861 }
1862 }
1863 }
1864
1865 /****************************************************************************/
1866
1867 /**
1868 * @defgroup SCSpellCommands Server->Client spell command functions.
1869 * @{
1870 */
1871
1872 /**
1873 *
1874 * @param data
1875 * @param len
1876 */
AddspellCmd(unsigned char * data,int len)1877 void AddspellCmd(unsigned char *data, int len)
1878 {
1879 guint8 nlen;
1880 guint16 mlen, pos = 0;
1881 Spell *newspell, *tmp;
1882
1883 while (pos < len) {
1884 newspell = calloc(1, sizeof(Spell));
1885
1886 /* Get standard spell information (spellmon 1)
1887 */
1888 newspell->tag = GetInt_String(data+pos);
1889 pos += 4;
1890 newspell->level = GetShort_String(data+pos);
1891 pos += 2;
1892 newspell->time = GetShort_String(data+pos);
1893 pos += 2;
1894 newspell->sp = GetShort_String(data+pos);
1895 pos += 2;
1896 newspell->grace = GetShort_String(data+pos);
1897 pos += 2;
1898 newspell->dam = GetShort_String(data+pos);
1899 pos += 2;
1900 newspell->skill_number = GetChar_String(data+pos);
1901 pos += 1;
1902 newspell->path = GetInt_String(data+pos);
1903 pos += 4;
1904 newspell->face = GetInt_String(data+pos);
1905 pos += 4;
1906 nlen = GetChar_String(data+pos);
1907 pos += 1;
1908 strncpy(newspell->name, (char*)data+pos, nlen);
1909 pos += nlen;
1910 newspell->name[nlen] = '\0'; /* To ensure we are null terminated */
1911 mlen = GetShort_String(data+pos);
1912 pos += 2;
1913 strncpy(newspell->message, (char*)data+pos, mlen);
1914 pos += mlen;
1915 newspell->message[mlen] = '\0'; /* To ensure we are null terminated */
1916 rstrip(newspell->message, mlen);
1917
1918 if (spellmon_level < 2) {
1919
1920 /* The server is not sending spellmon 2 extended information, so
1921 * initialize the spell data fields as unused/empty.
1922 */
1923 newspell->usage = 0;
1924 newspell->requirements[0] = '\0';
1925
1926 } else if (pos < len) {
1927
1928 /* The server is sending extended spell information (spellmon 2) so
1929 * process it.
1930 */
1931 newspell->usage = GetChar_String(data+pos);
1932 pos += 1;
1933 nlen = GetChar_String(data+pos);
1934 pos += 1;
1935 strncpy(newspell->requirements, (char*) data+pos, nlen);
1936 pos += nlen;
1937 newspell->requirements[nlen] = '\0'; /* Ensure null-termination */
1938 }
1939
1940 /* Compute the derived spell information.
1941 */
1942 newspell->skill = skill_names[newspell->skill_number-CS_STAT_SKILLINFO];
1943
1944 /* Add the spell to the player struct.
1945 */
1946 if (!cpl.spelldata) {
1947 cpl.spelldata = newspell;
1948 } else {
1949 for (tmp = cpl.spelldata; tmp->next; tmp = tmp->next)
1950 ;
1951 tmp->next = newspell;
1952 }
1953 /* Check to see if there are more spells to add.
1954 */
1955 }
1956 if (pos > len) {
1957 LOG(LOG_WARNING, "common::AddspellCmd", "Overread buffer: %d > %d", pos, len);
1958 }
1959 cpl.spells_updated = 1;
1960 }
1961
UpdspellCmd(unsigned char * data,int len)1962 void UpdspellCmd(unsigned char *data, int len) {
1963 int flags, pos = 0;
1964 guint32 tag;
1965 Spell *tmp;
1966
1967 if (!cpl.spelldata) {
1968 LOG(LOG_WARNING, "common::UpdspellCmd", "I know no spells to update");
1969 return;
1970 }
1971
1972 flags = GetChar_String(data+pos);
1973 pos += 1;
1974 tag = GetInt_String(data+pos);
1975 pos += 4;
1976 for (tmp = cpl.spelldata; tmp && tmp->tag != tag; tmp = tmp->next)
1977 ;
1978 if (!tmp) {
1979 LOG(LOG_WARNING, "common::UpdspellCmd", "Invalid tag: %d", tag);
1980 return;
1981 }
1982 if (flags&UPD_SP_MANA) {
1983 tmp->sp = GetShort_String(data+pos);
1984 pos += 2;
1985 }
1986 if (flags&UPD_SP_GRACE) {
1987 tmp->grace = GetShort_String(data+pos);
1988 pos += 2;
1989 }
1990 if (flags&UPD_SP_DAMAGE) {
1991 tmp->dam = GetShort_String(data+pos);
1992 pos += 2;
1993 }
1994 if (pos > len) {
1995 LOG(LOG_WARNING, "common::UpdspellCmd", "Overread buffer: %d > %d", pos, len);
1996 }
1997 cpl.spells_updated = 1;
1998 }
1999
DeleteSpell(unsigned char * data,int len)2000 void DeleteSpell(unsigned char *data, int len) {
2001 guint32 tag;
2002 Spell *tmp, *target;
2003
2004 if (!cpl.spelldata) {
2005 LOG(LOG_WARNING, "common::DeleteSpell", "I know no spells to delete");
2006 return;
2007 }
2008
2009 tag = GetInt_String(data);
2010 /* Special case: the first spell is the one removed */
2011 if (cpl.spelldata->tag == tag) {
2012 target = cpl.spelldata;
2013 if (target->next) {
2014 cpl.spelldata = target->next;
2015 } else {
2016 cpl.spelldata = NULL;
2017 }
2018 free(target);
2019 return;
2020 }
2021
2022 for (tmp = cpl.spelldata; tmp->next && tmp->next->tag != tag; tmp = tmp->next)
2023 ;
2024 if (!tmp->next) {
2025 LOG(LOG_WARNING, "common::DeleteSpell", "Invalid tag: %d", tag);
2026 return;
2027 }
2028 target = tmp->next;
2029 if (target->next) {
2030 tmp->next = target->next;
2031 } else {
2032 tmp->next = NULL;
2033 }
2034 free(target);
2035 cpl.spells_updated = 1;
2036 }
2037
2038 /****************************************************************************/
2039
2040 /**
2041 * @} */ /* EndOf SCSpellCommands
2042 */
2043
2044 /**
2045 * @defgroup SCMapCommands Server->Client map command functions.
2046 * @{
2047 */
2048
2049 /**
2050 *
2051 * @param data
2052 * @param len
2053 */
NewmapCmd(unsigned char * data,int len)2054 void NewmapCmd(unsigned char *data, int len)
2055 {
2056 (void)data; /* __UNUSED__ */
2057 (void)len; /* __UNUSED__ */
2058
2059 mapdata_newmap();
2060 }
2061
2062 /* This is the common processing block for the map1 and map1a protocol
2063 * commands. The map1a mieks minor extensions and are easy to deal with
2064 * inline (in fact, this code doesn't even care what rev is - just certain
2065 * bits will only bet set when using the map1a command. rev is 0 for map1, 1
2066 * for map1a. It conceivable that there could be future revisions.
2067 */
2068
2069 /* NUM_LAYERS should only be used for the map1{a} which only has a few layers.
2070 * Map2 has 10 layers. However, some of the map1 logic requires this to be
2071 * set right.
2072 */
2073 #define NUM_LAYERS (MAP1_LAYERS-1)
2074
2075 /**
2076 *
2077 * @param data
2078 * @param len
2079 */
Map2Cmd(unsigned char * data,int len)2080 void Map2Cmd(unsigned char *data, int len)
2081 {
2082 int mask, x, y, pos = 0, space_len, value;
2083 guint8 type;
2084
2085 /* Not really using map1 protocol, but some draw logic differs from the
2086 * original draw logic, and map2 is closest.
2087 */
2088 while (pos < len) {
2089 mask = GetShort_String(data+pos);
2090 pos += 2;
2091 x = ((mask>>10)&0x3f)-MAP2_COORD_OFFSET;
2092 y = ((mask>>4)&0x3f)-MAP2_COORD_OFFSET;
2093
2094 /* This is a scroll then. Go back and fetch another coordinate */
2095 if (mask&0x1) {
2096 mapdata_scroll(x, y);
2097 continue;
2098 }
2099
2100 if (x<0) {
2101 LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative x!");
2102 x = 0;
2103 } else if (x >= MAX_VIEW) {
2104 LOG(LOG_WARNING, "commands.c::Map2Cmd", "got x >= MAX_VIEW!");
2105 x = MAX_VIEW - 1;
2106 }
2107
2108 if (y<0) {
2109 LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative y!");
2110 y = 0;
2111 } else if (y >= MAX_VIEW) {
2112 LOG(LOG_WARNING, "commands.c::Map2Cmd", "got y >= MAX_VIEW!");
2113 y = MAX_VIEW - 1;
2114 }
2115
2116 assert(0 <= x && x < MAX_VIEW);
2117 assert(0 <= y && y < MAX_VIEW);
2118 /* Clearing old cell data as needed (was in mapdata_set_face_layer()
2119 * before however that caused darkness to only work if sent after the
2120 * layers).
2121 */
2122 mapdata_clear_old(x, y);
2123
2124 /* Inner loop is for the data on the space itself */
2125 while (pos < len) {
2126 type = data[pos++];
2127 /* type == 255 means nothing more for this space */
2128 if (type == 255) {
2129 mapdata_set_check_space(x, y);
2130 break;
2131 }
2132 space_len = type>>5;
2133 type &= 0x1f;
2134 /* Clear the space */
2135 if (type == MAP2_TYPE_CLEAR) {
2136 mapdata_clear_space(x, y);
2137 continue;
2138 } else if (type == MAP2_TYPE_DARKNESS) {
2139 value = data[pos++];
2140 mapdata_set_darkness(x, y, value);
2141 continue;
2142 } else if (type >= MAP2_LAYER_START && type < MAP2_LAYER_START+MAXLAYERS) {
2143 int layer, opt;
2144
2145 /* This is face information for a layer. */
2146 layer = type&0xf;
2147
2148 if (layer < 0) {
2149 LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative layer!");
2150 layer = 0;
2151 } else if (layer >= MAXLAYERS) {
2152 LOG(LOG_WARNING, "commands.c::Map2Cmd", "got layer >= MAXLAYERS!");
2153 layer = MAXLAYERS - 1;
2154 }
2155 assert(0 <= layer && layer < MAXLAYERS);
2156
2157 /* This is the face */
2158 value = GetShort_String(data+pos);
2159 pos += 2;
2160 if (!(value&FACE_IS_ANIM)) {
2161 mapdata_set_face_layer(x, y, value, layer);
2162 }
2163
2164 if (space_len > 2) {
2165 opt = data[pos++];
2166 if (value&FACE_IS_ANIM) {
2167 /* Animation speed */
2168 mapdata_set_anim_layer(x, y, value, opt, layer);
2169 } else {
2170 /* Smooth info */
2171 mapdata_set_smooth(x, y, opt, layer);
2172 }
2173 }
2174 /* Currently, if 4 bytes, must be a smooth byte */
2175 if (space_len > 3) {
2176 opt = data[pos++];
2177 mapdata_set_smooth(x, y, opt, layer);
2178 }
2179 continue;
2180 } /* if image layer */
2181 } /* while pos<len inner loop for space */
2182 } /* While pos<len outer loop */
2183 mapupdatesent = 0;
2184 display_map_doneupdate(FALSE, FALSE);
2185 }
2186
2187 /**
2188 * Scrolls the map on the client by some amount.
2189 *
2190 * @param data
2191 * @param len
2192 */
map_scrollCmd(char * data,int len)2193 void map_scrollCmd(char *data, int len)
2194 {
2195 int dx, dy;
2196 char *buf;
2197
2198 (void)len; /* __UNUSED__ */
2199
2200 dx = atoi(data);
2201 buf = strchr(data, ' ');
2202 if (!buf) {
2203 LOG(LOG_WARNING, "common::map_scrollCmd", "Got short packet.");
2204 return;
2205 }
2206 buf++;
2207 dy = atoi(buf);
2208
2209 mapdata_scroll(dx, dy);
2210 display_map_doneupdate(FALSE, TRUE);
2211 }
2212
2213 /**
2214 * Extract smoothing infos from an extendedmapinfo packet part data is located
2215 * at the beginning of the smooth datas
2216 *
2217 * @param data
2218 * @param len
2219 * @param x
2220 * @param y
2221 * @param layer
2222 */
ExtSmooth(unsigned char * data,int len,int x,int y,int layer)2223 int ExtSmooth(unsigned char *data, int len, int x, int y, int layer)
2224 {
2225 static int dx[8] = { 0, 1, 1, 1, 0, -1, -1, -1, };
2226 static int dy[8] = { -1, -1, 0, 1, 1, 1, 0, -1, };
2227 int i, rx, ry;
2228 int newsm;
2229
2230 if (len < 1) {
2231 return 0;
2232 }
2233
2234 x += pl_pos.x;
2235 y += pl_pos.y;
2236 newsm = GetChar_String(data);
2237
2238 if (mapdata_cell(x, y)->smooth[layer] != newsm) {
2239 for (i = 0; i < 8; i++) {
2240 rx = x+dx[i];
2241 ry = y+dy[i];
2242 if (!mapdata_contains(rx, ry)) {
2243 continue;
2244 }
2245 mapdata_cell(x, y)->need_resmooth = 1;
2246 }
2247 }
2248 mapdata_cell(x, y)->smooth[layer] = newsm;
2249 return 1;/*Cause smooth infos only use 1 byte*/
2250 }
2251
2252 /**
2253 * Handle MapExtended command
2254 * Warning! if you add commands to extended, take care that the 'layer'
2255 * argument of main loop is the opposite of the layer of the map so if you
2256 * reference a layer, use NUM_LAYERS-layer.
2257 *
2258 * @param data
2259 * @param len
2260 */
MapExtendedCmd(unsigned char * data,int len)2261 void MapExtendedCmd(unsigned char *data, int len)
2262 {
2263 int mask, x, y, pos = 0, layer;
2264 int noredraw = 0;
2265 int hassmooth = 0;
2266 int entrysize;
2267 int startpackentry;
2268
2269 mapupdatesent = 1;
2270 mask = GetChar_String(data+pos);
2271 pos += 1;
2272 if (mask&EMI_NOREDRAW) {
2273 noredraw = 1;
2274 }
2275 if (mask&EMI_SMOOTH) {
2276 hassmooth = 1;
2277 }
2278 while (mask&EMI_HASMOREBITS) {
2279 /*There may be bits we ignore about*/
2280 mask = GetChar_String(data+pos);
2281 pos += 1;
2282 }
2283 entrysize = GetChar_String(data+pos);
2284 pos = pos+1;
2285
2286 while (pos+entrysize+2 <= len) {
2287 mask = GetShort_String(data+pos);
2288 pos += 2;
2289 x = (mask>>10)&0x3f;
2290 y = (mask>>4)&0x3f;
2291 for (layer = NUM_LAYERS; layer >= 0; layer--) {
2292 if (mask&(1<<layer)) {
2293 /*handle an entry*/
2294 if (pos+entrysize > len) { /*erroneous packet*/
2295 break;
2296 }
2297 startpackentry = pos;
2298 /* If you had extended infos to the server, this is where, in
2299 * the client, you may add your code
2300 */
2301 if (hassmooth) {
2302 pos = pos+ExtSmooth(data+pos, len-pos, x, y, NUM_LAYERS-layer);
2303 }
2304 /* Continue with other if you add new extended infos to server
2305 *
2306 * Now point to the next data
2307 */
2308 pos = startpackentry+entrysize;
2309 }
2310 }
2311 }
2312 if (!noredraw) {
2313 display_map_doneupdate(FALSE, FALSE);
2314 mapupdatesent = 0;
2315 }
2316 }
2317
2318 /**
2319 *
2320 * @param data
2321 * @param len
2322 */
MagicMapCmd(unsigned char * data,int len)2323 void MagicMapCmd(unsigned char *data, int len)
2324 {
2325 unsigned char *cp;
2326 int i;
2327
2328 /* First, extract the size/position information. */
2329 if (sscanf((const char*)data, "%hd %hd %hd %hd", &cpl.mmapx, &cpl.mmapy, &cpl.pmapx, &cpl.pmapy) != 4) {
2330 LOG(LOG_WARNING, "common::MagicMapCmd", "Was not able to properly extract magic map size, pos");
2331 return;
2332 }
2333
2334 if (cpl.mmapx == 0 || cpl.mmapy == 0) {
2335 LOG(LOG_WARNING, "common::MagicMapCmd", "empty map");
2336 return;
2337 }
2338
2339 /* Now we need to find the start of the actual data. There are 4 space
2340 * characters we need to skip over.
2341 */
2342 for (cp = data, i = 0; i < 4 && cp < data+len; cp++) {
2343 if (*cp == ' ') {
2344 i++;
2345 }
2346 }
2347 if (i != 4) {
2348 LOG(LOG_WARNING, "common::MagicMapCmd", "Was unable to find start of magic map data");
2349 return;
2350 }
2351 i = len-(cp-data); /* This should be the number of bytes left */
2352 if (i != cpl.mmapx*cpl.mmapy) {
2353 LOG(LOG_WARNING, "common::MagicMapCmd", "Magic map size mismatch. Have %d bytes, should have %d",
2354 i, cpl.mmapx*cpl.mmapy);
2355 return;
2356 }
2357 free(cpl.magicmap);
2358 cpl.magicmap = g_malloc(cpl.mmapx*cpl.mmapy);
2359 /* Order the server puts it in should be just fine. Note that the only
2360 * requirement that this works is that magicmap by 8 bits, being that is
2361 * the size specified in the protocol and what the server sends us.
2362 */
2363 memcpy(cpl.magicmap, cp, cpl.mmapx*cpl.mmapy);
2364 draw_magic_map();
2365 }
2366
2367 /**
2368 * @} */ /* EndOf SCMapCommands
2369 */
2370
2371 /**
2372 *
2373 * @param data
2374 * @param len
2375 */
SinkCmd(unsigned char * data,int len)2376 void SinkCmd(unsigned char *data, int len)
2377 {
2378 }
2379
2380 /**
2381 * Got a tick from the server. We currently don't care what tick number it
2382 * is, but just have the code in case at some time we do.
2383 *
2384 * @param data
2385 * @param len
2386 */
TickCmd(guint8 * data,int len)2387 void TickCmd(guint8 *data, int len)
2388 {
2389 /* Up to the specific client to decide what to do */
2390 client_tick(GetInt_String(data));
2391 }
2392
2393 /**
2394 * Server gives us current player's pickup.
2395 *
2396 * @param data
2397 * buffer sent by server.
2398 * @param len
2399 * length of data.
2400 */
PickupCmd(guint8 * data,int len)2401 void PickupCmd(guint8 *data, int len)
2402 {
2403 guint32 pickup = GetInt_String(data);
2404 client_pickup(pickup);
2405 }
2406
2407 /**
2408 * Handles a failure return from the server.
2409 *
2410 * @param buf
2411 * buffer sent by server.
2412 * @param len
2413 * length of data.
2414 */
FailureCmd(char * buf,int len)2415 void FailureCmd(char *buf, int len)
2416 {
2417 char *cp;
2418
2419 /* The format of the buffer is 'command error message'. We need to
2420 * extract the failed command, and then pass in the error message to the
2421 * appropriate handler. So find the space, set it to null. in that way,
2422 * buf is now just the failure command, and cp is the message.
2423 */
2424 cp = strchr(buf,' ');
2425 if (!cp) {
2426 return;
2427 }
2428
2429 *cp = 0;
2430 cp++;
2431
2432 if (!strcmp(buf,"accountlogin")) {
2433 account_login_failure(cp);
2434 } else if (!strcmp(buf,"accountnew")) {
2435 account_creation_failure(cp);
2436 } else if (!strcmp(buf,"accountaddplayer")) {
2437 account_add_character_failure(cp);
2438 } else if (!strcmp(buf,"createplayer")) {
2439 create_new_character_failure(cp);
2440 } else if (!strcmp(buf, "accountpw")) {
2441 account_change_password_failure(cp);
2442 } else if (!strcmp(buf, "accountplay")) {
2443 // This creates a dialog that says the failure message.
2444 // It should suffice for what we want here anyway.
2445 create_new_character_failure(cp);
2446 } else
2447 /* This really is an error - if this happens it menas the server
2448 * failed to process a request that the client made - the client
2449 * should be able to handle failures for all request types it makes.
2450 * But this is also a problem in that it means that the server is
2451 * waiting for a correct response, and if we do not display anything,
2452 * the player is unlikely to know this.
2453 */
2454 LOG(LOG_ERROR, "common::FailureCmd", "Got a failure response we can not handle: %s:%s",
2455 buf, cp);
2456 }
2457
2458 /**
2459 * This handles the accountplayers command
2460 */
AccountPlayersCmd(char * buf,int len)2461 void AccountPlayersCmd(char *buf, int len) {
2462 int level, pos, faceno;
2463 guint8 flen;
2464 char name[MAX_BUF], class[MAX_BUF], race[MAX_BUF],
2465 face[MAX_BUF], party[MAX_BUF], map[MAX_BUF];
2466
2467 /* This is called first so it can clear out the existing data store.
2468 */
2469 choose_character_init();
2470
2471 level=0;
2472 name[0]=0;
2473 class[0]=0;
2474 race[0]=0;
2475 face[0]=0;
2476 party[0]=0;
2477 map[0]=0;
2478 faceno=0;
2479
2480 pos=1;
2481 while (pos < len) {
2482 flen = buf[pos];
2483 /* flen == 0 is to note that we got end of character data */
2484 if (flen == 0) {
2485 update_character_choose(name, class, race, face, party, map, level, faceno);
2486 /* Blank all the values - it is no sure thing that the next
2487 * character will fill all these in.
2488 */
2489 level=0;
2490 name[0]=0;
2491 class[0]=0;
2492 race[0]=0;
2493 face[0]=0;
2494 party[0]=0;
2495 map[0]=0;
2496 faceno=0;
2497 pos++;
2498 continue;
2499 }
2500 pos++;
2501 if ((pos +flen) > len || flen>=MAX_BUF) {
2502 LOG(LOG_ERROR,"commands.c:AccountPlayerCmd", "data overran buffer");
2503 return;
2504 }
2505 switch (buf[pos]) {
2506 case ACL_NAME:
2507 strncpy(name, buf + pos +1, flen-1);
2508 name[flen-1] = 0;
2509 break;
2510
2511 case ACL_CLASS:
2512 strncpy(class, buf + pos +1, flen-1);
2513 class[flen-1] = 0;
2514 break;
2515
2516 case ACL_RACE:
2517 strncpy(race, buf + pos +1, flen-1);
2518 race[flen-1] = 0;
2519 break;
2520
2521 case ACL_FACE:
2522 strncpy(face, buf + pos +1, flen-1);
2523 face[flen-1] = 0;
2524 break;
2525
2526 case ACL_PARTY:
2527 strncpy(party, buf + pos +1, flen-1);
2528 party[flen-1] = 0;
2529 break;
2530
2531 case ACL_MAP:
2532 strncpy(map, buf + pos +1, flen-1);
2533 map[flen-1] = 0;
2534 break;
2535
2536 case ACL_LEVEL:
2537 level = GetShort_String((unsigned char *)buf + pos + 1);
2538 break;
2539 case ACL_FACE_NUM:
2540 faceno = GetShort_String((unsigned char *)buf + pos + 1);
2541 break;
2542 }
2543 pos += flen;
2544 }
2545 }
2546
2547