A few months ago, my colleagues and I decided to make a multiplayer realtime game that could work on the web. We decided to use node.js for our server. This decision led to a very convincing success - our server worked for several months without a single crash or process restart.
We decided to write our game on node.js, because we heard a lot of good things about this platform and really wanted to play a little with it. And it was amazing - we quickly entered the topic. For node.js there are many interesting libraries that can solve completely different tasks. A side benefit of using node for the server part is, in fact, javascript - a very easy to use language. This allowed us to focus on the problems that occur in all real-time games, without too much fuss, restrictions and the need to compile code, as happens when using less dynamic languages.
')
Also, node.js has proven to be a very lightweight language, even at peak times.
For our game, the node.js process used only one thread and consumed only about 3-4% CPU while simultaneously running 8-10 copies of the game, each with its own collision detection engine.
Initial approach
The initial (naive) approach when writing a multiplayer realtime game looks like this:
var info = {}; info.position = {x:10, y:15}; info.velocity = {x:0.5,y: 0.2}; info.currentWeapon = 1; info.radius = 10; this.netChannel.send( info );
The server forwards all received data packets to each connected client:
function broadcastInfo( sendingClient, messageInfo ) { for(var aClient in allClients) { if(aClient != sendingClient) { aClient.connection.send( messageInfo ); } } }
Main problem
With this approach, you can not trust the information about the location of the user, which he sends to you. The user will always report that he is there, where he needs to, gets into all opponents with one hundred percent accuracy and has a full stock of health.
Another problem with this approach is that you can never truly display a moving object.
Sometimes this effect is called a bouncing ball. It is connected with the need to extrapolate the position of the object until the next packet arrives, based on the speed of the object, and further “adjusting” the position, etc ... When the ball is at the top of the parabola along which it moves, the speed is zero. Therefore, predicting its movement based on speed, we will get exactly the same point in which it is located, and the ball will hang in the air until it suddenly collapses sharply down.
Finally, another major problem is packet loss. If one of the packets shown in the figure with a dashed line does not reach the recipient, the object will follow an incorrect trajectory. And the further - the worse. Of course, time is measured in milliseconds, and this is not the end of the world. However, in fact, it looks very unnatural, since the objects of the real world can not move instantly.
Another approach is client-server model.
In the process of developing the game, we decided to find out how to solve this problem in practice. After all, someone has already done multiplayer realtime games. We found several interesting sources of information, in particular, the free source code of Quakeworld and some technical descriptions from Valve employees.
In this model, there is a single authoritative server that simulates the game. As well as customers sending only their input data. For example, I send only information that my space bar was pressed, and the server determines what it means in terms of the game. Thanks to this model, we are freed from the many problems of the previous implementation.
Rendering world
Our nervous system can adapt to the delay. If we use the same short delay time to draw an object, the person easily adjusts to it and stops noticing the delay at all. The key to resolving our problem is that we are rendering the world to all customers in the past tense, the way it was N milliseconds ago. The number N is chosen arbitrarily. For example, we used 75 milliseconds.
Basic procedure
- Initially, we configure the system so that the client receives from the server high-precision changes in the world at discrete time intervals.
- We keep changing the world in an array.
- When it comes time to draw the players on the scene, we do it at a time equal to the current time minus the interpolation time.
- Let's call this point in time - the rendering time, which is actually equal to the current time minus 75 milliseconds.
- With each rendering, we find two changes, between which is our rendering time.
- Once the changes are found, we use the linear interpolation function to position the objects exactly on their trajectories.
- If a packet was lost, for example, if we did not receive a 343 packet (see figure), we can still use interpolation between the two received packets 342 and 344.
RealtimeMultiplayerNodeJS
We faithfully believe in open-source, so we decided at the end of development to clean up the code a bit and put it as an open-source project. Since we didn’t really succeed in choosing good names, we called it RealtimeMultiplayerNodeJS.
RealtimeMultiplayerNodeJS is a framework designed specifically for creating multiplayer realtime games using HTML5 and a client-server model. In this model, users send only input data, and the game itself is modeled on the server. Clients interpolate the world between its two states based on the current rendering time.
How to use the project
- Download repository
- Start the server “node js / DemoHelloWorld / server.js” in the terminal
- Open the "/DemoHelloWorld.html" page (Note that the file must be visible from the server so that you can connect using websockets)
Physics Demonstration
This demo is made in order to show the idea of ​​synchronized physical processes when all modeling takes place on the server. To create the world used Box2D.js. It also shows the synchronization of the interaction of several users, as well as an example of sending a message to the server with its subsequent interpretation again on the client side.
DemoHelloWorld
The most simple but interesting demo that we were able to come up with. Objects move from left to right.
DemoCircle
Demonstration of a simple CircleCollision collision engine, which provides the simplest information about collisions and generates an event when two objects collide. This demo also shows the implementation of a special type of object that is controlled by a connected user using the keyboard.
Finally, just to emphasize the main idea - an entity interpolation diagram made in the ASCII technique:
(actual time) (snapshot interval) | | | _ (rendered time) | / \ | / vv / | ------|-----|----v|---|----|----|--v---------> 0.0sec 0.1sec 0.2sec 0.3sec time | | \ / \------\/----/ | | (interpolation time)