5Mixer / mphx

A little library to let you make multiplayer games easily with Haxe. No longer maintained and better options are available.
MIT License
125 stars 15 forks source link

Lag - Messages are being delayed. #36

Open 5Mixer opened 8 years ago

5Mixer commented 8 years ago

mphx seems to have a delay somewhere. It could be a general issue with the codebase or just a specific location, that hasn't really yet been worked out. However, it seems that messages are just purely causing lag, and the more messages, the more lag there is. At first this seems like a fundamental nature of networking, that more messages causes slowdown, but this seems overly clear with even simple mphx usage.

Take, for example, the movement example. When moving left to right, it would seem as though there is half the lag than when moving diagonal. The (poorly constructed and optimized) example sends two packets, one for up, and one for across. Clearly, with both packets being sent, the server can't keep up with the stream of packets and delays clients.

I have a suspicion that this is due to the nature of the program - it is sending more packets than required. In fact, I believe it is sending an update every second! In reality, this program could send one packet per second, and with tweening, would work just as fine. I'd still like to investigate though.

Also, this could potentially be related to the fact that mphx is based on TCP, where every message must be checked and processed, something that is not the case with UDP. UDP has risen a few times in regards to mphx, however that lack of target support for UDP has always pushed it away from me. There are a few haxe libs relating to UDP usage in haxe, however these are generally in the form of C++ externs, thus not really aligning with the 'supports all the targets!' idea mphx has. This doesn't mean that UDP will never be a thing with mphx, but without pull requests or more requests, it doesn't seem high on the priorities.

mphx lag

5Mixer commented 8 years ago

On my comment 'it could just be that it is sending more packets than required', I followed this up by sending a packet every three frames (66.6% decrease in packet sending), implemented some very basic tweening, and the results are clear. Although there is still some lag, the players are in sync far more than the gif above. Also notice that the lag isn't 'building up'.

At the moment, I'm just thinking of the issue as the server spending too much time per packet, and queuing them in a way that builds up lag. I also need to do some tests on different platforms, so far, this is all neko.

mphx lag better (Also, just a note, these gifs generally show more lag than there is due to my recording software. Just note the sync-ness, not the choppy fps)

The code for this will be up very soon. Edit: Ugh on the wrong branch. 623571e

ghost commented 8 years ago

Would it be possible to build some sort of interpolation support straight into the core to minimize the lag on the client so that we can send the client data one/multiple ticks behind?

5Mixer commented 8 years ago

It is something that I'd considered for a while, actually.

Some points to consider:

With that in mind, I'm hesitant. I'm thinking that potentially moving that into a separate module could work. I'm not really sure what I'd do, to be honest. And it is not like mphx provides access to a 'player' object with an X and Y, that is constantly checked and synced with the sprite, where those variables could be tweened. mphx works by sending an object and forgetting it. It's up to the user of mphx to decide what to do with the received object.

Any ideas? This site has lot's of things related to reducing game lag - client prediction etc. Not sure how I can take these objects and put them into a general, all game library.

Also, because it could be relevent, the lerping I implement in my previous message is super simple. Rather than the message directly feeding into the objects x and y, it feeds into a targetx and targety. Every update, this code is run:

x += (targetx-x)/3;
y += (targety-y)/3;

Can't get much simpler.

PS: I've been working on UDP tonight, per someone's request. Not sure if it will work yet. Target support is sketchy.

PXshadow commented 8 years ago

interp is shown in the haxeflixel movement demo. Because you take the velocity and add it to the client, you are predicting the next frame before it happens. problem with that example is if both players move at the same time continuous the 2 clients become way out of sync.

troyedwardsjr commented 8 years ago

I've actually implemented interpolation in my game with a combination of FlxTweening and an implementation similar to this https://gist.github.com/timetocode/11235041 , using an authoritative server model similar to this: http://www.gabrielgambetta.com/fpm1.html , and it works fine, and despite any inefficiencies there will always be lag when communicating with a remote server. In all modern multiplayer action games people will always see players in the past.

But the issue for me is not so much the server isn't receiving packets (since supposedly TCP ensures their arrival), or the lag it's building up, maybe that will be eventually, but right now it's within HaxeFlixel/OpenFL loop (possibly). The socket doesn't seem to be sending some of the events at all. When I send client inputs to the server, it works locally but not over a remote distance with lag, it desynchs even though all events should be received since each if/else statement is also sending inputs with socket.send() with each input.

So the client will add say +5 to the x or y values of the character on the screen if you press the "right" button but will also send a "player move right" event to the server so,

In pseudocode:

Client: If (right is pressed) { player.x += 5; socket.send("player move", {direction: right}); }

Server: socket.event.on("player move", function(direction, player) { if (direction == right) { player.x += 5; )};

Send the updated player data back to client.

Client:

(After making sure the last input by the client was recieved by the server, check to see whether the x and y values are in synch, and if not, set the server's x and y value as the clients x and y value.)

So if you press right three times, both the client and the server's player.x should = 15 after you stop pressing inputs right? Then the check returns false because you know the player isn't hacking and changing their x and y values on the client.

This works fine most of the time when hosting the server locally, and should as well remotely. But when I run it remotely x will not always = 15. Sometimes it'll be 5 or 10, at random it won't recieve any of the updates that are supposed to be sent, even though every time you press right, you should be sending the "player move" event as well.

So in my game, if everything is working as intended, you should never see the player warping around unless they're hacking. But the problem is the player will reach 15 on the client because they pressed right for 3 frames, but the server will say "no they only reached 10 or 5 x" and the player will get warped back to 10 or 5. This happens like 90% of the time on the remote server.

It works fine locally and doesn't desynch, but when hosting on a VPS not too far away from me (150 miles, 1 state away in the US), it doesn't match up. As seen in this test here with a variable being iterated over adding +1 on the client and server each time an input is pressed:

Iterator Test

(clientSocket.update() shouldn't really be in there forgot to remove that, but the result is the same regardless. The socket.update() is already in the update function in PlayState.hx being updated every frame.)

Every time an input is pressed, it adds 1 to a variable on the client and sends "Player Move" as well. And every time "Player Move" is received regardless of the direction of input on the server, it will add +1 to it's variable as well. Tried this several time while moving only one player. And it'll stay in sync for a while and eventually it'll hitch and the client will get ahead of the server. The hitching rarely happens only by 1 or 2 every once in a while if it's only sending data back to one player. But if it's two players connected and the discrepancy gets much larger much faster. And of course only one player is sending "Player Move".

Really there shouldn't be a discrepancy at all whether it's one player or 20 players. It should maybe take longer to send back data, ect. But It should be receiving all the packets / event requests. For whatever reason it isn't.

What it almost seems like is happening is HaxeFlixel's game loop is just producing more iterations on the client than it's sending to the server, which makes absolutely no sense because each if/else statement contains a "Player Move" socket event for each time it is. As if it's skipping part of the synchronous list of methods under the if/else statements, or socket.send just isn't sending anything at random times for whatever reason, or it's just deleting events from the queue.

Not sure if this is a networking issue, or a discrepancy between the loop timings or HaxeFlixel's game loop, ect. But it's incredibly confusing and in many ways contradicting itself.

I was thinking possibly because the mphx's server loop is running a while loop that stops every 1 ms Sys.sleep(0.1); which roughly equals 60hz (I think it would actually be Sys.sleep(1/ 60); which would equal 0.1666 repeating) and OpenFL has totally different code for it's 60hz game loop, that it may be causing issues. Could be wrong but I have no idea at this point. Just a shot in the dark guess among many possibilities.