Web Architecture

RSS feed for this section

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]);

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

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

Redis as Session Storage – Comparison to Memcached

Yesterday I was investigating Redis as a potential key-value storage. In particular, it’s potential for session storage. I thought I should share some of my findings here.

An application I’m currently helping to maintain uses Memcached to store session data. This is bad for at least 2 reasons:

  1. Memcached data is not persisted to disk, leading to potential of closing sessions if the server is having issues,
  2. Expired keys aren’t removed automatically; they only get deleted on the next retrieval, meaning that most of the time, sessions never get deleted, because the user stops responding, and their sessions remain stale until the next instance restart

Redis was seen as a good alternative. It supports key expiration that automatically deletes keys. It also saves data to disk, preventing data loss.

 

Command-Based Architecture using Comet

Here’s a picture I found in my archives from last year. It’s a rough sketch of how a game developer might use comet to make a command-based architecture for a multi-player browser game.

I mocked this up while I was getting my hands dirty in NodeJS. If I had to do something similar now, I would probably go with Socket.IO.

Command-Based Architecture using Comet

Command-Based Architecture using Comet