Posted 25 August 2013 - 02:08 AM
Distance based quality reduction of positions. Positional data is rather non-intuitive in terms of how you can reduce the quality without the player noticing, which I suspect, is why most folks don't do it. Basically don't think in absolute x/y/z but instead convert everything to polar coordinates around the client in question. Up to about 10 meters around a player, 3 bytes can transmit data which is within 2-3% of the absolute 24 bits of floating point data you would normally use. (Assuming your game unit floats, represent 1=1 meter.) The non-intuitive side, the further away data costs more bytes per message out past say 50 meters as the accuracy required to maintain the angles runs the distance portion out of bits. I.e. up to 10 meters 8:8:8 is likely accurate enough, from 10-50 9:9:6 is how you would compress the data because you effectively have 7 bits of distance since the message id changes the distance calculation. Beyond that the distance bands have to get more narrow, the angles use more bits and the distance is going to have to expand into a 4th byte. Though, for any reasonable view range, the worst case even in a space game should be 64bits or less, still saving 2 bytes compared to absolute floats. (NOTE: obviously with UDP you have to send a "last known received" position item which should be either "the position I'm sending in this packet"==0 or a relative to this packet saying "that's the last one you said you saw, that I know about. Hence, 5 bytes: ID, lon, lat, distance, relative packet id. Also, this assumes ID's are <128 and you are bit slicing such as GPB formatting for the integers. There are a couple more items to keep in mind if used in UDP, still much lower byte count than 3 floats.)
Rotation based on quality reduction. In general, for most purposes, send 3 unsigned bytes and one signed byte to represent a normalized quaternion. There is little reason for rotations to be all that accurate during transmission. (As I remember, the sign on the w is required, been a long time though, could remember it wrong.)
Depending on the game, use string dictionaries. I.e. send a message such as "present this string to user: id" and the client has the string tables. That sucks for quest data though, folks will just dig it out of the client and ruin most of the quests by showing everything. That happens eventually but the other option is to send "present this string to the user: id" and the client will then look it up, if it does not have it, it replies with "don't have string: id" at which point the server sends it across. Initially this costs more bandwidth but at least you haven't shipped the entire storyline/branches etc to the players so it can't be ruined with simple data mining. Overall though, you can do all your story expansions on the servers and the players only know about it if they actually find it and the cost is not too much more than providing the patch with the strings in it. Additionally, all string data "should" run through a zlib or related library, assuming lossless chat data/string data, the dictionaries for compression will be quite full and you'll get fair compression overall. It won't be as good as separate streams and compressors, but likely at least a 50% compression rate should still apply.
Another one which depends on the game type. Relative "distant" positions. Say you have a large army style of gameplay with the possibility to come into range of a lot of objects quickly, even though they are a long way away. If you have a sweep/prune system it is easy to build islands like you would do for collision, but instead you are building it for network messaging. Given an island, you transmit the distant polar coordinate of the island center then a stream of delta's to represent the items which need to be added. Given distance, basically you just grid out the size of the island based on desired accuracy and send ID+grid location within the island. Double compression effectively. (You of course start refining data with more bits per object later...)
As always, looking at things in a slightly different way can lead to impressive results. I've implemented most of these at one time or another and know they reduce bandwidth in big chunks. Using polar coordinates in TCP is a huge win, with UDP it's semi-iffy depending on the connection though it can be made solid. The other items are just common things to do except the relative distance stuff which is something I experimented with but never put in a shipped game.
Some additions to your list of things to consider: