Summary:This article specifically describes the process of developing spaceroom (our real-time multiplayer game framework), including a series of explorations and attempts, as well as some limitations on the node.js, WebKit platform itself, and the solution proposal.
"Editor's note" Node.js also created a large number of new applications, the birth of new tools. For example, based on the Node.js development framework, open source software and so on. This article is from the Alibaba user Experience department has a little blog, the author describes the use of Node.js, NODE-WEBKITK developed real-time multiplayer game framework Spaceroom process.
We can use it to do all kinds of things today in Node.js. Some time ago, the owner participated in the extreme relaxation activities, in this event we are intended to make a "bow family" can more exchanges of games, the core function is Lan party concept of real-time multiplayer interaction. The Geek Pine game was only 36 hours short, requiring everything to be quick and swift. In such a premise the initial preparations appear to be somewhat "ripe". Solution for Cross-platform Applications we chose Node-webkit, which is simple enough and meets our requirements.
According to the requirement, our development can be carried out separately according to the module. This article specifically describes the process of developing spaceroom (our real-time multiplayer game framework), including a series of explorations and attempts, as well as some limitations on the node.js, WebKit platform itself, and the solution proposal.
Getting Started A glimpse of Spaceroom
In the beginning, Spaceroom's design must be demand-driven. We want this framework to provide the following basic functionality: able to use the room (or channel) as a unit, to distinguish a group of users can receive instructions from the collection group within the client pair, can be in accordance with the prescribed interval accurate broadcast game data can be as far as possible to eliminate the impact of network latency
Of course, at the end of the coding, we provide more functionality for Spaceroom, including pausing the game, generating consistent random numbers among the clients (depending on the requirements, which can be achieved within the logic framework of the game, not necessarily using the Spaceroom This is more a framework for working at the communication level. APIs
The spaceroom is divided into two parts of the front and back. Server-side work involves maintaining a room list, providing the ability to create a room and join a room. Our client APIs look like this: Spaceroom.connect (Address, callback) – Connect to server Spaceroom.createroom (callback) – Create a room spaceroom.joinroom (roomid) – Join a room Spaceroom.on (event, callback) – Monitor Events ...
After the client connects to the server, it receives a variety of events. For example, a user in a room may receive events from a new player or start a game. We give the client a "lifecycle" that he will be in at any given time in the following states:
You can get the current state of the client by Spaceroom.state.
Using a server-side framework is a lot simpler, and if you use the default configuration file, it's OK to run the server-side framework directly. We have a basic requirement: The server code can run directly on the client without needing a separate server. Players who have played PS or PSP should know what I'm talking about. Of course, can run in a dedicated server, nature is also excellent.
The implementation of the logical code is abbreviated here. The spaceroom completes the function of a Socket server, which maintains a list of rooms, including the status of the room, as well as communication for each room corresponding to the game (instruction collection, bucket broadcast, etc.). Specific implementation can refer to the source code. Synchronization Algorithm
So how do you make everything that's displayed between the clients consistent in real time?
This thing sounds very interesting. Think about it, we need the server to help us deliver something. It's natural to think about what might be the logical inconsistency between clients: User directives. Since the code that handles the game logic is the same, then given the same condition, the code will run the same result. The only difference is in the game process, received a variety of player instructions. Of course, we need a way to synchronize these instructions. If all the clients can get the same instruction, then all the clients will theoretically have the same running result.
Network game synchronization algorithm strange, the application of the scene are also different. The synchronization algorithm adopted by Spaceroom is similar to the concept of frame locking. We divide the timeline into a single interval, each of which is called a bucket. Bucket is used to load instructions, which are maintained by the server side. At the end of each bucket time period, the server broadcasts bucket to all clients, and the client gets the bucket and then takes out the instructions and performs the validation.
In order to reduce the impact of network latency, the server received the instructions from the client each will be delivered according to a certain algorithm to the corresponding bucket, in accordance with the following steps: Set Order_start for instructions to carry the occurrence of the time, T for Order_start Bucket Start time if the T + delay_time <= The start time of the bucket that is currently collecting instructions, the instruction is posted to the bucket that is currently collecting instructions, otherwise the instruction is posted to the T + delay_time corresponding bucket by step 3. In
Where Delay_time is the agreed server delay time, it can be taken as the average delay between the clients, Spaceroom in the default value of 80, and bucket length default value of 48. At the end of each bucket time period, the server broadcasts this bucket to all clients and begins receiving instructions for the next bucket. The client controls the time error within an acceptable range, based on the bucket interval received and automatically in logic.
This means that, under normal circumstances, the client will receive a bucket from the server every 48ms, and the client will handle it when it reaches the time when it needs to handle the bucket. Suppose that the client fps=60, every 3 frames around the time, will receive a bucket, according to this bucket to update the logic. If you have not received bucket after time due to network fluctuations, the client suspends the game logic and waits. In a bucket time, a logical update can use the Lerp method.
In the case of delay_time = Bucket_size = 48, any instruction will be delayed by at least 96ms execution. Change these two parameters, for example, in the case of delay_time = Bucket_size = 32, any instruction will be delayed by at least 64ms execution.
the murders caused by the timer
The whole look down, our framework needs to have a precise timer when it runs. Perform bucket broadcasts under a fixed interval. Naturally, we first thought about using setinterval (), but the next second we realized how unreliable the idea was: Naughty setinterval () seemed to have very serious errors. What's more, every error will accumulate, causing more and more serious consequences.
So we immediately think of using settimeout (), by dynamically correcting the next time to the times to let our logic roughly stable in the prescribed interval or so. For example, the settimeout () 5ms less than expected, then we let him advance 5ms next time. But the test results were unsatisfactory, and it was not elegant enough.
So we have another idea. Whether you can get settimeout () to expire as soon as possible, and then we check whether the current time has reached the target time. For example, in our cycle, it looks like a good idea to use SetTimeout (callback, 1) to keep checking the time. disappointing timers we immediately wrote a piece of code to test our ideas and the results were disappointing. Run such a piece of code under the current Node.js stable (v0.10.32) and Windows platforms:
[JS] view plain copy var sum = 0, count = 0; function test () {var now = Date.now (); settimeout (function () {var diff = date.now ()-now; sum + + diff; count++; Test (); }); Test (); After a period of time in the console input sum/count, you can see a result, similar to:
[JS] view plain copy > Sum/count 15.624555160142348
What the?!! I want 1ms intervals, you tell me the actual average interval is 15.625ms. This picture is simply too beautiful. We did the same test on the Mac and the result was 1.4ms. So we were wondering: what the hell is this? If I were a fruit powder, I might have to come up with Windows too garbage and then give up windows, but luckily I was a rigorous front-end engineer, so I started to think about that number.
Wait, why is this number so familiar? 15.625MS this number will be too much like the maximum timer interval under Windows. Immediately downloaded a clockres for testing, the console ran as expected to get the following results: [JS] view plain copy Maximum timer interval:15.625 ms Minimum Timer INT ERVAL:0.500 MS Current timer interval:1.001 Ms. Consult the Node.js manual we can see a description of the settimeout:
The actual delay depends on external factors like OS timer granularity and system load.
However, the test results show that the actual delay is the maximum timer interval (note that at this time the system's current timer interval is only 1.001ms), in any case unacceptable, powerful curiosity to drive us to look at the source of Node.js to see.
bugs in Node.js believe most of you and I have a certain understanding of the node.js even loop mechanism, see the source code of the timer implementation we can get a general idea of the timer's implementation, let's start with the main loop of event loop:
[JS] View plain copy while (r != 0 && loop->stop_ flag == 0) { /* update global time */