Currently my game server is small (one area and ~50 AI) and each time it sends out state update packets (UDP), it sends out a complete state to each client. This creates a packet size of ~1100 bytes. Pretty much all it sends is the following information for all entities:
int uid
int avatarImage
float xPos
float yPos
int direction
int attackState
24 bytes
Edit: More efficient structure
int uid
byte avatarImage
float xPos
float yPos
byte direction & attackState
14 bytes
but I am going to need to send more information eventually for the entities. For instance I am adding to this:
float targetXPos
float targetYPos
float speed
As more data is needed to be sent for each entity, I am fast approaching and most likely already passed the maximum size of the packet. So I am trying to think of a few possible ways to fix my problem:
1) Just build up the status update packet until I run out of room and then leave out the rest. Very bad client view. Not really an option.
2) Only send the data for the N closest entities to a client. This requires that each state update I calculate the closest N for each client. This could be very time consuming.
3) Some how design the packets so that I can send multiple for the same update. Currently, the client assumes the packets are in the following structure:
int currentMessageIndex
int numberOfPCs
N * PC Entity data
int numberOfNPCs
N * NPS Entity data
The client then takes this new data and completely overwrites its copy of the state. Since the packets are complete self contained, even if the client miss a packet, it will be ok. I am not sure how I will implement the idea of multiple packets for the same update, because if I miss one of them, what then? I can’t overwrite the complete, outdated state with a update, partial state.
4) Only send the actual variables that change. For instance, for each entity I add one int that is a bit mask for each field. Things such as speed, target, direction, and avatarImage won’t need to be sent every update. I still come back to the issue of what happens if the client misses a packet that did actually need to update one of these values. I am not sure how critical this would be. This also requires a little more computation on both the client and server side for creating/reading the packet, but not too much.
Any better ideas out there?
I would go with number 4 and number 2.
As you have realized, it is usually better to only send updates instead of a complete game state. But make sure you always send absolute values and not deltas, so that no information is lost should a packet be dropped. You can use dead reckoning on the client side to make animations as smooth as possible under crappy networking conditions.
You have to design carefully for this so that it is not critical if a packet is lost.
As for number 2, it does not has to be time consuming if you design for it. For example, you can have your game area divided into a grid of squares where each entity is always in exactly one particular square and let the game world keep track on this. In that case, calculating the entities in the 9 surronding grids is a O(1) operation.