Jump to content

How to make a multiplayer online game with Phaser, Socket.io and Node.js


Jerorx
 Share

Recommended Posts

Hi,

I've been working on multiplayer online experiences with Phaser, Node.js and Socket.io recently. I thought that it might be interesting to make a tutorial detailing how to make a very basic multiplayer game with these tools, so here it is : How to make a multiplayer online game with Phaser, Socket.io and Node.js .

As I said, the game (which can be played here) is very very basic: you click on the small map and your character moves to where you clicked. The movements of the other players (if any) are displayed in real-time. No animations or collisions or whatever, the focus is on the networking aspect.

Feedback is more than welcome, as this is my first tutorial ever. If you find it interesting and would like me to make follow-up tutorials on some aspects, don't hesitate to ask!

Jérôme

Link to comment
Share on other sites

Nice example,I know it's just to demonstrate how simple it's setup a simple multiplayer game. 

But I'm afraid there is a slight problem with such concept. Especially in case the player position really matters. (for ex. in player combat.). 

Let's say you have a player A and B.

Player B is on a slower network and so are the responses from their network

  • Player A sends message telling server that he's moving to pos.x , pos.y
  • Server receives the message (20 ms later)
  • Broadcasts the information to all connected clients 
  • Player A  receives the message (50 ms later)
    • proceeds with executing the movePlayer method.
  • Player B receives the message (200 ms later). 
    • proceeds with executing the movePlayer method.

Now it's clear that the Player A will arrive at totally different time on Player B side as it will on Player A client.

So the question being, how do we solve this? Should I send the time when the action was performed

and calculate the elapsed time after receiving the message from server? And then try to predict the current position

of the player based on the elapsed time? 

Link to comment
Share on other sites

@kabuto178 : I invite you to have a look at the source code, and more particularly at client.js, which processes the messages from the server, and game.js, which contains the Phaser code. When a new player connects, the server sends the message 'newplayer' to all clients, along with the id and coordinates of the new player. This is handled in client.js by

Client.socket.on('newplayer',function(data){
    Game.addNewPlayer(data.id,data.x,data.y);
});

In game.js, Game.addNewPlayer() is defined :

Game.addNewPlayer = function(id,x,y){
    Game.playerMap[id] = game.add.sprite(x,y,'sprite');
};

So it boils down to using Phaser's game.add.sprite, using the coordinates sent by the server. (Game.playerMap is a map to organize the sprites by id).

Similar methods are used to update the position upon receiving a 'move' event from the server. In client.js:

Client.socket.on('move',function(data){
    Game.movePlayer(data.id,data.x,data.y);
});

And in game.js:

Game.movePlayer = function(id,x,y){
    var player = Game.playerMap[id];
    var distance = Phaser.Math.distance(player.x,player.y,x,y);
    var tween = game.add.tween(player);
    var duration = distance*10;
    tween.to({x:x,y:y}, duration);
    tween.start();
};

Where a basic tween is used to move the sprite. Let me know if this is helpful or not.

@FakeWizard: this is a good point indeed. Actually, I had to solve that problem for a game I just finished, which I will release in a few days. I'm also currently writing an article with a section precisely about this, which should come out at the same time. Both will be published on the same website as this tutorial.

The first solution I used was the one you mention, by associating timestamps to movements. But it turned out to be a bit problematic, because timestamps are not universal, they can vary wildly from one machine to another (if you can, open a browser on two different computers side by side, and in the console type console.log(Date.now()); you should see different values). It worked as long as I was testing on my machine, but once online it broke down.

I ended up simplifying this, and instead of using the timestamps, I only used the latency of the clients, which is the key factor here. Once you keep track of the latency, you can use it as follows:

  • Player A sends message telling server that he's moving to pos.x , pos.y
  • (In my implementation, since the environment is fixed, A doesn't have to wait the response from the server to move ; it moves immediately, and will be adjusted only if the server replies that the move was illegal, which can happen only by attempting to cheat. It makes the game more responsive but doesn't change your point.)
  • Server receives the message X ms later. The server has an estimate of X (= latency of player A) and stores it along the move request.
  • Server broadcasts the information to all connected clients; to each, it sends the information that A has moved, along with the latency X of A, + the latency Y, Z, etc. of each receiving client
  • Player B receives the message K ms later. If we consider that X was the latency of player A, and Y is the latency of player B, K = X + Y. The values of X and Y are estimated by the server and sent along the movement messages, so they are known by the client when he has to update position. Now, the position update is likely not instantaneous, to be smooth you are likely to use a tween or something. The duration of the tween can be adjusted to compensate for the delay K. If K is 150ms and the tween is supposed to last 400ms, the tween will end up lasting only 250ms, to make sure that on the screens of the two players, A's sprite arrives at his destination roughly at the same time. Typically, such changes in tween duration are not very perceptible; either the tween lasts a long time, and a difference of 150ms will be no difference, either it is short, and the tween will happen too fast anyway for a human observer to really perceive the difference.

Let me know what you think of this solution, I'm interested in feedbacks. Out of experience, it worked pretty well for my game in any case. Now if you make a fast-paced game such as an FPS or a MOBA, where very fast, short movements happen a lot, this solution might not be the best one (although with 200ms latency, players will be screwed no matter what in that kind of games). Dedicated solutions exist for this kind of games, but it's always relatively tricky and ad hoc.

If you use Twitter, I invite you to follow me to get notified when I release the article about this problem, it should interest you. If not, I'll notify you through here. ;)

Link to comment
Share on other sites

@Jerorx yeah, you're so right , the time stamp could vary from client to client. I wonder how come it didn't occur to me before :) ..The other solution indeed sounds less difficult to implement, although (IMHO) the ultimate solution for this kind of problem is to use position prediction algorithm. There is a great article about this - here 

Having said that, even with the best approach , the performance would still not be perfect. The whole communication between socket.io service and it's clients is done through TCP/IP.  Which is not recommended for real-time multiplayer games. It's not a big deal with few clients , but when you're building a game which could potentially grow up to thousands concurrent request per second , then your best bet would be to re-design your backend service to work in a cluster. So each cluster node could take care of a pre-defined number of clients.

If you're familiar with socket.io,  there is a great framework ( SocketCluster ) which can help you scaling your socket.io service both vertically and horizontally/

Link to comment
Share on other sites

@kabuto178 The problem would rather be the type and movements and the pace of the action, rather than the real-time aspect. You can have a look at Phaser Quest, which is the multiplayer online game I was referring to in my previous message. Similarly to BrowserQuest, it is designed to be an MMO-like, and the solution I discussed works well for this game.

@FakeWizard Good points.

Actually, having a tween execute the movement from point A to point B is some form of movement prediction: you assume that the player will follow the indicated trajectory until the end and render it accordingly. The trick is then to make sure that the timing of this rendering is correct!

Did you know that Blizzard's World of Warcraft uses TCP? It's true that UDP is often recommended for online games, but UDP and TCP both have pros and cons, and some AAA MMO's do use TCP apparently. I don't have a reference available here but I can try to post one later.

Indeed, to handle very big loads and scale well, eventually the back-end architecture has to change, even from the hardware point of view. Thanks for the pointer to SocketCluster. I still have some margin before any of my projects gather enough traffic that such improvements are necessary, so I admit I haven't looked into much of that yet, but you are right: eventually, any serious MMO project will have to resort to that.

Link to comment
Share on other sites

@Jerorx Well as far as I know, they'e using UDP for spatial calculation of game objects, for chat,mail,auction house they're using TCP , which makes sense cause It offsets way more safer and reliable communication than the UDP...The main problem with TCP in real-timei is that, when a packet is lost somewhere along the way, the connection is stall until re-transmission takes place. ... UDP on the other hand is just pushing the data from server to client and vice versa. If a packet is lost, it doesn't cause any delays, it will eventually arrive but it's up to the game client or server whether to process the out-of-date data or just ignore it. 

Also I've  just realized that Nagle's algorithm , is turned off by default in socket.io , which is always recommended to disable/turn off  if you're to use TCP in multiplayer games. 

There is still plenty of work ahead of me , and I'm really looking forward to see how it;s gonna all work ( or not) :) 

Another project worth checking is Incheon (Multiplayer game server based on Node.JS) http://incheon.gg/

Thanks again for sharing your thought with us! 

Link to comment
Share on other sites

Thanks for the pointer to Incheon, didn't know it, looks quite interesting!

@kabuto178 Keyboard-controlled movements would typically fall in the "fast-paced games" category, in which case the server would send the player coordinates multiple times per second, and the client would interpolate the sprite's position in-between. This is often done by not rendering the last update, but the one-before-last; this keeps the rendering slightly in the past but allows to cope with latency. Actually, this conversation made me think that I should make another tutorial similar to this one but for keyboard-controlled multiplayer games. That would be a good testing bed and would be useful for the discussion. I'll try to post that relatively soon!

Link to comment
Share on other sites

  • 6 months later...

Hi all

Hello @Jerorx  

I doing follow http://www.dynetisgames.com/2017/03/06/how-to-make-a-multiplayer-online-game-with-phaser-socket-io-and-node-js/

your demo here: https://basic-mmo-phaser.herokuapp.com/

I how to i can fix when a Hero move use tween

For example:

 Click points A. with distance Hero to A is 100Km

Hero moving 

After a second Click points B with distance Hero to B is 50Km

  Hero movie to poins B but i see hero stop points A ( it continue move to A)

How to fix this?

Thank you.

demo phaser.png

Link to comment
Share on other sites

  • 2 months later...

Sorry for the delay in replying!

If I understand what you ask correctly, then you need to stop the ongoing tween before starting another one. I kept it simple for the tutorial, but you could easily add this by storing the tween somewhere, e.g.

player.tween = game.add.tween(player)

and then at the beginning of movePlayer(), do:

if(player.tween) player.tween.stop() 

 

Link to comment
Share on other sites

  • 2 years later...

Another solution to the latency problem as described in many forums and blogs is use a constant lag between the server states and client states. For example suppose server world state is at S_t at time t but the clients will not try to render S_t but it will try to render S_(t-100). The clients will have some earlier updates from the server using which the clients and interpolate the object positions at time t-100. and as the clients have atmost 100ms to get the next update packets from server every object movements will be smooth and same for all clients (as long as latency for each client < 100ms). Human brain can quickly adjust to this constant lag. All the other methods for extimating latency and client side predictions can cause jerkineess due to the random fluctuations of the RTT. If you don't understand from this poor explanation you can checkout articles and blogs like this one and others can be found easily through google.

But in all the blogs its is done for a simple case like moving a cube specially no game engine is used for simplicity (I guess ;)). 

Now I an trying to implement this in my phaser game and don't know how to do it. I want to use phasers arcade phy engine for caculating the player physics  but the rendered should use the earlier data for rendering. I counldn't find any example where this method is used with phaser in nice modular way.

TIA  

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...