Play the game at www.RainingChain.com.
Check the source code on Github.

Wednesday, 25 February 2015

Getting Rid of Global Variables

When I started this project about 2 years, I had little programming knowledge. Every function I coded was in the global scope and it didn't really cause problems. Eventually, the project became bigger and it started to be hard to remember the function names, let alone in what file they were.

I eventually decided to group functions into global modules:
moveActor became Actor.move
teleportActor became Actor.teleport

The Actor object was still a global object but it was a lot better than before. At this point, maintenance is quite easy. There's one thing however that cause problems: dependencies.
When the variable is global, any module can access it. This means the module for a quest made by a contributor can access the Database module, which is a security issue. It also has the problem that we can't directly know what are the dependencies of a certain module. So making a change in a module can result in unexpected consequences in other modules.

Adding dependencies on the server side is easy using require('moduleId'). NodeJS was meant for that. Declaring a variable with keyword var will create a variable only reachable within the scope of the page.

The problem comes with the client. By default, the scope is global. So creating a variable with the keyword var will result in a global variable.

To fix this, anonymous functions are needed:
//Old:
var myPrivateVar = 1;
var myPrivateFunction(){

}

//New:
(function(){
 var myPrivateVar = 1;
 var myPrivateFunction = function(){
 
 }
})();

It is quite annoying to do but it works...
Another way to insure that no global variables is to use "use strict" at the top of the page.
If global variables are needed, then first declare them at the top with no value.
In order to load dependencies on the client, I created a exports and require system similar to NodeJS.

var myGlobalVar = 10;
"use strict";
(function(){
 var myDependency = require("myDependency");
 
 var Actor = exports.Actor = {};
 Actor.publicVar = 100;
 var myPrivateVar = 1;
 var myPrivateFunction = function(){
 
 }
})();

//in the main file
require = function(id){
 return exports[id];
}

Thursday, 12 February 2015

Compressing Creation Data

Data transmission is a big part of the CPU usage for the Nodejs server (and bandwidth obviously). The goal here is to minimize the size of the data we send. One important thing to understand is that unlike conventional websites, a HTML5 MMORPG send small packages very often rather than big ones.

In this post, I will only focus on data that always follow the same pattern. A good example is the Creation Package concerning the base information about an entity the first time the player sees it. 

Uncompressed (and simplified), it looks like:
{
    "x":100,
    "y":100,
    "sprite":"bob",
    "hitpoints":10,
    "maximumHitpoints":10,
    "combat":true,
    "name":"Goblin"
}

One could think of using a compression library to minimize the size. However, if you already know the structure of what you're going to send, it's always better to compress it with custom functions first and most of the time it's enough. It offers better compression and is way faster to perform.

Considering that we always send the same attributes every time, a good compression method would be to get rid of the attributes like so:

[100,100,"bob",10,10,true,"Goblin"]

More optimizations could be done such as having default values and having different types of package. In Raining Chain, combat Npc and non-combat Npc use different packages.

At this point, using a compression library would still work but it would be quite CPU intensive and would achieve little improvement. Generic libraries are handy when dealing with large files but when dealing with fast packages, custom compression is better.

One very important thing when creating homemade compression is to never manipulate a compressed object. It is really tempting sometimes to just change a little thing but always uncompress, change, recompress. The last thing you do before sending is the compression and the first thing you do when receiving it is to uncompress. It is also highly recommended to put the uncompress and the compress functions next to each other in the same file. And finally, only use this when needed. Custom compression makes the code more complex and harder to maintain. 

Check here for the Actor package compression implementation in Raining Chain.

Drawing Map Efficiently

There are many ways to draw the map where the player is. This is a quite big client side operation and map images take a lot of bandwidth so optimizing it is important.

The most intuitive way is to generate a giant image representing the map and simply drawing it. The problem with that approach is that the browser handles large files not very well.

A better way would be to subdivide the map in regions and only draw the region where the player stands. The main problem with that is region transition. If the player stands near the edge of a region, he will see nothing. One way to fix that would be to make larger regions overlapping each other. But by doing so, you would duplicate data.

Another approach, which is the one I'm using in Raining Chain HTML5 MMORPG, is to make quite tiny regions and draw multiple of them at the same time. Check here for a map image example and here for the implementation (check MapModel.draw function).

In another post, I will talk about another option, which is to only send the double array of data about the map and using that data to generate the image on the fly on the client side.

Try and Catch Performance Trap

I've recently started to optimize the code for Raining Chain. After using a profiler with Google Chrome, I realized that some functions had a triangle indicating that the Javascript V8 engine couldn't optimize them. Puzzled, I checked online and learned a bit more about the V8 optimizer.

Long story short, functions are optimized individually. It turns out that functions that contain a try and catch statement can not be optimized. This is a very big deal considering the V8 optimizer is what makes Javascript run fast. And obviously, when buidling a HTML5 MMORPG, performance is very important. After some JSPerf tests, I estimated that the try and catch slows down a function by a lot (x1.25 to x10 slower).

As an alternative, I create a dummy function that contains the try and catch that calls the real function. Doing so is a lot faster but also somewhat annoying to do. It is only worth doing if the function containing the try and catch has a considerable size, otherwise the extra call will nullify the gain from the optimizer.

//Before (slower)
a = function(){
    try {
     //Lots of stuff
    } catch(err){}; 
}

//After (faster)
b = function(){
    try {
     c();
    } catch(err){};
}
c = function(){
 //Lots of stuff
}

Optimizing Entity Collisions

Testing the collision between an actor and an attack is a very CPU intensive problem. Effectively, it's a n^2 problem: for every actor (n), you need to test if it collides with every attack (n). The worst part is that in a HTML5 MMORPG, we need to perform that operation very often so it's a crucial part to optimize.

Every time you need to handle a n^2 problem, you try to minimize the n as much as possible.

Splitting the group in two subgroups then running the algorithm will speed up the process:
2 * (n/2)^2 = n^2 / 2 (x2 faster)

Splitting in 3 groups would be :
3 * (n/3)^2 = n^2 / 3 (x3 faster)

So now, the question is: how to subdivide the group without missing collision? That's where the ActiveList comes into play. Every actor keeps a list of other entities around him (seen within a screen). When testing the collision, only the attacks within that ActiveList are tested, resulting in far less calculations. The thing you need to make sure though is that the ActiveList is relevant by refreshing it often. Refreshing it completely works every time but for every actor, that's a n problem (resulting in a overall n^2 test). This means we want to minimize when we do it. Let's say that we refresh every second. I should be good since it's very rare that an entity will travel an entire screen within 1 second.

However, when a new bullet is spawned, we can't wait for 1 second for the ActiveList to be refreshed. That means every time a new entity is spawned, we need to generate its ActiveList and add it to the concerned entities. This is the system Raining Chain is using and it is working very fine.

The same logic is used to set the target of a monster. The possible targets are only searched within the ActiveList of the monster rather than the map entity list.