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

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.46 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 pippijn 1.33 *
4 root 1.43 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5     * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6     * Copyright (©) 1992,2007 Frank Tore Johansen
7 pippijn 1.33 *
8 root 1.46 * Crossfire TRT is free software: you can redistribute it and/or modify
9     * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation, either version 3 of the License, or
11     * (at your option) any later version.
12 pippijn 1.33 *
13 root 1.46 * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17 pippijn 1.33 *
18 root 1.46 * You should have received a copy of the GNU General Public License
19     * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 root 1.43 *
21     * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 pippijn 1.33 */
23 elmex 1.1
24     /**
25     * \file
26     * Basic client output functions.
27     *
28     * \date 2003-12-02
29     *
30     * This file implements some of the simpler output functions to the
31     * client. Basically, things like sending text strings along
32     */
33    
34     #include <global.h>
35     #include <sproto.h>
36     #include <stdarg.h>
37     #include <spells.h>
38     #include <skills.h>
39    
40 root 1.45 #include <cstring>
41    
42 elmex 1.1 /**
43     * Draws a normal message on the client. It is pretty
44     * much the same thing as the draw_info above, but takes a color
45     * parameter. the esrv_drawinfo functions should probably be
46     * replaced with this, just using black as the color.
47     */
48 root 1.5 static void
49 root 1.22 esrv_print_msg (client *ns, int color, const char *str)
50 elmex 1.1 {
51 root 1.45 ns->send_msg (color, "info", str);
52 elmex 1.1 }
53    
54     /**
55     * Draws an extended message on the client.
56     * ns the socket to send message to
57     * color color informations (used mainly if client does not support message type)
58     * type,
59     * subtype type and subtype of text message
60     * intro Intro message to send with main message if client does not support the message type
61     * message The main message
62     */
63 root 1.5 static void
64 root 1.22 esrv_print_ext_msg (client *ns, int color, uint8 type, uint8 subtype, const char *message)
65 elmex 1.1 {
66 root 1.45 ns->send_packet_printf ("drawextinfo %d %u %u %s", color, type, subtype, message);
67 elmex 1.1 }
68    
69     /**
70     * Frontend for esrv_print_msg
71     * \param colr message color
72     * \param pl player to send to. Can be NULL
73     * \param tmp message to send. Can be NULL
74     *
75     * If pl is NULL or without contr set, writes message to log.
76     *
77     * Else sends message to player via esrv_print_msg
78     */
79 root 1.5 static void
80     print_message (int colr, const object *pl, const char *tmp)
81     {
82 root 1.11 if (!tmp)
83     tmp = "[NULL]";
84 elmex 1.1
85     if (!pl || (pl->type == PLAYER && pl->contr == NULL))
86     return;
87    
88 root 1.5 if (pl->type == PLAYER)
89 root 1.45 esrv_print_msg (pl->contr->ns, colr, (char *)tmp);
90 elmex 1.1 }
91    
92 root 1.38 bool
93     client::msg_suppressed (const char *msg)
94 elmex 1.1 {
95 root 1.38 if (!pl)
96     return false;
97 elmex 1.1
98 root 1.38 if (pl->outputs_count <= 1 || !pl->outputs_sync)
99     return false;
100 elmex 1.1
101 root 1.38 int len = strlen (msg);
102 elmex 1.1
103 root 1.38 if (len > MSG_BUF_SIZE)
104     return false;
105 elmex 1.1
106 root 1.38 msg_buf *lru = msgbuf;
107     for (msg_buf *buf = msgbuf; buf < msgbuf + MSG_BUF_COUNT; ++buf)
108 root 1.5 {
109 root 1.38 if (len == buf->len && !memcmp (msg, buf->msg, len))
110 root 1.5 {
111 root 1.38 // found matching buf, see if expired
112     if (buf->expire <= pticks || !buf->count)
113 root 1.5 {
114 root 1.38 // yes, take over matching buffer, print
115     buf->expire = pticks + pl->outputs_sync;
116     buf->count = pl->outputs_count;
117    
118     return false;
119 root 1.2 }
120 root 1.38
121     // no, suppress
122     --buf->count;
123     return true;
124 root 1.2 }
125 root 1.5
126 root 1.38 if (lru->expire > buf->expire)
127     lru = buf;
128 elmex 1.1 }
129 root 1.38
130     // new message, evoke oldest buffer
131     lru->expire = pticks + pl->outputs_sync;
132     lru->count = pl->outputs_count;
133     lru->len = len;
134     memcpy (lru->msg, msg, len);
135    
136     return false;
137 elmex 1.1 }
138 root 1.5
139 elmex 1.1 /**
140     * Sends message to player(s).
141     *
142     * flags is various flags - mostly color, plus a few specials.
143     *
144     * pri is priority. It is a little odd - the lower the value, the more
145     * important it is. Thus, 0 gets sent no matter what. Otherwise, the
146     * value must be less than the listening level that the player has set.
147     * Unfortunately, there is no clear guideline on what each level does what.
148     *
149     * pl can be passed as NULL - in fact, this will be done if NDI_ALL is set
150     * in the flags.
151     *
152     * If message is black, and not NDI_UNIQUE, gets sent through output buffers.
153     *
154     */
155 root 1.5 void
156 root 1.41 new_draw_info (int flags, int pri, const object *op, const char *buf)
157 elmex 1.1 {
158 root 1.5 if (flags & NDI_ALL)
159 root 1.42 {
160     for_all_players (pl)
161     new_draw_info (flags & ~NDI_ALL, pri, pl->ob, buf);
162     }
163 root 1.41 else
164 root 1.5 {
165 root 1.41 if (!op || !op->contr || !op->contr->ns)
166     return;
167    
168     if (pri >= op->contr->listening)
169     return;
170 elmex 1.1
171 root 1.41 if ((flags & (NDI_COLOR_MASK | NDI_UNIQUE)) != NDI_BLACK
172     || !op->contr->ns->msg_suppressed (buf))
173     print_message (flags & NDI_COLOR_MASK, op, buf);
174 elmex 1.1 }
175     }
176    
177     /**
178     * Wrapper for new_draw_info printf-like.
179     *
180     * This is a pretty trivial function, but it allows us to use printf style
181     * formatting, so instead of the calling function having to do it, we do
182     * it here. It may also have advantages in the future for reduction of
183     * client/server bandwidth (client could keep track of various strings
184     */
185 root 1.5 void
186     new_draw_info_format (int flags, int pri, const object *pl, const char *format, ...)
187 elmex 1.1 {
188 root 1.5 char buf[HUGE_BUF];
189    
190     va_list ap;
191     va_start (ap, format);
192     vsnprintf (buf, HUGE_BUF, format, ap);
193     va_end (ap);
194 elmex 1.1
195 root 1.5 new_draw_info (flags, pri, pl, buf);
196 elmex 1.1 }
197    
198 root 1.5 void
199     draw_ext_info (int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
200     {
201    
202     if (!pl || (pl->type != PLAYER) || (pl->contr == NULL))
203     return;
204    
205     if (pri >= pl->contr->listening)
206     return;
207 root 1.17
208 root 1.23 if (!CLIENT_SUPPORT_READABLES (pl->contr->ns, type))
209 root 1.5 {
210     char *buf = (char *) malloc (strlen (oldmessage == NULL ? message : oldmessage) + 1);
211    
212     if (buf == NULL)
213     LOG (llevError, "info::draw_ext_info -> Out of memory!");
214     else
215     {
216     strcpy (buf, oldmessage == NULL ? message : oldmessage);
217     strip_media_tag (buf);
218     new_draw_info (flags, pri, pl, buf);
219     free (buf);
220 elmex 1.1 }
221 root 1.5 }
222     else
223 root 1.23 esrv_print_ext_msg (pl->contr->ns, flags & NDI_COLOR_MASK, type, subtype, message);
224 elmex 1.1 }
225    
226 root 1.5 void
227     draw_ext_info_format (int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *old_format, char *new_format, ...)
228     {
229     char buf[HUGE_BUF];
230    
231     if (!pl || (pl->type != PLAYER) || (pl->contr == NULL))
232     return;
233    
234     if (pri >= pl->contr->listening)
235     return;
236 root 1.17
237 root 1.23 if (!CLIENT_SUPPORT_READABLES (pl->contr->ns, type))
238 root 1.5 {
239     va_list ap;
240    
241     LOG (llevDebug, "Non supported extension text type for client.\n");
242     va_start (ap, new_format);
243     vsnprintf (buf, HUGE_BUF, old_format, ap);
244     va_end (ap);
245     new_draw_info (flags, pri, pl, buf);
246     return;
247     }
248     else
249     {
250     va_list ap;
251    
252     va_start (ap, new_format);
253     vsnprintf (buf, HUGE_BUF, new_format, ap);
254     va_end (ap);
255     strip_media_tag (buf);
256 root 1.23 esrv_print_ext_msg (pl->contr->ns, flags & NDI_COLOR_MASK, type, subtype, buf);
257 elmex 1.1 }
258     }
259 root 1.5
260 elmex 1.1 /**
261     * Writes to everyone on the map *except* op. This is useful for emotions.
262     */
263    
264 root 1.5 void
265 root 1.8 new_info_map_except (int color, maptile * map, object *op, const char *str)
266 root 1.5 {
267 root 1.24 for_all_players (pl)
268 root 1.5 if (pl->ob != NULL && pl->ob->map == map && pl->ob != op)
269 root 1.31 new_draw_info (color, 0, pl->ob, str);
270 elmex 1.1 }
271    
272     /**
273     * Writes to everyone on the map except op1 and op2
274     */
275 root 1.5 void
276 root 1.8 new_info_map_except2 (int color, maptile * map, object *op1, object *op2, const char *str)
277 root 1.5 {
278 root 1.24 for_all_players (pl)
279 root 1.5 if (pl->ob != NULL && pl->ob->map == map && pl->ob != op1 && pl->ob != op2)
280 root 1.31 new_draw_info (color, 0, pl->ob, str);
281 elmex 1.1 }
282    
283     /**
284     * Writes to everyone on the specified map
285     */
286 root 1.5 void
287 root 1.8 new_info_map (int color, maptile * map, const char *str)
288 root 1.5 {
289 root 1.24 for_all_players (pl)
290 root 1.5 if (pl->ob != NULL && pl->ob->map == map)
291 root 1.31 new_draw_info (color, 0, pl->ob, str);
292 elmex 1.1 }
293    
294     /**
295     * Sets player title.
296     */
297 root 1.5 void
298     set_title (object *pl, char *buf)
299 elmex 1.1 {
300 root 1.5 /* Eneq(@csd.uu.se): Let players define their own titles. */
301     if (pl->contr->own_title[0] == '\0')
302     sprintf (buf, "Player: %s the %s", (const char *) pl->name, (const char *) pl->contr->title);
303     else
304     sprintf (buf, "Player: %s %s", (const char *) pl->name, (const char *) pl->contr->own_title);
305 elmex 1.1 }
306    
307 root 1.36 // formerly a macro, used only by magic map, so optimised it out
308     static inline faceidx
309     GET_MAP_FACE (maptile *m, int x, int y, int layer)
310     {
311     if (object *op = GET_MAP_FACE_OBJ (m, x, y, layer))
312     return op->face;
313     else
314     return 0;
315     }
316    
317 elmex 1.1 /**
318     * Helper for magic map creation.
319     *
320     * Takes a player, the map_mark array and an x and y starting position.
321     * pl is the player.
322     * px, py are offsets from the player.
323     *
324     * This function examines all the adjacant spaces next to px, py.
325     * It updates the map_mark arrow with the color and high bits set
326     * for various code values.
327     */
328 root 1.5 static void
329     magic_mapping_mark_recursive (object *pl, char *map_mark, int px, int py)
330 elmex 1.1 {
331 root 1.5 int x, y, dx, dy, mflags;
332     sint16 nx, ny;
333 root 1.7 maptile *mp;
334 root 1.5
335     for (dx = -1; dx <= 1; dx++)
336     {
337     for (dy = -1; dy <= 1; dy++)
338     {
339     x = px + dx;
340     y = py + dy;
341    
342     if (FABS (x) >= MAGIC_MAP_HALF || FABS (y) >= MAGIC_MAP_HALF)
343     continue;
344    
345     mp = pl->map;
346     nx = pl->x + x;
347     ny = pl->y + y;
348    
349     mflags = get_map_flags (pl->map, &mp, nx, ny, &nx, &ny);
350     if (mflags & P_OUT_OF_MAP)
351     continue;
352    
353     if (map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] == 0)
354     {
355 root 1.35 int f = GET_MAP_FACE (mp, nx, ny, 0);
356 root 1.5 if (f == blank_face)
357 root 1.35 {
358     f = GET_MAP_FACE (mp, nx, ny, 1);
359     if (f == blank_face)
360     f = GET_MAP_FACE (mp, nx, ny, 2);
361     }
362    
363     int magicmap = faces [f].magicmap;
364 root 1.5
365     /* Should probably have P_NO_MAGIC here also, but then shops don't
366     * work.
367     */
368     if (mflags & P_BLOCKSVIEW)
369 root 1.35 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_WALL | magicmap;
370 root 1.5 else
371     {
372 root 1.35 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_FLOOR | magicmap;
373 root 1.5 magic_mapping_mark_recursive (pl, map_mark, x, y);
374 root 1.2 }
375     }
376     }
377 elmex 1.1 }
378     }
379    
380     /**
381     * Creates magic map for player.
382     *
383     * Note: For improved magic mapping display, the space that blocks
384     * the view is now marked with value 2. Any dependencies of map_mark
385     * being nonzero have been changed to check for 1. Also, since
386     * map_mark is a char value, putting 2 in should cause no problems.
387     *
388     * This function examines the map the player is on, and determines what
389     * is visible. 2 is set for walls or objects that blocks view. 1
390 pippijn 1.29 * is for open spaces. map_mark should already have been initialised
391 elmex 1.1 * to zero before this is called.
392     * strength is an initial strength*2 rectangular area that we automatically
393     * see in/penetrate through.
394     */
395 root 1.5 void
396     magic_mapping_mark (object *pl, char *map_mark, int strength)
397 elmex 1.1 {
398 root 1.5 int x, y, mflags;
399     sint16 nx, ny;
400 root 1.7 maptile *mp;
401 root 1.5
402     for (x = -strength; x < strength; x++)
403     {
404     for (y = -strength; y < strength; y++)
405     {
406     mp = pl->map;
407     nx = pl->x + x;
408     ny = pl->y + y;
409 root 1.35
410 root 1.5 mflags = get_map_flags (pl->map, &mp, nx, ny, &nx, &ny);
411     if (mflags & P_OUT_OF_MAP)
412     continue;
413 root 1.35
414     int f = GET_MAP_FACE (mp, nx, ny, 0);
415     if (f == blank_face)
416 root 1.5 {
417 root 1.35 f = GET_MAP_FACE (mp, nx, ny, 1);
418 root 1.5 if (f == blank_face)
419     f = GET_MAP_FACE (mp, nx, ny, 2);
420 root 1.2 }
421    
422 root 1.35 int magicmap = faces [f].magicmap;
423    
424 root 1.5 if (mflags & P_BLOCKSVIEW)
425 root 1.35 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_WALL | magicmap;
426 root 1.5 else
427     {
428 root 1.35 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_FLOOR | magicmap;
429 root 1.5 magic_mapping_mark_recursive (pl, map_mark, x, y);
430 root 1.2 }
431     }
432 elmex 1.1 }
433     }
434    
435     /**
436     * Creates and sends magic map to player.
437     *
438     * The following function is a lot messier than it really should be,
439     * but there is no real easy solution.
440     *
441     * Mark Wedel
442     */
443 root 1.5 void
444     draw_magic_map (object *pl)
445 elmex 1.1 {
446 root 1.13 char *map_mark = (char *)calloc (MAGIC_MAP_SIZE * MAGIC_MAP_SIZE, 1);
447 root 1.5 int xmin, xmax, ymin, ymax;
448    
449     if (pl->type != PLAYER)
450     {
451     LOG (llevError, "Non player object called draw_map.\n");
452     return;
453     }
454    
455     /* First, we figure out what spaces are 'reachable' by the player */
456     magic_mapping_mark (pl, map_mark, 3);
457    
458     /* We now go through and figure out what spaces have been
459     * marked, and thus figure out rectangular region we send
460     * to the client (eg, if only a 10x10 area is visible, we only
461     * want to send those 100 spaces.)
462     */
463     xmin = MAGIC_MAP_SIZE;
464     ymin = MAGIC_MAP_SIZE;
465     xmax = 0;
466     ymax = 0;
467 root 1.13
468     for (int x = 0; x < MAGIC_MAP_SIZE; x++)
469     for (int y = 0; y < MAGIC_MAP_SIZE; y++)
470 root 1.25 if (map_mark[x + pl->map->width * y] | FACE_FLOOR)
471 root 1.5 {
472 root 1.13 xmin = x < xmin ? x : xmin;
473     xmax = x > xmax ? x : xmax;
474     ymin = y < ymin ? y : ymin;
475     ymax = y > ymax ? y : ymax;
476 root 1.2 }
477 elmex 1.1
478 root 1.15 packet sl;
479 root 1.13 sl.printf ("magicmap %d %d %d %d ", (xmax - xmin + 1), (ymax - ymin + 1),
480     MAGIC_MAP_HALF - xmin, MAGIC_MAP_HALF - ymin);
481    
482     for (int y = ymin; y <= ymax; y++)
483     for (int x = xmin; x <= xmax; x++)
484     sl << uint8 (map_mark[x + MAGIC_MAP_SIZE * y] & ~FACE_FLOOR);
485    
486 root 1.23 pl->contr->ns->send_packet (sl);
487 root 1.5
488     free (map_mark);
489 elmex 1.1 }
490    
491     /**
492     * Send a kill log record to sockets
493     */
494 root 1.5 void
495     Log_Kill (const char *Who, const char *What, int WhatType, const char *With, int WithType)
496     {
497     size_t len;
498     char buf[MAX_BUF];
499    
500     if (With != NULL)
501 root 1.11 snprintf (buf, MAX_BUF, "%s\t%s\t%d\t%s\t%d\n", Who, What, WhatType, With, WithType);
502 root 1.5 else
503 root 1.11 snprintf (buf, MAX_BUF, "%s\t%s\t%d\n", Who, What, WhatType);
504    
505 root 1.5 len = strlen (buf);
506 elmex 1.1 }
507 root 1.22