ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/request.C
Revision: 1.73
Committed: Wed Mar 14 00:04:59 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.72: +12 -94 lines
Log Message:
- rewrote smooth face handling, as a side-effect, smoothing seems to work
  again and smooth faces can be reloaded.
- the server now sends the full animation for an object the first time
  it is seen, this uses slightly more bandwidth initially, but avoids
  the flickering for objects change their face later.

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