It took mere 4 days for meepen/salien-bot to get (at the time of writing) 466 stars, 56 watchers and 149 forks (developers are players, that’s the proof) with 69 closed issues. The author created the bot that helps you get experience and win battles in the game. The impressive part of the project is a fact that it can be run both in Script Mode (with TamperMonkey and ViolentMonkey, GreaseMonkey-style tooling), as well as in Headless mode, using NodeJS. That’s brought my attention — I wanted to check how that both version differs — and now I know they differ a lot.

Let start with the version run in the Browser.

Browser Mode

TamperMonkey icon

Installation with TamperMonkey was blazing fast. The game-script is started as soon as we visit Start Page. We can now leave a game with our Salien killing other aliens and winning games to our collection. Let’s take a look at the source code of the script we run moment ago.

Mentioned script is using *.user.js naming convention used by all clones of Greasemonkey. Thanks to that, it can be recognised automatically by TamperMonkey. which itself is controlled with header definition the file starts with:

That allows to run script only on specific pages. What brings attention is a fact that the script cannot be (explicitly) run with GreaseMonkey:

A great thing in doing a review of greenfield repo is a fact that we can dig up a reason why it was implemented that way: Due to issues with GreaseMonkey, creators decided to drop support for it.

I never wrote such a game bot, so it’s interesting to see how much monkey pathing creators add to make everything run smoothly. There is code injected to automatically reload game on an error, to update score in the custom way or to fix some other minor issues.

But probably the most interesting part is core game logic itself.

Before I started reading the code of the game, I was wondering how attacking enemies was implemented. At first, I was thinking about some interesting visual tracking. The truth is far easier but also probably far more pragmatic and clever:

(a context is in truth window there)

From window object, the whole state of the game is retrieved. Now, event-loop, which is something typical for gaming programming, is run:

With every tick of the original game, Bot is doing following computations:

It’s loading difficulty of the game, which allows him to retrieve a number of enemies that will be spawned.

It’s checking what is current status of the game and if the player is fighting stage game or should choose next planet to defend (for which bot has special logic).

If the player is in the game, enemies are read from the EnemyManager and attacks are executed. There are many possible attacks, which depend on the level of current player:

What’s unusual for JavaScript, they are implemented in the object-oriented way (with quite expanded class hierarchy) and have a simple heuristic in method shouldAttack deciding if given attack should be executed, and on which enemy. If there is the affirmative answer, a mouse is targeting on given enemy and the specific attack is triggered by keycode manager. Additionally, every skill has its own logic and rules — implemented to handle corner cases of every type of action.

Wrapping this section up, Browser code plays the game in a really efficient way, spamming with special attacks and choosing the best-fitted level to play. It’s a rather impressive example of what can be done using UserScripts.

Now it’s time to check second part — headless mode.

Headless Mode

Headless mode is written using Node.js. There is more setup needed to play the game that way — user needs to retrieve the game token from Steam page (which is surprisingly easy). Node app has an only singular dependency which is request.

“Headless” part is a bit confusing there. When I saw the file name like that, I was nearly sure that I will see standard game run in the headless mode of Chrome or Firefox. Nothing different — all communication is performed using Ajax and… faked jQuery.

Yes, this project contains a module called jquery_node.js. which in internals is just wrapper over request:

The first though — unusual design decision :) It’s got even more interesting when I found webapi.js file, which contains 4003 lines of really complex Steam-related code. I couldn’t believe somebody wrote that all in 4 days, so I started digging. What I found after a quick search is that isn’t a file from some different Github Repo but… copy of shared_global.js from Steam site. Probably this strange jQuery dependency is there for compatibility reasons too. Very pragmatic way to make it run very quickly!

That’s also a reason why the headless version of the application doesn’t share any code with browser one. While browser code was controlled by the state in memory, the headless mode is fully Ajax-driven, which change… about everything — in headless mode, there is no playing at all!

After choosing some planet, the timer is started, and after the round ends… request to the server is sent with the precalculated maximum result.

The funniest part of this discovery is the impression that Steam is ok with that’s the whole forgery. It’s proof what kind of promotion-bait whole game is. Being programmed without literally any cheat-resistance…. After running it for an few hours, I’m already nearly maxed with experience — and that doesn’t matter at all — the only thing that counts for the possibility of winning the game is… time spent on the specific planet.