ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/request.C
Revision: 1.91
Committed: Mon Apr 30 04:25:30 2007 UTC (17 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.90: +32 -0 lines
Log Message:
This is the first rough cut of the skill use system (use the STABLE tag).

Details will likely change, and combat skills do not work very well, but
it works quite well.

Players no longer have a shoottype or range slots, instead, each player
has these members:

   combat_skill/combat_ob  the currently selected skill (and weapon)
                           for direct attacks.
   ranged_skill/ranged_ob  the currently selected ranged skill (and
                           bow/spell/item)
   golem                   the currently-controlled golem, if any.

File Contents

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