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

File Contents

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