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

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game
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
25 /** \file
26 * Image related communication
27 *
28 * \date 2003-12-02
29 *
30 * This file deals with the image related communication to the
31 * client. I've located all the functions in this file - this
32 * localizes it more, and means that we don't need to declare
33 * things like all the structures as globals.
34 */
35
36 #include <global.h>
37 #include <sproto.h>
38
39 #include "crc.h"
40
41 #define MAX_FACE_SETS 20 /**< Maximum number of image sets the program will handle */
42
43 /** Information about one image */
44 typedef struct FaceInfo
45 {
46 uint8 *data; /**< image data */
47 uint16 datalen; /**< length of the xpm data */
48 uint32 checksum; /**< Checksum of face data */
49 } FaceInfo;
50
51 /** Information about one face set */
52 typedef struct
53 {
54 char *prefix; /**< */
55 char *fullname;
56
57 uint8 fallback; /**< faceset to use when an image is not found in this faceset */
58 char *size;
59 char *extension;
60 char *comment;
61
62 FaceInfo *faces; /**< images in this faceset */
63 } FaceSets;
64
65 static FaceSets facesets[MAX_FACE_SETS]; /**< All facesets */
66
67 /**
68 * Checks specified faceset is valid
69 * \param fsn faceset number
70 */
71 int
72 is_valid_faceset (int fsn)
73 {
74 if (fsn >= 0 && fsn < MAX_FACE_SETS && facesets[fsn].prefix)
75 return TRUE;
76 return FALSE;
77 }
78
79 /**
80 * Frees all faceset information
81 */
82 void
83 free_socket_images (void)
84 {
85 int num, q;
86
87 for (num = 0; num < MAX_FACE_SETS; num++)
88 {
89 if (facesets[num].prefix)
90 {
91 for (q = 0; q < faces.size (); q++)
92 if (facesets[num].faces[q].data)
93 free (facesets[num].faces[q].data);
94
95 free (facesets[num].prefix);
96 free (facesets[num].fullname);
97 free (facesets[num].size);
98 free (facesets[num].extension);
99 free (facesets[num].comment);
100 free (facesets[num].faces);
101 }
102 }
103 }
104
105 /**
106 * This returns the set we will actually use when sending
107 * a face. This is used because the image files may be sparse.
108 * This function is recursive. imageno is the face number we are
109 * trying to send
110 *
111 * If face is not found in specified faceset, tries with 'fallback' faceset.
112 *
113 * \param faceset faceset to check
114 * \param imageno image number
115 *
116 */
117 static int
118 get_face_fallback (int faceset, int imageno)
119 {
120 /* faceset 0 is supposed to have every image, so just return. Doing
121 * so also prevents infinite loops in the case if it not having
122 * the face, but in that case, we are likely to crash when we try
123 * to access the data, but that is probably preferable to an infinite
124 * loop.
125 */
126 if (faceset == 0)
127 return 0;
128
129 if (!facesets[faceset].prefix)
130 {
131 LOG (llevError, "get_face_fallback called with unused set (%d)?\n", faceset);
132 return 0; /* use default set */
133 }
134
135 if (facesets[faceset].faces[imageno].data)
136 return faceset;
137
138 return get_face_fallback (facesets[faceset].fallback, imageno);
139 }
140
141 /**
142 * Checks fallback are correctly defined.
143 * This is a simple recursive function that makes sure the fallbacks
144 * are all proper (eg, the fall back to defined sets, and also
145 * eventually fall back to 0). At the top level, togo is set to MAX_FACE_SETS,
146 * if togo gets to zero, it means we have a loop.
147 * This is only run when we first load the facesets.
148 */
149 static void
150 check_faceset_fallback (int faceset, int togo)
151 {
152 int fallback = facesets[faceset].fallback;
153
154 /* proper case - falls back to base set */
155 if (fallback == 0)
156 return;
157
158 if (!facesets[fallback].prefix)
159 {
160 LOG (llevError, "Face set %d falls to non set faceset %d\n", faceset, fallback);
161 abort ();
162 }
163
164 togo--;
165 if (togo == 0)
166 {
167 LOG (llevError, "Infinite loop found in facesets. aborting.\n");
168 abort ();
169 }
170
171 check_faceset_fallback (fallback, togo);
172 }
173
174 #define MAX_IMAGE_SIZE 10000
175
176 /**
177 * Client tells us what type of faces it wants. Also sets
178 * the caching attribute.
179 *
180 */
181 void
182 SetFaceMode (char *buf, int len, client *ns)
183 {
184 int mask = (atoi (buf) & CF_FACE_CACHE), mode = (atoi (buf) & ~CF_FACE_CACHE);
185
186 if (mode == CF_FACE_NONE)
187 ns->facecache = 1;
188 else if (mode != CF_FACE_PNG)
189 ns->send_packet_printf ("drawinfo %d %s", NDI_RED, "Warning - send unsupported face mode. Will use Png");
190
191 if (mask)
192 ns->facecache = 1;
193 }
194
195 /**
196 * Client has requested pixmap that it somehow missed getting.
197 * This will be called often if the client is
198 * caching images.
199 */
200 void
201 SendFaceCmd (char *buf, int len, client *ns)
202 {
203 ns->send_face (atoi (buf), 1);
204 }
205
206 // how lame
207 static void print_facename (packet &sl, const facedata &d)
208 {
209 for (int i = 0; i < CHKSUM_SIZE; ++i)
210 sl.printf ("%02x", d.chksum [i]);
211 }
212
213 // gcfclient uses the server-provided checksum for comparison,
214 // but always wrotes a broken checksum to its cache file, so we
215 // have to provide gcfclient with a useless checksum just to
216 // have to cache the image despite its bugs.
217 static uint32 gcfclient_checksum (const facedata *d)
218 {
219 uint32 csum = 0;
220
221 for (std::string::const_iterator i = d->data.begin ();
222 i != d->data.end ();
223 ++i)
224 {
225 csum = rotate_right (csum);
226 csum += *(uint8 *)&*i;
227 }
228
229 return csum;
230 }
231
232 /**
233 * Sends a face to a client if they are in pixmap mode
234 * nothing gets sent in bitmap mode.
235 * If nocache is true (nonzero), ignore the cache setting from the client -
236 * this is needed for the askface, in which we really do want to send the
237 * face (and askface is the only place that should be setting it). Otherwise,
238 * we look at the facecache, and if set, send the image name.
239 */
240 void
241 client::send_face (faceidx facenum, bool forced)
242 {
243 // never send face 0. ever. it does not exist.
244 if (!facenum)
245 return;
246
247 const facedata *d = face_data (facenum, faceset);
248
249 if (!d)
250 {
251 LOG (llevError, "client::send_face (%d) out of bounds??\n", facenum);
252 return;
253 }
254
255 packet sl;
256
257 if (facecache && !forced)
258 {
259 if (faces_sent[facenum] & NS_FACESENT_FACE)
260 return;
261
262 faces_sent[facenum] |= NS_FACESENT_FACE;
263
264 if (force_face0)
265 sl << "face " << uint16 (facenum);
266 else if (image2)
267 sl << "face2 " << uint16 (facenum) << uint8 (0) << uint32 (force_bad_checksum ? gcfclient_checksum (d) : 0);
268 else
269 sl << "face1 " << uint16 (facenum) << uint32 (force_bad_checksum ? gcfclient_checksum (d) : 0);
270
271 // how lame
272 print_facename (sl, *d);
273 }
274 else
275 {
276 fprintf (stderr, "image %d\n", facenum);//D
277 sl << (image2 ? "image2 " : "image ")
278 << uint32 (facenum);
279
280 if (image2)
281 sl << uint8 (0);
282
283 sl << uint32 (d->data.size ())
284 << data (d->data.data (), d->data.size ());
285 }
286
287 send_packet (sl);
288 }
289
290 // send all faces of this object to the client
291 // this uses more bandwidth initially, but makes
292 // animations look much smoother, and every client
293 // is supposed to do client-side caching anyways.
294 void
295 client::send_faces (object *ob)
296 {
297 send_face (ob->face);
298
299 if (ob->animation_id)
300 {
301 animation &anim = animations [ob->animation_id];
302
303 for (int i = 0; i < anim.num_animations; i++)
304 send_face (anim.faces [i]);
305 }
306 }
307
308 /**
309 * A lot like the old AskSmooth (in fact, now called by AskSmooth).
310 * Basically, it makes no sense to wait for the client to request a
311 * a piece of data from us that we know the client wants. So
312 * if we know the client wants it, might as well push it to the
313 * client.
314 */
315 void
316 client::send_smooth (faceidx face)
317 {
318 faceinfo *f = face_info (face);
319
320 if (!f)
321 return;
322
323 if (faces_sent[face] & NS_FACESENT_SMOOTH)
324 return;
325
326 faces_sent[face] |= NS_FACESENT_SMOOTH;
327
328 /* If we can't find a face, return and set it so we won't try to send this
329 * again.
330 */
331 if (!f->smooth)
332 {
333 LOG (llevError, "could not findsmooth for %d.\n", face);
334 return;
335 }
336
337 send_face (f->smooth);
338
339 packet sl ("smooth");
340
341 sl << uint16 (face)
342 << uint16 (f->smooth);
343
344 send_packet (sl);
345 }
346
347 /**
348 * Need to send an animation sequence to the client.
349 * We will send appropriate face commands to the client if we haven't
350 * sent them the face yet (this can become quite costly in terms of
351 * how much we are sending - on the other hand, this should only happen
352 * when the player logs in and picks stuff up.
353 */
354 void
355 client::send_animation (short anim_num)
356 {
357 /* Do some checking on the anim_num we got. Note that the animations
358 * are added in contigous order, so if the number is in the valid
359 * range, it must be a valid animation.
360 */
361 if (anim_num < 0 || anim_num > num_animations)
362 {
363 LOG (llevError, "esrv_send_anim (%d) out of bounds??\n", anim_num);
364 return;
365 }
366
367 packet sl ("anim");
368
369 sl << uint16 (anim_num)
370 << uint16 (0); /* flags - not used right now */
371
372 /* Build up the list of faces. Also, send any information (ie, the
373 * the face itself) down to the client.
374 */
375 for (int i = 0; i < animations[anim_num].num_animations; i++)
376 {
377 send_face (animations[anim_num].faces[i]);
378 sl << uint16 (animations[anim_num].faces[i]); /* flags - not used right now */
379 }
380
381 send_packet (sl);
382
383 anims_sent[anim_num] = 1;
384 }
385
386 /**
387 * Sends the number of images, checksum of the face file,
388 * and the image_info file information. See the doc/Developers/protocol
389 * if you want further detail.
390 */
391 void
392 send_image_info (client *ns, char *params)
393 {
394 packet sl;
395
396 //TODO: second parameter is a checksum, but it makes no sense in this current framework
397 sl.printf ("replyinfo image_info\n%d\n%u\n", MAX_FACES, 0);
398
399 sl << "0:base:standard:0:32x32:none:The old 32x32 faceset.\n";
400
401 ns->send_packet (sl);
402 }
403
404 /**
405 * Sends requested face information.
406 * \param ns socket to send to
407 * \param params contains first and last index of face
408 *
409 * For each image in [start..stop] sends
410 * - checksum
411 * - name
412 */
413 void
414 send_image_sums (client *ns, char *params)
415 {
416 int start, stop;
417 char *cp;
418
419 packet sl;
420
421 start = atoi (params);
422 for (cp = params; *cp != '\0'; cp++)
423 if (*cp == ' ')
424 break;
425
426 stop = atoi (cp);
427 if (stop < start || *cp == '\0' || (stop - start) > 1000 || stop >= MAX_FACES)
428 {
429 sl.printf ("replyinfo image_sums %d %d", start, stop);
430 ns->send_packet (sl);
431 sl.reset ();
432 return;
433 }
434
435 sl.printf ("replyinfo image_sums %d %d ", start, stop);
436
437 for (int i = start; i <= stop && i < faces.size (); i++)
438 {
439 ns->faces_sent[i] |= NS_FACESENT_FACE;
440
441 const facedata *d = face_data (i, ns->faceset);
442
443 if (sl.room () < 2 + 4 + 1 + d->data.size () + 1)
444 break;
445
446 sl << uint16 (i)
447 << uint32 (0) // checksum
448 << uint8 (ns->faceset);
449
450 print_facename (sl, *d); sl << uint8 (0);
451 }
452
453 /* It would make more sense to catch this pre-emptively in the code above.
454 * however, if this really happens, we probably just want to cut down the
455 * size to less than 1000, since that is what we claim the protocol would
456 * support.
457 */
458 //TODO: taken care of above, should simply abort or make sure the above code is correct
459 if (sl.length () >= MAXSOCKBUF)
460 {
461 LOG (llevError, "send_image_send: buffer overrun, %d > %d\n", sl.length (), MAXSOCKBUF);
462 abort ();
463 }
464
465 ns->send_packet (sl);
466 }
467