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

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * 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 *
8 * 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 *
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, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 */
23
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 #include <cstring>
41
42 /**
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 static void
49 esrv_print_msg (client *ns, int color, const char *str)
50 {
51 ns->send_msg (color, "info", str);
52 }
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 static void
64 esrv_print_ext_msg (client *ns, int color, uint8 type, uint8 subtype, const char *message)
65 {
66 ns->send_packet_printf ("drawextinfo %d %u %u %s", color, type, subtype, message);
67 }
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 static void
80 print_message (int colr, const object *pl, const char *tmp)
81 {
82 if (!tmp)
83 tmp = "[NULL]";
84
85 if (!pl || (pl->type == PLAYER && pl->contr == NULL))
86 return;
87
88 if (pl->type == PLAYER)
89 esrv_print_msg (pl->contr->ns, colr, (char *)tmp);
90 }
91
92 bool
93 client::msg_suppressed (const char *msg)
94 {
95 if (!pl)
96 return false;
97
98 if (pl->outputs_count <= 1 || !pl->outputs_sync)
99 return false;
100
101 int len = strlen (msg);
102
103 if (len > MSG_BUF_SIZE)
104 return false;
105
106 msg_buf *lru = msgbuf;
107 for (msg_buf *buf = msgbuf; buf < msgbuf + MSG_BUF_COUNT; ++buf)
108 {
109 if (len == buf->len && !memcmp (msg, buf->msg, len))
110 {
111 // found matching buf, see if expired
112 if (buf->expire <= pticks || !buf->count)
113 {
114 // yes, take over matching buffer, print
115 buf->expire = pticks + pl->outputs_sync;
116 buf->count = pl->outputs_count;
117
118 return false;
119 }
120
121 // no, suppress
122 --buf->count;
123 return true;
124 }
125
126 if (lru->expire > buf->expire)
127 lru = buf;
128 }
129
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 }
138
139 /**
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 void
156 new_draw_info (int flags, int pri, const object *op, const char *buf)
157 {
158 if (flags & NDI_ALL)
159 {
160 for_all_players (pl)
161 new_draw_info (flags & ~NDI_ALL, pri, pl->ob, buf);
162 }
163 else
164 {
165 if (!op || !op->contr || !op->contr->ns)
166 return;
167
168 if (pri >= op->contr->listening)
169 return;
170
171 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 }
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 void
186 new_draw_info_format (int flags, int pri, const object *pl, const char *format, ...)
187 {
188 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
195 new_draw_info (flags, pri, pl, buf);
196 }
197
198 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
208 if (!CLIENT_SUPPORT_READABLES (pl->contr->ns, type))
209 {
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 }
221 }
222 else
223 esrv_print_ext_msg (pl->contr->ns, flags & NDI_COLOR_MASK, type, subtype, message);
224 }
225
226 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
237 if (!CLIENT_SUPPORT_READABLES (pl->contr->ns, type))
238 {
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 esrv_print_ext_msg (pl->contr->ns, flags & NDI_COLOR_MASK, type, subtype, buf);
257 }
258 }
259
260 /**
261 * Writes to everyone on the map *except* op. This is useful for emotions.
262 */
263
264 void
265 new_info_map_except (int color, maptile * map, object *op, const char *str)
266 {
267 for_all_players (pl)
268 if (pl->ob != NULL && pl->ob->map == map && pl->ob != op)
269 new_draw_info (color, 0, pl->ob, str);
270 }
271
272 /**
273 * Writes to everyone on the map except op1 and op2
274 */
275 void
276 new_info_map_except2 (int color, maptile * map, object *op1, object *op2, const char *str)
277 {
278 for_all_players (pl)
279 if (pl->ob != NULL && pl->ob->map == map && pl->ob != op1 && pl->ob != op2)
280 new_draw_info (color, 0, pl->ob, str);
281 }
282
283 /**
284 * Writes to everyone on the specified map
285 */
286 void
287 new_info_map (int color, maptile * map, const char *str)
288 {
289 for_all_players (pl)
290 if (pl->ob != NULL && pl->ob->map == map)
291 new_draw_info (color, 0, pl->ob, str);
292 }
293
294 /**
295 * Sets player title.
296 */
297 void
298 set_title (object *pl, char *buf)
299 {
300 /* 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 }
306
307 // 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 /**
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 static void
329 magic_mapping_mark_recursive (object *pl, char *map_mark, int px, int py)
330 {
331 int x, y, dx, dy, mflags;
332 sint16 nx, ny;
333 maptile *mp;
334
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 int f = GET_MAP_FACE (mp, nx, ny, 0);
356 if (f == blank_face)
357 {
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
365 /* Should probably have P_NO_MAGIC here also, but then shops don't
366 * work.
367 */
368 if (mflags & P_BLOCKSVIEW)
369 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_WALL | magicmap;
370 else
371 {
372 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_FLOOR | magicmap;
373 magic_mapping_mark_recursive (pl, map_mark, x, y);
374 }
375 }
376 }
377 }
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 * is for open spaces. map_mark should already have been initialised
391 * 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 void
396 magic_mapping_mark (object *pl, char *map_mark, int strength)
397 {
398 int x, y, mflags;
399 sint16 nx, ny;
400 maptile *mp;
401
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
410 mflags = get_map_flags (pl->map, &mp, nx, ny, &nx, &ny);
411 if (mflags & P_OUT_OF_MAP)
412 continue;
413
414 int f = GET_MAP_FACE (mp, nx, ny, 0);
415 if (f == blank_face)
416 {
417 f = GET_MAP_FACE (mp, nx, ny, 1);
418 if (f == blank_face)
419 f = GET_MAP_FACE (mp, nx, ny, 2);
420 }
421
422 int magicmap = faces [f].magicmap;
423
424 if (mflags & P_BLOCKSVIEW)
425 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_WALL | magicmap;
426 else
427 {
428 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_FLOOR | magicmap;
429 magic_mapping_mark_recursive (pl, map_mark, x, y);
430 }
431 }
432 }
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 void
444 draw_magic_map (object *pl)
445 {
446 char *map_mark = (char *)calloc (MAGIC_MAP_SIZE * MAGIC_MAP_SIZE, 1);
447 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
468 for (int x = 0; x < MAGIC_MAP_SIZE; x++)
469 for (int y = 0; y < MAGIC_MAP_SIZE; y++)
470 if (map_mark[x + pl->map->width * y] | FACE_FLOOR)
471 {
472 xmin = x < xmin ? x : xmin;
473 xmax = x > xmax ? x : xmax;
474 ymin = y < ymin ? y : ymin;
475 ymax = y > ymax ? y : ymax;
476 }
477
478 packet sl;
479 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 pl->contr->ns->send_packet (sl);
487
488 free (map_mark);
489 }
490
491 /**
492 * Send a kill log record to sockets
493 */
494 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 snprintf (buf, MAX_BUF, "%s\t%s\t%d\t%s\t%d\n", Who, What, WhatType, With, WithType);
502 else
503 snprintf (buf, MAX_BUF, "%s\t%s\t%d\n", Who, What, WhatType);
504
505 len = strlen (buf);
506 }
507