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

File Contents

# User Rev Content
1 elmex 1.1 /*
2 pippijn 1.64 * 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 elmex 1.1
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 pippijn 1.26 #include <sys/types.h>
53     #include <sys/time.h>
54     #include <sys/socket.h>
55     #include <netinet/in.h>
56     #include <netdb.h>
57 elmex 1.1
58 root 1.38 #include <unistd.h>
59     #include <sys/time.h>
60 elmex 1.1
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 root 1.16 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 elmex 1.1 };
84    
85 root 1.2 static void
86 root 1.43 socket_map_scroll (client *ns, int dx, int dy)
87 root 1.2 {
88 root 1.16 struct Map newmap;
89     int x, y, mx, my;
90 root 1.2
91 root 1.42 ns->send_packet_printf ("map_scroll %d %d", dx, dy);
92 root 1.2
93 root 1.16 /* 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 root 1.2
103 root 1.16 /* 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 root 1.25 /* clear cells outside the viewable area */
115     memset (&newmap.cells[x][y], 0, sizeof (struct MapCell));
116 root 1.16 else if ((x + dx) < 0 || (x + dx) >= ns->mapx || (y + dy) < 0 || (y + dy) >= ns->mapy)
117 root 1.25 /* clear newly visible tiles within the viewable area */
118     memset (&(newmap.cells[x][y]), 0, sizeof (struct MapCell));
119 root 1.16 else
120 root 1.25 memcpy (&(newmap.cells[x][y]), &(ns->lastmap.cells[x + dx][y + dy]), sizeof (struct MapCell));
121 root 1.12 }
122 root 1.2 }
123    
124 root 1.16 memcpy (&(ns->lastmap), &newmap, sizeof (struct Map));
125 root 1.2
126 root 1.16 /* Make sure that the next "map1" command will be sent (even if it is
127     * empty).
128     */
129     ns->sent_scroll = 1;
130 root 1.2 }
131    
132 root 1.7 static void
133     clear_map (player *pl)
134     {
135 root 1.50 memset (&pl->ns->lastmap, 0, sizeof (pl->ns->lastmap));
136 root 1.7
137 root 1.50 if (pl->ns->newmapcmd == 1)
138     pl->ns->send_packet ("newmap");
139 root 1.7
140 root 1.50 pl->ns->floorbox_reset ();
141 root 1.7 }
142    
143 root 1.66 /** check for map/region change and send new map data */
144 elmex 1.1 static void
145     check_map_change (player *pl)
146     {
147 root 1.50 client &socket = *pl->ns;
148 root 1.2 object *ob = pl->ob;
149 root 1.16 char buf[MAX_BUF]; /* eauugggh */
150 elmex 1.1
151 root 1.2 if (socket.current_map != ob->map)
152     {
153     socket.current_map = ob->map;
154 elmex 1.1
155 root 1.7 clear_map (pl);
156 elmex 1.1
157 root 1.2 if (socket.mapinfocmd)
158 elmex 1.1 {
159 root 1.16 if (ob->map && ob->map->path[0])
160 root 1.2 {
161     int flags = 0;
162 elmex 1.1
163 root 1.16 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 root 1.2
172     snprintf (buf, MAX_BUF, "mapinfo - spatial %d %d %d %d %d %s",
173 root 1.55 flags, socket.mapx / 2 - ob->x, socket.mapy / 2 - ob->y, ob->map->width, ob->map->height, &ob->map->path);
174 root 1.2 }
175     else
176     snprintf (buf, MAX_BUF, "mapinfo current");
177 elmex 1.1
178 root 1.32 socket.send_packet (buf);
179 root 1.16 }
180 elmex 1.1 }
181 root 1.2 else if (socket.current_x != ob->x || socket.current_y != ob->y)
182     {
183 root 1.7 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 root 1.16 clear_map (pl); // current (<= 1.9.1) clients have unchecked buffer overflows
188 root 1.7 else
189     {
190     socket_map_scroll (&socket, ob->x - socket.current_x, ob->y - socket.current_y);
191 root 1.46 socket.floorbox_reset ();
192 root 1.7 }
193 root 1.2 }
194    
195     socket.current_x = ob->x;
196     socket.current_y = ob->y;
197 root 1.66
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 elmex 1.1 }
205    
206 root 1.40 /**
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 root 1.41 RequestInfo (char *buf, int len, client * ns)
213 root 1.40 {
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 root 1.16 void
251     ExtCmd (char *buf, int len, player *pl)
252 elmex 1.1 {
253 root 1.10 INVOKE_PLAYER (EXTCMD, pl, ARG_DATA (buf, len));
254 elmex 1.1 }
255    
256 root 1.16 void
257 root 1.45 ExtiCmd (char *buf, int len, client *ns)
258     {
259     INVOKE_CLIENT (EXTICMD, ns, ARG_DATA (buf, len));
260     }
261    
262     void
263 root 1.16 MapInfoCmd (char *buf, int len, player *pl)
264 elmex 1.1 {
265     // <mapinfo tag spatial tile-path
266     // >mapinfo tag spatial flags x y w h hash
267 root 1.16
268 pippijn 1.15 char bigbuf[MAX_BUF], *token;
269 elmex 1.1
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 root 1.21 maptile *map = pl->ob->map;
283 elmex 1.1 sint16 dx, dy;
284 root 1.50 int mapx = pl->ns->mapx / 2 - pl->ob->x;
285     int mapy = pl->ns->mapy / 2 - pl->ob->y;
286 root 1.16 int max_distance = 8; // limit maximum path length to something generous
287 elmex 1.1
288     while (*buf && map && max_distance)
289     {
290     int dir = *buf++;
291    
292     switch (dir)
293     {
294 root 1.58 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 elmex 1.1 }
319    
320     --max_distance;
321     }
322    
323     if (!max_distance)
324     snprintf (bigbuf, MAX_BUF, "mapinfo %s error", token);
325 root 1.16 else if (map && map->path[0])
326 elmex 1.1 {
327     int flags = 0;
328    
329 root 1.16 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 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);
339 elmex 1.1 }
340     else
341     snprintf (bigbuf, MAX_BUF, "mapinfo %s nomap", token);
342     }
343     else
344     snprintf (bigbuf, MAX_BUF, "mapinfo %s unsupported", token);
345 root 1.16
346 root 1.50 pl->ns->send_packet (bigbuf);
347 elmex 1.1 }
348    
349     /** This is the Setup cmd - easy first implementation */
350 root 1.16 void
351 root 1.41 SetUp (char *buf, int len, client * ns)
352 elmex 1.1 {
353 root 1.16 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 root 1.32 //LOG (llevInfo, "Get SetupCmd:: %s\n", buf);
364    
365 root 1.16 strcpy (cmdback, "setup");
366 root 1.32 for (s = 0; s < len; )
367 root 1.16 {
368     cmd = &buf[s];
369 root 1.12
370 root 1.16 /* find the next space, and put a null there */
371 root 1.32 for (; buf[s] && buf[s] != ' '; s++)
372     ;
373    
374 root 1.16 buf[s++] = 0;
375 root 1.32
376 root 1.16 while (buf[s] == ' ')
377     s++;
378    
379     if (s >= len)
380     break;
381    
382     param = &buf[s];
383 root 1.12
384 root 1.32 for (; buf[s] && buf[s] != ' '; s++)
385     ;
386    
387 root 1.16 buf[s++] = 0;
388 root 1.32
389 root 1.16 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 root 1.12
397 root 1.16 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 root 1.52 safe_strcat (cmdback, param, &slen, HUGE_BUF);
404 root 1.16 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 root 1.71
502 root 1.16 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 root 1.32
555 root 1.16 LOG (llevInfo, "SendBack SetupCmd:: %s\n", cmdback);
556 root 1.32 ns->send_packet (cmdback);
557 elmex 1.1 }
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 root 1.16 void
566 root 1.41 AddMeCmd (char *buf, int len, client * ns)
567 elmex 1.1 {
568 root 1.51 INVOKE_CLIENT (ADDME, ns, ARG_DATA (buf, len));
569 elmex 1.1 }
570    
571     /** Reply to ExtendedInfos command */
572 root 1.16 void
573 root 1.41 ToggleExtendedInfos (char *buf, int len, client * ns)
574 root 1.16 {
575     char cmdback[MAX_BUF];
576     char command[50];
577     int info, nextinfo;
578    
579     cmdback[0] = '\0';
580     nextinfo = 0;
581 root 1.17
582 root 1.16 while (1)
583     {
584     /* 1. Extract an info */
585     info = nextinfo;
586 root 1.17
587 root 1.16 while ((info < len) && (buf[info] == ' '))
588     info++;
589 root 1.17
590 root 1.16 if (info >= len)
591     break;
592 root 1.17
593 root 1.16 nextinfo = info + 1;
594 root 1.17
595 root 1.16 while ((nextinfo < len) && (buf[nextinfo] != ' '))
596     nextinfo++;
597 root 1.17
598 root 1.16 if (nextinfo - info >= 49) /*Erroneous info asked */
599     continue;
600 root 1.17
601 root 1.16 strncpy (command, &(buf[info]), nextinfo - info);
602 root 1.17
603 root 1.16 /* 2. Interpret info */
604     if (!strcmp ("smooth", command))
605 root 1.17 /* Toggle smoothing */
606     ns->EMI_smooth = !ns->EMI_smooth;
607 root 1.16 else
608 root 1.17 /*bad value */;
609    
610 root 1.16 /*3. Next info */
611     }
612 root 1.17
613 root 1.16 strcpy (cmdback, "ExtendedInfoSet");
614 root 1.17
615 root 1.16 if (ns->EMI_smooth)
616     {
617     strcat (cmdback, " ");
618     strcat (cmdback, "smoothing");
619     }
620 root 1.17
621 root 1.32 ns->send_packet (cmdback);
622 elmex 1.1 }
623 root 1.16
624 elmex 1.1 /*
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 root 1.16
632 elmex 1.1 /** Reply to ExtendedInfos command */
633 root 1.16 void
634 root 1.41 ToggleExtendedText (char *buf, int len, client * ns)
635 root 1.16 {
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 root 1.17
643 root 1.16 nextinfo = 0;
644     while (1)
645     {
646     /* 1. Extract an info */
647     info = nextinfo;
648 root 1.17
649 root 1.16 while ((info < len) && (buf[info] == ' '))
650     info++;
651 root 1.17
652 root 1.16 if (info >= len)
653     break;
654 root 1.17
655 root 1.16 nextinfo = info + 1;
656 root 1.17
657 root 1.16 while ((nextinfo < len) && (buf[nextinfo] != ' '))
658     nextinfo++;
659 root 1.17
660 root 1.16 if (nextinfo - info >= 49) /*Erroneous info asked */
661     continue;
662 root 1.17
663 root 1.16 strncpy (command, &(buf[info]), nextinfo - info);
664     command[nextinfo - info] = '\0';
665     /* 2. Interpret info */
666     i = sscanf (command, "%d", &flag);
667 root 1.17
668 root 1.16 if ((i == 1) && (flag > 0) && (flag <= MSG_TYPE_LAST))
669     ns->supported_readables |= (1 << flag);
670     /*3. Next info */
671     }
672 root 1.17
673 root 1.16 /* Send resulting state */
674     strcpy (cmdback, "ExtendedTextSet");
675 root 1.17
676 root 1.16 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 root 1.17
684 root 1.32 ns->send_packet (cmdback);
685 elmex 1.1 }
686    
687     /**
688 root 1.68 * Tells client the picture it has to use
689     * to smooth a picture number given as argument.
690     */
691 root 1.16 void
692 root 1.44 AskSmooth (char *buf, int len, client *ns)
693 root 1.16 {
694 root 1.73 ns->send_smooth (atoi (buf));
695 elmex 1.1 }
696    
697     /**
698     * This handles the general commands from the client (ie, north, fire, cast,
699     * etc.)
700     */
701 root 1.16 void
702     PlayerCmd (char *buf, int len, player *pl)
703 elmex 1.1 {
704 root 1.16 /* 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 root 1.44
711 root 1.16 buf = strchr (buf, ' '); /* advance beyond the numbers */
712     if (!buf)
713 root 1.44 return;
714    
715 root 1.16 buf++;
716 elmex 1.1 }
717 root 1.44
718 root 1.16 execute_newserver_command (pl->ob, (char *) buf);
719 root 1.44
720 root 1.16 /* 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 elmex 1.1 }
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 root 1.16 void
736 root 1.39 NewPlayerCmd (char *buf, int len, player *pl)
737 elmex 1.1 {
738 root 1.44 if (len <= 6)
739 root 1.16 {
740     LOG (llevDebug, "Corrupt ncom command <%s> not long enough - discarding\n", buf);
741     return;
742     }
743    
744 root 1.44 uint16 cmdid = net_uint16 ((uint8 *)buf);
745     sint32 repeat = net_sint32 ((uint8 *)buf + 2);
746 root 1.30
747 root 1.16 /* -1 is special - no repeat, but don't update */
748     if (repeat != -1)
749 root 1.28 pl->count = repeat;
750    
751 root 1.44 buf += 6; //len -= 6;
752 root 1.16
753 root 1.44 execute_newserver_command (pl->ob, buf);
754 root 1.28
755 root 1.16 /* 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 root 1.44 //TODO: schmorp thinks whatever this calculates, it makes no sense at all
763 root 1.65 int time = fabs (pl->ob->speed) < 0.001
764 root 1.44 ? time = MAX_TIME * 100
765 root 1.65 : time = (int) (MAX_TIME / fabs (pl->ob->speed));
766 root 1.27
767 root 1.28 /* Send confirmation of command execution now */
768 root 1.47 packet sl ("comc");
769     sl << uint16 (cmdid) << uint32 (time);
770 root 1.50 pl->ns->send_packet (sl);
771 elmex 1.1 }
772    
773     /** This is a reply to a previous query. */
774 root 1.16 void
775 root 1.49 ReplyCmd (char *buf, int len, client *ns)
776 elmex 1.1 {
777 root 1.50 if (ns->state == ST_CUSTOM)
778     {
779     INVOKE_CLIENT (REPLY, ns, ARG_DATA (buf, len));
780     return;
781     }
782    
783 root 1.49 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 root 1.16 /* This is to synthesize how the data would be stored if it
790 root 1.44 * was normally entered. A bit of a hack, and should be cleaned up
791 root 1.16 * 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 root 1.50 switch (ns->state)
802 root 1.16 {
803 root 1.44 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 elmex 1.1
815 root 1.44 default:
816 root 1.50 LOG (llevError, "Unknown input state: %d\n", ns->state);
817 elmex 1.1 }
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 root 1.16 void
828 root 1.41 VersionCmd (char *buf, int len, client * ns)
829 elmex 1.1 {
830 root 1.16 if (!buf)
831     {
832     LOG (llevError, "CS: received corrupted version command\n");
833     return;
834 elmex 1.1 }
835    
836 root 1.16 ns->cs_version = atoi (buf);
837     ns->sc_version = ns->cs_version;
838 root 1.44
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 root 1.16 if (!cp)
848     return;
849 root 1.44
850 root 1.16 ns->sc_version = atoi (cp);
851 root 1.44
852     //if (VERSION_SC != ns->sc_version)
853     // unchecked;
854    
855 root 1.16 cp = strchr (cp + 1, ' ');
856 root 1.44
857 root 1.16 if (cp)
858     {
859 root 1.41 assign (ns->version, cp + 1);
860 root 1.16
861 root 1.29 if (ns->sc_version < 1026)
862 root 1.44 ns->send_packet_printf ("drawinfo %d %s", NDI_RED,
863 root 1.16 "**** VERSION WARNING ****\n**** CLIENT IS TOO OLD!! UPDATE THE CLIENT!! ****");
864 elmex 1.1 }
865     }
866    
867     /** sound related functions. */
868 root 1.16 void
869 root 1.41 SetSound (char *buf, int len, client * ns)
870 elmex 1.1 {
871 root 1.16 ns->sound = atoi (buf);
872 elmex 1.1 }
873    
874     /** client wants the map resent */
875 root 1.16 void
876     MapRedrawCmd (char *buf, int len, player *pl)
877 elmex 1.1 {
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 root 1.16 void
890     MoveCmd (char *buf, int len, player *pl)
891 elmex 1.1 {
892 root 1.16 int vals[3], i;
893 elmex 1.1
894 root 1.16 /* A little funky here. We only cycle for 2 records, because
895 root 1.44 * we obviously are not going to find a space after the third
896     * record. Perhaps we should just replace this with a
897 root 1.16 * sscanf?
898     */
899     for (i = 0; i < 2; i++)
900     {
901     vals[i] = atoi (buf);
902 root 1.44
903 root 1.16 if (!(buf = strchr (buf, ' ')))
904     {
905     LOG (llevError, "Incomplete move command: %s\n", buf);
906     return;
907 root 1.12 }
908 root 1.44
909 root 1.16 buf++;
910 elmex 1.1 }
911 root 1.44
912 root 1.16 vals[2] = atoi (buf);
913 elmex 1.1
914     /* LOG(llevDebug,"Move item %d (nrof=%d) to %d.\n", vals[1], vals[2], vals[0]);*/
915 root 1.16 esrv_move_object (pl->ob, vals[0], vals[1], vals[2]);
916 elmex 1.1 }
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 root 1.16 void
929 root 1.44 send_query (client *ns, uint8 flags, char *text)
930 elmex 1.1 {
931 root 1.44 ns->send_packet_printf ("query %d %s", flags, text ? text : "");
932 elmex 1.1 }
933    
934     #define AddIfInt64(Old,New,Type) if (Old != New) {\
935 root 1.12 Old = New; \
936 root 1.28 sl << uint8 (Type) << uint64 (New); \
937 root 1.12 }
938 elmex 1.1
939     #define AddIfInt(Old,New,Type) if (Old != New) {\
940 root 1.12 Old = New; \
941 root 1.28 sl << uint8 (Type) << uint32 (New); \
942 root 1.12 }
943 elmex 1.1
944     #define AddIfShort(Old,New,Type) if (Old != New) {\
945 root 1.12 Old = New; \
946 root 1.28 sl << uint8 (Type) << uint16 (New); \
947 root 1.12 }
948 elmex 1.1
949     #define AddIfFloat(Old,New,Type) if (Old != New) {\
950 root 1.12 Old = New; \
951 root 1.28 sl << uint8 (Type) << uint32 (New*FLOAT_MULTI); \
952 root 1.12 }
953 elmex 1.1
954     #define AddIfString(Old,New,Type) if (Old == NULL || strcmp(Old,New)) {\
955 root 1.28 free(Old); Old = strdup (New);\
956     sl << uint8 (Type) << data8 (New); \
957 root 1.12 }
958 elmex 1.1
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 root 1.16 void
966     esrv_update_stats (player *pl)
967 elmex 1.1 {
968 root 1.16 char buf[MAX_BUF];
969     uint16 flags;
970    
971 root 1.61 client *ns = pl->ns;
972     if (!ns)
973     return;
974    
975     object *ob = pl->ob;
976     if (!ob)
977 root 1.53 return;
978    
979 root 1.47 packet sl ("stats");
980 elmex 1.1
981 root 1.61 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 root 1.22
995 root 1.52 for (int s = 0; s < NUM_SKILLS; s++)
996 root 1.61 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 root 1.28
1009 root 1.61 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 root 1.22
1019 root 1.16 flags = 0;
1020 root 1.22
1021 root 1.16 if (pl->fire_on)
1022     flags |= SF_FIREON;
1023 root 1.22
1024 root 1.16 if (pl->run_on)
1025     flags |= SF_RUNON;
1026    
1027 root 1.61 AddIfShort (ns->last_flags, flags, CS_STAT_FLAGS);
1028 root 1.22
1029 root 1.61 if (ns->sc_version < 1025)
1030     { AddIfShort (ns->last_resist[ATNR_PHYSICAL], ob->resist[ATNR_PHYSICAL], CS_STAT_ARMOUR) }
1031 root 1.16 else
1032 root 1.44 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 root 1.16
1038 root 1.61 AddIfShort (ns->last_resist[i], ob->resist[i], atnr_cs_stat[i]);
1039 root 1.44 }
1040 root 1.22
1041 root 1.50 if (pl->ns->monitor_spells)
1042 root 1.16 {
1043 root 1.61 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 root 1.16 }
1047 root 1.22
1048 root 1.61 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 root 1.16
1053     /* Only send it away if we have some actual data */
1054 root 1.34 if (sl.length () > 6)
1055 root 1.61 ns->send_packet (sl);
1056 elmex 1.1 }
1057    
1058     /**
1059     * Tells the client that here is a player it should start using.
1060     */
1061 root 1.16 void
1062     esrv_new_player (player *pl, uint32 weight)
1063 elmex 1.1 {
1064 root 1.47 packet sl ("player");
1065 elmex 1.1
1066 root 1.28 sl << uint32 (pl->ob->count)
1067     << uint32 (weight)
1068 root 1.69 << uint32 (pl->ob->face)
1069 root 1.28 << data8 (pl->ob->name);
1070 root 1.16
1071 root 1.61 pl->ns->last_weight = weight;
1072 root 1.50 pl->ns->send_packet (sl);
1073 root 1.16 SET_FLAG (pl->ob, FLAG_CLIENT_SENT);
1074 elmex 1.1 }
1075    
1076     /******************************************************************************
1077     *
1078     * Start of map related commands.
1079     *
1080     ******************************************************************************/
1081    
1082     /** Clears a map cell */
1083 root 1.16 static void
1084 root 1.70 map_clearcell (struct MapCell *cell, int count)
1085 elmex 1.1 {
1086 root 1.70 cell->faces[0] = 0;
1087     cell->faces[1] = 0;
1088     cell->faces[2] = 0;
1089 root 1.25 cell->count = count;
1090     cell->stat_hp = 0;
1091     cell->flags = 0;
1092     cell->player = 0;
1093 elmex 1.1 }
1094    
1095     #define MAX_LAYERS 3
1096    
1097     /**
1098     * Removes the need to replicate the same code for each layer.
1099     * this returns true if this space is now in fact different than
1100     * it was.
1101     * sl is the socklist this data is going into.
1102     * ns is the socket we are working on - all the info we care
1103     * about is in this socket structure, so now need not pass the
1104     * entire player object.
1105     * mx and my are map coordinate offsets for map mp
1106     * sx and sy are the offsets into the socket structure that
1107     * holds the old values.
1108     * layer is the layer to update, with 2 being the floor and 0 the
1109     * top layer (this matches what the GET_MAP_FACE and GET_MAP_FACE_OBJ)
1110     * take. Interesting to note that before this function, the map1 function
1111     * numbers the spaces differently - I think this was a leftover from
1112     * the map command, where the faces stack up. Sinces that is no longer
1113     * the case, it seems to make more sense to have these layer values
1114     * actually match.
1115     */
1116 root 1.16 static int
1117 root 1.41 update_space (packet &sl, client &ns, maptile *mp, int mx, int my, int sx, int sy, int layer)
1118 elmex 1.1 {
1119 root 1.16 uint16 face_num;
1120     int bx, by, i;
1121    
1122 root 1.70 object *ob = GET_MAP_FACE_OBJ (mp, mx, my, layer);
1123 root 1.16
1124     /* If there is no object for this space, or if the face for the object
1125     * is the blank face, set the face number to zero.
1126     * else if we have the stored head object for this space, that takes
1127     * precedence over the other object for this space.
1128     * otherwise, we do special head processing
1129     */
1130     if (!ob || ob->face == blank_face)
1131     face_num = 0;
1132     else
1133 root 1.70 /* In this case, we are already at the lower right or single part object,
1134     * so nothing special
1135     */
1136     face_num = ob->face;
1137 root 1.16
1138     /* We've gotten what face we want to use for the object. Now see if
1139     * if it has changed since we last sent it to the client.
1140     */
1141 root 1.28 if (ns.lastmap.cells[sx][sy].faces[layer] != face_num)
1142 root 1.16 {
1143 root 1.28 ns.lastmap.cells[sx][sy].faces[layer] = face_num;
1144 root 1.73
1145 root 1.28 if (!(ns.faces_sent[face_num] & NS_FACESENT_FACE))
1146 root 1.73 if (ob)
1147     ns.send_faces (ob);
1148     else
1149     ns.send_face (face_num);
1150 root 1.28
1151     sl << uint16 (face_num);
1152 root 1.16 return 1;
1153 elmex 1.1 }
1154 root 1.28
1155 root 1.16 /* Nothing changed */
1156     return 0;
1157 elmex 1.1 }
1158    
1159     /**
1160     * This function is mainly a copy of update_space,
1161     * except it handles update of the smoothing updates,
1162     * not the face updates.
1163     * Removes the need to replicate the same code for each layer.
1164     * this returns true if this smooth is now in fact different
1165     * than it was.
1166     * sl is the socklist this data is going into.
1167     * ns is the socket we are working on - all the info we care
1168     * about is in this socket structure, so know need to pass the
1169     * entire player object.
1170     * mx and my are map coordinate offsets for map mp
1171     * sx and sy are the offsets into the socket structure that
1172     * holds the old values.
1173     * layer is the layer to update, with 2 being the floor and 0 the
1174     * top layer (this matches what the GET_MAP_FACE and GET_MAP_FACE_OBJ
1175     * take.
1176     */
1177 root 1.16 static inline int
1178 root 1.41 update_smooth (packet &sl, client &ns, maptile *mp, int mx, int my, int sx, int sy, int layer)
1179 elmex 1.1 {
1180 root 1.73 object *ob = GET_MAP_FACE_OBJ (mp, mx, my, layer);
1181 elmex 1.1
1182 root 1.16 /* If there is no object for this space, or if the face for the object
1183     * is the blank face, set the smoothlevel to zero.
1184     */
1185 root 1.73 int smoothlevel = ob && ob->face != blank_face ? ob->smoothlevel : 0;
1186 root 1.16
1187     /* We've gotten what face we want to use for the object. Now see if
1188     * if it has changed since we last sent it to the client.
1189     */
1190     if (smoothlevel > 255)
1191     smoothlevel = 255;
1192     else if (smoothlevel < 0)
1193     smoothlevel = 0;
1194 root 1.28
1195     if (ns.lastmap.cells[sx][sy].smooth[layer] != smoothlevel)
1196 root 1.16 {
1197 root 1.73 if (smoothlevel)
1198     ns.send_smooth (ob->face);
1199    
1200 root 1.28 ns.lastmap.cells[sx][sy].smooth[layer] = smoothlevel;
1201     sl << uint8 (smoothlevel);
1202 root 1.16 return 1;
1203 elmex 1.1 }
1204 root 1.28
1205 root 1.16 /* Nothing changed */
1206     return 0;
1207 elmex 1.1 }
1208    
1209     /**
1210     * Returns the size of a data for a map square as returned by
1211     * mapextended. There are CLIENTMAPX*CLIENTMAPY*LAYERS entries
1212     * available.
1213     */
1214 root 1.16 int
1215 root 1.41 getExtendedMapInfoSize (client * ns)
1216 root 1.16 {
1217     int result = 0;
1218    
1219     if (ns->ext_mapinfos)
1220     {
1221     if (ns->EMI_smooth)
1222     result += 1; /*One byte for smoothlevel */
1223 elmex 1.1 }
1224 root 1.44
1225 root 1.16 return result;
1226 elmex 1.1 }
1227 root 1.16
1228 root 1.62 // prefetch (and touch) all maps within a specific distancd
1229     static void
1230     prefetch_surrounding_maps (maptile *map, int distance)
1231     {
1232     map->last_access = runtime;
1233    
1234     if (--distance)
1235     for (int dir = 4; --dir; )
1236     if (const shstr &path = map->tile_path [dir])
1237     if (maptile *&neigh = map->tile_map [dir])
1238     prefetch_surrounding_maps (neigh, distance);
1239     else
1240     neigh = maptile::find_async (path, map);
1241     }
1242    
1243     // prefetch a generous area around the player
1244     static void
1245     prefetch_surrounding_maps (object *op)
1246     {
1247     prefetch_surrounding_maps (op->map, 3);
1248     }
1249    
1250 elmex 1.1 /**
1251     * This function uses the new map1 protocol command to send the map
1252     * to the client. It is necessary because the old map command supports
1253     * a maximum map size of 15x15.
1254     * This function is much simpler than the old one. This is because
1255     * the old function optimized to send as few face identifiers as possible,
1256     * at the expense of sending more coordinate location (coordinates were
1257     * only 1 byte, faces 2 bytes, so this was a worthwhile savings). Since
1258     * we need 2 bytes for coordinates and 2 bytes for faces, such a trade off
1259     * maps no sense. Instead, we actually really only use 12 bits for coordinates,
1260     * and use the other 4 bits for other informatiion. For full documentation
1261     * of what we send, see the doc/Protocol file.
1262     * I will describe internally what we do:
1263 root 1.50 * the ns->lastmap shows how the map last looked when sent to the client.
1264 elmex 1.1 * in the lastmap structure, there is a cells array, which is set to the
1265     * maximum viewable size (As set in config.h).
1266     * in the cells, there are faces and a count value.
1267     * we use the count value to hold the darkness value. If -1, then this space
1268     * is not viewable.
1269     * we use faces[0] faces[1] faces[2] to hold what the three layers
1270     * look like.
1271     */
1272 root 1.16 void
1273     draw_client_map1 (object *pl)
1274 elmex 1.1 {
1275 root 1.72 int x, y, ax, ay, startlen, max_x, max_y, oldlen;
1276 root 1.16 sint16 nx, ny;
1277     int estartlen, eoldlen;
1278     uint8 eentrysize;
1279     uint16 ewhatstart, ewhatflag;
1280     uint8 extendedinfos;
1281 root 1.21 maptile *m;
1282 root 1.16
1283 root 1.50 client &socket = *pl->contr->ns;
1284 root 1.16
1285 root 1.60 if (!pl->active)
1286 root 1.58 return;
1287    
1288 root 1.16 check_map_change (pl->contr);
1289 root 1.62 prefetch_surrounding_maps (pl);
1290 root 1.16
1291 root 1.47 packet sl (socket.mapmode == Map1Cmd ? "map1" : "map1a");
1292 root 1.33 packet esl;
1293 root 1.28
1294 root 1.34 startlen = sl.length ();
1295 root 1.25
1296 root 1.16 /*Extendedmapinfo structure initialisation */
1297     if (socket.ext_mapinfos)
1298     {
1299     extendedinfos = EMI_NOREDRAW;
1300 root 1.25
1301 root 1.16 if (socket.EMI_smooth)
1302     extendedinfos |= EMI_SMOOTH;
1303 root 1.25
1304 root 1.34 ewhatstart = esl.length ();
1305 root 1.16 ewhatflag = extendedinfos; /*The EMI_NOREDRAW bit
1306     could need to be taken away */
1307     eentrysize = getExtendedMapInfoSize (&socket);
1308 root 1.28 esl << "mapextended "
1309     << uint8 (extendedinfos)
1310     << uint8 (eentrysize);
1311 root 1.34
1312     estartlen = esl.length ();
1313 root 1.16 }
1314     else
1315     {
1316     /* suppress compiler warnings */
1317     ewhatstart = 0;
1318     ewhatflag = 0;
1319     estartlen = 0;
1320     }
1321 root 1.25
1322 root 1.16 /* x,y are the real map locations. ax, ay are viewport relative
1323     * locations.
1324     */
1325     ay = 0;
1326    
1327     /* We could do this logic as conditionals in the if statement,
1328     * but that started to get a bit messy to look at.
1329     */
1330     max_x = pl->x + (socket.mapx + 1) / 2;
1331     max_y = pl->y + (socket.mapy + 1) / 2;
1332 root 1.25
1333 root 1.16 for (y = pl->y - socket.mapy / 2; y < max_y; y++, ay++)
1334     {
1335     ax = 0;
1336     for (x = pl->x - socket.mapx / 2; x < max_x; x++, ax++)
1337     {
1338 root 1.72 int emask, mask;
1339 root 1.16 emask = mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4;
1340    
1341 root 1.25 MapCell &lastcell = socket.lastmap.cells[ax][ay];
1342 root 1.16
1343     /* If the coordinates are not valid, or it is too dark to see,
1344     * we tell the client as such
1345     */
1346     nx = x;
1347     ny = y;
1348     m = get_map_from_coord (pl->map, &nx, &ny);
1349 root 1.25
1350 root 1.16 if (!m)
1351     {
1352     /* space is out of map. Update space and clear values
1353     * if this hasn't already been done. If the space is out
1354     * of the map, it shouldn't have a head
1355     */
1356     if (lastcell.count != -1)
1357     {
1358 root 1.28 sl << uint16 (mask);
1359 root 1.70 map_clearcell (&lastcell, -1);
1360 root 1.12 }
1361 root 1.72
1362     continue;
1363 root 1.16 }
1364 root 1.72
1365     m->touch ();
1366    
1367     int d = pl->contr->blocked_los[ax][ay];
1368    
1369     if (d > 3)
1370 root 1.16 {
1371 root 1.56
1372 root 1.16 int need_send = 0, count;
1373    
1374     /* This block deals with spaces that are not visible for whatever
1375 root 1.71 * reason. Still may need to send the head for this space.
1376 root 1.16 */
1377 root 1.12
1378 root 1.34 oldlen = sl.length ();
1379 root 1.3
1380 root 1.28 sl << uint16 (mask);
1381 root 1.25
1382 root 1.16 if (lastcell.count != -1)
1383     need_send = 1;
1384 root 1.25
1385 root 1.16 count = -1;
1386 root 1.3
1387 root 1.70 /* properly clear a previously sent big face */
1388 root 1.72 if (lastcell.faces[0] || lastcell.faces[1] || lastcell.faces[2]
1389 root 1.70 || lastcell.stat_hp || lastcell.flags || lastcell.player)
1390     need_send = 1;
1391 root 1.3
1392 root 1.70 map_clearcell (&lastcell, count);
1393 root 1.3
1394 root 1.16 if ((mask & 0xf) || need_send)
1395 root 1.37 sl[oldlen + 1] = mask & 0xff;
1396 root 1.16 else
1397 root 1.34 sl.reset (oldlen);
1398 root 1.16 }
1399     else
1400     {
1401 root 1.71 /* In this block, the space is visible.
1402 root 1.16 */
1403    
1404     /* Rather than try to figure out what everything that we might
1405     * need to send is, then form the packet after that,
1406     * we presume that we will in fact form a packet, and update
1407     * the bits by what we do actually send. If we send nothing,
1408 root 1.34 * we just back out sl.length () to the old value, and no harm
1409 root 1.16 * is done.
1410     * I think this is simpler than doing a bunch of checks to see
1411     * what if anything we need to send, setting the bits, then
1412     * doing those checks again to add the real data.
1413     */
1414 root 1.72 oldlen = sl.length ();
1415 root 1.34 eoldlen = esl.length ();
1416 root 1.72
1417 root 1.28 sl << uint16 (mask);
1418 root 1.16
1419     if (socket.ext_mapinfos)
1420 root 1.28 esl << uint16 (emask);
1421 root 1.16
1422     unsigned char dummy;
1423     unsigned char *last_ext = &dummy;
1424    
1425     /* Darkness changed */
1426     if (lastcell.count != d && socket.darkness)
1427     {
1428     mask |= 0x8;
1429    
1430     if (socket.extmap)
1431     {
1432     *last_ext |= 0x80;
1433 root 1.37 last_ext = &sl[sl.length ()];
1434 root 1.28 sl << uint8 (d);
1435 root 1.16 }
1436     else
1437 root 1.28 sl << uint8 (255 - 64 * d);
1438 root 1.16 }
1439 root 1.3
1440 root 1.16 lastcell.count = d;
1441 root 1.8
1442 root 1.16 if (socket.extmap)
1443     {
1444     uint8 stat_hp = 0;
1445     uint8 stat_width = 0;
1446 root 1.24 uint8 flags = 0;
1447 root 1.27 UUID player = 0;
1448 root 1.16
1449     // send hp information, if applicable
1450     if (object *op = GET_MAP_FACE_OBJ (m, nx, ny, 0))
1451     {
1452     if (op->head || op->invisible)
1453     ; // do not show
1454     else if (op->type == PLAYER
1455     || QUERY_FLAG (op, FLAG_MONSTER) || QUERY_FLAG (op, FLAG_ALIVE) || QUERY_FLAG (op, FLAG_GENERATOR))
1456     {
1457     if (op->stats.maxhp > 0 && (unsigned) op->stats.maxhp > (unsigned) op->stats.hp)
1458     {
1459     stat_hp = 255 - (op->stats.hp * 255 + 254) / op->stats.maxhp;
1460     stat_width = op->arch->tail_x;
1461     }
1462     }
1463 elmex 1.1
1464 root 1.24 if (op->msg && op->msg[0] == '@')
1465     flags |= 1;
1466    
1467 root 1.16 if (op->type == PLAYER && op != pl)
1468     player = op->count;
1469     }
1470 root 1.12
1471 root 1.16 if (lastcell.stat_hp != stat_hp)
1472     {
1473     lastcell.stat_hp = stat_hp;
1474    
1475     mask |= 0x8;
1476     *last_ext |= 0x80;
1477 root 1.37 last_ext = &sl[sl.length ()];
1478 root 1.27
1479     sl << uint8 (5) << uint8 (stat_hp);
1480 root 1.16
1481     if (stat_width > 1)
1482     {
1483     *last_ext |= 0x80;
1484 root 1.37 last_ext = &sl[sl.length ()];
1485 root 1.27
1486     sl << uint8 (6) << uint8 (stat_width);
1487 root 1.16 }
1488     }
1489 root 1.12
1490 root 1.20 if (lastcell.player != player)
1491 root 1.16 {
1492     lastcell.player = player;
1493    
1494     mask |= 0x8;
1495     *last_ext |= 0x80;
1496 root 1.37 last_ext = &sl[sl.length ()];
1497 root 1.27
1498     sl << uint8 (0x47) << uint8 (8) << (uint64)player;
1499 root 1.16 }
1500 root 1.24
1501     if (lastcell.flags != flags)
1502     {
1503     lastcell.flags = flags;
1504    
1505     mask |= 0x8;
1506     *last_ext |= 0x80;
1507 root 1.37 last_ext = &sl[sl.length ()];
1508 root 1.27
1509     sl << uint8 (8) << uint8 (flags);
1510 root 1.24 }
1511 root 1.16 }
1512 root 1.12
1513 root 1.16 /* Floor face */
1514 root 1.28 if (update_space (sl, socket, m, nx, ny, ax, ay, 2))
1515 root 1.16 mask |= 0x4;
1516    
1517     if (socket.EMI_smooth)
1518 root 1.28 if (update_smooth (esl, socket, m, nx, ny, ax, ay, 2))
1519 root 1.16 emask |= 0x4;
1520    
1521     /* Middle face */
1522 root 1.28 if (update_space (sl, socket, m, nx, ny, ax, ay, 1))
1523 root 1.16 mask |= 0x2;
1524    
1525     if (socket.EMI_smooth)
1526 root 1.28 if (update_smooth (esl, socket, m, nx, ny, ax, ay, 1))
1527 root 1.16 emask |= 0x2;
1528    
1529     if (nx == pl->x && ny == pl->y && pl->invisible & (pl->invisible < 50 ? 4 : 1))
1530     {
1531 root 1.69 if (lastcell.faces[0] != pl->face)
1532 root 1.16 {
1533 root 1.69 lastcell.faces[0] = pl->face;
1534 root 1.16 mask |= 0x1;
1535 root 1.25
1536 root 1.73 socket.send_faces (pl);
1537 root 1.25
1538 root 1.69 sl << uint16 (pl->face);
1539 root 1.12 }
1540     }
1541 root 1.16 else
1542     {
1543 root 1.25 /* Top face */
1544 root 1.28 if (update_space (sl, socket, m, nx, ny, ax, ay, 0))
1545 root 1.16 mask |= 0x1;
1546 root 1.25
1547 root 1.16 if (socket.EMI_smooth)
1548 root 1.28 if (update_smooth (esl, socket, m, nx, ny, ax, ay, 0))
1549 root 1.25 emask |= 0x1;
1550 root 1.16 }
1551 root 1.25
1552 root 1.16 /* Check to see if we are in fact sending anything for this
1553     * space by checking the mask. If so, update the mask.
1554     * if not, reset the len to that from before adding the mask
1555     * value, so we don't send those bits.
1556     */
1557     if (mask & 0xf)
1558 root 1.37 sl[oldlen + 1] = mask & 0xff;
1559 root 1.16 else
1560 root 1.34 sl.reset (oldlen);
1561 root 1.25
1562 root 1.16 if (emask & 0xf)
1563 root 1.37 esl[eoldlen + 1] = emask & 0xff;
1564 root 1.16 else
1565 root 1.34 esl.reset (eoldlen);
1566 root 1.16 } /* else this is a viewable space */
1567     } /* for x loop */
1568     } /* for y loop */
1569    
1570     /* Verify that we in fact do need to send this */
1571     if (socket.ext_mapinfos)
1572     {
1573 root 1.34 if (!(sl.length () > startlen || socket.sent_scroll))
1574 root 1.16 {
1575     /* No map data will follow, so don't say the client
1576     * it doesn't need draw!
1577     */
1578 root 1.72 ewhatflag &= ~EMI_NOREDRAW;
1579 root 1.37 esl[ewhatstart + 1] = ewhatflag & 0xff;
1580 root 1.16 }
1581 root 1.18
1582 root 1.34 if (esl.length () > estartlen)
1583 root 1.37 socket.send_packet (esl);
1584 root 1.16 }
1585 root 1.18
1586 root 1.34 if (sl.length () > startlen || socket.sent_scroll)
1587 root 1.16 {
1588 root 1.37 socket.send_packet (sl);
1589 root 1.16 socket.sent_scroll = 0;
1590 elmex 1.1 }
1591     }
1592    
1593     /**
1594     * Draws client map.
1595     */
1596 root 1.16 void
1597     draw_client_map (object *pl)
1598 elmex 1.1 {
1599 root 1.16 int i, j;
1600 pippijn 1.67 sint16 ax, ay; /* ax and ay goes from 0 to max-size of arrays */
1601     int mflags;
1602 root 1.16 struct Map newmap;
1603 root 1.21 maptile *m, *pm;
1604 root 1.16
1605     if (pl->type != PLAYER)
1606     {
1607     LOG (llevError, "draw_client_map called with non player/non eric-server\n");
1608     return;
1609     }
1610    
1611     pm = pl->map;
1612    
1613     /* If player is just joining the game, he isn't here yet, so the map
1614     * can get swapped out. If so, don't try to send them a map. All will
1615     * be OK once they really log in.
1616     */
1617     if (pm == NULL || pm->in_memory != MAP_IN_MEMORY)
1618     return;
1619    
1620     memset (&newmap, 0, sizeof (struct Map));
1621    
1622 root 1.50 for (j = (pl->y - pl->contr->ns->mapy / 2); j < (pl->y + (pl->contr->ns->mapy + 1) / 2); j++)
1623     for (i = (pl->x - pl->contr->ns->mapx / 2); i < (pl->x + (pl->contr->ns->mapx + 1) / 2); i++)
1624 root 1.48 {
1625     ax = i;
1626     ay = j;
1627     m = pm;
1628     mflags = get_map_flags (m, &m, ax, ay, &ax, &ay);
1629    
1630     if (mflags & P_OUT_OF_MAP)
1631     continue;
1632    
1633     /* If a map is visible to the player, we don't want to swap it out
1634     * just to reload it. This should really call something like
1635     * swap_map, but this is much more efficient and 'good enough'
1636     */
1637     if (mflags & P_NEW_MAP)
1638     m->timeout = 50;
1639     }
1640 root 1.44
1641 root 1.16 /* do LOS after calls to update_position */
1642     if (pl->contr->do_los)
1643     {
1644     update_los (pl);
1645     pl->contr->do_los = 0;
1646     }
1647    
1648 root 1.34 /* Big maps need a different drawing mechanism to work */
1649     draw_client_map1 (pl);
1650 elmex 1.1 }
1651    
1652    
1653     /*****************************************************************************/
1654 root 1.16
1655 elmex 1.1 /* GROS: The following one is used to allow a plugin to send a generic cmd to*/
1656 root 1.16
1657 elmex 1.1 /* a player. Of course, the client need to know the command to be able to */
1658 root 1.16
1659 elmex 1.1 /* manage it ! */
1660 root 1.16
1661 elmex 1.1 /*****************************************************************************/
1662 root 1.16 void
1663     send_plugin_custom_message (object *pl, char *buf)
1664 elmex 1.1 {
1665 root 1.50 pl->contr->ns->send_packet (buf);
1666 elmex 1.1 }
1667    
1668     /**
1669     * This sends the skill number to name mapping. We ignore
1670     * the params - we always send the same info no matter what.
1671     */
1672 root 1.16 void
1673 root 1.41 send_skill_info (client *ns, char *params)
1674 elmex 1.1 {
1675 root 1.33 packet sl;
1676 root 1.28 sl << "replyinfo skill_info\n";
1677    
1678     for (int i = 1; i < NUM_SKILLS; i++)
1679 root 1.29 sl.printf ("%d:%s\n", i + CS_STAT_SKILLINFO, &skill_names[i]);
1680 elmex 1.1
1681 root 1.34 if (sl.length () >= MAXSOCKBUF)
1682 root 1.16 {
1683     LOG (llevError, "Buffer overflow in send_skill_info!\n");
1684     fatal (0);
1685 elmex 1.1 }
1686 root 1.28
1687 root 1.37 ns->send_packet (sl);
1688 elmex 1.1 }
1689    
1690     /**
1691     * This sends the spell path to name mapping. We ignore
1692     * the params - we always send the same info no matter what.
1693     */
1694 root 1.16 void
1695 root 1.41 send_spell_paths (client * ns, char *params)
1696 root 1.16 {
1697 root 1.33 packet sl;
1698 root 1.28
1699     sl << "replyinfo spell_paths\n";
1700    
1701     for (int i = 0; i < NRSPELLPATHS; i++)
1702 root 1.29 sl.printf ("%d:%s\n", 1 << i, spellpathnames[i]);
1703 root 1.16
1704 root 1.34 if (sl.length () >= MAXSOCKBUF)
1705 root 1.16 {
1706     LOG (llevError, "Buffer overflow in send_spell_paths!\n");
1707     fatal (0);
1708 elmex 1.1 }
1709 root 1.28
1710 root 1.37 ns->send_packet (sl);
1711 elmex 1.1 }
1712    
1713     /**
1714     * This looks for any spells the player may have that have changed their stats.
1715     * it then sends an updspell packet for each spell that has changed in this way
1716     */
1717 root 1.16 void
1718     esrv_update_spells (player *pl)
1719     {
1720 root 1.53 if (!pl->ns)
1721     return;
1722    
1723 root 1.50 if (!pl->ns->monitor_spells)
1724 root 1.16 return;
1725 root 1.28
1726     for (object *spell = pl->ob->inv; spell; spell = spell->below)
1727 root 1.16 {
1728     if (spell->type == SPELL)
1729     {
1730 root 1.28 int flags = 0;
1731    
1732 root 1.16 /* check if we need to update it */
1733     if (spell->last_sp != SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA))
1734     {
1735     spell->last_sp = SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA);
1736     flags |= UPD_SP_MANA;
1737     }
1738 root 1.28
1739 root 1.16 if (spell->last_grace != SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE))
1740     {
1741     spell->last_grace = SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE);
1742     flags |= UPD_SP_GRACE;
1743     }
1744 root 1.28
1745 root 1.16 if (spell->last_eat != spell->stats.dam + SP_level_dam_adjust (pl->ob, spell))
1746     {
1747     spell->last_eat = spell->stats.dam + SP_level_dam_adjust (pl->ob, spell);
1748     flags |= UPD_SP_DAMAGE;
1749     }
1750 root 1.28
1751     if (flags)
1752 root 1.16 {
1753 root 1.33 packet sl;
1754 root 1.27
1755 root 1.28 sl << "updspell "
1756     << uint8 (flags)
1757     << uint32 (spell->count);
1758 root 1.27
1759     if (flags & UPD_SP_MANA ) sl << uint16 (spell->last_sp);
1760     if (flags & UPD_SP_GRACE ) sl << uint16 (spell->last_grace);
1761     if (flags & UPD_SP_DAMAGE) sl << uint16 (spell->last_eat);
1762    
1763 root 1.50 pl->ns->send_packet (sl);
1764 root 1.12 }
1765     }
1766 elmex 1.1 }
1767     }
1768    
1769 root 1.16 void
1770     esrv_remove_spell (player *pl, object *spell)
1771     {
1772 root 1.50 if (!pl->ns->monitor_spells)
1773 root 1.16 return;
1774 root 1.28
1775 root 1.16 if (!pl || !spell || spell->env != pl->ob)
1776     {
1777     LOG (llevError, "Invalid call to esrv_remove_spell");
1778     return;
1779     }
1780 root 1.27
1781 root 1.47 packet sl ("delspell");
1782 root 1.28
1783 root 1.47 sl << uint32 (spell->count);
1784 root 1.27
1785 root 1.50 pl->ns->send_packet (sl);
1786 elmex 1.1 }
1787    
1788     /* appends the spell *spell to the Socklist we will send the data to. */
1789 root 1.16 static void
1790 root 1.33 append_spell (player *pl, packet &sl, object *spell)
1791 root 1.16 {
1792 pippijn 1.67 int i, skill = 0;
1793 root 1.16
1794     if (!(spell->name))
1795     {
1796     LOG (llevError, "item number %d is a spell with no name.\n", spell->count);
1797     return;
1798     }
1799 root 1.27
1800 root 1.16 /* store costs and damage in the object struct, to compare to later */
1801 root 1.27 spell->last_sp = SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA);
1802 root 1.16 spell->last_grace = SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE);
1803 root 1.27 spell->last_eat = spell->stats.dam + SP_level_dam_adjust (pl->ob, spell);
1804 elmex 1.1
1805 root 1.16 /* figure out which skill it uses, if it uses one */
1806     if (spell->skill)
1807     {
1808     for (i = 1; i < NUM_SKILLS; i++)
1809     if (!strcmp (spell->skill, skill_names[i]))
1810     {
1811     skill = i + CS_STAT_SKILLINFO;
1812     break;
1813     }
1814 elmex 1.1 }
1815    
1816 root 1.69 // spells better have a face
1817     if (!spell->face)
1818     {
1819     LOG (llevError, "%s: spell has no face, but face is mandatory.\n", &spell->name);
1820     spell->face = face_find ("burnout.x11", blank_face);
1821     }
1822    
1823 root 1.27 /* send the current values */
1824     sl << uint32 (spell->count)
1825     << uint16 (spell->level)
1826     << uint16 (spell->casting_time)
1827     << uint16 (spell->last_sp)
1828     << uint16 (spell->last_grace)
1829     << uint16 (spell->last_eat)
1830     << uint8 (skill)
1831     << uint32 (spell->path_attuned)
1832 root 1.69 << uint32 (spell->face)
1833 root 1.27 << data8 (spell->name)
1834     << data16 (spell->msg);
1835 elmex 1.1 }
1836    
1837     /**
1838     * This tells the client to add the spell *ob, if *ob is NULL, then add
1839     * all spells in the player's inventory.
1840     */
1841 root 1.16 void
1842     esrv_add_spells (player *pl, object *spell)
1843     {
1844     if (!pl)
1845     {
1846     LOG (llevError, "esrv_add_spells, tried to add a spell to a NULL player");
1847     return;
1848     }
1849 root 1.28
1850 root 1.50 if (!pl->ns->monitor_spells)
1851 root 1.16 return;
1852 root 1.28
1853 root 1.47 packet sl ("addspell");
1854 root 1.28
1855 root 1.16 if (!spell)
1856     {
1857 root 1.69 for (spell = pl->ob->inv; spell; spell = spell->below)
1858 root 1.16 {
1859     /* were we to simply keep appending data here, we could exceed
1860     * MAXSOCKBUF if the player has enough spells to add, we know that
1861     * append_spells will always append 19 data bytes, plus 4 length
1862     * bytes and 3 strings (because that is the spec) so we need to
1863     * check that the length of those 3 strings, plus the 23 bytes,
1864     * won't take us over the length limit for the socket, if it does,
1865     * we need to send what we already have, and restart packet formation
1866     */
1867     /* Seeing crashes by overflowed buffers. Quick arithemetic seems
1868     * to show add_spell is 26 bytes + 2 strings. However, the overun
1869     * is hundreds of bytes off, so correcting 22 vs 26 doesn't seem
1870     * like it will fix this
1871     */
1872     if (spell->type != SPELL)
1873     continue;
1874 root 1.27
1875 root 1.34 if (sl.length () >= (MAXSOCKBUF - (26 + strlen (spell->name) + (spell->msg ? strlen (spell->msg) : 0))))
1876 root 1.16 {
1877 root 1.50 pl->ns->send_packet (sl);
1878 root 1.34
1879     sl.reset ();
1880     sl << "addspell ";
1881 root 1.16 }
1882 root 1.27
1883     append_spell (pl, sl, spell);
1884 root 1.16 }
1885     }
1886     else if (spell->type != SPELL)
1887     {
1888     LOG (llevError, "Asked to send a non-spell object as a spell");
1889     return;
1890     }
1891     else
1892 root 1.27 append_spell (pl, sl, spell);
1893    
1894 root 1.28 if (sl.length () >= MAXSOCKBUF)
1895 root 1.16 {
1896     LOG (llevError, "Buffer overflow in esrv_add_spells!\n");
1897     fatal (0);
1898     }
1899 root 1.27
1900 root 1.16 /* finally, we can send the packet */
1901 root 1.50 pl->ns->send_packet (sl);
1902 elmex 1.1 }
1903 root 1.28