--- deliantra/server/socket/image.C 2006/08/13 17:16:05 1.1
+++ deliantra/server/socket/image.C 2007/07/06 03:52:51 1.46
@@ -1,31 +1,26 @@
/*
- * static char *rcsid_init_c =
- * "$Id: image.C,v 1.1 2006/08/13 17:16:05 elmex Exp $";
+ * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
+ *
+ * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
+ * Copyright (©) 2001,2007 Mark Wedel
+ * Copyright (©) 1992,2007 Frank Tore Johansen
+ *
+ * Crossfire TRT is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * The authors can be reached via e-mail to
*/
-/*
- CrossFire, A Multiplayer game for X-windows
-
- Copyright (C) 2001 Mark Wedel
- Copyright (C) 1992 Frank Tore Johansen
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- The author can be reached via e-mail to crossfire-devel@real-time.com
-*/
-
/** \file
* Image related communication
*
@@ -40,28 +35,30 @@
#include
#include
-#include
-#include
-#include
+#include "crc.h"
#define MAX_FACE_SETS 20 /**< Maximum number of image sets the program will handle */
/** Information about one image */
-typedef struct FaceInfo {
- uint8 *data; /**< image data */
- uint16 datalen; /**< length of the xpm data */
- uint32 checksum; /**< Checksum of face data */
+typedef struct FaceInfo
+{
+ uint8 *data; /**< image data */
+ uint16 datalen; /**< length of the xpm data */
+ uint32 checksum; /**< Checksum of face data */
} FaceInfo;
/** Information about one face set */
-typedef struct {
- char *prefix; /**< */
- char *fullname;
- uint8 fallback; /**< faceset to use when an image is not found in this faceset */
- char *size;
- char *extension;
- char *comment;
- FaceInfo *faces; /**< images in this faceset */
+typedef struct
+{
+ char *prefix; /**< */
+ char *fullname;
+
+ uint8 fallback; /**< faceset to use when an image is not found in this faceset */
+ char *size;
+ char *extension;
+ char *comment;
+
+ FaceInfo *faces; /**< images in this faceset */
} FaceSets;
static FaceSets facesets[MAX_FACE_SETS]; /**< All facesets */
@@ -70,30 +67,37 @@
* Checks specified faceset is valid
* \param fsn faceset number
*/
-int is_valid_faceset(int fsn)
+int
+is_valid_faceset (int fsn)
{
- if (fsn >=0 && fsn < MAX_FACE_SETS && facesets[fsn].prefix) return TRUE;
- return FALSE;
+ if (fsn >= 0 && fsn < MAX_FACE_SETS && facesets[fsn].prefix)
+ return TRUE;
+ return FALSE;
}
/**
* Frees all faceset information
*/
-void free_socket_images(void)
+void
+free_socket_images (void)
{
- int num,q;
+ int num, q;
- for(num=0;num=MAX_FACE_SETS) {
- LOG(llevError,"To high a setnum in image_info file: %d > %d\n",
- len, MAX_FACE_SETS);
- abort();
- }
- facesets[len].prefix = strdup_local(cps[1]);
- facesets[len].fullname = strdup_local(cps[2]);
- facesets[len].fallback = atoi(cps[3]);
- facesets[len].size = strdup_local(cps[4]);
- facesets[len].extension = strdup_local(cps[5]);
- facesets[len].comment = strdup_local(cps[6]);
- }
- }
- close_and_delete(infile,compressed);
- for (i=0; i=nrofpixmaps) {
- LOG(llevError,"read_client_images: Image num %d not in 0..%d\n%s",
- num,nrofpixmaps,buf);
- abort();
- }
- /* Skip accross the number data */
- for (cp=buf+6; *cp!=' '; cp++) ;
- len = atoi(cp);
- if (len==0 || len>MAX_IMAGE_SIZE) {
- LOG(llevError,"read_client_images: length not valid: %d > %d \n%s",
- len,MAX_IMAGE_SIZE,buf);
- abort();
- }
- /* We don't actualy care about the name if the image that
- * is embedded in the image file, so just ignore it.
- */
- facesets[fileno].faces[num].datalen = len;
- facesets[fileno].faces[num].data = (uint8*) malloc(len);
- if ((i=fread(facesets[fileno].faces[num].data, len, 1, infile))!=1) {
- LOG(llevError,"read_client_images: Did not read desired amount of data, wanted %d, got %d\n%s",
- len, i, buf);
- abort();
- }
- facesets[fileno].faces[num].checksum=0;
- for (i=0; ifacecache = 1;
+ else if (mode != CF_FACE_PNG)
+ ns->send_packet_printf ("drawinfo %d %s", NDI_RED, "Warning - send unsupported face mode. Will use Png");
-void SetFaceMode(char *buf, int len, NewSocket *ns)
+ if (mask)
+ ns->facecache = 1;
+}
+
+/**
+ * client requested an image. send it rate-limited
+ * before flushing.
+ */
+void
+AskFaceCmd (char *buf, int len, client *ns)
{
- char tmp[256];
+ int idx = 0, pri = 0;
- int mask =(atoi(buf) & CF_FACE_CACHE), mode=(atoi(buf) & ~CF_FACE_CACHE);
+ sscanf (buf, "%d %d", &idx, &pri);
- if (mode==CF_FACE_NONE) {
- ns->facecache=1;
- } else if (mode!=CF_FACE_PNG) {
- sprintf(tmp,"drawinfo %d %s", NDI_RED,"Warning - send unsupported face mode. Will use Png");
- Write_String_To_Socket(ns, tmp, strlen(tmp));
-#ifdef ESRV_DEBUG
- LOG(llevDebug,"SetFaceMode: Invalid mode from client: %d\n", mode);
-#endif
+ const facedata *d = face_data (idx, ns->faceset);
+
+ if (!d)
+ return; // doh
+
+ client::ixsend ix;
+
+ ix.pri = pri;
+ ix.idx = idx;
+ ix.ofs = d->data.size ();
+
+ auto (pos, ns->ixface.end ());
+
+ if (ns->fxix < 2)
+ {
+ // gcfclient does not support prioritising, older cfplus versions
+ // do not support interleaved transfers.
+ if (!ns->ixface.empty ())
+ pos = ns->ixface.end () - 1;
}
- if (mask) {
- ns->facecache=1;
+ else
+ {
+ // the by far most common case will be to insert
+ // near the end, so little looping.
+ while (pos != ns->ixface.begin ())
+ {
+ --pos;
+
+ // sort within 2k bins, to slightly prefer smaller images
+ if (pri > pos->pri || (pri == pos->pri && (ix.ofs >> 11) <= (pos->ofs >> 11)))
+ {
+ ++pos;
+ break;
+ }
+ }
}
+
+ ns->ixface.insert (pos, ix);
+
+#if 0
+ for (auto (i, ns->ixface.begin ()); i != ns->ixface.end (); ++i)
+ fprintf (stderr, "<%d,%d> ", i->pri, i->ofs);
+ fprintf (stderr, "\n");
+#endif
}
/**
- * Client has requested pixmap that it somehow missed getting.
- * This will be called often if the client is
- * caching images.
+ * Tells client the picture it has to use
+ * to smooth a picture number given as argument.
*/
+void
+AskSmooth (char *buf, int len, client *ns)
+{
+ ns->send_face (atoi (buf));
+ ns->flush_fx ();
+}
-void SendFaceCmd(char *buff, int len, NewSocket *ns)
+// how lame
+static void print_facename (packet &sl, const facedata &d)
{
- long tmpnum = atoi(buff);
- short facenum=tmpnum & 0xffff;
+ for (int i = 0; i < CHKSUM_SIZE; ++i)
+ sl.printf ("%02x", d.chksum [i]);
+}
- if(facenum!=0)
- esrv_send_face(ns, facenum,1);
+// gcfclient uses the server-provided checksum for comparison, but always
+// writes a broken checksum to its cache file, so we have to provide
+// gcfclient with the same broken (and useless) checksum just to have it
+// cache the image despite its bugs.
+static uint32 gcfclient_checksum (const facedata *d)
+{
+ uint32 csum = 0;
+
+ for (std::string::const_iterator i = d->data.begin ();
+ i != d->data.end ();
+ ++i)
+ {
+ csum = rotate_right (csum);
+ csum += *(uint8 *)&*i;
+ }
+
+ return csum;
}
/**
@@ -321,60 +293,200 @@
* face (and askface is the only place that should be setting it). Otherwise,
* we look at the facecache, and if set, send the image name.
*/
+void
+client::send_face (faceidx facenum)
+{
+ // never send face 0. ever. it does not exist.
+ if (!facenum)
+ return;
+
+ const facedata *d = face_data (facenum, faceset);
+
+ if (!d)
+ {
+ LOG (llevError, "client::send_face (%d) out of bounds??\n", facenum);
+ return;
+ }
+
+ if (faces_sent[facenum])
+ return;
+
+ faces_sent[facenum] = true;
+
+ // if for some reason we let a client without face caching connect,
+ // we better support that decision here and implement it.
+ if (!facecache)
+ return send_image (facenum);
+
+ if (fxix)
+ {
+ fxface.push_back (facenum);
+ return;
+ }
+
+ packet sl;
+
+ if (force_face0)
+ sl << "face " << uint16 (facenum);
+ else if (image2)
+ sl << "face2 " << uint16 (facenum) << uint8 (0) << uint32 (force_bad_checksum ? gcfclient_checksum (d) : 0);
+ else
+ sl << "face1 " << uint16 (facenum) << uint32 (force_bad_checksum ? gcfclient_checksum (d) : 0);
+
+ // how lame
+ print_facename (sl, *d);
+ send_packet (sl);
-void esrv_send_face(NewSocket *ns,short face_num, int nocache)
+ if (EMI_smooth)
+ {
+ faceinfo *f = face_info (facenum);
+
+ if (f->smooth)
+ {
+ send_face (f->smooth);
+
+ packet sl ("smooth");
+
+ sl << uint16 (facenum)
+ << uint16 (f->smooth);
+
+ send_packet (sl);
+ }
+ }
+}
+
+void client::flush_fx ()
+{
+ while (!fxface.empty ())
+ {
+ packet fx ("fx");
+ packet sx ("sx");
+
+ do
+ {
+ faceidx facenum = fxface.back (); fxface.pop_back ();
+
+ const facedata *d = face_data (facenum, faceset);
+
+ if (d)
+ {
+ fx << ber32 (facenum)
+ << data8 (d->chksum, CHKSUM_SIZE);
+
+ if (smoothing)
+ {
+ faceinfo *f = face_info (facenum);
+
+ if (f->smooth)
+ {
+ send_face (f->smooth);
+ sx << ber32 (facenum)
+ << ber32 (f->smooth)
+ << ber32 (f->smoothlevel);
+ }
+ }
+ }
+ }
+ while (!fxface.empty ()
+ && fx.room () > ber32::size + CHKSUM_SIZE + 1
+ && sx.room () > ber32::size * 3);
+
+ send_packet (fx);
+ if (sx.length () > 3) send_packet (sx);
+ }
+}
+
+void
+client::send_image (faceidx facenum)
+{
+ // never send face 0. ever. it does not exist.
+ if (!facenum)
+ return;
+
+ const facedata *d = face_data (facenum, faceset);
+
+ faces_sent[facenum] = true;
+
+ if (!d)
+ {
+ LOG (llevError, "client::send_image (%d) out of bounds??\n", facenum);
+ return;
+ }
+
+ //TODO: check type here?
+
+ if (force_image_newmap)
+ force_newmap = true;
+
+ packet sl;
+
+ sl << (image2 ? "image2 " : "image ")
+ << uint32 (facenum);
+
+ if (image2)
+ sl << uint8 (0);
+
+ sl << uint32 (d->data.size ())
+ << data (d->data.data (), d->data.size ());
+
+ send_packet (sl);
+}
+
+// send all faces of this object to the client
+// this uses more bandwidth initially, but makes
+// animations look much smoother, and every client
+// is supposed to do client-side caching anyways.
+void
+client::send_faces (object *ob)
+{
+ send_face (ob->face);
+
+ if (ob->animation_id)
+ {
+ animation &anim = animations [ob->animation_id];
+
+ for (int i = 0; i < anim.num_animations; i++)
+ send_face (anim.faces [i]);
+ }
+}
+
+/**
+ * Need to send an animation sequence to the client.
+ * We will send appropriate face commands to the client if we haven't
+ * sent them the face yet (this can become quite costly in terms of
+ * how much we are sending - on the other hand, this should only happen
+ * when the player logs in and picks stuff up.
+ */
+void
+client::send_animation (short anim_num)
{
- SockList sl;
- char fallback;
+ /* Do some checking on the anim_num we got. Note that the animations
+ * are added in contigous order, so if the number is in the valid
+ * range, it must be a valid animation.
+ */
+ if (anim_num < 0 || anim_num >= animations.size ())
+ {
+ LOG (llevError, "esrv_send_anim (%d) out of bounds??\n", anim_num);
+ return;
+ }
- if (face_num <= 0 || face_num >= nrofpixmaps) {
- LOG(llevError,"esrv_send_face (%d) out of bounds??\n",face_num);
- return;
- }
-
- sl.buf = (unsigned char*) malloc(MAXSOCKBUF);
- fallback = get_face_fallback(ns->faceset, face_num);
-
- if (facesets[fallback].faces[face_num].data == NULL) {
- LOG(llevError,"esrv_send_face: faces[%d].data == NULL\n",face_num);
- return;
- }
-
- if (ns->facecache && !nocache) {
- if (ns->image2)
- strcpy((char*)sl.buf, "face2 ");
- else if (ns->sc_version >= 1026)
- strcpy((char*)sl.buf, "face1 ");
- else
- strcpy((char*)sl.buf, "face ");
-
- sl.len=strlen((const char*)sl.buf);
- SockList_AddShort(&sl, face_num);
- if (ns->image2)
- SockList_AddChar(&sl, fallback);
- if (ns->sc_version >= 1026)
- SockList_AddInt(&sl, facesets[fallback].faces[face_num].checksum);
- strcpy((char*)sl.buf + sl.len, new_faces[face_num].name);
- sl.len += strlen(new_faces[face_num].name);
- Send_With_Handling(ns, &sl);
- }
- else {
- if (ns->image2)
- strcpy((char*)sl.buf, "image2 ");
- else
- strcpy((char*)sl.buf, "image ");
- sl.len=strlen((char*)sl.buf);
- SockList_AddInt(&sl, face_num);
- if (ns->image2)
- SockList_AddChar(&sl, fallback);
- SockList_AddInt(&sl, facesets[fallback].faces[face_num].datalen);
- memcpy(sl.buf+sl.len, facesets[fallback].faces[face_num].data,
- facesets[fallback].faces[face_num].datalen);
- sl.len += facesets[fallback].faces[face_num].datalen;
- Send_With_Handling(ns, &sl);
+ packet sl ("anim");
+
+ sl << uint16 (anim_num)
+ << uint16 (0); /* flags - not used right now */
+
+ /* Build up the list of faces. Also, send any information (ie, the
+ * the face itself) down to the client.
+ */
+ for (int i = 0; i < animations[anim_num].num_animations; i++)
+ {
+ send_face (animations[anim_num].faces[i]);
+ sl << uint16 (animations[anim_num].faces[i]); /* flags - not used right now */
}
- ns->faces_sent[face_num] |= NS_FACESENT_FACE;
- free(sl.buf);
+
+ send_packet (sl);
+
+ anims_sent[anim_num] = 1;
}
/**
@@ -382,25 +494,17 @@
* and the image_info file information. See the doc/Developers/protocol
* if you want further detail.
*/
-
-void send_image_info(NewSocket *ns, char *params)
+void
+send_image_info (client *ns, char *params)
{
- SockList sl;
- int i;
+ packet sl;
+
+ //TODO: second parameter is a checksum, but it makes no sense in this current framework
+ sl.printf ("replyinfo image_info\n%d\n%u\n", MAX_FACES, 0);
- sl.buf = (unsigned char *) malloc(MAXSOCKBUF);
+ sl << "0:base:standard:0:32x32:none:The old 32x32 faceset.\n";
- sprintf((char*)sl.buf,"replyinfo image_info\n%d\n%d\n", nrofpixmaps-1, bmaps_checksum);
- for (i=0; isend_packet (sl);
}
/**
@@ -412,53 +516,58 @@
* - checksum
* - name
*/
-void send_image_sums(NewSocket *ns, char *params)
+void
+send_image_sums (client *ns, char *params)
{
- int start, stop;
- short i;
- char qq;
- char *cp, buf[MAX_BUF];
- SockList sl;
-
- sl.buf = (unsigned char *) malloc(MAXSOCKBUF);
-
- start = atoi(params);
- for (cp = params; *cp != '\0'; cp++)
- if (*cp == ' ') break;
-
- stop = atoi(cp);
- if (stop < start || *cp == '\0' || (stop-start)>1000 || stop >= nrofpixmaps) {
- sprintf(buf,"replyinfo image_sums %d %d", start, stop);
- cs_write_string(ns, buf, strlen(buf));
- return;
- }
- sprintf((char*)sl.buf,"replyinfo image_sums %d %d ", start, stop);
-
- sl.len = strlen((const char*)sl.buf);
-
- for (i=start; i<=stop; i++) {
- SockList_AddShort(&sl, i);
- ns->faces_sent[i] |= NS_FACESENT_FACE;
-
- qq = get_face_fallback(ns->faceset, i);
- SockList_AddInt(&sl, facesets[qq].faces[i].checksum);
- SockList_AddChar(&sl, qq);
-
- qq = strlen(new_faces[i].name);
- SockList_AddChar(&sl, ( char )( qq + 1 ));
- strcpy((char*)sl.buf + sl.len, new_faces[i].name);
- sl.len += qq;
- SockList_AddChar(&sl, 0);
- }
- /* It would make more sense to catch this pre-emptively in the code above.
- * however, if this really happens, we probably just want to cut down the
- * size to less than 1000, since that is what we claim the protocol would
- * support.
- */
- if (sl.len >= MAXSOCKBUF) {
- LOG(llevError,"send_image_send: buffer overrun, %d > %d\n", sl.len, MAXSOCKBUF);
- abort();
+ int start, stop;
+ char *cp;
+
+ packet sl;
+
+ start = atoi (params);
+ for (cp = params; *cp != '\0'; cp++)
+ if (*cp == ' ')
+ break;
+
+ stop = atoi (cp);
+ if (stop < start || *cp == '\0' || (stop - start) > 1000 || stop >= MAX_FACES)
+ {
+ sl.printf ("replyinfo image_sums %d %d", start, stop);
+ ns->send_packet (sl);
+ sl.reset ();
+ return;
}
- Send_With_Handling(ns, &sl);
- free(sl.buf);
+
+ sl.printf ("replyinfo image_sums %d %d ", start, stop);
+
+ for (int i = start; i <= stop && i < faces.size (); i++)
+ {
+ ns->faces_sent[i] = true;
+
+ const facedata *d = face_data (i, ns->faceset);
+
+ if (sl.room () < 2 + 4 + 1 + d->data.size () + 1)
+ break;
+
+ sl << uint16 (i)
+ << uint32 (0) // checksum
+ << uint8 (ns->faceset);
+
+ print_facename (sl, *d); sl << uint8 (0);
+ }
+
+ /* It would make more sense to catch this pre-emptively in the code above.
+ * however, if this really happens, we probably just want to cut down the
+ * size to less than 1000, since that is what we claim the protocol would
+ * support.
+ */
+ //TODO: taken care of above, should simply abort or make sure the above code is correct
+ if (sl.length () >= MAXSOCKBUF)
+ {
+ LOG (llevError, "send_image_send: buffer overrun, %d > %d\n", sl.length (), MAXSOCKBUF);
+ abort ();
+ }
+
+ ns->send_packet (sl);
}
+