• Advertisement
Sign in to follow this  

udp for sending state changes

This topic is 2995 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

hi, I was just wondering... would udp be stable enough to send object state changes across a fps online game. for e.g. when one player opens a door an OPEN_DOOR msg could be sent in a udp packet to every other client but I've read that udp is unreliable and that packets could be lost so what if one client doesn't recieve the OPEN_DOOR msg and gets stuck outside (or inside) ?? and has to witness other players walking through a closed door? would I need to broadcast every object's state constantly every so often if I'm using udp? what about games like quake? they must need to send some pakcets of data at the beginning of the game to let players know about each other (names, character skins etc.) what if these get lost?

Share this post


Link to post
Share on other sites
Advertisement
Look at question 12 of the Forum FAQ for how quake and others do things. About using UDP you can send reliable UDP packets by implementing parts of TCP yourself. Question 11 covers libraries. ENet and others have support for reliable UDP. Also are you positive you want to use P2P? Question 30 is new apparently and covers some things.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sirisian
Look at question 12 of the Forum FAQ for how quake and others do things.

It doesn't help that 2 of the 3 links in that FAQ are broken. :P (The other is 10 years old though I'm hoping that most of the principles still apply.)

Share this post


Link to post
Share on other sites
I've read the links you posted and about quake... they basically send all object states every frame and use delta compression?

what happens if they run out of room to store everything in one packet, or is this a limitation they impose.

I think I'm getting mixed up about the server client model... would the server calculate everything such as the current frames in animation etc. and then send this to each client?

and what does the client send to the server, just the key/mouse input or more?

Share this post


Link to post
Share on other sites
Quote:
Original post by staticVoid2
I've read the links you posted and about quake... they basically send all object states every frame and use delta compression?


You delta compress in-memory, using the difference for each entity state between the ack state and the latest state, and you can compress that packet further with Huffman compression. Entities that haven't changed since the client ack number don't need to be sent. That means that the first state sent to a client (snapshot), will be much bigger than updates, since you need to send the base state of every entity in the game.

But since the sending is done during map loading, or can be throttled easily for clients joining games in progress, that's not a problem. Loading can take quite a while depending on the size of the initial game state.

Quote:
what happens if they run out of room to store everything in one packet, or is this a limitation they impose.

No limitation is necessary. You can design a packet segmentation and reconstruction manually, it's not that hard. But you can use the UDP limit (64K) if you think your packets will never reach that limit.

Quote:

I think I'm getting mixed up about the server client model... would the server calculate everything such as the current frames in animation etc. and then send this to each client?

pretty much. Although the more dead-reckoning you can do on clients the better (basically, make educated guesses to save bandwidth.

Quote:

and what does the client send to the server, just the key/mouse input or more?

Commands usually. Not really inputs, but what the inputs should do (move left, move right, look towards (30, 123, 537), ect...).

Look at Gaffer's article on Networked physics as an example and case-study.

Share this post


Link to post
Share on other sites
thanks for all your help, I have one more question (if you don't mind)...

for data that remians fairly constant such as player names etc. would this data also need to be sent every frame?

Share this post


Link to post
Share on other sites
Quote:
Original post by oliii
But you can use the UDP limit (64K) if you think your packets will never reach that limit.
While 64K is indeed the protocol limit, routers tend to fragment anything much over about 1200 bytes, which makes large packets extremely unreliable over anything except a LAN.

Share this post


Link to post
Share on other sites
Quote:
Original post by staticVoid2
thanks for all your help, I have one more question (if you don't mind)...

for data that remians fairly constant such as player names etc. would this data also need to be sent every frame?


No, you only need to send static data like that when a new player joins the game:

- send the new player's static data to everyone in the game
- send everyone currently in the game's static data to the new player

Share this post


Link to post
Share on other sites
Quote:
Original post by staticVoid2
thanks for all your help, I have one more question (if you don't mind)...

for data that remians fairly constant such as player names etc. would this data also need to be sent every frame?


Nope. The static data would be part of the initial base state. However, since the data doesn't change, as soon as the client aknowledge the base state, that data does not need to be sent again (the delta compression will detect no change and will not send it).

This can work better if you 'lock' your update until the first state is aknowledged. If you can send the initial state reliably and wait until the client aknowledges it before sending further updates, you can 'throttle' the bandwidth and make sure you are not sending data more rapidly than your connection can handle (something TCP/IP does automatically, but you can roughly emulate that protocol through UDP).

But that's if you have a lot of static data with a large initial state. Say your initial state is 24K kilobytes in size, and if you limit your outstream bandwidth to an 4KBytes per client (16 clients -> 64kBytes outstream -> 512 Kbits outstream, what a pretty good DSL line could achieve), it could take up to 6 seconds to send that initial state to every clients. So it's not something to be sniffed at :)

Sending the initial state reliably can be as simple as sending each segment (say your UDP packets are 1200 bytes in size, and your state is 24K total, that will make about 20 segments) one after the other and repeat, until the client aknowledges that he received everything. Packet loss may mean some of the segments might get lost in the process, so you'll need to resend them.

It's not optimum, so it could take quite a while if you have lots of packet lost.

Share this post


Link to post
Share on other sites
thanks again, I have just one more question...

to trigger events over a network using this method I take it that the client would just need to recognise a state change in the udp packet and then call the appropriate function(s)?

for example if the udp packet had an integer informing the client of who currently held the flag in a capture the flag game and the current state of the flag...


struct Packet
{
int flagState;
int flagHolder;
};


then by checking the previous udp packet against the current one you would be able to tell if someone dropped the flag...



void Client::recv(UDPPacket cur)
{
switch(this->prev.flagState)
{
// the flag was previously being carried by a player
case CARRYING:
{
switch(cur.flagState)
{
// player dropped the flag
case DROPPED:
{
this->players[this->prev.flagHolder]->dropFlag();
}
break;
}
}
break;
}

this->prev = cur;
}






Or would I be better to just send the position of the flag every frame and hide all object states etc. from the client?

Share this post


Link to post
Share on other sites
The first solution is more authoritative, and that's what I would do. Then it will also save bandwidth, since you wont have to send the flag position every frame (as the flag will be moving around when carried).

Also, with interpolation and client-side prediction, the positions of the player carrying the flag and the flag itself may not be 100% in sync, so you'll end up with a 'floating' flag near the player, but not exactly where it should be. In L4D2, you can sometimes see that effect when a charger or hunter pins down a player.

You will probably need to send the position of the flag when in the dropped state. But as soon as it is carried, that position doesn't need to be updated. And when it's dropped, the position would be welded set on the floor, and not change (unless you want the flag to bounce around, but that can lead to exploits and bugs where the flag ends up in unreachable places).

Triggering events based on states can be a bit of a problem. You can either trigger events client-side by detecting state changes.


- player A picks up the flag.
flag state changes from ON_FLOOR to CARRIED
-> display "player A picked up the flag".

- player B steals the flag from player A.
- player carrying flag changes from A to B.
-> display "player B stole flag from Player A".


but you can miss events if they occur in rapid succession or if you experience packet loss.

I suspect that's what happens sometimes in Quake3 when some events dont get registered on clients (commentator misses to inform a client that something happens).

However, as Carmack said, the state-based approach of the Quake3 network engine virtually eliminates the need to send reliable messages and commands, as the client can work out what to do when the state of an object changes.

For example, when the server starts loading, you can either send a "start loading map(x)" rpc call to the clients, or have a "loading state" object that changes from "lobby" to "loading map(x)". Then the clients know what to do by comapring the previous "loading state" they receive to the new one.

Share this post


Link to post
Share on other sites
To solve the "who picked up the flag" problem, you let the server arbitrate.

1) Player locally detects picking up the flag.
2) The flag is removed from display on the local client, and a message to request the flag is sent to the server.
3) The server checks whether the flag is available where the player is.
4) If the player could pick up the flag on the server, the server sends the "player A picked up the flag" message to everybody
5) When the pick-up message is received, everybody display player A as carrying the flag.

This will work even if player A misses the flag by a fraction, and player B picks it up. What will happen is that the flag will locally removed (because A picks it up), and A will send the message to the server. B will actually have picked it up, so the message received from the server will be "B picked up the flag," which in turn means that A knows to display B carrying the flag, not A.

Share this post


Link to post
Share on other sites
why not just let the server determine who has the flag? since it knows the positions of every client and the events from the client are being constantly sent to the server.

Then the server could just set this data in the gamestate packet. e.g.



void Server::update()
{
GameState gamestate;
for(unsigned int i = 0; i < this->clients.size(); ++i)
{
if(clients.position.distanceTo(this->flagPos) < 1.0f)
{
gamestate.flagHolder = i;
gamestate.flagState = CARRYING;
}
}
broadcast(&gamestate, sizeof(gamestate));
}



Share this post


Link to post
Share on other sites
The short answer is "lag".

basically, the server does know everything - so it has the most accurate information. It can know perfectly if a flag has been picked up, or if a bullet has hit a player.

The problem is that if a player has a 100ms latency, then that player will have to wait that much time until they know if they have picked the flag up, or if their bullet has hit.

So certain things are done on the client first - and then the server can later say "yes, that was correct" or "no, that was incorrect", and the client can update accordingly.

This is a good approach, given most of the time the client will be correct - and it prevents the client from having to wait and see if they picked the flag up, or if their bullet hit their target.

This, combined with interpolation between updates, provides the client with a smooth view of the game world, even if they have a bit of lag.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement