ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/request.C
Revision: 1.112
Committed: Sun Jul 1 05:00:20 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.111: +11 -12 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5 * Copyright (©) 2001,2007 Mark Wedel
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Crossfire TRT is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 */
23
24 /**
25 * \file
26 * Client handling.
27 *
28 * \date 2003-12-02
29 *
30 * This file implements all of the goo on the server side for handling
31 * clients. It's got a bunch of global variables for keeping track of
32 * each of the clients.
33 *
34 * Note: All functions that are used to process data from the client
35 * have the prototype of (char *data, int datalen, int client_num). This
36 * way, we can use one dispatch table.
37 *
38 * esrv_map_new starts updating the map
39 *
40 */
41
42 #include <global.h>
43 #include <sproto.h>
44
45 #include <living.h>
46 #include <commands.h>
47
48 /* This block is basically taken from socket.c - I assume if it works there,
49 * it should work here.
50 */
51 #include <sys/types.h>
52 #include <sys/time.h>
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <netdb.h>
56
57 #include <unistd.h>
58 #include <sys/time.h>
59
60 #include "sounds.h"
61
62 /**
63 * This table translates the attack numbers as used within the
64 * program to the value we use when sending STATS command to the
65 * client. If a value is -1, then we don't send that to the
66 * client.
67 */
68 short atnr_cs_stat[NROFATTACKS] = { CS_STAT_RES_PHYS,
69 CS_STAT_RES_MAG, CS_STAT_RES_FIRE, CS_STAT_RES_ELEC,
70 CS_STAT_RES_COLD, CS_STAT_RES_CONF, CS_STAT_RES_ACID,
71 CS_STAT_RES_DRAIN, -1 /* weaponmagic */ ,
72 CS_STAT_RES_GHOSTHIT, CS_STAT_RES_POISON,
73 CS_STAT_RES_SLOW, CS_STAT_RES_PARA, CS_STAT_TURN_UNDEAD,
74 CS_STAT_RES_FEAR, -1 /* Cancellation */ ,
75 CS_STAT_RES_DEPLETE, CS_STAT_RES_DEATH,
76 -1 /* Chaos */ , -1 /* Counterspell */ ,
77 -1 /* Godpower */ , CS_STAT_RES_HOLYWORD,
78 CS_STAT_RES_BLIND,
79 -1, /* Internal */
80 -1, /* life stealing */
81 -1 /* Disease - not fully done yet */
82 };
83
84 static void
85 socket_map_scroll (client *ns, int dx, int dy)
86 {
87 struct Map newmap;
88 int x, y, mx, my;
89
90 ns->send_packet_printf ("map_scroll %d %d", dx, dy);
91
92 /* If we are using the Map1aCmd, we may in fact send
93 * head information that is outside the viewable map.
94 * So set the mx,my to the max value we want to
95 * look for. Removed code to do so - it caused extra
96 * complexities for the client, and probably doesn't make
97 * that much difference in bandwidth.
98 */
99 mx = ns->mapx;
100 my = ns->mapy;
101
102 /* the x and y here are coordinates for the new map, i.e. if we moved
103 * (dx,dy), newmap[x][y] = oldmap[x-dx][y-dy]. For this reason,
104 * if the destination x or y coordinate is outside the viewable
105 * area, we clear the values - otherwise, the old values
106 * are preserved, and the check_head thinks it needs to clear them.
107 */
108 for (x = 0; x < mx; x++)
109 {
110 for (y = 0; y < my; y++)
111 {
112 if (x >= ns->mapx || y >= ns->mapy)
113 /* clear cells outside the viewable area */
114 memset (&newmap.cells[x][y], 0, sizeof (struct MapCell));
115 else if ((x + dx) < 0 || (x + dx) >= ns->mapx || (y + dy) < 0 || (y + dy) >= ns->mapy)
116 /* clear newly visible tiles within the viewable area */
117 memset (&(newmap.cells[x][y]), 0, sizeof (struct MapCell));
118 else
119 memcpy (&(newmap.cells[x][y]), &(ns->lastmap.cells[x + dx][y + dy]), sizeof (struct MapCell));
120 }
121 }
122
123 memcpy (&(ns->lastmap), &newmap, sizeof (struct Map));
124
125 /* Make sure that the next "map1" command will be sent (even if it is
126 * empty).
127 */
128 ns->sent_scroll = 1;
129 }
130
131 static void
132 clear_map (player *pl)
133 {
134 memset (&pl->ns->lastmap, 0, sizeof (pl->ns->lastmap));
135
136 pl->ns->force_newmap = false;
137
138 if (pl->ns->newmapcmd == 1)
139 pl->ns->send_packet ("newmap");
140
141 pl->ns->floorbox_reset ();
142 }
143
144 /** check for map/region change and send new map data */
145 static void
146 check_map_change (player *pl)
147 {
148 client &socket = *pl->ns;
149 object *ob = pl->observe;
150
151 if (socket.current_map != ob->map || socket.force_newmap)
152 {
153 clear_map (pl);
154 socket.current_map = ob->map;
155
156 if (socket.mapinfocmd)
157 {
158 if (ob->map && ob->map->path[0])
159 {
160 int flags = 0;
161
162 if (ob->map->tile_path[0]) flags |= 1;
163 if (ob->map->tile_path[1]) flags |= 2;
164 if (ob->map->tile_path[2]) flags |= 4;
165 if (ob->map->tile_path[3]) flags |= 8;
166
167 socket.send_packet_printf ("mapinfo - spatial %d %d %d %d %d %s",
168 flags, socket.mapx / 2 - ob->x, socket.mapy / 2 - ob->y,
169 ob->map->width, ob->map->height, &ob->map->path);
170 }
171 else
172 socket.send_packet ("mapinfo current");
173 }
174 }
175 else if (socket.current_x != ob->x || socket.current_y != ob->y)
176 {
177 int dx = ob->x - socket.current_x;
178 int dy = ob->y - socket.current_y;
179
180 if (socket.buggy_mapscroll && (abs (dx) > 8 || abs (dy) > 8))
181 clear_map (pl); // current (<= 1.9.1) clients have unchecked buffer overflows
182 else
183 {
184 socket_map_scroll (&socket, ob->x - socket.current_x, ob->y - socket.current_y);
185 socket.floorbox_reset ();
186 }
187 }
188
189 socket.current_x = ob->x;
190 socket.current_y = ob->y;
191
192 region *reg = ob->region ();
193 if (socket.current_region != reg)
194 {
195 socket.current_region = reg;
196 socket.send_packet_printf ("drawinfo 0 You are now %s.\n(use whereami for more details)", &reg->longname);
197 }
198 }
199
200 /**
201 * RequestInfo is sort of a meta command. There is some specific
202 * request of information, but we call other functions to provide
203 * that information.
204 */
205 void
206 RequestInfo (char *buf, int len, client * ns)
207 {
208 char *params = NULL, *cp;
209
210 /* No match */
211 char bigbuf[MAX_BUF];
212 int slen;
213
214 /* Set up replyinfo before we modify any of the buffers - this is used
215 * if we don't find a match.
216 */
217 strcpy (bigbuf, "replyinfo ");
218 slen = strlen (bigbuf);
219 safe_strcat (bigbuf, buf, &slen, MAX_BUF);
220
221 /* find the first space, make it null, and update the
222 * params pointer.
223 */
224 for (cp = buf; *cp != '\0'; cp++)
225 if (*cp == ' ')
226 {
227 *cp = '\0';
228 params = cp + 1;
229 break;
230 }
231
232 if (!strcmp (buf, "image_info"))
233 send_image_info (ns, params);
234 else if (!strcmp (buf, "image_sums"))
235 send_image_sums (ns, params);
236 else if (!strcmp (buf, "skill_info"))
237 send_skill_info (ns, params);
238 else if (!strcmp (buf, "spell_paths"))
239 send_spell_paths (ns, params);
240 else
241 ns->send_packet (bigbuf, len);
242 }
243
244 void
245 ExtCmd (char *buf, int len, player *pl)
246 {
247 INVOKE_PLAYER (EXTCMD, pl, ARG_DATA (buf, len));
248 }
249
250 void
251 ExtiCmd (char *buf, int len, client *ns)
252 {
253 INVOKE_CLIENT (EXTICMD, ns, ARG_DATA (buf, len));
254 }
255
256 void
257 MapInfoCmd (char *buf, int len, player *pl)
258 {
259 // <mapinfo tag spatial tile-path
260 // >mapinfo tag spatial flags x y w h hash
261
262 char bigbuf[MAX_BUF], *token;
263
264 token = buf;
265 // copy token
266 if (!(buf = strchr (buf, ' ')))
267 return;
268
269 *buf++ = 0;
270
271 if (!strncmp (buf, "spatial ", 8))
272 {
273 buf += 8;
274
275 // initial map and its origin
276 maptile *map = pl->ob->map;
277 sint16 dx, dy;
278 int mapx = pl->ns->mapx / 2 - pl->ob->x;
279 int mapy = pl->ns->mapy / 2 - pl->ob->y;
280 int max_distance = 8; // limit maximum path length to something generous
281
282 while (*buf && map && max_distance)
283 {
284 int dir = *buf++;
285
286 switch (dir)
287 {
288 case '1':
289 dx = 0;
290 dy = -1;
291 map = map->xy_find (dx, dy);
292 map && (mapy -= map->height);
293 break;
294 case '2':
295 mapx += map->width;
296 dx = map->width;
297 dy = 0;
298 map = map->xy_find (dx, dy);
299 break;
300 case '3':
301 mapy += map->height;
302 dx = 0;
303 dy = map->height;
304 map = map->xy_find (dx, dy);
305 break;
306 case '4':
307 dx = -1;
308 dy = 0;
309 map = map->xy_find (dx, dy);
310 map && (mapx -= map->width);
311 break;
312 }
313
314 --max_distance;
315 }
316
317 if (!max_distance)
318 snprintf (bigbuf, MAX_BUF, "mapinfo %s error", token);
319 else if (map && map->path[0])
320 {
321 int flags = 0;
322
323 if (map->tile_path[0]) flags |= 1;
324 if (map->tile_path[1]) flags |= 2;
325 if (map->tile_path[2]) flags |= 4;
326 if (map->tile_path[3]) flags |= 8;
327
328 snprintf (bigbuf, MAX_BUF, "mapinfo %s spatial %d %d %d %d %d %s", token, flags, mapx, mapy, map->width, map->height, &map->path);
329 }
330 else
331 snprintf (bigbuf, MAX_BUF, "mapinfo %s nomap", token);
332 }
333 else
334 snprintf (bigbuf, MAX_BUF, "mapinfo %s unsupported", token);
335
336 pl->ns->send_packet (bigbuf);
337 }
338
339 /** This is the Setup cmd */
340 void
341 SetUp (char *buf, int len, client * ns)
342 {
343 INVOKE_CLIENT (SETUP, ns, ARG_DATA (buf, len));
344 }
345
346 /**
347 * The client has requested to be added to the game.
348 * This is what takes care of it. We tell the client how things worked out.
349 * I am not sure if this file is the best place for this function. however,
350 * it either has to be here or init_sockets needs to be exported.
351 */
352 void
353 AddMeCmd (char *buf, int len, client *ns)
354 {
355 INVOKE_CLIENT (ADDME, ns, ARG_DATA (buf, len));
356 }
357
358 /** Reply to ExtendedInfos command */
359 void
360 ToggleExtendedInfos (char *buf, int len, client * ns)
361 {
362 char cmdback[MAX_BUF];
363 char command[50];
364 int info, nextinfo;
365
366 cmdback[0] = '\0';
367 nextinfo = 0;
368
369 while (1)
370 {
371 /* 1. Extract an info */
372 info = nextinfo;
373
374 while ((info < len) && (buf[info] == ' '))
375 info++;
376
377 if (info >= len)
378 break;
379
380 nextinfo = info + 1;
381
382 while ((nextinfo < len) && (buf[nextinfo] != ' '))
383 nextinfo++;
384
385 if (nextinfo - info >= 49) /*Erroneous info asked */
386 continue;
387
388 strncpy (command, &(buf[info]), nextinfo - info);
389
390 /* 2. Interpret info */
391 if (!strcmp ("smooth", command))
392 /* Toggle smoothing */
393 ns->EMI_smooth = !ns->EMI_smooth;
394 else
395 /*bad value */;
396
397 /*3. Next info */
398 }
399
400 strcpy (cmdback, "ExtendedInfoSet");
401
402 if (ns->EMI_smooth)
403 {
404 strcat (cmdback, " ");
405 strcat (cmdback, "smoothing");
406 }
407
408 ns->send_packet (cmdback);
409 }
410
411 /*
412 #define MSG_TYPE_BOOK 1
413 #define MSG_TYPE_CARD 2
414 #define MSG_TYPE_PAPER 3
415 #define MSG_TYPE_SIGN 4
416 #define MSG_TYPE_MONUMENT 5
417 #define MSG_TYPE_SCRIPTED_DIALOG 6*/
418
419 /** Reply to ExtendedInfos command */
420 void
421 ToggleExtendedText (char *buf, int len, client * ns)
422 {
423 char cmdback[MAX_BUF];
424 char temp[10];
425 char command[50];
426 int info, nextinfo, i, flag;
427
428 cmdback[0] = '\0';
429
430 nextinfo = 0;
431 while (1)
432 {
433 /* 1. Extract an info */
434 info = nextinfo;
435
436 while ((info < len) && (buf[info] == ' '))
437 info++;
438
439 if (info >= len)
440 break;
441
442 nextinfo = info + 1;
443
444 while ((nextinfo < len) && (buf[nextinfo] != ' '))
445 nextinfo++;
446
447 if (nextinfo - info >= 49) /*Erroneous info asked */
448 continue;
449
450 strncpy (command, &(buf[info]), nextinfo - info);
451 command[nextinfo - info] = '\0';
452 /* 2. Interpret info */
453 i = sscanf (command, "%d", &flag);
454
455 if ((i == 1) && (flag > 0) && (flag <= MSG_TYPE_LAST))
456 ns->supported_readables |= (1 << flag);
457 /*3. Next info */
458 }
459
460 /* Send resulting state */
461 strcpy (cmdback, "ExtendedTextSet");
462
463 for (i = 0; i <= MSG_TYPE_LAST; i++)
464 if (ns->supported_readables & (1 << i))
465 {
466 strcat (cmdback, " ");
467 snprintf (temp, sizeof (temp), "%d", i);
468 strcat (cmdback, temp);
469 }
470
471 ns->send_packet (cmdback);
472 }
473
474 /**
475 * This handles the general commands from the client (ie, north, fire, cast,
476 * etc.)
477 */
478 void
479 PlayerCmd (char *buf, int len, player *pl)
480 {
481 /* Check if there is a count. In theory, a zero count could also be
482 * sent, so check for that also.
483 */
484 if (atoi (buf) || buf[0] == '0')
485 {
486 pl->count = atoi ((char *) buf);
487
488 buf = strchr (buf, ' '); /* advance beyond the numbers */
489 if (!buf)
490 return;
491
492 buf++;
493 }
494
495 execute_newserver_command (pl->ob, (char *) buf);
496
497 /* Perhaps something better should be done with a left over count.
498 * Cleaning up the input should probably be done first - all actions
499 * for the command that issued the count should be done before any other
500 * commands.
501 */
502 pl->count = 0;
503 }
504
505 /**
506 * This handles the general commands from the client (ie, north, fire, cast,
507 * etc.). It is a lot like PlayerCmd above, but is called with the
508 * 'ncom' method which gives more information back to the client so it
509 * can throttle.
510 */
511 void
512 NewPlayerCmd (char *buf, int len, player *pl)
513 {
514 if (len <= 6)
515 {
516 LOG (llevDebug, "Corrupt ncom command <%s> not long enough - discarding\n", buf);
517 return;
518 }
519
520 uint16 cmdid = net_uint16 ((uint8 *)buf);
521 sint32 repeat = net_sint32 ((uint8 *)buf + 2);
522
523 /* -1 is special - no repeat, but don't update */
524 if (repeat != -1)
525 pl->count = repeat;
526
527 buf += 6; //len -= 6;
528
529 execute_newserver_command (pl->ob, buf);
530
531 /* Perhaps something better should be done with a left over count.
532 * Cleaning up the input should probably be done first - all actions
533 * for the command that issued the count should be done before any other
534 * commands.
535 */
536 pl->count = 0;
537
538 //TODO: schmorp thinks whatever this calculates, it makes no sense at all
539 int time = fabs (pl->ob->speed) < 0.001
540 ? time = MAX_TIME * 100
541 : time = (int) (MAX_TIME / fabs (pl->ob->speed));
542
543 /* Send confirmation of command execution now */
544 packet sl ("comc");
545 sl << uint16 (cmdid) << uint32 (time);
546 pl->ns->send_packet (sl);
547 }
548
549 /** This is a reply to a previous query. */
550 void
551 ReplyCmd (char *buf, int len, client *ns)
552 {
553 if (ns->state == ST_CUSTOM)
554 {
555 INVOKE_CLIENT (REPLY, ns, ARG_DATA (buf, len));
556 return;
557 }
558
559 if (!ns->pl)
560 return; //TODO: depends on the exact reply we are after
561 //TODO: but right now, we always have a ns->pl
562
563 player *pl = ns->pl;
564
565 /* This is to synthesize how the data would be stored if it
566 * was normally entered. A bit of a hack, and should be cleaned up
567 * once all the X11 code is removed from the server.
568 *
569 * We pass 13 to many of the functions because this way they
570 * think it was the carriage return that was entered, and the
571 * function then does not try to do additional input.
572 */
573 snprintf (pl->write_buf, sizeof (pl->write_buf), ":%s", buf);
574
575 /* this avoids any hacking here */
576
577 switch (ns->state)
578 {
579 case ST_PLAYING:
580 LOG (llevError, "Got reply message with ST_PLAYING input state\n");
581 break;
582
583 case ST_GET_PARTY_PASSWORD: /* Get password for party */
584 receive_party_password (pl->ob, 13);
585 break;
586
587 default:
588 LOG (llevError, "Unknown input state: %d\n", ns->state);
589 }
590 }
591
592 /**
593 * Client tells its version. If there is a mismatch, we close the
594 * socket. In real life, all we should care about is the client having
595 * something older than the server. If we assume the client will be
596 * backwards compatible, having it be a later version should not be a
597 * problem.
598 */
599 void
600 VersionCmd (char *buf, int len, client * ns)
601 {
602 if (!buf)
603 {
604 LOG (llevError, "CS: received corrupted version command\n");
605 return;
606 }
607
608 ns->cs_version = atoi (buf);
609 ns->sc_version = ns->cs_version;
610
611 LOG (llevDebug, "connection from client <%s>\n", buf);
612
613 //TODO: should log here just for statistics
614
615 //if (VERSION_CS != ns->cs_version)
616 // unchecked;
617
618 char *cp = strchr (buf + 1, ' ');
619 if (!cp)
620 return;
621
622 ns->sc_version = atoi (cp);
623
624 //if (VERSION_SC != ns->sc_version)
625 // unchecked;
626
627 cp = strchr (cp + 1, ' ');
628
629 if (cp)
630 {
631 ns->version = cp + 1;
632
633 if (ns->sc_version < 1026)
634 ns->send_packet_printf ("drawinfo %d %s", NDI_RED,
635 "**** VERSION WARNING ****\n**** CLIENT IS TOO OLD!! UPDATE THE CLIENT!! ****");
636 }
637 }
638
639 /** sound related functions. */
640 void
641 SetSound (char *buf, int len, client * ns)
642 {
643 ns->sound = atoi (buf);
644 }
645
646 /** client wants the map resent */
647 void
648 MapRedrawCmd (char *buf, int len, player *pl)
649 {
650 /* This function is currently disabled; just clearing the map state results in
651 * display errors. It should clear the cache and send a newmap command.
652 * Unfortunately this solution does not work because some client versions send
653 * a mapredraw command after receiving a newmap command.
654 */
655 }
656
657 /**
658 * Moves an object (typically, container to inventory).
659 * syntax is: move (to) (tag) (nrof)
660 */
661 void
662 MoveCmd (char *buf, int len, player *pl)
663 {
664 int vals[3], i;
665
666 /* A little funky here. We only cycle for 2 records, because
667 * we obviously are not going to find a space after the third
668 * record. Perhaps we should just replace this with a
669 * sscanf?
670 */
671 for (i = 0; i < 2; i++)
672 {
673 vals[i] = atoi (buf);
674
675 if (!(buf = strchr (buf, ' ')))
676 {
677 LOG (llevError, "Incomplete move command: %s\n", buf);
678 return;
679 }
680
681 buf++;
682 }
683
684 vals[2] = atoi (buf);
685
686 /* LOG(llevDebug,"Move item %d (nrof=%d) to %d.\n", vals[1], vals[2], vals[0]);*/
687 esrv_move_object (pl->ob, vals[0], vals[1], vals[2]);
688 }
689
690 /******************************************************************************
691 *
692 * Start of commands the server sends to the client.
693 *
694 ******************************************************************************/
695
696 /**
697 * Asks the client to query the user. This way, the client knows
698 * it needs to send something back (vs just printing out a message)
699 */
700 void
701 send_query (client *ns, uint8 flags, const char *text)
702 {
703 ns->send_packet_printf ("query %d %s", flags, text ? text : "");
704 }
705
706 /**
707 * Get player's current range attack in obuf.
708 */
709 static void
710 rangetostring (player *pl, char *obuf)
711 {
712 dynbuf_text buf;
713
714 if (pl->ranged_ob)
715 buf << " Range" << (pl->ob->current_weapon == pl->ranged_ob ? "*" : "") << ": " << pl->ranged_ob->name;
716
717 if (pl->combat_ob)
718 buf << " Combat" << (pl->ob->current_weapon == pl->combat_ob ? "*" : "") << ": " << pl->combat_ob->name;
719
720 #if 0
721 //TODO: remove this when slot system is working, this is only for debugging
722 if (pl->ob->chosen_skill)
723 buf << " Skill*: " << pl->ob->chosen_skill->name;
724 #endif
725
726 //TODO: maybe golem should become the current_weapon, quite simply?
727 if (pl->golem)
728 buf << " Golem*: " << pl->golem->name;
729
730 buf << '\0';
731 buf.linearise (obuf);
732 }
733
734 #define AddIfInt64(Old,New,Type) if (Old != New) {\
735 Old = New; \
736 sl << uint8 (Type) << uint64 (New); \
737 }
738
739 #define AddIfInt(Old,New,Type) if (Old != New) {\
740 Old = New; \
741 sl << uint8 (Type) << uint32 (New); \
742 }
743
744 #define AddIfShort(Old,New,Type) if (Old != New) {\
745 Old = New; \
746 sl << uint8 (Type) << uint16 (New); \
747 }
748
749 #define AddIfFloat(Old,New,Type,mult) if (Old != New) {\
750 Old = New; \
751 sl << uint8 (Type) << uint32 (New*FLOAT_MULTI*mult); \
752 }
753
754 #define AddIfString(Old,New,Type) if (Old == NULL || strcmp(Old,New)) {\
755 free(Old); Old = strdup (New);\
756 sl << uint8 (Type) << data8 (New); \
757 }
758
759 /**
760 * Sends a statistics update. We look at the old values,
761 * and only send what has changed. Stat mapping values are in newclient.h
762 * Since this gets sent a lot, this is actually one of the few binary
763 * commands for now.
764 */
765 void
766 esrv_update_stats (player *pl)
767 {
768 char buf[MAX_BUF];
769 uint16 flags;
770
771 client *ns = pl->ns;
772 if (!ns)
773 return;
774
775 object *ob = pl->observe;
776 if (!ob)
777 return;
778
779 player *opl = ob->contr ? static_cast<player *>(ob->contr) : pl;
780
781 packet sl ("stats");
782
783 AddIfShort (ns->last_stats.hp, ob->stats.hp, CS_STAT_HP);
784 AddIfShort (ns->last_stats.maxhp, ob->stats.maxhp, CS_STAT_MAXHP);
785 AddIfShort (ns->last_stats.sp, ob->stats.sp, CS_STAT_SP);
786 AddIfShort (ns->last_stats.maxsp, ob->stats.maxsp, CS_STAT_MAXSP);
787 AddIfShort (ns->last_stats.grace, ob->stats.grace, CS_STAT_GRACE);
788 AddIfShort (ns->last_stats.maxgrace, ob->stats.maxgrace, CS_STAT_MAXGRACE);
789 AddIfShort (ns->last_stats.Str, ob->stats.Str, CS_STAT_STR);
790 AddIfShort (ns->last_stats.Dex, ob->stats.Dex, CS_STAT_DEX);
791 AddIfShort (ns->last_stats.Con, ob->stats.Con, CS_STAT_CON);
792 AddIfShort (ns->last_stats.Int, ob->stats.Int, CS_STAT_INT);
793 AddIfShort (ns->last_stats.Wis, ob->stats.Wis, CS_STAT_WIS);
794 AddIfShort (ns->last_stats.Pow, ob->stats.Pow, CS_STAT_POW);
795 AddIfShort (ns->last_stats.Cha, ob->stats.Cha, CS_STAT_CHA);
796
797 for (int s = 0; s < NUM_SKILLS; s++)
798 if (object *skill = opl->last_skill_ob[s])
799 if (skill->stats.exp != ns->last_skill_exp [s])
800 {
801 ns->last_skill_exp [s] = skill->stats.exp;
802
803 /* Always send along the level if exp changes. This is only
804 * 1 extra byte, but keeps processing simpler.
805 */
806 sl << uint8 (s + CS_STAT_SKILLINFO)
807 << uint8 (skill->level)
808 << uint64 (skill->stats.exp);
809 }
810
811 AddIfInt64 (ns->last_stats.exp, ob->stats.exp, CS_STAT_EXP64);
812 AddIfShort (ns->last_level, ob->level, CS_STAT_LEVEL);
813 AddIfShort (ns->last_stats.wc, ob->stats.wc, CS_STAT_WC);
814 AddIfShort (ns->last_stats.ac, ob->stats.ac, CS_STAT_AC);
815 AddIfShort (ns->last_stats.dam, ob->stats.dam, CS_STAT_DAM);
816 AddIfFloat (ns->last_speed, ob->speed, CS_STAT_SPEED, 1.f/TICK);
817 AddIfShort (ns->last_stats.food, ob->stats.food, CS_STAT_FOOD);
818 AddIfFloat (ns->last_weapon_sp, pl->weapon_sp, CS_STAT_WEAP_SP, 1.f/TICK);
819 AddIfInt (ns->last_weight_limit, weight_limit[ob->stats.Str], CS_STAT_WEIGHT_LIM);
820
821 flags = 0;
822
823 if (opl->fire_on)
824 flags |= SF_FIREON;
825
826 if (opl->run_on)
827 flags |= SF_RUNON;
828
829 AddIfShort (ns->last_flags, flags, CS_STAT_FLAGS);
830
831 if (ns->sc_version < 1025)
832 { AddIfShort (ns->last_resist[ATNR_PHYSICAL], ob->resist[ATNR_PHYSICAL], CS_STAT_ARMOUR) }
833 else
834 for (int i = 0; i < NROFATTACKS; i++)
835 {
836 /* Skip ones we won't send */
837 if (atnr_cs_stat[i] == -1)
838 continue;
839
840 AddIfShort (ns->last_resist[i], ob->resist[i], atnr_cs_stat[i]);
841 }
842
843 if (pl->ns->monitor_spells)
844 {
845 AddIfInt (ns->last_path_attuned, ob->path_attuned, CS_STAT_SPELL_ATTUNE);
846 AddIfInt (ns->last_path_repelled, ob->path_repelled, CS_STAT_SPELL_REPEL);
847 AddIfInt (ns->last_path_denied, ob->path_denied, CS_STAT_SPELL_DENY);
848 }
849
850 rangetostring (opl, buf); /* we want use the new fire & run system in new client */
851 AddIfString (ns->stats.range, buf, CS_STAT_RANGE);
852 set_title (ob, buf);
853 AddIfString (ns->stats.title, buf, CS_STAT_TITLE);
854
855 /* Only send it away if we have some actual data */
856 if (sl.length () > 6)
857 ns->send_packet (sl);
858 }
859
860 /**
861 * Tells the client that here is a player it should start using.
862 */
863 void
864 esrv_new_player (player *pl, uint32 weight)
865 {
866 packet sl ("player");
867
868 sl << uint32 (pl->ob->count)
869 << uint32 (weight)
870 << uint32 (pl->ob->face)
871 << data8 (pl->ob->name);
872
873 pl->ns->last_weight = weight;
874 pl->ns->send_packet (sl);
875 SET_FLAG (pl->ob, FLAG_CLIENT_SENT);
876 }
877
878 /******************************************************************************
879 *
880 * Start of map related commands.
881 *
882 ******************************************************************************/
883
884 /** Clears a map cell */
885 static void
886 map_clearcell (struct MapCell *cell, int count)
887 {
888 cell->faces[0] = 0;
889 cell->faces[1] = 0;
890 cell->faces[2] = 0;
891 cell->smooth[0] = 0;
892 cell->smooth[1] = 0;
893 cell->smooth[2] = 0;
894 cell->count = count;
895 cell->stat_hp = 0;
896 cell->flags = 0;
897 cell->player = 0;
898 }
899
900 #define MAX_LAYERS 3
901
902 /**
903 * Removes the need to replicate the same code for each layer.
904 * this returns true if this space is now in fact different than
905 * it was.
906 * sl is the socklist this data is going into.
907 * ns is the socket we are working on - all the info we care
908 * about is in this socket structure, so now need not pass the
909 * entire player object.
910 * layer is the layer to update, with 2 being the floor and 0 the
911 * top layer (this matches what the GET_MAP_FACE and GET_MAP_FACE_OBJ)
912 * take. Interesting to note that before this function, the map1 function
913 * numbers the spaces differently - I think this was a leftover from
914 * the map command, where the faces stack up. Sinces that is no longer
915 * the case, it seems to make more sense to have these layer values
916 * actually match.
917 */
918 static int
919 update_space (packet &sl, client &ns, mapspace &ms, MapCell &lastcell, int layer)
920 {
921 object *ob = ms.faces_obj [layer];
922
923 /* If there is no object for this space, or if the face for the object
924 * is the blank face, set the face number to zero.
925 * else if we have the stored head object for this space, that takes
926 * precedence over the other object for this space.
927 * otherwise, we do special head processing
928 */
929 uint16 face_num = ob && ob->face != blank_face ? ob->face : 0;
930
931 /* We've gotten what face we want to use for the object. Now see if
932 * if it has changed since we last sent it to the client.
933 */
934 if (lastcell.faces[layer] != face_num)
935 {
936 lastcell.faces[layer] = face_num;
937
938 if (!ns.faces_sent[face_num])
939 if (ob)
940 ns.send_faces (ob);
941 else
942 ns.send_face (face_num);
943
944 sl << uint16 (face_num);
945 return 1;
946 }
947
948 /* Nothing changed */
949 return 0;
950 }
951
952 /**
953 * Returns the size of a data for a map square as returned by
954 * mapextended. There are CLIENTMAPX*CLIENTMAPY*LAYERS entries
955 * available.
956 */
957 int
958 getExtendedMapInfoSize (client * ns)
959 {
960 int result = 0;
961
962 if (ns->ext_mapinfos)
963 {
964 if (ns->EMI_smooth)
965 result += 1; /*One byte for smoothlevel */
966 }
967
968 return result;
969 }
970
971 // prefetch (and touch) all maps within a specific distancd
972 static void
973 prefetch_surrounding_maps (maptile *map, int distance)
974 {
975 map->last_access = runtime;
976
977 if (--distance)
978 for (int dir = 4; --dir; )
979 if (const shstr &path = map->tile_path [dir])
980 if (maptile *&neigh = map->tile_map [dir])
981 prefetch_surrounding_maps (neigh, distance);
982 else
983 neigh = maptile::find_async (path, map);
984 }
985
986 // prefetch a generous area around the player
987 static void
988 prefetch_surrounding_maps (object *op)
989 {
990 prefetch_surrounding_maps (op->map, 3);
991 }
992
993 /**
994 * Draws client map.
995 */
996 void
997 draw_client_map (player *pl)
998 {
999 object *ob = pl->observe;
1000 if (!ob->active)
1001 return;
1002
1003 maptile *plmap = ob->map;
1004
1005 /* If player is just joining the game, he isn't here yet, so the map
1006 * can get swapped out. If so, don't try to send them a map. All will
1007 * be OK once they really log in.
1008 */
1009 if (!plmap || plmap->in_memory != MAP_IN_MEMORY)
1010 return;
1011
1012 int x, y, ax, ay, startlen, max_x, max_y, oldlen;
1013 int estartlen, eoldlen;
1014 uint8 eentrysize;
1015 uint16 ewhatstart, ewhatflag;
1016 uint8 extendedinfos;
1017
1018 check_map_change (pl);
1019 prefetch_surrounding_maps (pl->ob);
1020
1021 /* do LOS after calls to update_position */
1022 if (ob != pl->ob)
1023 clear_los (pl);
1024 else if (pl->do_los)
1025 {
1026 update_los (ob);
1027 pl->do_los = 0;
1028 }
1029
1030 /**
1031 * This function uses the new map1 protocol command to send the map
1032 * to the client. It is necessary because the old map command supports
1033 * a maximum map size of 15x15.
1034 * This function is much simpler than the old one. This is because
1035 * the old function optimized to send as few face identifiers as possible,
1036 * at the expense of sending more coordinate location (coordinates were
1037 * only 1 byte, faces 2 bytes, so this was a worthwhile savings). Since
1038 * we need 2 bytes for coordinates and 2 bytes for faces, such a trade off
1039 * maps no sense. Instead, we actually really only use 12 bits for coordinates,
1040 * and use the other 4 bits for other informatiion. For full documentation
1041 * of what we send, see the doc/Protocol file.
1042 * I will describe internally what we do:
1043 * the ns->lastmap shows how the map last looked when sent to the client.
1044 * in the lastmap structure, there is a cells array, which is set to the
1045 * maximum viewable size (As set in config.h).
1046 * in the cells, there are faces and a count value.
1047 * we use the count value to hold the darkness value. If -1, then this space
1048 * is not viewable.
1049 * we use faces[0] faces[1] faces[2] to hold what the three layers
1050 * look like.
1051 */
1052
1053 client &socket = *pl->ns;
1054
1055 packet sl (socket.mapmode == Map1Cmd ? "map1" : "map1a");
1056 packet esl;
1057
1058 startlen = sl.length ();
1059
1060 /*Extendedmapinfo structure initialisation */
1061 if (socket.ext_mapinfos)
1062 {
1063 extendedinfos = EMI_NOREDRAW;
1064
1065 if (socket.EMI_smooth)
1066 extendedinfos |= EMI_SMOOTH;
1067
1068 ewhatstart = esl.length ();
1069 ewhatflag = extendedinfos; /*The EMI_NOREDRAW bit
1070 could need to be taken away */
1071 eentrysize = getExtendedMapInfoSize (&socket);
1072 esl << "mapextended "
1073 << uint8 (extendedinfos)
1074 << uint8 (eentrysize);
1075
1076 estartlen = esl.length ();
1077 }
1078
1079 /* x,y are the real map locations. ax, ay are viewport relative
1080 * locations.
1081 */
1082 ay = 0;
1083
1084 /* We could do this logic as conditionals in the if statement,
1085 * but that started to get a bit messy to look at.
1086 */
1087 max_x = ob->x + (socket.mapx + 1) / 2;
1088 max_y = ob->y + (socket.mapy + 1) / 2;
1089
1090 for (y = ob->y - socket.mapy / 2; y < max_y; y++, ay++)
1091 {
1092 sint16 nx, ny;
1093 maptile *m = 0;
1094
1095 ax = 0;
1096 for (x = ob->x - socket.mapx / 2; x < max_x; x++, ax++)
1097 {
1098 // check to see if we can simply go one right quickly
1099 ++nx;
1100 if (m && nx >= m->width)
1101 m = 0;
1102
1103 if (!m)
1104 {
1105 nx = x; ny = y; m = plmap;
1106
1107 if (!xy_normalise (m, nx, ny))
1108 m = 0;
1109 }
1110
1111 int emask, mask;
1112 emask = mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4;
1113
1114 MapCell &lastcell = socket.lastmap.cells[ax][ay];
1115
1116 /* If the coordinates are not valid, or it is too dark to see,
1117 * we tell the client as such
1118 */
1119 if (!m)
1120 {
1121 /* space is out of map. Update space and clear values
1122 * if this hasn't already been done. If the space is out
1123 * of the map, it shouldn't have a head
1124 */
1125 if (lastcell.count != -1)
1126 {
1127 sl << uint16 (mask);
1128 map_clearcell (&lastcell, -1);
1129 }
1130
1131 continue;
1132 }
1133
1134 m->touch ();
1135
1136 int d = pl->blocked_los[ax][ay];
1137
1138 if (d > 3)
1139 {
1140
1141 int need_send = 0, count;
1142
1143 /* This block deals with spaces that are not visible for whatever
1144 * reason. Still may need to send the head for this space.
1145 */
1146
1147 oldlen = sl.length ();
1148
1149 sl << uint16 (mask);
1150
1151 if (lastcell.count != -1)
1152 need_send = 1;
1153
1154 count = -1;
1155
1156 /* properly clear a previously sent big face */
1157 if (lastcell.faces[0] || lastcell.faces[1] || lastcell.faces[2]
1158 || lastcell.stat_hp || lastcell.flags || lastcell.player)
1159 need_send = 1;
1160
1161 map_clearcell (&lastcell, count);
1162
1163 if ((mask & 0xf) || need_send)
1164 sl[oldlen + 1] = mask & 0xff;
1165 else
1166 sl.reset (oldlen);
1167 }
1168 else
1169 {
1170 /* In this block, the space is visible.
1171 */
1172
1173 /* Rather than try to figure out what everything that we might
1174 * need to send is, then form the packet after that,
1175 * we presume that we will in fact form a packet, and update
1176 * the bits by what we do actually send. If we send nothing,
1177 * we just back out sl.length () to the old value, and no harm
1178 * is done.
1179 * I think this is simpler than doing a bunch of checks to see
1180 * what if anything we need to send, setting the bits, then
1181 * doing those checks again to add the real data.
1182 */
1183 oldlen = sl.length ();
1184 eoldlen = esl.length ();
1185
1186 sl << uint16 (mask);
1187
1188 unsigned char dummy;
1189 unsigned char *last_ext = &dummy;
1190
1191 /* Darkness changed */
1192 if (lastcell.count != d && socket.darkness)
1193 {
1194 mask |= 0x8;
1195
1196 if (socket.extmap)
1197 {
1198 *last_ext |= 0x80;
1199 last_ext = &sl[sl.length ()];
1200 sl << uint8 (d);
1201 }
1202 else
1203 sl << uint8 (255 - 64 * d);
1204 }
1205
1206 lastcell.count = d;
1207
1208 mapspace &ms = m->at (nx, ny);
1209 ms.update ();
1210
1211 if (socket.extmap)
1212 {
1213 uint8 stat_hp = 0;
1214 uint8 stat_width = 0;
1215 uint8 flags = 0;
1216 tag_t player = 0;
1217
1218 // send hp information, if applicable
1219 if (object *op = ms.faces_obj [0])
1220 {
1221 if (op->head || op->invisible)
1222 ; // do not show
1223 else if (op->type == PLAYER
1224 || QUERY_FLAG (op, FLAG_MONSTER) || QUERY_FLAG (op, FLAG_ALIVE) || QUERY_FLAG (op, FLAG_GENERATOR))
1225 {
1226 if (op->stats.maxhp > 0 && (unsigned) op->stats.maxhp > (unsigned) op->stats.hp)
1227 {
1228 stat_hp = 255 - (op->stats.hp * 255 + 254) / op->stats.maxhp;
1229 stat_width = op->arch->max_x - op->arch->x; //TODO: should be upper-left edge
1230 }
1231 }
1232
1233 if (op->msg && op->msg[0] == '@')
1234 flags |= 1;
1235
1236 if (op->type == PLAYER && op != ob)
1237 player = op->count;
1238 }
1239
1240 if (lastcell.stat_hp != stat_hp)
1241 {
1242 lastcell.stat_hp = stat_hp;
1243
1244 mask |= 0x8;
1245 *last_ext |= 0x80;
1246 last_ext = &sl[sl.length ()];
1247
1248 sl << uint8 (5) << uint8 (stat_hp);
1249
1250 if (stat_width > 1)
1251 {
1252 *last_ext |= 0x80;
1253 last_ext = &sl[sl.length ()];
1254
1255 sl << uint8 (6) << uint8 (stat_width);
1256 }
1257 }
1258
1259 if (lastcell.player != player)
1260 {
1261 lastcell.player = player;
1262
1263 mask |= 0x8;
1264 *last_ext |= 0x80;
1265 last_ext = &sl[sl.length ()];
1266
1267 sl << uint8 (0x47) << uint8 (4) << (uint32)player;
1268 }
1269
1270 if (lastcell.flags != flags)
1271 {
1272 lastcell.flags = flags;
1273
1274 mask |= 0x8;
1275 *last_ext |= 0x80;
1276 last_ext = &sl[sl.length ()];
1277
1278 sl << uint8 (8) << uint8 (flags);
1279 }
1280 }
1281
1282 /* Floor face */
1283 if (update_space (sl, socket, ms, lastcell, 2))
1284 mask |= 0x4;
1285
1286 /* Middle face */
1287 if (update_space (sl, socket, ms, lastcell, 1))
1288 mask |= 0x2;
1289
1290 if (ms.player () == ob
1291 && (ob->invisible & (ob->invisible < 50 ? 1 : 7)))
1292 {
1293 // force player to be visible to himself if invisible
1294 if (lastcell.faces[0] != ob->face)
1295 {
1296 lastcell.faces[0] = ob->face;
1297
1298 mask |= 0x1;
1299 sl << uint16 (ob->face);
1300
1301 socket.send_faces (ob);
1302 }
1303 }
1304 /* Top face */
1305 else if (update_space (sl, socket, ms, lastcell, 0))
1306 mask |= 0x1;
1307
1308 /* Check to see if we are in fact sending anything for this
1309 * space by checking the mask. If so, update the mask.
1310 * if not, reset the len to that from before adding the mask
1311 * value, so we don't send those bits.
1312 */
1313 if (mask & 0xf)
1314 sl[oldlen + 1] = mask & 0xff;
1315 else
1316 sl.reset (oldlen);
1317
1318 if (socket.ext_mapinfos)
1319 esl << uint16 (emask);
1320
1321 if (socket.EMI_smooth)
1322 {
1323 for (int layer = 2+1; layer--; )
1324 {
1325 object *ob = ms.faces_obj [layer];
1326
1327 // If there is no object for this space, or if the face for the object
1328 // is the blank face, set the smoothlevel to zero.
1329 int smoothlevel = ob && ob->face != blank_face ? ob->smoothlevel : 0;
1330
1331 // We've gotten what face we want to use for the object. Now see if
1332 // if it has changed since we last sent it to the client.
1333 if (lastcell.smooth[layer] != smoothlevel)
1334 {
1335 lastcell.smooth[layer] = smoothlevel;
1336 esl << uint8 (smoothlevel);
1337 emask |= 1 << layer;
1338 }
1339 }
1340
1341 if (emask & 0xf)
1342 esl[eoldlen + 1] = emask & 0xff;
1343 else
1344 esl.reset (eoldlen);
1345 }
1346 } /* else this is a viewable space */
1347 } /* for x loop */
1348 } /* for y loop */
1349
1350 socket.flush_fx ();
1351
1352 /* Verify that we in fact do need to send this */
1353 if (socket.ext_mapinfos)
1354 {
1355 if (!(sl.length () > startlen || socket.sent_scroll))
1356 {
1357 /* No map data will follow, so don't say the client
1358 * it doesn't need draw!
1359 */
1360 ewhatflag &= ~EMI_NOREDRAW;
1361 esl[ewhatstart + 1] = ewhatflag & 0xff;
1362 }
1363
1364 if (esl.length () > estartlen)
1365 socket.send_packet (esl);
1366 }
1367
1368 if (sl.length () > startlen || socket.sent_scroll)
1369 {
1370 socket.send_packet (sl);
1371 socket.sent_scroll = 0;
1372 }
1373 }
1374
1375 /*****************************************************************************/
1376 /* GROS: The following one is used to allow a plugin to send a generic cmd to*/
1377 /* a player. Of course, the client need to know the command to be able to */
1378 /* manage it ! */
1379 /*****************************************************************************/
1380 void
1381 send_plugin_custom_message (object *pl, char *buf)
1382 {
1383 pl->contr->ns->send_packet (buf);
1384 }
1385
1386 /**
1387 * This sends the skill number to name mapping. We ignore
1388 * the params - we always send the same info no matter what.
1389 */
1390 void
1391 send_skill_info (client *ns, char *params)
1392 {
1393 packet sl;
1394 sl << "replyinfo skill_info\n";
1395
1396 for (int i = 1; i < NUM_SKILLS; i++)
1397 sl.printf ("%d:%s\n", i + CS_STAT_SKILLINFO, &skill_names[i]);
1398
1399 if (sl.length () >= MAXSOCKBUF)
1400 {
1401 LOG (llevError, "Buffer overflow in send_skill_info!\n");
1402 fatal (0);
1403 }
1404
1405 ns->send_packet (sl);
1406 }
1407
1408 /**
1409 * This sends the spell path to name mapping. We ignore
1410 * the params - we always send the same info no matter what.
1411 */
1412 void
1413 send_spell_paths (client * ns, char *params)
1414 {
1415 packet sl;
1416
1417 sl << "replyinfo spell_paths\n";
1418
1419 for (int i = 0; i < NRSPELLPATHS; i++)
1420 sl.printf ("%d:%s\n", 1 << i, spellpathnames[i]);
1421
1422 if (sl.length () >= MAXSOCKBUF)
1423 {
1424 LOG (llevError, "Buffer overflow in send_spell_paths!\n");
1425 fatal (0);
1426 }
1427
1428 ns->send_packet (sl);
1429 }
1430
1431 /**
1432 * This looks for any spells the player may have that have changed their stats.
1433 * it then sends an updspell packet for each spell that has changed in this way
1434 */
1435 void
1436 esrv_update_spells (player *pl)
1437 {
1438 if (!pl->ns)
1439 return;
1440
1441 if (!pl->ns->monitor_spells)
1442 return;
1443
1444 for (object *spell = pl->ob->inv; spell; spell = spell->below)
1445 {
1446 if (spell->type == SPELL)
1447 {
1448 int flags = 0;
1449
1450 /* check if we need to update it */
1451 if (spell->last_sp != SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA))
1452 {
1453 spell->last_sp = SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA);
1454 flags |= UPD_SP_MANA;
1455 }
1456
1457 if (spell->last_grace != SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE))
1458 {
1459 spell->last_grace = SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE);
1460 flags |= UPD_SP_GRACE;
1461 }
1462
1463 if (spell->last_eat != spell->stats.dam + SP_level_dam_adjust (pl->ob, spell))
1464 {
1465 spell->last_eat = spell->stats.dam + SP_level_dam_adjust (pl->ob, spell);
1466 flags |= UPD_SP_DAMAGE;
1467 }
1468
1469 if (flags)
1470 {
1471 packet sl;
1472
1473 sl << "updspell "
1474 << uint8 (flags)
1475 << uint32 (spell->count);
1476
1477 if (flags & UPD_SP_MANA ) sl << uint16 (spell->last_sp);
1478 if (flags & UPD_SP_GRACE ) sl << uint16 (spell->last_grace);
1479 if (flags & UPD_SP_DAMAGE) sl << uint16 (spell->last_eat);
1480
1481 pl->ns->send_packet (sl);
1482 }
1483 }
1484 }
1485 }
1486
1487 void
1488 esrv_remove_spell (player *pl, object *spell)
1489 {
1490 if (!pl->ns->monitor_spells)
1491 return;
1492
1493 if (!pl || !spell || spell->env != pl->ob)
1494 {
1495 LOG (llevError, "Invalid call to esrv_remove_spell");
1496 return;
1497 }
1498
1499 packet sl ("delspell");
1500
1501 sl << uint32 (spell->count);
1502
1503 pl->ns->send_packet (sl);
1504 }
1505
1506 /* appends the spell *spell to the Socklist we will send the data to. */
1507 static void
1508 append_spell (player *pl, packet &sl, object *spell)
1509 {
1510 int i, skill = 0;
1511
1512 if (!(spell->name))
1513 {
1514 LOG (llevError, "item number %d is a spell with no name.\n", spell->count);
1515 return;
1516 }
1517
1518 /* store costs and damage in the object struct, to compare to later */
1519 spell->last_sp = SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA);
1520 spell->last_grace = SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE);
1521 spell->last_eat = spell->stats.dam + SP_level_dam_adjust (pl->ob, spell);
1522
1523 /* figure out which skill it uses, if it uses one */
1524 if (spell->skill)
1525 {
1526 for (i = 1; i < NUM_SKILLS; i++)
1527 if (!strcmp (spell->skill, skill_names[i]))
1528 {
1529 skill = i + CS_STAT_SKILLINFO;
1530 break;
1531 }
1532 }
1533
1534 // spells better have a face
1535 if (!spell->face)
1536 {
1537 LOG (llevError, "%s: spell has no face, but face is mandatory.\n", &spell->name);
1538 spell->face = face_find ("burnout.x11", blank_face);
1539 }
1540
1541 /* send the current values */
1542 sl << uint32 (spell->count)
1543 << uint16 (spell->level)
1544 << uint16 (spell->casting_time)
1545 << uint16 (spell->last_sp)
1546 << uint16 (spell->last_grace)
1547 << uint16 (spell->last_eat)
1548 << uint8 (skill)
1549 << uint32 (spell->path_attuned)
1550 << uint32 (spell->face)
1551 << data8 (spell->name)
1552 << data16 (spell->msg);
1553 }
1554
1555 /**
1556 * This tells the client to add the spell *ob, if *ob is NULL, then add
1557 * all spells in the player's inventory.
1558 */
1559 void
1560 esrv_add_spells (player *pl, object *spell)
1561 {
1562 if (!pl)
1563 {
1564 LOG (llevError, "esrv_add_spells, tried to add a spell to a NULL player");
1565 return;
1566 }
1567
1568 if (!pl->ns->monitor_spells)
1569 return;
1570
1571 packet sl ("addspell");
1572
1573 if (!spell)
1574 {
1575 for (spell = pl->ob->inv; spell; spell = spell->below)
1576 {
1577 /* were we to simply keep appending data here, we could exceed
1578 * MAXSOCKBUF if the player has enough spells to add, we know that
1579 * append_spells will always append 19 data bytes, plus 4 length
1580 * bytes and 3 strings (because that is the spec) so we need to
1581 * check that the length of those 3 strings, plus the 23 bytes,
1582 * won't take us over the length limit for the socket, if it does,
1583 * we need to send what we already have, and restart packet formation
1584 */
1585 /* Seeing crashes by overflowed buffers. Quick arithemetic seems
1586 * to show add_spell is 26 bytes + 2 strings. However, the overun
1587 * is hundreds of bytes off, so correcting 22 vs 26 doesn't seem
1588 * like it will fix this
1589 */
1590 if (spell->type != SPELL)
1591 continue;
1592
1593 if (sl.length () >= (MAXSOCKBUF - (26 + strlen (spell->name) + (spell->msg ? strlen (spell->msg) : 0))))
1594 {
1595 pl->ns->send_packet (sl);
1596
1597 sl.reset ();
1598 sl << "addspell ";
1599 }
1600
1601 append_spell (pl, sl, spell);
1602 }
1603 }
1604 else if (spell->type != SPELL)
1605 {
1606 LOG (llevError, "Asked to send a non-spell object as a spell");
1607 return;
1608 }
1609 else
1610 append_spell (pl, sl, spell);
1611
1612 if (sl.length () >= MAXSOCKBUF)
1613 {
1614 LOG (llevError, "Buffer overflow in esrv_add_spells!\n");
1615 fatal (0);
1616 }
1617
1618 /* finally, we can send the packet */
1619 pl->ns->send_packet (sl);
1620 }
1621