Advertisement

Running both server and client on one PC causes lag despite ping < 1 ms.

Started by October 09, 2016 12:53 PM
26 comments, last by Heelp 8 years, 1 month ago
It depends. What else is the computer doing at the same time?
Is it rendering three different windows?
Is the graphics loop involved?
Given that you're using RakNet, and RakNet schedules its own packet sends (a send isn't necessarily generated immediately when you write some data,) I would expect that kind of latency even for local round-trips.
enum Bool { True, False, FileNotFound };

Guys, I will assume for now that the problem is in my laptop, because when I run 2 clients and server, it gets to 90 degrees celsius.

The first idea that comes to mind is that I need to try the apps on some friend's laptop. But I have only one friend and she is imaginary and she doesn't have a laptop.

My second best chance is to run the server&client apps on one of the computers at the university, but the problem is that they are running Windows(like me), and Windows requires administrator rights in order to give permission to execute the server's .exe file. And I don't have the password and they don't give it to anybody, any ideas? Is there a way to override this?

It depends. What else is the computer doing at the same time? Is it rendering three different windows? Is the graphics loop involved?

Yes, it's rendering three different small windows.

Yes, I guess graphics loop is involved, I don't really know what this means, but I'm rendering on every all apps, both client and server.

Can the problem be that my laptop is slow and some threads react slowly or something? i5 2.50ghz dual core, semi-broken.

Advertisement
The problem might not be that the laptop is "slow," but that scheduling of threads happens. If the code puts some data to the network library, then renders a frame, and then tells the network library to flush data to network if any is available, for example, then the time for rendering will be included in your measured latency.
If the three or four clients each render, and have to wait for the others to finish rendering before they can start their frame, then you may also be bound on the speed of your GPU's ability to work on many different things.

I believe that the internals of RakNet do not send a message immediately when you write some data, but instead collects possibly multiple sends, until it actually "flushes" the data into a packet.
Thus, your latency will always show whatever internal scheduling latency and batching happens in both your client, and the networking library. 20 milliseconds is not a terribly bad amount of latency.
enum Bool { True, False, FileNotFound };

This is how I do the packet sending in my server. Done every 75 milliseconds. Timer starts counting when the server application is started.


void NetworkManager::serverSendPackets( vector<Player>& players )
{
    //Create a bitstream and broadcast all players' positions to every connected client
    RakNet::BitStream bsOut;
    bsOut.Write( ( RakNet::MessageID )ALL_PLAYERS_POSITIONS );

    for( int i = 0; i < players.size(); ++ i )
    {
        glm::vec3 vec = players[ i ].mainCam.getCurrentPosition();
        bsOut.Write( vec.x );
        bsOut.Write( vec.y );
        bsOut.Write( vec.z );
    }
    peer->Send( &bsOut, IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true );//Broadcast set to true.
}

And the client sending is absolutely lightweight, I just send a few RakNet::MessageIDs which are of type 'unsigned char'. Done every 75 milliseconds. The timer starts counting when the client application starts.


void NetworkManager::clientSendPackets( vector<Player>& players )
{
    RakNet::BitStream bsOut;

    if( myPlayerID >= 0 && players.size() > 0 )
    {
        if( players[ myPlayerID ].moveForward == true ) { bsOut.Write( ( RakNet::MessageID )PLAYER_MOVE_FORWARD ); }
        if( players[ myPlayerID ].moveBackwards == true ) { bsOut.Write( ( RakNet::MessageID )PLAYER_MOVE_BACKWARD ); }
        if( players[ myPlayerID ].moveLeft == true ) { bsOut.Write( ( RakNet::MessageID )PLAYER_MOVE_LEFT ); }
        if( players[ myPlayerID ].moveRight == true ) { bsOut.Write( ( RakNet::MessageID )PLAYER_MOVE_RIGHT ); }
    }

    if( bsOut.GetNumberOfBitsUsed() != 0 )
    {
        peer->Send( &bsOut, IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, serverGUID, false );
    }
}

EDIT: or maybe this RELIABLE_ORDERED stuff slows something down?

EDIT No.2: ApochPiQ, I was randomly changing values I don't understand and I suddenly found the right one: I changed RELIABLE_ORDERED to UNRELIABLE_SEQUENCED, and IMMEDIATE_PRIORITY to MEDIUM_PRIORITY. I tested it, there is no lag now. But every 4-5 seconds I get a dropped packet and my game freezes for 50 ms, which sucks

I timestamped the position packets that are sent from the server and the time varies from 22ms to 36ms and it's mostly 26-27ms. And I'm using 127.0.0.1 to connect. I think this is too much for two 3D vectors, am I right?

[...]

EDIT: Another weird problem. When I start the server, and then the client, sometimes the timestamping is 24-32ms as I told you earlier, but sometimes it gets to 52ms, 52ms, 51ms, 52ms and something like that. It depends on which time I start the client app.

Good news: This has nothing to do with your network or laptop.

If every 75ms the client sends it's location to the server, and every 75ms, the server sends its location to the client, that means purely based on your code, your game can artificially delay for between 0 and 150ms.

Imagine your position is PosA, and the client sends PosA, and 5ms later the client processes the "move up" key (you're now at PosB). Oops, you have to wait 70ms before the client decides to inform the server of the new position. Finally, years later, the client tells the server "I'm at PosB", but the server's update tick happened 5ms ago, so now the server pointlessly waits 70ms before sending back to the client the new authoritative position. Round trip in this example: 140ms.

Toss in graphics VSyncing, and RakNet deciding to hold onto packets for a few extra millaseconds, and an occasional dropped frame, and then it seems like your game "randomly" chooses a new round-trip time.

The solution is to make the client use whatever information it has at its disposal to make the game visually look smooth, which means, yes, move your character instantly (doing client-side collision tests) even while waiting for the server to respond with the authorative position.

The server must be the authority. The client can't tell the server "I am here, trust me!", but the server tells the client, "You are here, trust me". So the server has to check collisions also (though you can get by with less accurate checking, simply to detect cheating). But processing the client's move client-side should begin instantly.

peer->Send( &bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, serverGUID, false );

Not sure if that contributes to the problem, but have you looked at the documentation?

Specifically:

HIGH_PRIORITY

For every 2 IMMEDIATE_PRIORITY messages, 1 HIGH_PRIORITY will be sent. Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control.

RELIABLE_ORDERED

This message is reliable and will arrive in the order you sent it. Messages will be delayed while waiting for out of order messages. Same overhead as UNRELIABLE_SEQUENCED. Sequenced and ordered messages sent on the same channel will arrive in the order sent.

Both are not exactly the kind of thing that guarantees low-lag, low-latency.

Note that the effect of the second flag is not visible right now, you only feel it in presence of packet loss or reordering (that is, on a real-life server on a real network, not with 3 clients over loopback). It is something to be generally aware of, though -- if low-latency matters for your game.

samoth, I tried all the different combinations of flags multiple times. I concluded that this is not the problem. But basically you are right, UNRELIABLE_SEQUENCED is better that RELIABLE_ORDERED, but just slightly.

SotL, I think I started out with the wrong basic assumptions. I thought that a "dumb client" will work fine on LAN, by 'dumb client' I mean that the client doesn't know anything about the game, he just sends states, and the server knows everything. And I thought that this will do fine for LAN games, but I guess I was wrong.(or maybe I've missed something)

Your option is to move the player instantly while waiting for the server, it sounds cool, but this means that the client app now needs to know a lot more stuff and it's not what I had in mind when I started doing this, I didn't want to bother with client-side prediction and error-correcting from server, but I guess I will have to. -_-

And you kind of missed the discussion, I think it's my fault because my posts were too long, but when I change the packet sending rate to 75-100ms or more, and the flag to UNRELIABLE_SEQUENCED it works perfectly fine, no lag at all, but when I increase the rate to 50ms per packet, it just gets too much. I decided that I wouldn't bother with client prediction for now and just deal with a slight delay.

Advertisement

Latency between 2 computers on a LAN is typically less than a millisecond - but as you're seeing, latency between 2 processes can be a lot higher since each process may not be sending or receiving packets immediately.

I expect there is a way of getting Raknet to send things ASAP with the various flags mentioned above, but I doubt you'll get much below 20ms latency if the library is hard-wired to work at 10ms intervals. We still don't know how your apps decide when and how often to call the receive function and that will affect things too.

Guys, aside from the topic, I see that none of you actually uses RakNet, and I was wondering what are the reasons, do you have your own networking libraries, or maybe you just don't find RakNet advanced enough for the stuff you do, or maybe you need some low level stuff that RakNet doesn't provide?

I've never had to choose a 3rd party networking library so I have nothing useful to add there.

But basically you are right, UNRELIABLE_SEQUENCED is better that RELIABLE_ORDERED, but just slightly.

Not just slightly, for a "fast paced" game.

RELIABLE_ORDERED is basically TCP (it is not, it's still UDP, but it works the same as TCP). This is great if you need to get data delivered, and you want it in correct order, too. This is rarely what one wants in a game, unless performance doesn't really matter all that much (if performance is secondary, knowing that stuff will eventually just arrive correctly, and in order, is big win!).

UNRELIABLE_SEQUENCED is basically UDP with a counter added. Most packets, most of the time, arrive in correct order even though it's not guaranteed. If any datagram is received where the counter is not higher than the previous datagram's counter, it is silently dropped. End of story. This is what most people who use UDP do.

Hell, no. Why would one want to do this? That's crazy!

Although UDP is "unreliable", it is in some way none more or less reliable than TCP. They both build upon IP, which is unreliable. Only just, TCP resends packets after a while when no ACKs come in, and TCP assumes them being lost. Any of the RELIABLE modes do just that, too. That sounds great, but it really isn't that great. What it means is that in addition to waiting for a packet that doesn't arrive, you now wait for a resend packet that comes in maybe some 50-100ms later, and you aren't getting to look at any other data that comes in during that time because the network layer holds them back. That, and you possibly have another packet lost because it interferes with the resend packet. Plus, there's a chance you now get two packets (which the network layer will silently drop, but you still have to expend bandwidth for it).

All that when, in fact, you give a shit about that state. It's old and obsolete, you have meanwhile received 3 or 4 more recent packets. You could just as well take the next packet, or only the most recent one, and move on. Forget about the rest. Well... if only you could, but you have no way of doing that because the network layer holds it back.

This is the one main reason why people (often, not always) choose UDP over TCP for games. If something doesn't arrive, you just forget about it and move on.

Packets being lost is a rare, but perfectly normal condition. You regularly lose a packet or two to congestion control (this is not a failure, but as-designed) but otherwise packets are delivered >99% reliably all the time. Well, maybe not over WiFi... but as far as cables of some sort are involved, yes. That's true except when something happens, such as a router being flooded and you suddenly have 100% packet loss for half a second (or... longer, if your roommate stumbled over your network cable, or if lightning struck the DSLAM box, or whatever). But in that case, anything you try sucks the same. Resending doesn't help when resends are dropped, too. Time, however, goes on and on, and on. And your state that is being resent and resent gets older and older.

Also, please re-read the quote about HIGH_PRIORITY. There is indeed something you can do here as well. You can use IMMEDIATE_PRIORITY, which will send packets at twice the rate when competing with HIGH_PRIORITY, but more importantly it will not queue your packets for 10ms before sending them.

This 10ms queueing is basically a kind of "Nagle's Algorithm" built into RakNet. This is fine for some applications because it may reduce network load, but it's not what you want for realtime updates.

This topic is closed to new replies.

Advertisement