ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/request.C
Revision: 1.75
Committed: Wed Mar 14 04:12:29 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.74: +57 -102 lines
Log Message:
- rewrote more face handling code
- automatically send smooth faces, as the client will need them anyways
  and it makes little sense to wait for the client to axk for it. of course,
  gcfclient suffers from weird ordering problems again.
- UP_OBJ_FACE was often abused in situations where other things changed,
  updated lots of spaces, probably more to be done.
- update_smooth became so small that inlining it actually clarified
  the code. similar for update_space, which is not inlined for other reasons.
- faces were not initialised properly
- add versioncheck for face data
- rewrite invisibility handling a bit: god finger etc. now makes you blink,
  blinking routine has changed to be less annoying and more useful while
  still indicating invisibleness.

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_face (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->smooth[0] = 0;
1090 cell->smooth[1] = 0;
1091 cell->smooth[2] = 0;
1092 cell->count = count;
1093 cell->stat_hp = 0;
1094 cell->flags = 0;
1095 cell->player = 0;
1096 }
1097
1098 #define MAX_LAYERS 3
1099
1100 /**
1101 * Removes the need to replicate the same code for each layer.
1102 * this returns true if this space is now in fact different than
1103 * it was.
1104 * sl is the socklist this data is going into.
1105 * ns is the socket we are working on - all the info we care
1106 * about is in this socket structure, so now need not pass the
1107 * entire player object.
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, mapspace &ms, MapCell &lastcell, int layer)
1118 {
1119 object *ob = ms.faces_obj [layer];
1120
1121 /* If there is no object for this space, or if the face for the object
1122 * is the blank face, set the face number to zero.
1123 * else if we have the stored head object for this space, that takes
1124 * precedence over the other object for this space.
1125 * otherwise, we do special head processing
1126 */
1127 uint16 face_num = ob && ob->face != blank_face ? ob->face : 0;
1128
1129 /* We've gotten what face we want to use for the object. Now see if
1130 * if it has changed since we last sent it to the client.
1131 */
1132 if (lastcell.faces[layer] != face_num)
1133 {
1134 lastcell.faces[layer] = face_num;
1135
1136 if (!ns.faces_sent[face_num])
1137 if (ob)
1138 ns.send_faces (ob);
1139 else
1140 ns.send_face (face_num);
1141
1142 sl << uint16 (face_num);
1143 return 1;
1144 }
1145
1146 /* Nothing changed */
1147 return 0;
1148 }
1149
1150 /**
1151 * Returns the size of a data for a map square as returned by
1152 * mapextended. There are CLIENTMAPX*CLIENTMAPY*LAYERS entries
1153 * available.
1154 */
1155 int
1156 getExtendedMapInfoSize (client * ns)
1157 {
1158 int result = 0;
1159
1160 if (ns->ext_mapinfos)
1161 {
1162 if (ns->EMI_smooth)
1163 result += 1; /*One byte for smoothlevel */
1164 }
1165
1166 return result;
1167 }
1168
1169 // prefetch (and touch) all maps within a specific distancd
1170 static void
1171 prefetch_surrounding_maps (maptile *map, int distance)
1172 {
1173 map->last_access = runtime;
1174
1175 if (--distance)
1176 for (int dir = 4; --dir; )
1177 if (const shstr &path = map->tile_path [dir])
1178 if (maptile *&neigh = map->tile_map [dir])
1179 prefetch_surrounding_maps (neigh, distance);
1180 else
1181 neigh = maptile::find_async (path, map);
1182 }
1183
1184 // prefetch a generous area around the player
1185 static void
1186 prefetch_surrounding_maps (object *op)
1187 {
1188 prefetch_surrounding_maps (op->map, 3);
1189 }
1190
1191 /**
1192 * This function uses the new map1 protocol command to send the map
1193 * to the client. It is necessary because the old map command supports
1194 * a maximum map size of 15x15.
1195 * This function is much simpler than the old one. This is because
1196 * the old function optimized to send as few face identifiers as possible,
1197 * at the expense of sending more coordinate location (coordinates were
1198 * only 1 byte, faces 2 bytes, so this was a worthwhile savings). Since
1199 * we need 2 bytes for coordinates and 2 bytes for faces, such a trade off
1200 * maps no sense. Instead, we actually really only use 12 bits for coordinates,
1201 * and use the other 4 bits for other informatiion. For full documentation
1202 * of what we send, see the doc/Protocol file.
1203 * I will describe internally what we do:
1204 * the ns->lastmap shows how the map last looked when sent to the client.
1205 * in the lastmap structure, there is a cells array, which is set to the
1206 * maximum viewable size (As set in config.h).
1207 * in the cells, there are faces and a count value.
1208 * we use the count value to hold the darkness value. If -1, then this space
1209 * is not viewable.
1210 * we use faces[0] faces[1] faces[2] to hold what the three layers
1211 * look like.
1212 */
1213 void
1214 draw_client_map1 (object *pl)
1215 {
1216 int x, y, ax, ay, startlen, max_x, max_y, oldlen;
1217 sint16 nx, ny;
1218 int estartlen, eoldlen;
1219 uint8 eentrysize;
1220 uint16 ewhatstart, ewhatflag;
1221 uint8 extendedinfos;
1222 maptile *m;
1223
1224 client &socket = *pl->contr->ns;
1225
1226 if (!pl->active)
1227 return;
1228
1229 check_map_change (pl->contr);
1230 prefetch_surrounding_maps (pl);
1231
1232 packet sl (socket.mapmode == Map1Cmd ? "map1" : "map1a");
1233 packet esl;
1234
1235 startlen = sl.length ();
1236
1237 /*Extendedmapinfo structure initialisation */
1238 if (socket.ext_mapinfos)
1239 {
1240 extendedinfos = EMI_NOREDRAW;
1241
1242 if (socket.EMI_smooth)
1243 extendedinfos |= EMI_SMOOTH;
1244
1245 ewhatstart = esl.length ();
1246 ewhatflag = extendedinfos; /*The EMI_NOREDRAW bit
1247 could need to be taken away */
1248 eentrysize = getExtendedMapInfoSize (&socket);
1249 esl << "mapextended "
1250 << uint8 (extendedinfos)
1251 << uint8 (eentrysize);
1252
1253 estartlen = esl.length ();
1254 }
1255 else
1256 {
1257 /* suppress compiler warnings */
1258 ewhatstart = 0;
1259 ewhatflag = 0;
1260 estartlen = 0;
1261 }
1262
1263 /* x,y are the real map locations. ax, ay are viewport relative
1264 * locations.
1265 */
1266 ay = 0;
1267
1268 /* We could do this logic as conditionals in the if statement,
1269 * but that started to get a bit messy to look at.
1270 */
1271 max_x = pl->x + (socket.mapx + 1) / 2;
1272 max_y = pl->y + (socket.mapy + 1) / 2;
1273
1274 for (y = pl->y - socket.mapy / 2; y < max_y; y++, ay++)
1275 {
1276 ax = 0;
1277 for (x = pl->x - socket.mapx / 2; x < max_x; x++, ax++)
1278 {
1279 int emask, mask;
1280 emask = mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4;
1281
1282 MapCell &lastcell = socket.lastmap.cells[ax][ay];
1283
1284 /* If the coordinates are not valid, or it is too dark to see,
1285 * we tell the client as such
1286 */
1287 nx = x;
1288 ny = y;
1289 m = get_map_from_coord (pl->map, &nx, &ny);
1290
1291 if (!m)
1292 {
1293 /* space is out of map. Update space and clear values
1294 * if this hasn't already been done. If the space is out
1295 * of the map, it shouldn't have a head
1296 */
1297 if (lastcell.count != -1)
1298 {
1299 sl << uint16 (mask);
1300 map_clearcell (&lastcell, -1);
1301 }
1302
1303 continue;
1304 }
1305
1306 m->touch ();
1307
1308 int d = pl->contr->blocked_los[ax][ay];
1309
1310 if (d > 3)
1311 {
1312
1313 int need_send = 0, count;
1314
1315 /* This block deals with spaces that are not visible for whatever
1316 * reason. Still may need to send the head for this space.
1317 */
1318
1319 oldlen = sl.length ();
1320
1321 sl << uint16 (mask);
1322
1323 if (lastcell.count != -1)
1324 need_send = 1;
1325
1326 count = -1;
1327
1328 /* properly clear a previously sent big face */
1329 if (lastcell.faces[0] || lastcell.faces[1] || lastcell.faces[2]
1330 || lastcell.stat_hp || lastcell.flags || lastcell.player)
1331 need_send = 1;
1332
1333 map_clearcell (&lastcell, count);
1334
1335 if ((mask & 0xf) || need_send)
1336 sl[oldlen + 1] = mask & 0xff;
1337 else
1338 sl.reset (oldlen);
1339 }
1340 else
1341 {
1342 /* In this block, the space is visible.
1343 */
1344
1345 /* Rather than try to figure out what everything that we might
1346 * need to send is, then form the packet after that,
1347 * we presume that we will in fact form a packet, and update
1348 * the bits by what we do actually send. If we send nothing,
1349 * we just back out sl.length () to the old value, and no harm
1350 * is done.
1351 * I think this is simpler than doing a bunch of checks to see
1352 * what if anything we need to send, setting the bits, then
1353 * doing those checks again to add the real data.
1354 */
1355 oldlen = sl.length ();
1356 eoldlen = esl.length ();
1357
1358 sl << uint16 (mask);
1359
1360 unsigned char dummy;
1361 unsigned char *last_ext = &dummy;
1362
1363 /* Darkness changed */
1364 if (lastcell.count != d && socket.darkness)
1365 {
1366 mask |= 0x8;
1367
1368 if (socket.extmap)
1369 {
1370 *last_ext |= 0x80;
1371 last_ext = &sl[sl.length ()];
1372 sl << uint8 (d);
1373 }
1374 else
1375 sl << uint8 (255 - 64 * d);
1376 }
1377
1378 lastcell.count = d;
1379
1380 mapspace &ms = m->at (nx, ny);
1381
1382 if (socket.extmap)
1383 {
1384 uint8 stat_hp = 0;
1385 uint8 stat_width = 0;
1386 uint8 flags = 0;
1387 UUID player = 0;
1388
1389 // send hp information, if applicable
1390 if (object *op = ms.faces_obj [0])
1391 {
1392 if (op->head || op->invisible)
1393 ; // do not show
1394 else if (op->type == PLAYER
1395 || QUERY_FLAG (op, FLAG_MONSTER) || QUERY_FLAG (op, FLAG_ALIVE) || QUERY_FLAG (op, FLAG_GENERATOR))
1396 {
1397 if (op->stats.maxhp > 0 && (unsigned) op->stats.maxhp > (unsigned) op->stats.hp)
1398 {
1399 stat_hp = 255 - (op->stats.hp * 255 + 254) / op->stats.maxhp;
1400 stat_width = op->arch->tail_x;
1401 }
1402 }
1403
1404 if (op->msg && op->msg[0] == '@')
1405 flags |= 1;
1406
1407 if (op->type == PLAYER && op != pl)
1408 player = op->count;
1409 }
1410
1411 if (lastcell.stat_hp != stat_hp)
1412 {
1413 lastcell.stat_hp = stat_hp;
1414
1415 mask |= 0x8;
1416 *last_ext |= 0x80;
1417 last_ext = &sl[sl.length ()];
1418
1419 sl << uint8 (5) << uint8 (stat_hp);
1420
1421 if (stat_width > 1)
1422 {
1423 *last_ext |= 0x80;
1424 last_ext = &sl[sl.length ()];
1425
1426 sl << uint8 (6) << uint8 (stat_width);
1427 }
1428 }
1429
1430 if (lastcell.player != player)
1431 {
1432 lastcell.player = player;
1433
1434 mask |= 0x8;
1435 *last_ext |= 0x80;
1436 last_ext = &sl[sl.length ()];
1437
1438 sl << uint8 (0x47) << uint8 (8) << (uint64)player;
1439 }
1440
1441 if (lastcell.flags != flags)
1442 {
1443 lastcell.flags = flags;
1444
1445 mask |= 0x8;
1446 *last_ext |= 0x80;
1447 last_ext = &sl[sl.length ()];
1448
1449 sl << uint8 (8) << uint8 (flags);
1450 }
1451 }
1452
1453 /* Floor face */
1454 if (update_space (sl, socket, ms, lastcell, 2))
1455 mask |= 0x4;
1456
1457 /* Middle face */
1458 if (update_space (sl, socket, ms, lastcell, 1))
1459 mask |= 0x2;
1460
1461 if (ms.player () == pl
1462 && (pl->invisible & (pl->invisible < 50 ? 1 : 7)))
1463 {
1464 // force player to be visible to himself if invisible
1465 if (lastcell.faces[0] != pl->face)
1466 {
1467 lastcell.faces[0] = pl->face;
1468
1469 mask |= 0x1;
1470 sl << uint16 (pl->face);
1471
1472 socket.send_faces (pl);
1473 }
1474 }
1475 /* Top face */
1476 else if (update_space (sl, socket, ms, lastcell, 0))
1477 mask |= 0x1;
1478
1479
1480 /* Check to see if we are in fact sending anything for this
1481 * space by checking the mask. If so, update the mask.
1482 * if not, reset the len to that from before adding the mask
1483 * value, so we don't send those bits.
1484 */
1485 if (mask & 0xf)
1486 sl[oldlen + 1] = mask & 0xff;
1487 else
1488 sl.reset (oldlen);
1489
1490 if (socket.ext_mapinfos)
1491 esl << uint16 (emask);
1492
1493 if (socket.EMI_smooth)
1494 {
1495 for (int layer = 2+1; layer--; )
1496 {
1497 object *ob = ms.faces_obj [layer];
1498
1499 // If there is no object for this space, or if the face for the object
1500 // is the blank face, set the smoothlevel to zero.
1501 int smoothlevel = ob && ob->face != blank_face ? ob->smoothlevel : 0;
1502
1503 // We've gotten what face we want to use for the object. Now see if
1504 // if it has changed since we last sent it to the client.
1505 if (lastcell.smooth[layer] != smoothlevel)
1506 {
1507 lastcell.smooth[layer] = smoothlevel;
1508 esl << uint8 (smoothlevel);
1509 emask |= 1 << layer;
1510 }
1511 }
1512
1513 if (emask & 0xf)
1514 esl[eoldlen + 1] = emask & 0xff;
1515 else
1516 esl.reset (eoldlen);
1517 }
1518 } /* else this is a viewable space */
1519 } /* for x loop */
1520 } /* for y loop */
1521
1522 /* Verify that we in fact do need to send this */
1523 if (socket.ext_mapinfos)
1524 {
1525 if (!(sl.length () > startlen || socket.sent_scroll))
1526 {
1527 /* No map data will follow, so don't say the client
1528 * it doesn't need draw!
1529 */
1530 ewhatflag &= ~EMI_NOREDRAW;
1531 esl[ewhatstart + 1] = ewhatflag & 0xff;
1532 }
1533
1534 if (esl.length () > estartlen)
1535 socket.send_packet (esl);
1536 }
1537
1538 if (sl.length () > startlen || socket.sent_scroll)
1539 {
1540 socket.send_packet (sl);
1541 socket.sent_scroll = 0;
1542 }
1543 }
1544
1545 /**
1546 * Draws client map.
1547 */
1548 void
1549 draw_client_map (object *pl)
1550 {
1551 int i, j;
1552 sint16 ax, ay; /* ax and ay goes from 0 to max-size of arrays */
1553 int mflags;
1554 struct Map newmap;
1555 maptile *m, *pm;
1556
1557 if (pl->type != PLAYER)
1558 {
1559 LOG (llevError, "draw_client_map called with non player/non eric-server\n");
1560 return;
1561 }
1562
1563 pm = pl->map;
1564
1565 /* If player is just joining the game, he isn't here yet, so the map
1566 * can get swapped out. If so, don't try to send them a map. All will
1567 * be OK once they really log in.
1568 */
1569 if (pm == NULL || pm->in_memory != MAP_IN_MEMORY)
1570 return;
1571
1572 memset (&newmap, 0, sizeof (struct Map));
1573
1574 for (j = (pl->y - pl->contr->ns->mapy / 2); j < (pl->y + (pl->contr->ns->mapy + 1) / 2); j++)
1575 for (i = (pl->x - pl->contr->ns->mapx / 2); i < (pl->x + (pl->contr->ns->mapx + 1) / 2); i++)
1576 {
1577 ax = i;
1578 ay = j;
1579 m = pm;
1580 mflags = get_map_flags (m, &m, ax, ay, &ax, &ay);
1581
1582 if (mflags & P_OUT_OF_MAP)
1583 continue;
1584
1585 /* If a map is visible to the player, we don't want to swap it out
1586 * just to reload it. This should really call something like
1587 * swap_map, but this is much more efficient and 'good enough'
1588 */
1589 if (mflags & P_NEW_MAP)
1590 m->timeout = 50;
1591 }
1592
1593 /* do LOS after calls to update_position */
1594 if (pl->contr->do_los)
1595 {
1596 update_los (pl);
1597 pl->contr->do_los = 0;
1598 }
1599
1600 /* Big maps need a different drawing mechanism to work */
1601 draw_client_map1 (pl);
1602 }
1603
1604
1605 /*****************************************************************************/
1606
1607 /* GROS: The following one is used to allow a plugin to send a generic cmd to*/
1608
1609 /* a player. Of course, the client need to know the command to be able to */
1610
1611 /* manage it ! */
1612
1613 /*****************************************************************************/
1614 void
1615 send_plugin_custom_message (object *pl, char *buf)
1616 {
1617 pl->contr->ns->send_packet (buf);
1618 }
1619
1620 /**
1621 * This sends the skill number to name mapping. We ignore
1622 * the params - we always send the same info no matter what.
1623 */
1624 void
1625 send_skill_info (client *ns, char *params)
1626 {
1627 packet sl;
1628 sl << "replyinfo skill_info\n";
1629
1630 for (int i = 1; i < NUM_SKILLS; i++)
1631 sl.printf ("%d:%s\n", i + CS_STAT_SKILLINFO, &skill_names[i]);
1632
1633 if (sl.length () >= MAXSOCKBUF)
1634 {
1635 LOG (llevError, "Buffer overflow in send_skill_info!\n");
1636 fatal (0);
1637 }
1638
1639 ns->send_packet (sl);
1640 }
1641
1642 /**
1643 * This sends the spell path to name mapping. We ignore
1644 * the params - we always send the same info no matter what.
1645 */
1646 void
1647 send_spell_paths (client * ns, char *params)
1648 {
1649 packet sl;
1650
1651 sl << "replyinfo spell_paths\n";
1652
1653 for (int i = 0; i < NRSPELLPATHS; i++)
1654 sl.printf ("%d:%s\n", 1 << i, spellpathnames[i]);
1655
1656 if (sl.length () >= MAXSOCKBUF)
1657 {
1658 LOG (llevError, "Buffer overflow in send_spell_paths!\n");
1659 fatal (0);
1660 }
1661
1662 ns->send_packet (sl);
1663 }
1664
1665 /**
1666 * This looks for any spells the player may have that have changed their stats.
1667 * it then sends an updspell packet for each spell that has changed in this way
1668 */
1669 void
1670 esrv_update_spells (player *pl)
1671 {
1672 if (!pl->ns)
1673 return;
1674
1675 if (!pl->ns->monitor_spells)
1676 return;
1677
1678 for (object *spell = pl->ob->inv; spell; spell = spell->below)
1679 {
1680 if (spell->type == SPELL)
1681 {
1682 int flags = 0;
1683
1684 /* check if we need to update it */
1685 if (spell->last_sp != SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA))
1686 {
1687 spell->last_sp = SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA);
1688 flags |= UPD_SP_MANA;
1689 }
1690
1691 if (spell->last_grace != SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE))
1692 {
1693 spell->last_grace = SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE);
1694 flags |= UPD_SP_GRACE;
1695 }
1696
1697 if (spell->last_eat != spell->stats.dam + SP_level_dam_adjust (pl->ob, spell))
1698 {
1699 spell->last_eat = spell->stats.dam + SP_level_dam_adjust (pl->ob, spell);
1700 flags |= UPD_SP_DAMAGE;
1701 }
1702
1703 if (flags)
1704 {
1705 packet sl;
1706
1707 sl << "updspell "
1708 << uint8 (flags)
1709 << uint32 (spell->count);
1710
1711 if (flags & UPD_SP_MANA ) sl << uint16 (spell->last_sp);
1712 if (flags & UPD_SP_GRACE ) sl << uint16 (spell->last_grace);
1713 if (flags & UPD_SP_DAMAGE) sl << uint16 (spell->last_eat);
1714
1715 pl->ns->send_packet (sl);
1716 }
1717 }
1718 }
1719 }
1720
1721 void
1722 esrv_remove_spell (player *pl, object *spell)
1723 {
1724 if (!pl->ns->monitor_spells)
1725 return;
1726
1727 if (!pl || !spell || spell->env != pl->ob)
1728 {
1729 LOG (llevError, "Invalid call to esrv_remove_spell");
1730 return;
1731 }
1732
1733 packet sl ("delspell");
1734
1735 sl << uint32 (spell->count);
1736
1737 pl->ns->send_packet (sl);
1738 }
1739
1740 /* appends the spell *spell to the Socklist we will send the data to. */
1741 static void
1742 append_spell (player *pl, packet &sl, object *spell)
1743 {
1744 int i, skill = 0;
1745
1746 if (!(spell->name))
1747 {
1748 LOG (llevError, "item number %d is a spell with no name.\n", spell->count);
1749 return;
1750 }
1751
1752 /* store costs and damage in the object struct, to compare to later */
1753 spell->last_sp = SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA);
1754 spell->last_grace = SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE);
1755 spell->last_eat = spell->stats.dam + SP_level_dam_adjust (pl->ob, spell);
1756
1757 /* figure out which skill it uses, if it uses one */
1758 if (spell->skill)
1759 {
1760 for (i = 1; i < NUM_SKILLS; i++)
1761 if (!strcmp (spell->skill, skill_names[i]))
1762 {
1763 skill = i + CS_STAT_SKILLINFO;
1764 break;
1765 }
1766 }
1767
1768 // spells better have a face
1769 if (!spell->face)
1770 {
1771 LOG (llevError, "%s: spell has no face, but face is mandatory.\n", &spell->name);
1772 spell->face = face_find ("burnout.x11", blank_face);
1773 }
1774
1775 /* send the current values */
1776 sl << uint32 (spell->count)
1777 << uint16 (spell->level)
1778 << uint16 (spell->casting_time)
1779 << uint16 (spell->last_sp)
1780 << uint16 (spell->last_grace)
1781 << uint16 (spell->last_eat)
1782 << uint8 (skill)
1783 << uint32 (spell->path_attuned)
1784 << uint32 (spell->face)
1785 << data8 (spell->name)
1786 << data16 (spell->msg);
1787 }
1788
1789 /**
1790 * This tells the client to add the spell *ob, if *ob is NULL, then add
1791 * all spells in the player's inventory.
1792 */
1793 void
1794 esrv_add_spells (player *pl, object *spell)
1795 {
1796 if (!pl)
1797 {
1798 LOG (llevError, "esrv_add_spells, tried to add a spell to a NULL player");
1799 return;
1800 }
1801
1802 if (!pl->ns->monitor_spells)
1803 return;
1804
1805 packet sl ("addspell");
1806
1807 if (!spell)
1808 {
1809 for (spell = pl->ob->inv; spell; spell = spell->below)
1810 {
1811 /* were we to simply keep appending data here, we could exceed
1812 * MAXSOCKBUF if the player has enough spells to add, we know that
1813 * append_spells will always append 19 data bytes, plus 4 length
1814 * bytes and 3 strings (because that is the spec) so we need to
1815 * check that the length of those 3 strings, plus the 23 bytes,
1816 * won't take us over the length limit for the socket, if it does,
1817 * we need to send what we already have, and restart packet formation
1818 */
1819 /* Seeing crashes by overflowed buffers. Quick arithemetic seems
1820 * to show add_spell is 26 bytes + 2 strings. However, the overun
1821 * is hundreds of bytes off, so correcting 22 vs 26 doesn't seem
1822 * like it will fix this
1823 */
1824 if (spell->type != SPELL)
1825 continue;
1826
1827 if (sl.length () >= (MAXSOCKBUF - (26 + strlen (spell->name) + (spell->msg ? strlen (spell->msg) : 0))))
1828 {
1829 pl->ns->send_packet (sl);
1830
1831 sl.reset ();
1832 sl << "addspell ";
1833 }
1834
1835 append_spell (pl, sl, spell);
1836 }
1837 }
1838 else if (spell->type != SPELL)
1839 {
1840 LOG (llevError, "Asked to send a non-spell object as a spell");
1841 return;
1842 }
1843 else
1844 append_spell (pl, sl, spell);
1845
1846 if (sl.length () >= MAXSOCKBUF)
1847 {
1848 LOG (llevError, "Buffer overflow in esrv_add_spells!\n");
1849 fatal (0);
1850 }
1851
1852 /* finally, we can send the packet */
1853 pl->ns->send_packet (sl);
1854 }
1855