Run PhantomJS 2(.1.1) on TravisCI

The fact, that some of my PhantomJS tests passed through green on my local machine, but were red on Travis urged me to find the hidden cause. After a while (unfortunately not previously) someone tapped my shoulder and said: “Did you check the Phantom version on Travis?” Of course I had not ;) . To make a long story short: I had to install PhantomJS 2.1.1 on Travis because their preinstalled 1.9.8 didn’t work, but 2.1.1 did. Here is the snippet for .travis.yml:

before_install:
  - "export PATH=$PWD/travis_phantomjs/phantomjs-2.1.1-linux-x86_64/bin:$PATH"
  - "if [ $(phantomjs --version) != '2.1.1' ]; then rm -rf $PWD/travis_phantomjs; mkdir -p $PWD/travis_phantomjs; fi"
  - "if [ $(phantomjs --version) != '2.1.1' ]; then wget https://assets.membergetmember.co/software/phantomjs-2.1.1-linux-x86_64.tar.bz2 -O $PWD/travis_phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2; fi"
  - "if [ $(phantomjs --version) != '2.1.1' ]; then tar -xvf $PWD/travis_phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi"
  - "phantomjs --version"

I found it here.

Work remote

I always thought, working remotely for a company whose many people are scattered all over the world would be cool. Now that I’m working with one: It is!

It’s weird to some degree, because unless you don’t care enough about the processes and ongoings, you are lost and separated somehow. But when you realize that caring is responded with warmth, help and sympathy it is just great.

Talk to each other. Asynchronous mostly, but prompt.

Skeptics proven wrong :) .

Good book regarding this topic: Remote: Office Not Required

Angular two-way binding or “The Big Dot”

Pitfall alarm! While (finally) playing around with AngularJS I stumbled upon a beginners pitfall. Take those two code blocks:

  
  .controller('MyCtrl', ['$scope', function($scope) {
    $scope.submit = function() {
      console.log($scope.foo);
    };
  }])

Run, enter “bar” into the text field and submit. Unfortunately the console.log(foo) doesn’t say ‘bar’ as (at least I) expected. It says ‘undefined’. But why? First the code that works:

  
  .controller('MyCtrl', ['$scope', function($scope) {
    $scope.data = {}

    $scope.submit = function() {
      console.log($scope.data.foo);
    };
  }])

You see the difference? It’s the nasty dot. The helpful hints I found at StackOverflow. Read it and follow its links for the detailed explanation. Key part that helped me out there is:

“If you use ng-model, you have to have a dot in there.”
Make your model point to an object.property and you’ll be good to go.

XPath Madness

Because I currently work with Nokogiri and XPath I pinned some cheat sheets I found earlier here: http://scraping.pro/5-best-xpath-cheat-sheets-and-quick-references. I currently prefer XPathers Sheet: http://xpath.alephzarro.com/content/cheatsheet.html

Install Rails without docs

This one, again, is a note to myself. How do you suppress the time consuming generation of documentation when installing Rails (or any other gem)? This way:

gem install --no-rdoc --no-ri rails

You can also disable this feature for any gem install command when you add this to your .gemrc:

gem: --no-ri --no-rdoc

Latest Work Project: Tribal Wars 2

Finally it’s out of the box for a few days now. Hopefully (and pretty much probably) it will become a success for InnoGames: Tribal Wars 2, the successor of the ten year old Tribal Wars game. It was time ;) .

2013-11-22-tribalwars2

More important than that is the team. It’s their work, it will be their success. Here they are, the great team members who made it possible and brought it to reality. In no specific order … names.shuffle!

Martin Klöppner (iOS)
Gerrit Alves (iOS)
Marcello di Simone (Web & Android)
Lars Gerckens (iOS)
Timo Drick (Android)
Tobias Dunz (Graphics)
Annika Fischer (Graphics)
Ulrich Schmidt-Goertz (Backend)
Thomas Fischer (Game Design)
Judith Gastell (Graphics)
Sanjin Haracic (Web)
Florian Heissenberg (Web)
Luca Hofmann (Android)
Benjamin Holzapfel (Android)
Jendrik Johannes (Android)
Marc Kamps (Producing)
Gordon Kemper (Community)
Sem Köppen (Backend)
Tino Kreinberger (Graphics)
Hilko Krüger (Web)
Tim Miller (Web)
me (Lead)
Barry Nagel (Web)
Nino Protic (Product Management)
Tobias Rojahn (Backend)
Kilian Henning Schmidt (Game Design)
Catalin Stefan (Quality Assurance)
Carsten Witzke (iOS)
Terence Küderle (iOS)
Dennis Rohlfing (Producing)
Jan Prieser (Backend)
Matthias Borchardt (Graphics)

Believe: These people are great.

You’ll like the game for sure! Right now pre-registration is open. Just visit http://www.tribalwars2.com.

Great technology to run online games

The following post was published on the Engine Yard Developer Blog first. I have written it about half a year ago – it took a while until publication. In addition to the post I’d like to say, that the ideas where vastly developed, implemented and maintained by my current team. These guys rock! Really! This team is the best I worked with so far.

If you like to you can also go through the presentation slides of Tobias Rojahn (German) who presented the architecture meanwhile at different occasions.

Here we go…the original post:

I’ve been working on a great massive multi-player game for about half a year now with some very high expectations. In doing so, we’re trying a new and unique approach in our technology stack that we haven’t used before. Our goal is to enable a massive number of clients to connect to the game simultaneously while maintaining cross-platform functionality and scalability.

The big picture

frontend –(via WebSockets)-> Node.js –> Redis –> PHP workers –> Persistence via MySQL / PostgreSQL / MongoDB –aaand-> back!

We began with a single HTML5 front-end driving all our gamers’ experiences. However, we encountered some severe performance problems, so we decided to explore a service-oriented architecture. Our new system has several frontends which connect via WebSockets. The front end technology is not important as long as it speaks WebSockets, however, common frontends include JavaScript clients, iOS apps or Android apps. In this setup, the front-end sends simple JSON messages to a Node.js application. Those messages contain information about who is addressed (controller) and has to do what (Action) with some data. Here the Node.js application registers and stores a WebSocket connection and puts it into a Redis queue.

diagram

We use our Redis box as a simple queue and take advantage of some of the more advanced Redis features such as PubSub. The Node backend stores the socket’s ID along with the message and enqueues it in Redis:

receive: function (socket, msg) {
  msg.client = socket.identity;
  pub.rpush(config.push_list, JSON.stringify(msg));
}

All the heavy lifting is done by PHP workers listening to the Redis PubSub channel:

while (!$this->terminate && $this->maxRequests > 0) {
  $logger->logWorker($message);
 
  if ($message = $this->queue->listen()) {
    try {
      pcntl_alarm($this->max_exection_time);
      $dispatcher->route($message);
    } catch (\Exception $e) {
      $this->handleException($e, $message, $logger);
    }
  }
  pcntl_alarm(0);
}

Due to the open nature of our messaging infrastructure we could be using anything else, Ruby for example. And it really has a simple job to do: take the incoming data, validate it, react and put the corresponding response back to Redis. Since the serious heavy lifting is done on the client side, this allows the service to be fast, available, responsive and scalable.

Every game-logic related action happens in the validation and reaction phase. While PHP is not necessarily built to run as a daemon, that is exactly how our PHP workers run. To make the system error resilient, we monitor the daemons and, since they use only our queue and the database to store state, it is safe to just restart them whenever any one of them dies. This way we can also introduce more advanced monitoring techniques like restarting workers which leak memory and grow over a set RAM boundary or randomly killing workers to make sure the error recovering works as expected. While there are no particular problems with PHP’s memory consumption, leaks in your code are not unlikely. However, as long as you monitor your workers, it’s not a problem. For example, whenever the consumption exceeds a specific threshold then the daemon in question gets killed. The monitor afterwards will notice that the desired number of daemons isn’t running anymore and simply spawns a new one.

By the way, we don’t rely on any framework for the demonization of PHP. All we do is more or less a call to pcntl_fork().

In this configuration, a worker is able to do its job within 2-5 milliseconds when only reading data and sending it back. When the database result is cached, then the job is done even faster.

Through Redis the response goes smoothly back to Node that has subscribed to those events and is then sending the results back to the clients. If there are multiple clients connected for that same gamer Node is fanning out the results to all of them, so that you can play on your iPad and your phone at the same time, seeing the same game.

Scaling

This system can be scaled by adding Node servers as needed. Because they simply function as gatekeepers and messengers between the game and the clients, their load is not very high. However, if this becomes a bottleneck, we can scale horizontally. The same is pretty-much true for the PHP workers.

Scaling the Redis queue might get a bit trickier. Since Redis’ PubSub implementation is bound by the number of subscribers (rather than publishers) and messages will be propagated transparently to all slaves. So, this is solvable by adding more slaves to the Redis cluster.

Our first simple implementation looked up connections by looping through the whole pool of clients. We did this for each incoming request which turned out to be a major bottleneck. The easy fix was to store connections intelligently in a hash table. Each request that comes in now just does a simple lookup in the hash and immediately has the right connection at hand.

When it comes to optimizing performance, there is plenty of room for that in our code itself. Right now the various clients just hammer the backend with so many requests which could be condensed and intelligently bundled. We could also add functionality to the Node layer to split up combined messages, so that the actual game logic in PHP would not even notice the new, bundled messages coming in from the client.

As a last resort, we could also add more caching. Node could actually answer lots of the incoming requests from the clients directly. Many parts of the game data are not changing frequently and therefore are perfect candidates to cache them. Not going through the whole queueing process and hitting up a PHP worker would free lots of resources down the line.

All in all we are pretty happy with our current setup. It’s nothing that we have done before for games in production. But so far, things are working smoothly. The combination of WebSockets, Node.js and PHP workers is superfast and reliable.

How to: Chessboard

The algorithm almost is to simple to write about it. Otherwise: Why not? There is always someone looking for solutions, even simple ones.

What it does is it draws a chessboard.

chessboard

Here comes the pseudo code:

  for y = 0, rows do
    for x = 0, columns do
      if (x + y) % 2 == 0 then
        drawBlackSquare(x, y)
      else
        drawWhiteSquare(x, y)
      end
    end
  end

The important part is the (x + y) % 2 which toggles between zero and one all the time.

Oil well – a first draft

Uh! How cool is that?! And it’s only a first draft. We are on a good way.

oil-well-animation

MongoMapper: Querying embedded documents

I’m playing around with MongoDB using MongoMapper. Nice and easy thing so far. But have you ever tried to find objects by querying their embedded documents? I didn’t find any hints on that in the MongoMapper docs. But I stumbled upon this piece and it works. For example users with devices:

class User
  include MongoMapper::Document

  key :name

  many :devices
end

class Device
  include MongoMapper::EmbeddedDocument

  key :udid, ObjectId

  belongs_to :user
end

Find users with a device UDID “123456”:

User.where('devices.udid' => '123456')