ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/image.C
Revision: 1.14
Committed: Sat Dec 16 03:08:26 2006 UTC (17 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.13: +1 -5 lines
Log Message:
- socket i/o is completely asynchronous now
- some command sare handled immediately
- others (most palying related commands) are queued
  for later (as of now synchronous) processing
- specifying a max queue length (in seconds) is possible, but disabled
- also add some syntax sugar for network code

File Contents

# User Rev Content
1 elmex 1.1 /*
2     CrossFire, A Multiplayer game for X-windows
3    
4     Copyright (C) 2001 Mark Wedel
5     Copyright (C) 1992 Frank Tore Johansen
6    
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21 root 1.4 The author can be reached via e-mail to <crossfire@schmorp.de>
22 elmex 1.1 */
23    
24     /** \file
25     * Image related communication
26     *
27     * \date 2003-12-02
28     *
29     * This file deals with the image related communication to the
30     * client. I've located all the functions in this file - this
31     * localizes it more, and means that we don't need to declare
32     * things like all the structures as globals.
33     */
34    
35     #include <global.h>
36     #include <sproto.h>
37    
38     #define MAX_FACE_SETS 20 /**< Maximum number of image sets the program will handle */
39    
40     /** Information about one image */
41 root 1.3 typedef struct FaceInfo
42     {
43    
44     uint8 *data; /**< image data */
45     uint16 datalen; /**< length of the xpm data */
46     uint32 checksum; /**< Checksum of face data */
47 elmex 1.1 } FaceInfo;
48    
49     /** Information about one face set */
50 root 1.3 typedef struct
51     {
52    
53     char *prefix; /**< */
54     char *fullname;
55    
56     uint8 fallback; /**< faceset to use when an image is not found in this faceset */
57     char *size;
58     char *extension;
59     char *comment;
60    
61     FaceInfo *faces; /**< images in this faceset */
62 elmex 1.1 } FaceSets;
63    
64     static FaceSets facesets[MAX_FACE_SETS]; /**< All facesets */
65    
66     /**
67     * Checks specified faceset is valid
68     * \param fsn faceset number
69     */
70 root 1.3 int
71     is_valid_faceset (int fsn)
72 elmex 1.1 {
73 root 1.3 if (fsn >= 0 && fsn < MAX_FACE_SETS && facesets[fsn].prefix)
74     return TRUE;
75     return FALSE;
76 elmex 1.1 }
77    
78     /**
79     * Frees all faceset information
80     */
81 root 1.3 void
82     free_socket_images (void)
83 elmex 1.1 {
84 root 1.3 int num, q;
85 elmex 1.1
86 root 1.3 for (num = 0; num < MAX_FACE_SETS; num++)
87     {
88     if (facesets[num].prefix)
89     {
90     for (q = 0; q < nrofpixmaps; q++)
91     if (facesets[num].faces[q].data)
92     free (facesets[num].faces[q].data);
93     free (facesets[num].prefix);
94     free (facesets[num].fullname);
95     free (facesets[num].size);
96     free (facesets[num].extension);
97     free (facesets[num].comment);
98     free (facesets[num].faces);
99 root 1.2 }
100 elmex 1.1 }
101     }
102    
103     /**
104     * This returns the set we will actually use when sending
105     * a face. This is used because the image files may be sparse.
106     * This function is recursive. imageno is the face number we are
107     * trying to send
108     *
109     * If face is not found in specified faceset, tries with 'fallback' faceset.
110     *
111     * \param faceset faceset to check
112     * \param imageno image number
113     *
114     */
115 root 1.3 static int
116     get_face_fallback (int faceset, int imageno)
117 elmex 1.1 {
118 root 1.3 /* faceset 0 is supposed to have every image, so just return. Doing
119     * so also prevents infinite loops in the case if it not having
120     * the face, but in that case, we are likely to crash when we try
121     * to access the data, but that is probably preferable to an infinite
122     * loop.
123     */
124     if (faceset == 0)
125     return 0;
126    
127     if (!facesets[faceset].prefix)
128     {
129     LOG (llevError, "get_face_fallback called with unused set (%d)?\n", faceset);
130     return 0; /* use default set */
131     }
132     if (facesets[faceset].faces[imageno].data)
133     return faceset;
134     return get_face_fallback (facesets[faceset].fallback, imageno);
135 elmex 1.1 }
136    
137     /**
138     * Checks fallback are correctly defined.
139     * This is a simple recursive function that makes sure the fallbacks
140     * are all proper (eg, the fall back to defined sets, and also
141     * eventually fall back to 0). At the top level, togo is set to MAX_FACE_SETS,
142     * if togo gets to zero, it means we have a loop.
143     * This is only run when we first load the facesets.
144     */
145 root 1.3 static void
146     check_faceset_fallback (int faceset, int togo)
147 elmex 1.1 {
148 root 1.3 int fallback = facesets[faceset].fallback;
149 elmex 1.1
150 root 1.3 /* proper case - falls back to base set */
151     if (fallback == 0)
152     return;
153    
154     if (!facesets[fallback].prefix)
155     {
156     LOG (llevError, "Face set %d falls to non set faceset %d\n", faceset, fallback);
157     abort ();
158     }
159     togo--;
160     if (togo == 0)
161     {
162     LOG (llevError, "Infinite loop found in facesets. aborting.\n");
163     abort ();
164 elmex 1.1 }
165 root 1.3 check_faceset_fallback (fallback, togo);
166 elmex 1.1 }
167    
168     #define MAX_IMAGE_SIZE 10000
169    
170     /**
171     * Loads all the image types into memory.
172     *
173     * This way, we can easily send them to the client. We should really do something
174     * better than abort on any errors - on the other hand, these are all fatal
175     * to the server (can't work around them), but the abort just seems a bit
176     * messy (exit would probably be better.)
177     *
178     * Couple of notes: We assume that the faces are in a continous block.
179     * This works fine for now, but this could perhaps change in the future
180     *
181     * Function largely rewritten May 2000 to be more general purpose.
182     * The server itself does not care what the image data is - to the server,
183     * it is just data it needs to allocate. As such, the code is written
184     * to do such.
185     */
186    
187 root 1.3 void
188     read_client_images (void)
189 elmex 1.1 {
190 root 1.3 char filename[400];
191     char buf[HUGE_BUF];
192     char *cp, *cps[7];
193     FILE *infile;
194     int num, len, compressed, fileno, i, badline;
195    
196     memset (facesets, 0, sizeof (facesets));
197     sprintf (filename, "%s/image_info", settings.datadir);
198     if ((infile = open_and_uncompress (filename, 0, &compressed)) == NULL)
199     {
200     LOG (llevError, "Unable to open %s\n", filename);
201     abort ();
202     }
203     while (fgets (buf, HUGE_BUF - 1, infile) != NULL)
204     {
205     badline = 0;
206    
207     if (buf[0] == '#')
208     continue;
209     if (!(cps[0] = strtok (buf, ":")))
210     badline = 1;
211     for (i = 1; i < 7; i++)
212     {
213     if (!(cps[i] = strtok (NULL, ":")))
214     badline = 1;
215     }
216     if (badline)
217     {
218     LOG (llevError, "Bad line in image_info file, ignoring line:\n %s", buf);
219 root 1.2 }
220 root 1.3 else
221     {
222     len = atoi (cps[0]);
223     if (len >= MAX_FACE_SETS)
224     {
225     LOG (llevError, "To high a setnum in image_info file: %d > %d\n", len, MAX_FACE_SETS);
226     abort ();
227 root 1.2 }
228 root 1.12 facesets[len].prefix = strdup (cps[1]);
229     facesets[len].fullname = strdup (cps[2]);
230 root 1.3 facesets[len].fallback = atoi (cps[3]);
231 root 1.12 facesets[len].size = strdup (cps[4]);
232     facesets[len].extension = strdup (cps[5]);
233     facesets[len].comment = strdup (cps[6]);
234 root 1.2 }
235 elmex 1.1 }
236 root 1.3 close_and_delete (infile, compressed);
237     for (i = 0; i < MAX_FACE_SETS; i++)
238     {
239     if (facesets[i].prefix)
240     check_faceset_fallback (i, MAX_FACE_SETS);
241     }
242     /* Loaded the faceset information - now need to load up the
243     * actual faces.
244     */
245    
246     for (fileno = 0; fileno < MAX_FACE_SETS; fileno++)
247     {
248     /* if prefix is not set, this is not used */
249     if (!facesets[fileno].prefix)
250     continue;
251     facesets[fileno].faces = (FaceInfo *) calloc (nrofpixmaps, sizeof (FaceInfo));
252    
253     sprintf (filename, "%s/crossfire.%d", settings.datadir, fileno);
254     LOG (llevDebug, "Loading image file %s\n", filename);
255    
256     if ((infile = open_and_uncompress (filename, 0, &compressed)) == NULL)
257     {
258     LOG (llevError, "Unable to open %s\n", filename);
259     abort ();
260 root 1.2 }
261 root 1.3 while (fgets (buf, HUGE_BUF - 1, infile) != NULL)
262     {
263     if (strncmp (buf, "IMAGE ", 6) != 0)
264     {
265     LOG (llevError, "read_client_images:Bad image line - not IMAGE, instead\n%s", buf);
266     abort ();
267 root 1.2 }
268 root 1.3 num = atoi (buf + 6);
269     if (num < 0 || num >= nrofpixmaps)
270     {
271     LOG (llevError, "read_client_images: Image num %d not in 0..%d\n%s", num, nrofpixmaps, buf);
272     abort ();
273 root 1.2 }
274 root 1.3 /* Skip accross the number data */
275     for (cp = buf + 6; *cp != ' '; cp++);
276     len = atoi (cp);
277     if (len == 0 || len > MAX_IMAGE_SIZE)
278     {
279     LOG (llevError, "read_client_images: length not valid: %d > %d \n%s", len, MAX_IMAGE_SIZE, buf);
280     abort ();
281 root 1.2 }
282 root 1.5 /* We don't actualy care about the name of the image that
283 root 1.3 * is embedded in the image file, so just ignore it.
284     */
285     facesets[fileno].faces[num].datalen = len;
286     facesets[fileno].faces[num].data = (uint8 *) malloc (len);
287     if ((i = fread (facesets[fileno].faces[num].data, len, 1, infile)) != 1)
288     {
289     LOG (llevError, "read_client_images: Did not read desired amount of data, wanted %d, got %d\n%s", len, i, buf);
290     abort ();
291 root 1.2 }
292 root 1.3 facesets[fileno].faces[num].checksum = 0;
293     for (i = 0; i < len; i++)
294     {
295     ROTATE_RIGHT (facesets[fileno].faces[num].checksum);
296     facesets[fileno].faces[num].checksum += facesets[fileno].faces[num].data[i];
297     facesets[fileno].faces[num].checksum &= 0xffffffff;
298 root 1.2 }
299     }
300 root 1.3 close_and_delete (infile, compressed);
301     } /* For fileno < MAX_FACE_SETS */
302 elmex 1.1 }
303    
304     /**
305     * Client tells us what type of faces it wants. Also sets
306     * the caching attribute.
307     *
308     */
309    
310 root 1.3 void
311 root 1.13 SetFaceMode (char *buf, int len, client *ns)
312 elmex 1.1 {
313 root 1.3 int mask = (atoi (buf) & CF_FACE_CACHE), mode = (atoi (buf) & ~CF_FACE_CACHE);
314 elmex 1.1
315 root 1.3 if (mode == CF_FACE_NONE)
316 root 1.7 ns->facecache = 1;
317 root 1.3 else if (mode != CF_FACE_PNG)
318 root 1.14 ns->send_packet_printf ("drawinfo %d %s", NDI_RED, "Warning - send unsupported face mode. Will use Png");
319 root 1.7
320 root 1.3 if (mask)
321 root 1.7 ns->facecache = 1;
322 elmex 1.1 }
323    
324     /**
325     * Client has requested pixmap that it somehow missed getting.
326     * This will be called often if the client is
327     * caching images.
328     */
329    
330 root 1.3 void
331 root 1.13 SendFaceCmd (char *buff, int len, client * ns)
332 elmex 1.1 {
333 root 1.3 long tmpnum = atoi (buff);
334     short facenum = tmpnum & 0xffff;
335 elmex 1.1
336 root 1.3 if (facenum != 0)
337     esrv_send_face (ns, facenum, 1);
338 elmex 1.1 }
339    
340     /**
341     * Sends a face to a client if they are in pixmap mode
342     * nothing gets sent in bitmap mode.
343     * If nocache is true (nonzero), ignore the cache setting from the client -
344     * this is needed for the askface, in which we really do want to send the
345     * face (and askface is the only place that should be setting it). Otherwise,
346     * we look at the facecache, and if set, send the image name.
347     */
348    
349 root 1.3 void
350 root 1.13 esrv_send_face (client *ns, short face_num, int nocache)
351 elmex 1.1 {
352 root 1.3 char fallback;
353 elmex 1.1
354 root 1.3 if (face_num <= 0 || face_num >= nrofpixmaps)
355     {
356     LOG (llevError, "esrv_send_face (%d) out of bounds??\n", face_num);
357     return;
358     }
359    
360 root 1.8 packet sl;
361 root 1.3 fallback = get_face_fallback (ns->faceset, face_num);
362    
363     if (facesets[fallback].faces[face_num].data == NULL)
364     {
365     LOG (llevError, "esrv_send_face: faces[%d].data == NULL\n", face_num);
366     return;
367     }
368    
369     if (ns->facecache && !nocache)
370     {
371 root 1.6 sl << (ns->image2 ? "face2 " : "face1 ")
372     << uint16 (face_num);
373    
374 root 1.3 if (ns->image2)
375 root 1.6 sl << uint8 (fallback);
376    
377     sl << uint32 (facesets[fallback].faces[face_num].checksum)
378     << new_faces[face_num].name;
379 root 1.3
380 root 1.11 ns->send_packet (sl);
381 root 1.3 }
382     else
383     {
384 root 1.6 sl << (ns->image2 ? "image2 " : "image ")
385     << uint32 (face_num);
386    
387 root 1.3 if (ns->image2)
388 root 1.6 sl << uint8 (fallback);
389    
390     sl << uint32 (facesets[fallback].faces[face_num].datalen)
391     << data (facesets[fallback].faces[face_num].data, facesets[fallback].faces[face_num].datalen);
392    
393 root 1.11 ns->send_packet (sl);
394 elmex 1.1 }
395 root 1.6
396 root 1.3 ns->faces_sent[face_num] |= NS_FACESENT_FACE;
397 elmex 1.1 }
398    
399     /**
400     * Sends the number of images, checksum of the face file,
401     * and the image_info file information. See the doc/Developers/protocol
402     * if you want further detail.
403     */
404    
405 root 1.3 void
406 root 1.13 send_image_info (client * ns, char *params)
407 elmex 1.1 {
408 root 1.8 packet sl;
409 elmex 1.1
410 root 1.6 sl.printf ("replyinfo image_info\n%d\n%d\n", nrofpixmaps - 1, bmaps_checksum);
411    
412     for (int i = 0; i < MAX_FACE_SETS; i++)
413     if (facesets[i].prefix)
414     sl.printf ("%d:%s:%s:%d:%s:%s:%s",
415     i, facesets[i].prefix, facesets[i].fullname, facesets[i].fallback,
416     facesets[i].size, facesets[i].extension, facesets[i].comment);
417 elmex 1.1
418 root 1.11 ns->send_packet (sl);
419 elmex 1.1 }
420    
421     /**
422     * Sends requested face information.
423     * \param ns socket to send to
424     * \param params contains first and last index of face
425     *
426     * For each image in [start..stop] sends
427     * - checksum
428     * - name
429     */
430 root 1.3 void
431 root 1.13 send_image_sums (client * ns, char *params)
432 elmex 1.1 {
433 root 1.3 int start, stop;
434     char *cp, buf[MAX_BUF];
435    
436 root 1.8 packet sl;
437 root 1.3
438     start = atoi (params);
439     for (cp = params; *cp != '\0'; cp++)
440     if (*cp == ' ')
441     break;
442    
443     stop = atoi (cp);
444     if (stop < start || *cp == '\0' || (stop - start) > 1000 || stop >= nrofpixmaps)
445     {
446 root 1.7 sl.printf ("replyinfo image_sums %d %d", start, stop);
447     ns->send_packet (sl);
448     sl.reset ();
449 root 1.3 return;
450     }
451    
452 root 1.6 sl.printf ("replyinfo image_sums %d %d ", start, stop);
453 root 1.3
454 root 1.6 for (int i = start; i <= stop; i++)
455 root 1.3 {
456     ns->faces_sent[i] |= NS_FACESENT_FACE;
457    
458 root 1.6 int qq = get_face_fallback (ns->faceset, i);
459    
460     sl << uint16 (i)
461     << uint32 (facesets[qq].faces[i].checksum)
462     << uint8 (qq)
463     << data8 (&new_faces[i].name, new_faces[i].name.length () + 1);
464 root 1.3 }
465 root 1.6
466 root 1.3 /* It would make more sense to catch this pre-emptively in the code above.
467     * however, if this really happens, we probably just want to cut down the
468     * size to less than 1000, since that is what we claim the protocol would
469     * support.
470     */
471 root 1.9 if (sl.length () >= MAXSOCKBUF)
472 root 1.3 {
473 root 1.9 LOG (llevError, "send_image_send: buffer overrun, %d > %d\n", sl.length (), MAXSOCKBUF);
474 root 1.3 abort ();
475 elmex 1.1 }
476 root 1.6
477 root 1.11 ns->send_packet (sl);
478 elmex 1.1 }
479 root 1.6