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

Monday, 9 March 2015

Modularity

Modularity is a very important concept when creating a large-scale project. It is the main reason why so many projects fail as they reach a considerable size.

Modularity means to minimize the relations between modules (aka files) and to make sure modules perform a precise job. Doing so makes it easier to add new features but more importantly, makes it easier to remove or alter them.

Modularity is by no mean absolute. Making everything 100% modular is impossible and impacts negatively the performance. Classes need to interact between each others but you need to make sure they interact correctly. It is important when refactoring a project to first dress the list of what needs to be modular and what doesn't need to.

The first question to ask is what will grow in size as the HTML5 MMORPG grows and what is the most likely to change. Those need to be as modular and independent as possible to make changes as easy as possible. Most of the time in a game, the maps and quests are the main concerned elements.

Typical Quest and Map Structure:

File item
info about item for quest 1
info about item for quest 2

File npc
info about actor for map 1 quest 1
info about actor for map 1 quest 2
info about actor for map 2 quest 1
info about actor for map 2 quest 2

File map1:
actor1
modify questVariable of quest 1 when talked with
actor2
modify questVariable of quest 2 when talked with

File map2
actor1
modify questVariable of quest 1 when talked with
actor2
modify questVariable of quest 2 when talked with

File quest1
logic concerning questVariable

File quest2
logic concerning questVariable

Note: A real structure for a HTML5 MMORPG is a lot more complex than this.

This structure seems modular. The file item handles items. The file actor handles actors. The map files handle maps and quest handles quests. But there's still a big problem in this structure. There is no way to know what elements a quest depends on other than checking in every map files, every item files, every actor files etc...

When the game has less than 10 quests, it's not a big deal. But when the game has 100+ maps, 100+ quests, 100+ npcs, 200+ items, and you want to remove a quest, the task becomes really time consuming and most likely, some parts will be forgotten.

There is also a big naming issue. If you don't use prefix for quests, you may name an actor with the same name than another actor, causing very hard to detect errors. If you want to create a new quest that uses the questVariable killMonsterCount, you would need to check in every quest file and make sure it's not used yet.

In Raining Chain, I opted to put all the data needed by a quest in the same file. Every element (item, equip, ability, dialogue, npc, map, variable, highscore, challenge) created comes automatically with a prefix to prevent collision. Adding an element in an external map is also done within the quest files.

quest1:
actor quest1-1
map quest1-1
item quest1-1
item quest1-2

quest2:
actor quest2-1
map quest2-1
item quest2-1
item quest2-2
add actor to map quest1-1

Using this system makes it very easy to remove a specific quest. All you need to do is remove the file and everything related to it is gone. However, doing it that way creates a relation between the quest file and every part of the game engine. So in theory, it is not modular... A modification about game engine may end up forcing you to go through every quest file. This means a solid Quest API is required in order to minimize the impact of future game engine changes. I will cover the Quest API in another post.

Preventing Socket.io DDOS

Raining Chain HTML5 MMORPG uses Socket io library for websockets. Even though it is a great library to handle websockets, it is very vulnerable to DDOS.

A player could open the console with F12, type
while(true) socket.emit('eventName',bigObject);
and crash your server (or at least slow it down).


This means you need to implement a system to disconnect a player that sends too much data.

On the server, instead of using:

io.on('connection', function (socket) {
 socket.on('eventId', function (data) {
  //stuff   
 });
 socket.on('eventId2', function (data) {
  //stuff2 
 });
});

Use

handleSocket = function(socket,eventId,data){
 if(Date.now() - socket.lastEventTimestamp < 5){
  socket.disconnect(); //optional
  return;
 }
 socket.lastEventTimestamp = Date.now();
 
 if(eventDb[eventId]){
  eventDb[eventId](socket,data);
 }
 
}
eventDb = {
 stuff:function(socket,data){
  //stuff
 }
 stuff2:function(socket,data){
  //stuff2
 }
}

This is the most simple system. One could implement different thresholds for every event and take into consideration the size of the data sent. Instead of disconnecting the socket when sending too fast, one could keep track of how many times it has happened and only disconnect if it happened more than 100 times in the last minute.

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.