/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * Copyright (©) 2001 Mark Wedel * Copyright (©) 1992 Frank Tore Johansen * * Deliantra is free software: you can redistribute it and/or modify it under * the terms of the Affero 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 Affero GNU General Public License * and the GNU General Public License along with this program. If not, see * . * * The authors can be reached via e-mail to */ /* * Image related communication * * This file deals with the image related communication to the * client. I've located all the functions in this file - this * localizes it more, and means that we don't need to declare * things like all the structures as globals. */ #include #include #include "crc.h" #define MAX_IMAGE_SIZE 10000 /** * client requested an image. send it rate-limited * before flushing. */ void AskFaceCmd (char *buf, int len, client *ns) { int idx = 0, pri = 0; sscanf (buf, "%d %d", &idx, &pri); //TODO: somehow fetch default priority from send_fx here const faceinfo *f = face_info (idx); if (!f) return; // doh int set = ns->tileset; if (!f->face [set].chksum_len) set = 0; /* cfperl_ix calls cf::face::ix which loads the data */ /* and then calls cf::client::send_ix to actually queue the ix */ cfperl_ix (ns, set, idx, pri); } void client::ix_send (faceidx idx, sint16 pri, SV *data_sv) { STRLEN size; char *data = SvPVbyte (data_sv, size); ixsend ix; ix.pri = pri; ix.idx = idx; ix.ofs = size; ix.data = (uint8 *)data; ix.data_sv = SvREFCNT_inc (data_sv); auto (pos, ixface.end ()); // the by far most common case will be to insert // near the end, so little looping. while (pos != 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; } } ixface.insert (pos, ix); #if 0 for (auto (i, ixface.begin ()); i != ixface.end (); ++i) fprintf (stderr, "<%d,%d> ", i->pri, i->ofs); fprintf (stderr, "\n"); #endif } void client::ix_pop () { ixsend &ix = ixface.back (); SvREFCNT_dec (ix.data_sv); ixface.pop_back (); } /** * Sends a face offer (fx) to a client. * If nocache is true (nonzero), ignore the cache setting from the client - * this is needed for the askface, in which we really do want to send the * 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, int pri) { // never send face 0. ever. it does not exist. if (!facenum) return; faceinfo *f = face_info (facenum); if (!f) { LOG (llevError | logBacktrace, "client::send_face (%d) out of bounds??\n", facenum); return; } // refuse to send non-image faces unless requested if (!fx_want [f->type]) return; if (faces_sent [facenum]) return; faces_sent [facenum] = true; fxface.push_back (facenum); } void client::flush_fx () { while (!fxface.empty ()) { packet fx ("fx"); packet sx ("sx"); int type = 0; do { faceidx facenum = fxface.back (); fxface.pop_back (); if (const faceinfo *f = face_info (facenum)) { if (f->type != type) { type = f->type; fx << ber32 (0) << uint8 (1) << uint8 (type); } const facedata *d = f->data (tileset); fx << ber32 (facenum) << data8 (d->chksum, d->chksum_len); if (smoothing) { faceinfo *f = face_info (facenum); if (f->smooth) { send_face (f->smooth, -110); sx << ber32 (facenum) << ber32 (f->smooth) << ber32 (f->smoothlevel); } } } } while (!fxface.empty () && fx.room () > ber32::size + CHKSUM_MAXLEN + 1 + 3 /* type switch */ && sx.room () > ber32::size * 3); send_packet (fx); if (sx.length () > 3) send_packet (sx); } } // 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, 10); if (ob->animation_id) { const animation &anim = ob->anim (); for (int i = 0; i < anim.num_animations; i++) send_face (anim.faces [i], -10); } } /** * 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) { /* Do some checking on the anim_num we got. Note that the animations * are added in contiguous 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; } 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], -20); sl << uint16 (animations[anim_num].faces[i]); } send_packet (sl); anims_sent[anim_num] = 1; } void client::invalidate_face (faceidx idx) { if (!faces_sent [idx]) return; faces_sent [idx] = false; send_face (idx); //TODO: check for active ix and abort it. } void client::invalidate_all_faces () { for (faceidx i = 0; i < faces_sent.size (); ++i) invalidate_face (i); }