Something
else worth considering is reducing the complexity of floating
point data. Traditional floating point is 32 bits long - 4 bytes.
The question is, do you really need that degree of accuracy? Reducing
32 bits to 16 of floating point is not out of the question; many
games do this, but I'll bet you haven't noticed. While we are
on that subject, being very sure of the size of the data you need
to transmit is also a necessity here. If you are sending a value
of between 0 and 170, do you really need a long word to do it?
It would fit in a byte, and you've just saved 3 bytes. Obvious
when you think about it, but you'd be surprised at how much it
gets forgotten about when you are just getting the game working.
Only sending
objects that have relevance to the scene you are displaying is
helpful. Remember, the client is dumb, and doesn't need to know
about what's out of the view or hearing threshold. Who cares?
They aren't being rendered or heard, so what difference does it
make? The server knows about them, and it's running the game,
not you. This sucks of course if you are out in the open, or in
a space sim, since everything is visible, but that's a game design
decision that you make based on your technical abilities.
Further
to that, offload special effects. Remember the client is pretty
dumb, but it's smart enough to do clever effects for you. There
is no reason for the server to be sending all the info on effects
to the client, wasting both server time and network space. It's
enough that the server says "an explosion happens here"
and the client does the rest, superimposing that effect on the
main display. We did this in Heretic II, which was the major reason
it ran so well on the lower end machines.
Of course
the biggest thing you can do to help packet size is to delta-compress
info. Without giving away all of our (game developers that is,
not just Raven) technical secrets, the idea here is to only transmit
data that has changed from one frame to the next. Simply keep
a copy of what you sent last time, and on an object-by-object
basis, compare what you want to send this frame with what you
sent last, and only transmit that which has changed. Of course
this doesn't work when you have a new object to transmit, since
it all has to go across. But then if you figure out the percentage
amount of this happening, it comes out to about between 5% and
10% of the time. Thats some savings.
If you
want to, you can implement some compression schemes on the resulting
packet to make it even smaller, but in these cases the trade off
of time to compress on the server and decompress on the client
can be worse than having a slightly large packet.
Frequency
- control over this is a must. Quake has a server that
runs at 10 frames per second, transmitting data over the net at
that rate. Actually, it does transmit faster than that when it's
doing stuff like downloading client requested files, or responding
to server info requests, but during game time, the client expects
data at a 10fps rate. It runs at 10fps a) because of the amount
of data it is processing for each client. And b) because this
is a nice easy network packet rate to sustain.
There,
that one was easy.
Out of
order and missing packets. The trick here is to only treat one
symptom and ignore the other. If you number your game packets
(when we talk about packets here, I mean game server frame packets
- IE the packet that contains a complete frame update from the
server) as they go out to the client, the client can know if it
gets an out of order packet. The simplest solution is to dump
it, and treat it as a missed packet entirely. Doing this is a
must if you are dealing with delta-ed packets, since the delta
values in the packet refer to the frame that came before.
If you
keep a copy of the last packet you received from the server on
the client, you can compare the latest one you got to it and see
if an object has been dropped. At that point, you can either just
dump the object immediately, or store it off into a list and check
a few packets down to be sure it's still gone, and then dump it.
The beauty here is that you never actually have to send a 'remove'
function to the client from the server, since by omission from
the game packet from the server, the object is gone. Even if you
have some dropped packets, it doesn't matter since eventually
you will get one and that object will still be missing in the
latest packet, and thus it will get deleted. Cool eh?