[crossfire] Protocol & compression.
Mark Wedel
mwedel at sonic.net
Tue Mar 28 01:26:15 CST 2006
tchize wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Just a note about the suggested
> s->c compressed <data>
> c->s compressed <data> (yeah imho shoud go in both directions)
I'm not sure if there is anything to really gain by the client compressing the
data. I can't really think of many times the client is actually sending enough
data that compression will do any good what so ever. The one exception might be
very long chat/shout/say messages, but even then, that seems fairly unlikely.
Granted, probably wouldn't be that hard to add, but does add some at least
minimal complication, and if it doesn't gain us anything, I'd rather avoid that.
>
> if we assume data contains a compressed list of 1 or more commands, i
> think the question on what need and what need not to be compressed os
> not immediate and we can have various attempt with various server /
> client version. The fact of assuming any command can be compressed by
> algorithm X but that not all commands will be compressed, is enough to
> write this protocol add-on. Then it's a matter of tuning the
> compression triggers, but this can be done without breaking
> interaction with previous versions. So imho, the 'when' to compress
> should not be fixed in protocol, only the how is to be fixed.
correct. That was my assumption - the server would figure out what it thinks
is worthy of compression. A server with gobs of bandwidth but not a lot of cpu
time could decide that nothing is worth compressing.
> One of the most important question is which algorithm do we use? You
> said you gave a try with zlib, but which algorithm does it uses? Does
> it involves a dictionnary? If yes, do we plan to reset the dictionnary
> content between each compress command or do we plan to keep it from
> begin to end?
I used the 'compress' function of zlib:
http://www.zlib.net/manual.html#compress
Since each call to compress is self contained, the compressed data then
includes all the dictionary or other info necessary. Note that given crossfire
will be working on multiple sockets, we can't use a library that holds any
state. And if there are new structures needed to hold state, this starts to
increase the complication level some.
>
> My opinion currently is
> - assume client can receive any data in compressed mode, but that not
> all datas are compressed (the sender has choice for every command)
> - assume the compression stream as something interleaved with not
> compressed stream, identified by a specific marker (compressed)
> - assume the compressed stream continuous (same 'zlib/any chosen
> algorithm' session, usefull considering number of repeated text messages)
This point is the real gotcha however - since the server can choose what data
to compress, the question then becomes what portion of the commands are being
compressed?
If less than say 50%, then we're better of going with explicit 'this data is
compressed' than continual stream, as we're then spending more bandwidth on the
transitions than we would just be prefixing.
Also note that the real question comes where do we do the compression. It is
trivially easy to modify Send_With_Handling to take another flag (compressible)
and compress the data and send it along. It is harder to do it as a stream
method, especially if we want to turn of compression.
This is because if you want to do it as a stream, easiest approach then is to
do the compression before writing to the actual socket. But by the point, all
we have in the input buffer is just a bunch of bytes - we have no idea if some
should or should not be compressed.
If we were to go that approach, the socket code basically has to be rewritten.
The best method would be to basically have a list of SockList structs, and add
to that a field for the compress byte.
Then when we go to send the actual data, the server could parse all the
SockList, and based on current state of socket and the compress flag, figure out
what to do (if for example, socket is in non compressed state and socklist says
don't compress, just send it along. states don't match, send command to
transition state. Compress all socklists with compress flag set as a single
stream, etc.
But this starts to lead down a pretty slippery slope. In that model, you
almost want to start reorganizing the packets - for example, right now, with the
map commands, you are going to often get images sent (non compressible) right
next to smoothing information (compressible). Ideally, you want to organize all
the face data together as one non compressible block, and all the smooth
information as a compressible block. But re-arranging order of commands starts
to get tricky - the client is currently coded (and says so in the spec) that it
will get image info before that image is referenced, so we can't re-order image
commands after map command. But knowing what to reorder and what not is where
this cans of worms is.
As I said before, at current time, I'm much more interested in code
cleanliness and simple code than getting things too complicated. Especially
because complicated code tends to take longer to write, and if it never gets
done, there isn't much point.
If we are going to do stream compression, I'd say we just compress everything
we send to the client, and don't care about the cpu time and/or data that
doesn't compress well. That is the simpler approach, and could get done in
relatively easily.
> - assume setup negociate the algorithm (client say i support x,y,z
> then server send ok, let's go for y algorithm, this is how http does it)
Yes, but IMO, I don't see the need for more than one algorithm. I'd say we
just standardize on zlib.
Sure, there may be other libraries which do marginally better. But once
again, is it worth while to have another compression method that might be
marginally better (in terms of code complication, library support, etc). If
there is some library that is clearly better, or one is written, we could easily
enough add support for it at that point. One could very well see things like:
gzstart
....
gzend
and
bz2start
....
bz2end
And so on. In many cases, the support for different methods is more for
backwards compatiblity (at the time, A was best, but now B is better). We're
not at that point, since we don't even have A.
I'd also think that we could use the method that is done right now for the map
commands. Basically, it goes like:
C->S: setup map2cmd 1
S->C: setup map2cmd false
C->S: setup map1acmd
S->C: setup map1acmd false
C->S: setup map1cmd
S->C: setup map1cmd true
(if the client sends true back for an earlier revision, the client stops its
fallback method). That logic would find for compress methods down the road
(setup compression gz, if that doesn't work, setup compression bz2, setup
compression whatever).
More information about the crossfire
mailing list