MultiRogue Dev Log – Level Refactoring

Since my last post, I’ve succeeded in creating functional AI beings in the level that have a mind of their own. They randomly wander the level, and start chasing players if they walk into their line of sight, using my own simple pathfinding algorithm. All this while maintaining my automated test suits and adding new ones.

Now, I’m at a point where I really need to refactor some things, because the complexity of my game server has gotten a bit out of hands. Particularly my level class, which has grown into a typical “manager” class.

http://aquablucondominium.com/

Leave a comment

MultiRogue Dev Log – AI Beings Brainstorming

I’m going to soon start developing some enemies for the map. I’m thinking that my first goal would be to create a being that moves towards the player when he becomes visible, and as soon as a collision occurs, the player is disconnected. Gotta make it somewhat risky, you know?

I came up with a reasonable way of implementing behaviour in an easy to understand way. The AI beings will have different “modes”, that represent their immediate actions. Example: still, wandering, berserk, run away. Berserk would mean finding and following the target using the fastest route possible (using A* algorithm, probably). Run away would mean the opposite.

On top of these modes, different behaviours could aggregate a few modes, and activate the modes under certain circumstances. For example, one behaviour, “wait and attack”, would give the being a “still” mode until a player becomes visible, which would then give a “berserk” mode.

More coming.

Leave a comment

MultiRogue Dev Log – Visibility Refactor

I’m finished the big visibility refactor. The server now decides what the clients are allowed to see, and sends the game objects that the client needs at any time.

At the same time, I implemented a game object component system. This will make aggregating new original game objects a breeze, and leverage emergent behaviour.

As I write this, there’s major cleaning up to do, and some new tests to write (disconnection, visibility range).

Leave a comment

Typescript Dynamic Instantiation using Grunt

I run into a roadblock earlier today while working on my pet project. Turns out Typescript makes it very challenging to dynamically instantiate objects using reflection. The only useful post I found on the internet outlined a solution that doesn’t really fit my project constraints (project running in a NodeJS environment, using commonjs). I had to be creative.

Here’s the solution I came up with: I created a Grunt task called dynamic_class_loader that iterates through the classes in a certain directory. It then generates a Typescript class called DynamicClassLoader that imports all the classes, and exports a function that instantiates classes using a big switch statement. Using this handy automation step, I can always trust the DynamicClassLoader to instantiate any of my classes correctly, no matter how big my project gets. Here’s my script:

    grunt.registerMultiTask('dynamic_class_loader', '', function() {
        var done = this.async();
        var content = '';
        var i=0;
        var src = this.files[0];
        var dist = this.files[1];

        var classNames = [];

        src.src.forEach(function(f){
            var filename = path.basename(f);
            var className = filename.replace('.ts', '');
            if(grunt.file.read(f).match(new RegExp('interface[ ]+'+className+' ','g'))) {
                ++i;
                return;
            }
            classNames.push(className);
            content += 'import '+className+' = require(\'.\/'+className+'\'); \n\n';
            if( ++i >= src.src.length) {
                content += 'var createInstance = function(className, args) {\n'+
                    'switch(className) {\n';

                classNames.forEach(function(className) {
                   content += 'case "'+className+'":\n'+
                       '  var obj = Object.create('+className+'.prototype);\n' +
                       '  obj.constructor.apply(obj, args);\n'+
                       '  return obj;\n'+
                       'break;\n';
                });

                content += '   }\n'+
                '};\n\n'+

                'export = createInstance;\n';

                grunt.file.write(dist.orig.src[0], content)
                done(true);
            }
        });
    });

Here’s an example output:

import GameObject = require('./GameObject');
import Item = require('./Item');
import Message = require('./Message');
import Repository = require('./Repository');
import Vector2D = require('./Vector2D'); 

var createInstance = function(className, args) {
    switch(className) {
        case "GameObject":
          var obj = Object.create(GameObject.prototype);
          obj.constructor.apply(obj, args);
          return obj;
        break;
        case "Item":
          var obj = Object.create(Item.prototype);
          obj.constructor.apply(obj, args);
          return obj;
        break;
        case "Message":
          var obj = Object.create(Message.prototype);
          obj.constructor.apply(obj, args);
          return obj;
        break;
        case "Repository":
          var obj = Object.create(Repository.prototype);
          obj.constructor.apply(obj, args);
          return obj;
        break;
        case "Vector2D":
          var obj = Object.create(Vector2D.prototype);
          obj.constructor.apply(obj, args);
          return obj;
        break;
    }
};

export = createInstance;

One challenge I ran into was that if the script tried to use the import of an interface, my typescript transpiler would complain. This is naturally because interfaces only represent a contract, and can’t be instantiated. I had to add a small hack to avoid including those interfaces in my class loader.

Now, I can easily dynamically instantiate classes using this function anywhere inside my application:

declare module 'DynamicClassLoader' {
    export function DynamicClassLoader(className:string, args:Array):any;
}

///
var DynamicClassLoader:any = require('./DynamicClassLoader');
var newVector = DynamicClassLoader('Vector2D', [5,6]);

Leave a comment

MultiRogue Dev Log – Visibility

Now that the automatic tests are in place, it’s time for me to focus on elements of fun in the game.

My next epic will be adding creatures into the level, and having them try to kill the players. Obviously this is a big step, so I’ll have to work many prerequisites features into the game to get to the point where this is possible with the engine. Here are some: server controller beings, attack actions, combat properties, health, the concept of dying, corpses, artificial intelligence (rudimentary), and visibility.

I’ve chosen to start working on visibility. At the moment, only the client has any concept of what’s visible and what’s not. This is a problem for many reasons:

  1. because the client is sent info about the entire level, hacking the client becomes an issue
  2. the server has no way of knowing which beings can see each other
  3. as a consequence of 2, notifications are sent to clients about events through-out the entire level, always

The start, I’m going to have to refactor the way the server sends info about the level to the client. It’ll have to send only info that’s necessary for the client to render what’s visible to the player. If I send more, this leaves the client prone to hackers. The engine being as it is, this is already a pretty big step.

Stay tuned

Source: https://github.com/MichelCarroll/MultiRogue

Leave a comment

MultiRogue Dev Logs – Mocha Tests

I’ve been spending the last two days building up the test framework to make tests as legible and easy to make as possible. From experience, this is the only way automated tests can even make their way into a project… if they’re dead simple.

I’m using a fluent interface and simple action verbs to send commands to the client. Here’s an example of one of my tests:

describe('client picking up and dropping stick', function() {
    var client;
    beforeEach(function(){
        client = Simulator.serverBoots().clientConnects();
    });
    it('should see the stick on the ground before', function() {
        should(client.getTileTokenAt(46, 27)).be.eql('/');
    });
    it('should not be holding stick at first', function() {
        should(client.isHoldingItem('Wooden Stick')).be.false();
        should(client.hasInLog('You pick up the Wooden Stick.')).be.false();
    });
    it('should be holding stick after picking it up', function() {
        client.moveLeft().moveLeft().moveDown().moveLeft()
            .picksUpOffFloor();
        should(client.isHoldingItem('Wooden Stick')).be.true();
        should(client.hasInLog('You pick up the Wooden Stick.')).be.true();
    });
    it('should not see the stick on the ground after picking it up', function() {
        client.moveLeft().moveLeft().moveDown().moveLeft()
            .picksUpOffFloor().moveLeft();
        should(client.getTileTokenAt(46, 27)).not.be.eql('/');
    });
    //... . .. ..

Next I’m going to be making a couple of tests suits to represent all the features that my game currently has. Once that’s done, I’ll be able to refactor and add new features without being afraid of causing regression bugs.

Leave a comment

MultiRogue Dev Log – Milestone & Mocha

I’ve reached a fairly important milestone today. My application now has the right adapters for making very understandable and predicable test suits. Here’s an example:


var testAdapter = new TestUIAdapter();
var server = new GameServer(new ServerParameters(null, 19582923));
var client = new GameClient(new ClientParameters(null, server.getMessageServer()), testAdapter);
client.handleCommand(new ShoutCommand('test'));

//OUTPUT:
/*
 [ { message: 'You\'re now connected as Player #101!',
    tag: 'info' },
  { message: 'It\'s your turn. You have 4 actions left.',
    tag: 'success' },
  { message: 'You shout "test"!!', tag: undefined },
  { message: 'You have 3 actions left.', tag: undefined } ]
*/

My next goal is to use Mocha.js to make some real tests suits, and integrate the tests with my grunt build script so that it can give me a pass/fail status.

2 Comments

MultiRogue Dev Log – Testing Strategy Implementation

To get to the point where I can easily write blackbox tests for the engine, I’ll first need to setup some interfaces so that my tests can communicate the test parameters and expectations in a straight-forward manner. I’ll also need to provide the client/server junction with an interface so that I can swap out the usual socket messaging with direct function calls so that my tests can run in a CLI environment without any complicated network setup.

From these requirements, here’s the list of interfaces that I’ll need to define:

  • client input (player commands)
  • client output (player feedback)
  • client socket inlet/outlet
  • server socket inlet/outlet
  • server world bootstrapping
  • server random seed

The world bootstrapping will provide a starting state for the server environment, which might vary from test suite to test suite. The random seed will provide deterministic values for the server to output, given certain inputs.

Source: https://github.com/MichelCarroll/MultiRogue

Leave a comment

MultiRogue Dev Log – Blackbox Testing Strategy

So far I’ve successfully created a build process in Grunt. Nothing too complicated so far… typescript compilation, watch, appending files into one, uglify, and browserify for the client. This will help streamline development.

Blackbox Testing

I’ve also been doing some thinking about how to leverage automatic tests to ensure that I don’t introduce regression bugs as I make major refactorings. Given that I have some ambitious ideas on how to generalize my engine to many classes of multiplayer rogue-like games, this is an investment that I think will pay off big time.

Because both the client and the server are implemented in Javascript using CommonJS for dependency management, this makes integrating both client-side and server-side code easily into same integration tests. This is giving me some very brilliant ideas. Imagine setting up a complete game scenario, including players connecting and interacting with the world, all done without the need for a complicated test setup. The client/server communication can be mocked by using direct messaging instead of sockets.

Stay tuned.

Source: https://github.com/MichelCarroll/MultiRogue

Leave a comment

MultiRogue Dev Log – Grunt, client/server architecture

Grunt & Build Process

It’s definitely time I apply my knowledge of grunt to this project. My build cycle has become dependent on PHPStorm to compile my Typescript files on save. Also, my client-side code is including all my source files in a traditional manner inside the index html file. It’s become a major pain-point, so putting in the time to make a decent build script will definitely pay off in the end.

Client/Server Architecture

I’ve started seriously thinking about the client/server architecture of the game. Up until now I’ve let those bigger-picture questions slide in favour of getting something playable up and running sooner rather than later. However, I’ve reached that point where new features have just enough complexity to potentially create lots of technical debt if not conceptualized intelligently.

After reading a couple of articles on multiplayer architectures (this one in particular), I’ve decided to split responsibilities this way: The server will handle all the game logic, including chat, connection, and arbitrating turns. The client will handle all player input management, user interface management, and decide on how to display the game. The server will only share information about a particular game state to a client if according to the game logic, the client is allowed to know about it (example, the server will dispatch a monster’s location only if it’s within visual range). This will minimize cheating.

There may be analogs between client and server logic (example: visual range computations), but I don’t think this can be avoided if I want to keep responsibilities in their correct places. One way to eliminate duplication would have been to make a completely dumb client, but this would be at the expense of a good user interface. MUD games, for example, have a pure client/server architecture, and a pretty lousy UI. I don’t want to fall into that trap.

Source: https://github.com/MichelCarroll/MultiRogue

Leave a comment