We’ve had a number of questions about how HaxArena is being made, so it’s high time I wrote about it.
HaxBall is built in Flash/Actionscript, and adopts a P2P architecture. In a P2P architecture, every client is running its own simulation of the game, and regularly pushes out updates to ALL peers in the room. This is a good idea in theory: all updates go directly where they’re needed instead of a central server, thereby reducing lag. But in practice, think about 12 simulations all trying to simultaneously keep themselves synchronized, with one simulation (the host) acting as “the boss”. Everybody has been in a game where a single player connects with a laggy connection and ruins the game for everyone. I don’t know the exact specifics of how it works, but now you’re beginning to understand why this happens.
HaxArena uses a central authoritative server. All clients connect to the server and regularly send their input commands there. The server runs the physics simulation and regularly pushes out updates to all connected clients. This means that if a player with a laggy connection enters the room, the game will feel sluggish to him only.
Another difference between the games is that Flash’s P2P technology uses UDP packets sent directly between peers, and this can spell disaster depending on your network topology. In some cases, it’s not even possible to connect because of a firewall, so you need to mess with your router settings at home to open ports, or beg your work or school’s network administrator to reconfigure the network (good luck with that!).
Socket.IO uses WebSockets to establish a connection to a single server only. WebSockets work by upgrading a normal HTTP request to a full-duplex TCP channel; once established, the client and server can communicate normally. TCP is used as a transport layer, so there is the possibility of some congestion due to dropped packets. However, because port 80 is normally used to establish the connection, router settings should never be a problem.
TL;DR: If you can visit websites, you can play HaxArena.
Let’s face it: the internet is a wonderful combination of switches that means sub-second communication between my machine and yours, but lag is still a reality of our world and we have to do what we can to reduce its effect. In the examples that follow, assume that the server pushes out updates at 10fps (i.e. every 100ms), and assume a network latency of 200ms (this is extreme).
Experienced programmers: this section is for you. Bring on the comments, please!
In the most naive approach, clients send inputs to the server, the server does all the calculations, and the server pushes updates to clients (locations, etc.) This leads to a very sluggish experience, since the effect of moving is not felt for 400ms. Furthermore, even after a player stops holding down the move button, the player will keep moving for 400ms, a “skating” effect. No good.
In this approach, our client sends inputs to the server, as before, but they are numbered sequentially. The client maintains a short buffer of input packages that have been sent. Server updates come equipped with the last of our input sequence numbers that have been observed and processed.
The client first resets the positions and velocities of all objects as specified by the update. It also locally replays all the frames that the server has not yet processed, using the buffer of inputs collected earlier. The inputs used by other players are assumed not to change for this purpose, an example of “dead reckoning”. In this way, the client is predicting the future: what the player sees is actually an approximation of the server’s states a couple of frames from now.
The nice thing about this approach is that the player’s experience should be smooth: he moves immediately when he holds down the key, and when server updates come in, the player does not get teleported back to where he started! Furthermore, when he stops, he will not “skate” forward.
The downside of this approach is that the client’s prediction of the future may not be accurate. Some other players may have taken other actions in the intervening frames that have a conflicting effect on the simulation on the server, and when this conflict then reaches the client, it will replay player inputs on a state that does not accurately reflect what the player saw at the time. This might be … bad.
One of the approaches used to reduce the appearance of lag was including client-side interpolation. In this approach, when an update comes in from the server, the client does not simply teleport the object there. Instead, it schedules a slow slide from its current position to its reported position to reduce the jarring effect of the teleport.
In my experience, this trick does not help. The interpolation gives the impression of velocity that is not actually there, and the resulting effect is not natural especially for the ball: it appears to change direction according to no known physical source. I would prefer for the ball to lag and teleport but stay true to known physical forces, at least this way I can still follow the action. This is simply my opinion.
The current state of the demo is an approximation of the client-side simulation approach. I say approximation because currently a close-but-not-perfect copy of code is running against Phaser’s box2d plugin on both server and client. My current effort is focused on cleaning this up so that the simulation code run on server and client are exactly identical, and that they run against the box2d engine directly instead of phaser’s plugin. Once this is in place I can begin messing around with the approaches described above.
I hope this sheds some light on the technical direction of HaxArena. I welcome all comments and feedback, especially from seasoned multiplayer game developers!