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

File Contents

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