Server providing real time game status updates
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 eventu开发者_运维百科ally 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.
This type of system is commonly solved using a Dead Reckoning or predictive contract algorithm. You can't depend on all clients getting updates at the same time, so you need to predict positions based on previously known values and then validate these predictions against server-generated results.
One problem I ran into sending delta updates (basically your 4th option), is that the data can be out of order or just old, depending on how concurrent your server is, basically race conditions of multiple clients updating the server at the same time.
My solution was to send an update notification to all the clients, with a bitmask setting a bit for each item that has been updated.
Then the client requests the current value of the specific data based on the bitmask, This also allows the client to only request data it is interested in.
The advantage of this is it avoids race conditions and the client always gets the latest value.
The disadvantage is it requires a roundtrip to get the actual value.
UPDATE to demonstrate the point I am trying to make.
Presume 4 clients A,B,C,D. A and B send simultaneous updates to a mutable state X on the server Xa and Xb. As B gets in somewhat later than A the final state of X on the server is X= Xb.
The server sends out the updated status as it gets it to all clients, so C and D get the updated status of X, as the order of delivery is indeterminant C gets Xa then Xb and D gets Xb then Xa, so at this point the clients C and D have different ideas of the state of X, one reflects what the server has the other doesn't, it has deprecated (or old) data.
On the other hand if the server just sends out a notification that X has changed to all the clients, C and D will get two status change notifications for X. They both make requests for the current state of X, and they both end up with the final state of X on the server which is Xb.
As the order of the status notification is irrelevant as there is no data in it, and the clients issue a request for the updated state on each notification they both end up with consistent data,
I hope that is more clear as to the point I was trying to make.
Yes it does increase the latency, but the designer has to decide what is more important, the latency or having all clients reflecting the same state of mutable data. This will depend on the data and the game.
精彩评论