Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Need help with simple UDP game  (Read 8897 times)

0 Members and 1 Guest are viewing this topic.

P@u1

  • Jr. Member
  • **
  • Posts: 83
    • View Profile
Need help with simple UDP game
« on: June 09, 2011, 11:24:24 pm »
Hi,

I'm trying to build a very simple game with UDP just for testing.
Unfortunately at the moment it works worse than it used to work with TCP...
I'm not exactly sure, what the problem is, I thought that you maybe can help me.

Here is the full code:
http://pastebin.com/2ERzSk1n
or
http://codepad.org/qsznZjow

And here are the two most important passages:

Code: [Select]

void Game::sendGameState()
{
sf::Packet toSend;
toSend << myServerPlayer.xPos << myServerPlayer.yPos << myServerPlayer.input
<< myClientPlayer.xPos << myClientPlayer.yPos;
sf::Socket::Status status = mySocket.Send(toSend, myIPAddress, PORT);
switch(status)
{
case sf::Socket::Disconnected:
cout << "Disconnected" << endl;
myIsConnected = false;
break;
case sf::Socket::Error:
cout << "Error sending" << endl;
break;
case sf::Socket::Done:
break;
case sf::Socket::NotReady:
//try again next time;
break;
}
}

void Game::receiveAndSyncGameState()
{
sf::Packet toReceive;
unsigned short port = PORT;
sf::Socket::Status status = mySocket.Receive(toReceive, myIPAddress, port);
switch(status)
{
case sf::Socket::Disconnected:
cout << "Disconnected" << endl;
myIsConnected = false;
break;
case sf::Socket::Error:
cout << "Error receiving game state" << endl;
break;
case sf::Socket::Done:
toReceive >> myServerPlayer.xPos >> myServerPlayer.yPos >> myServerPlayer.input
>> myClientPlayer.xPos >> myClientPlayer.yPos;
//no interpolation yet
break;
case sf::Socket::NotReady:
//try again next time;
break;
}
}


I'm sending gamestate data periodically from the server to the client and the client sends input data periodically to the server.
In the current code it is sent 100 times per second, which is quite huge, but I tried it with 20/s and it didn't work very well too.
At client side the rectangle controlled by the server is always "jumping" between two positions which aren't close to each other.
And after some time there also is quite big delay and some socket errors occur.
I'm not sure if the "jumping" is caused by UDP specific problems, maybe it is caused by lack of mechanism which ensure that data arrives correctly and in the right order?
Or it is just some silly bug i build into my code? :D

But there is also the delay... sending 20 times per second ~10bytes with UDP over LAN should be no problem, should it?

And I'm also not sure, if I'm using the asnyc sockets in the right way...

I can upload the .exe file if you want/need it.

It would be very nice, if you can help me :-)

P@u1

  • Jr. Member
  • **
  • Posts: 83
    • View Profile
Need help with simple UDP game
« Reply #1 on: June 13, 2011, 12:11:24 am »
I made some tests and recognized that the received packets often contain less data then when they were sent.

For testing this out I added to following code to the passage from post 1:
Code: [Select]

 case sf::Socket::Done:
      toReceive >> myServerPlayer.xPos >> myServerPlayer.yPos >> myServerPlayer.input
     if(!toReceive)
          cerr << "Partial packet received!" << endl;


And it does print this line very frequently!

This is strange, because I thought that UDP packets are either received as a whole or dropped as a whole.

I also tried sfml 2.0 with the recent fix, but it didn't help.

Do you have any idea, why this happens?

P@u1

  • Jr. Member
  • **
  • Posts: 83
    • View Profile
Need help with simple UDP game
« Reply #2 on: June 15, 2011, 08:30:36 pm »
I now tried boost::asio for sending/receiving and used sf::Packet to convert data.

It worked very fine!

So probably something with the sfml async udp still does not rly work, or I just used it the wrong way.

lemuzimi

  • Newbie
  • *
  • Posts: 4
    • View Profile
Need help with simple UDP game
« Reply #3 on: October 22, 2011, 03:09:03 pm »
Hi, i also have latency issues, I would be curious to see what is your implementation with boost.asio....

P@u1

  • Jr. Member
  • **
  • Posts: 83
    • View Profile
Need help with simple UDP game
« Reply #4 on: October 22, 2011, 03:58:41 pm »
I just read the tutorials from boost.
I will see if I can find the source code I used and if so, I will post it.

I always used the async_xx functions like async_send_to and async_receive_from.

But short after I started with boost i switched to enet because I recognized that implementing a reliable protocol on top of udp is not an too easy task.

Edit: I found the files with asio. I can upload them if you want, but it is not high-quality code :D

Here are some samples:
Code: [Select]

//some class instances you might need
boost::asio::io_service ioService_;
boost::asio::ip::udp::socket socket_;
boost::asio::ip::udp::endpoint remoteEndpointStorage_;
boost::array<char, 3000> readBuffer_;

//that is used to receive
socket_.async_receive_from(buffer(readBuffer_), remoteEndpointStorage_, 0, boost::bind(&AbstractNetworkObject::onReadFinished,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
//that is used to send
socket_.async_send_to(boost::asio::buffer(packet.GetData(),
packet.GetDataSize()), i->first,
boost::bind(&AbstractNetworkObject::onSendFinished, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));


//that is used to update the status:
while(ioService_.poll() > 0);

lemuzimi

  • Newbie
  • *
  • Posts: 4
    • View Profile
Need help with simple UDP game
« Reply #5 on: October 22, 2011, 08:03:14 pm »
I'm looking at enet now, and it sound interesting too, i'm not a network pro so maybe i should stay away from the low level...

Can we do the exact same thing with enet than your example, does it handle asynchronism ( non blocking socket ) so a client or server could listen and send packets in the same time ?

And also i there a way to send structured packet like in the sfml tutorial...

These are all my concerns, thanks a lot if you can help me and maybe share a bit of your code with me.

Regards,
Mat.

P@u1

  • Jr. Member
  • **
  • Posts: 83
    • View Profile
Need help with simple UDP game
« Reply #6 on: October 22, 2011, 11:54:53 pm »
With enet all sends and receives are asynchronously.

You can also specify what reliability guarantees you need (for example reliable sequenced which is like tcp or no guaranees at all which is like udp).
There are also things like unreliable sequenced, just take a look at the docs.

I recommend to use sf::Packet to serialize data and then get a pointer to the internal array and send the array using enet.

I'm not using enet at the moment, as I want to do something with tcp. So I use sfml atm, but I consider switchting to boost::asio again.

Sfml networking is extremely easy to use which is a good thing, but imo the asynchronous operations are not handled in a very good way, because as far as i know you need to do a lot of polling (calling receive and test if the status is != NotReady) on every socket manually. On the other side boost::asio is a lot more complex.

Maybe you can talk about for what you are going to use the networking, then it would be easier to recommend the appropriate library.

lemuzimi

  • Newbie
  • *
  • Posts: 4
    • View Profile
Need help with simple UDP game
« Reply #7 on: October 23, 2011, 04:10:11 pm »
Ok, ok, well i'm doing a project for uni, the idea is to do a multiplayer (over a network) space invaders with up to 4 players in the same game, i'm going to do it very simply, with maybe a loby before the game but that's all.

I've no preferences for the type of communications (TCD/UDP) but i've heard  UDP is the best way for games that need fast refreshing data ( 20/30 fps ) so i first started to use UDP and it's also easier ^^.

Since i want to use C++ i first started to look at boost.asio, but it was a bit too complex for me ( i'm not a network ninja... ), then I looked at many other solutions over the web and then a found SFML and your code example, which was perfect for me so i've implemented it, and i've to admit that i was a bit disappointed by the speed, still some lag at 20 fps some times ( the lag is not that big, but still a bit annoying for that kind of game, since all the logic is made on server side, the clients need to see their players moving at the exact same moment they pressed the key or else it sucks...).

I tried to play a bit with the code to find some solutions but could not do anything...and since it seems to come from asynchronism in SFML networking, i'm more than pleased to hear other solutions for implementing that...


PS : ( i'm also curious to see how do you use sf::Packet with other networking librairies... ).

Regards,
Mat.

P@u1

  • Jr. Member
  • **
  • Posts: 83
    • View Profile
Need help with simple UDP game
« Reply #8 on: October 23, 2011, 04:31:06 pm »
I think your main problem is that on client side you only refresh the game state when you receive something.

You should use some extrapolation - let the actors on client side move in the direction they are facing and when you get a new snapshot from the server you apply a correction.
But don't just overwrite the position with the server position, this would not look good, but instead apply some smoothing.
What worked quite good for me was to reduce the distance between client and server pos by 10% every time you receive a snapshot.
Other things like speed acceleration and so on are directly taken from the server.

Using sf::Packet with other libraries is not that hard.

Just create a packet and put some data into it:
Code: [Select]

sf::Packet toSend;
toSend << xPos << yPos << xSpeed << ySpeed;


Almost every networking libraries has some method like Send(const char* bytes, int size);

You then do it like this:
Code: [Select]

thirdPartySocket.Send(toSend.GetData(), toSend.GetDataSize());


Similarly for receiving:
You will probably get some kind of buffer (maybe char array) from the library. You must make sure that the message is received as a whole, but that's usually not a problem as it's the way udp works and most libraries won't split your messages.
It could look like this:
Code: [Select]

char buffer[4096];
int len = thirdPartySocket.Receive(buffer, sizeof(buffer));
sf::Packet received;
received.Append(buffer, len);
received >> xPos >> yPos >> xSpeed >> ySpeed;


Udp is a good choice for you, but for some things like lobby or chat messages you will need reliability.
I would recommend using enet, as it allows you to specify which reliabilities you need for each communication channel.
Then use sf::Socket to serialize your data (turning them into bytes) and to deserialize it (turning them back into useful data).

lemuzimi

  • Newbie
  • *
  • Posts: 4
    • View Profile
Need help with simple UDP game
« Reply #9 on: October 23, 2011, 06:57:09 pm »
Ok, so if i smooth the positions that could give me a fluid game at 60 fps for instance while my networking is only running at 10 fps, it's indeed a wise thing to do...

But what about the latency between client and server...

For instance when the client press a key, the input is sent to the server, the server update the game state and then send the new game state to the client so he could display the new position.

When using the code above I have sometime a delay between the moment the client press the key and the moment the caracter start moving....
 

Do you think using another library will reduce this latency ??

Thank you for your explications with the packet that is quite useful...but what do you mean by "using sf::Socket to serialize your data" with enet, and why and how do you use it ?

If indeed using another library like enet would reduce the latency, is it too much to ask you to show me your implementation using enet ?

Thanks again,
Mat.

P@u1

  • Jr. Member
  • **
  • Posts: 83
    • View Profile
Need help with simple UDP game
« Reply #10 on: October 23, 2011, 10:20:08 pm »
I don't think that using enet will reduce latency, if you are using udp anyway.
The main benefit of enet is that you can have reliable and unreliable messages.

I use sf::Packet because I somehow need to turn a struct or class into bytes.
If your class contains pointers it's not enough to just reinterpret_cast it into an byte array.
With sf::Packet you can turn the data component-wise into bytes.
The usage is as I already showed:
Code: [Select]

struct Data
{
  int a;
  int b;
  string s;
};

Data data;
sf::Packet toSend;
toSend << data.a << data.b << data.s;


To reduce input delay you could make the client react to the input immediately and later apply correction. It depends on the situation if that will work out or not.