Mud Engine + Server running on Mac OS X

As of tonight, the Mud Engine has been tested and runs on Macs running OS X. I was even able to wire up the server and get a OS X server running with the game.

Thanks to how the server was refactored into the adapter pattern I adopted a couple of weeks ago, it took very little code to wire up a OS X based server.

The code

class MainClass
{
    public static void Main(string[] args)
    {
        SetupMessageBrokering ();

        var bootstrap = new Bootstrap ();
        bool startupCompleted = false;
        bootstrap.Initialize ((game, server) =>
        {
            // Start the game loop.
            while(game.IsRunning)
            {
                if (!startupCompleted)
                {
                    startupCompleted = true;
                }

                Thread.Sleep(1);
            }
        });

        while(!startupCompleted)
        {
            Thread.Sleep (1);
        }
    }

    static void SetupMessageBrokering()
    {
        MessageBrokerFactory.Instance.Subscribe<InfoMessage>(
            (msg, subscription) => Console.WriteLine(msg.Content));

        MessageBrokerFactory.Instance.Subscribe<GameMessage>(
            (msg, subscription) => Console.WriteLine(msg.Content));

        MessageBrokerFactory.Instance.Subscribe<PlayerCreatedMessage>(
            (msg, sub) => Console.WriteLine("Player connected."));

        MessageBrokerFactory.Instance.Subscribe<PlayerDeletionMessage>(
            (msg, sub) =>
        {
            Console.WriteLine("Player disconnected.");
        });
    }
}

This code should look almost identical to the source previously posted showing the Mud Engine server running on Windows. The only difference is that I abstracted the code that can be shared across the two platforms into a bootstrap. The shared code is stuff like configuring the server and game, setting up the game and adapter and starting the game.

As development of the engine continues, I will regularly check and make sure it builds on OS X. The engine itself and its server should always run cross-platform without any issues.

I'm not sure yet what I am going to do in regards to the designer tools. Initially I was going to write it as a XAML app for Windows. Considering that the engine can now run on OS X (and I will be testing it on Linux soon), I might reconsider and look at a more cross-platform UI approach. Perhaps that would be a good excuse for me to pick up the new cross-platform ASP.Net MVC 6 stuff Microsoft is doing, and build a cross-platform self-contained web application for the designer.

Anyway, I wanted to share the progress on my cross-plat goals. At the moment, everything seems to be working just fine between Windows and OS X.

The new configurable Mud Engine Server Adapter

Back in May I posted about how you could configure the mud server for use with the Mud Engine. Since then, I have built out an implementation of the Adapter pattern. With that, I wanted to demonstrate how you would startup the server with the engine, and configure it.

Previously in the Mud Engine

In the last iteration of my configuration setup, you had to implement the abstract ServerBootstrap class, and then build out an implementation of IServerConfiguration. The bootstrap implementation looked something like this.

public class DefaultServerBootstrap : ServerBootstrap
{
    protected override void Run()
    {
        this.Server.GetCurrentGame()
            .NotificationCenter
            .Subscribe<ServerMessage>((msg, sub) => 
                Console.Write(msg.Content));

         while (this.Server.Status != ServerStatus.Stopped)
        {
            Thread.Sleep(100);
        }
    }

    protected override void RegisterAssemblies()
    {
        base.RegisterAssemblies();
    }

    protected override void ConfigureServices()
    {
    }

    protected override IServerConfiguration CreateServerConfiguration()
    {
        return new ServerConfiguration();
    }

    protected override IServer CreateServer()
    {
        var server = new Server();
        server.PlayerConnected += this.ExecuteInitialCommand;
        return server;
    }

    protected override IGame CreateGame()
    {
        return new DefaultGame();
    }

    protected override IInputCommand InitialConnectionCommand()
    {
        returnnew PlayerLoginCommand();
    }

    protected override void RegisterAllowedSecurityRoles(IEnumerable<ISecurityRole> roles)
    {
    }
}

Then in the application startup, you had to setup the bootstrap.

public static void Main(string[] args)
{
    var bootstrap = new DefaultServerBootstrap();    
    Task bootstrapTask = bootstrap.Initialize();
    bootstrapTask.Wait();
}

This was the minimum that had to be done in order to start the game with a server. There were several issues with this approach, one of which is violating SRP. The bootstrap does a lot of different things that aren't even related to the server. It defines the initial command, creates a player, and creates the game. None of which are specific to the server. These actions can be applicable in a single-player game as well.

The other thing that the bootstrap does is configures any services, and register security roles with the services. Gross. The server startup should be free of IoC dependency injection setup goo. So I set out to fix that with the new Mud Engine Adapters.

Setting up a server with adapters

Now that the server has been migrated over to use the adapter architecture, it's much more straight-forward to get a server running. Previously, the server owned the game. Now, the game owns the server - it just doesn't know it.

To start up the game, and give it a server, we do this.

static void Main(string[] args)
{        
    var serverConfig = new ServerConfiguration();
    IServer server = new WindowsServer(new TestPlayerFactory(), new ConnectionFactory(), serverConfig);

    var gameConfig = new GameConfiguration();
    gameConfig.UseAdapter(server);  

    var game = new MudGame();
    game.Configure(gameConfig);

    Task gameStartTask = game.StartAsync();
    gameStartTask.Wait();
}

In the code above, we create a serverconfiguration. Then we create a WindowsServer instance, giving it a couple of factories and our config. Next we we create a GameConfiguration and register our WindowsServer with the game config. When we call game.Configure(gameConfig), the game will consume the WindowsServer adapter and automatically configure the server using the ServerConfiguration.

Lastly, we start the game calling game.StartAsync(). This initialize and run all of the registered adapters that the game has. In our case, it initializes and runs the WindowsServer. At this point, the WindowsServer is running and can accept incoming client connections.

There is some flexability when it comes to configuring adapters. Each adapter can either skip configuration all-together, or opt into configuration by requiring a configuration class. This can be any class that implements the IConfiguration interface. Taking this approach allows each adapter to have their own config that can be tailored to what they want to expose. In the case of Server Configuration, there is quiet a bit we can do with it. For instance:

var serverConfig = new ServerConfiguration();
serverConfig.OnServerStartup = (context) => 
    Console.WriteLine($"Server running on port {context.Server.RunningPort}");

That code registers a callback that will be invoked when the server is starting. In this example, we just lookup the port the server is running on and write it out to the console. You can do more then just that however.

static void Main(string[] args)
{        
    var serverConfig = new ServerConfiguration();
    serverConfig.OnServerStartup = (context) =>
    {
        context.ListeningSocket.BeginAccept(
            new AsyncCallback(Program.HandleClientConnection), context.ListeningSocket);
        context.SetServerState(ServerStatus.Running);
        context.IsHandled = true;
    };

    IServer server = new WindowsServer(new TestPlayerFactory(), new ConnectionFactory(), serverConfig);

    var gameConfig = new GameConfiguration();
    gameConfig.UseAdapter(server);

    var game = new MudGame();
    game.Configure(gameConfig);

    Task gameStartTask = game.StartAsync();
    gameStartTask.Wait();
}

private static void HandleClientConnection(IAsyncResult result)
{
    Socket server = (Socket)result.AsyncState;
    Socket clientConnection = server.EndAccept(result);

    // Fetch the next incoming connection.
    server.BeginAccept(new AsyncCallback(HandleClientConnection), server);

    // Create player character here.
}

In this example, we used both the WindowsServer and it's Socket to completely replace the way incoming connections are handled. We're not replacing how client communication between the server and client are handled (that is planned) but allowing users to define custom server behavior without reimplementing IServer. At the end of the OnServerStartup callback, we set IsHandled to true, which tells the server to stop using its internal startup and rely on the custom logic being defined.

There are more things you can do, such as intercepting the shutdown of the server, client connections, disconnects and more.

Sharing information across adapters

The server publishes out messages that you can intercept and react to as well. An example is this:

static void Main(string[] args)
{
    SetupMessageBrokering();

    var serverConfig = new ServerConfiguration();
    IServer server = new WindowsServer(new TestPlayerFactory(), new ConnectionFactory(), serverConfig);

    var gameConfig = new GameConfiguration();
    gameConfig.UseAdapter(server);

    var game = new MudGame();
    game.Configure(gameConfig);

    Task gameStartTask = game.StartAsync();
    gameStartTask.Wait();
}

static void SetupMessageBrokering()
{
    MessageBrokerFactory.Instance.Subscribe<InfoMessage>(
        (msg, subscription) => Console.WriteLine(msg.Content));

    MessageBrokerFactory.Instance.Subscribe<GameMessage>(
        (msg, subscription) => Console.WriteLine(msg.Content));

    MessageBrokerFactory.Instance.Subscribe<PlayerCreatedMessage>(
        (msg, sub) => Console.WriteLine("Player connected."));

    MessageBrokerFactory.Instance.Subscribe<PlayerDeletionMessage>(
        (msg, sub) =>
        {
            Console.WriteLine("Player disconnected.");
        });
}

The first thing we do is subscribe, via the MessageBroker, to a series of messages that can be published by other objects. In this scenario, the WindowsServer will publish messages that are InfoMessage, PlayerCreatedMessage and PlayerDeletionMessage. The MudGame will publish GameMessage notifications. This looks like this when the server is running and has accepted several connections, and had a few disconnects.

The flexability given now with adapters and their custom configurators will make it easy to get up and running, and still provide you with a lot of flexability when you want to do more advanced things.

As always you can replace the IServer or IServerConfiguration implementations with your own all together. Since IServer inherits from IAdapter your custom implementation can plug straight into the game using the game.UseAdapter(customServer) call.

Handling what is missing

The original implementation shown at the start of this post had some other things being done. It handled the setup of security and commanding. That responsibility will be moved to additional adapters, which will run independent of the server and game. They can subscribe to messages sent from the server and react to them, launching the initial login command and handling security without knowing that the server even exists.

var gameConfig = new GameConfiguration();
gameConfig.UseAdapter(new WindowsServer());
gameConfig.UseAdapter(new CommandManager());
gameConfig.UseAdapter(new SecurityBroker());
gameConfig.UseAdapter(new WorldManager());

var game = new MudGame();
game.Configure(gameConfig);

Task gameStartTask = game.StartAsync();

Each one of the adapters shown above have not been written yet; they are coming. When they are ready, they will share the same level of flexability as the server, using configuration objects that you can intercept and interact with.

There is a lot of other new stuff happening in the engine that won't fit into this post, like a new home on GitHub (more on that in a different post), character APIs and real usage documentation.

More coming soon!

Introducing adapters in Mud Engine

I've had a pretty configurable startup sequence with the Mud Engine for a bit now, but I wasn't really happy with it. The issue that I had was that you still performed to much initialization logic in a boot strap class, or in the server app startup (not the server implementation themselves).

I wanted to make the whole configuration piece of the engine more extensible, and easier to plug into. The engine already uses a custom message brokering pipeline to publish messages to subscribers, and so I wanted to harness that a bit with the configuration.

Adapters

There is a new interface in the engine called IAdapter. It provides a series of methods for letting adapters subscribe to messages published from within the engine, along with publishing their own. It's an IInitializableComponent, so it can be initialized and deleted. This allows for adapters to perform cleanup, such as unsubscribing from the message broker, when they are being deleted.

The IAdapter includes a Name property so that the adapter can have a name; most importantly though is the Start(IGame) method. This method allows your adapters to spin off their own background tasks and do what ever they want while the game continues to run.

The engine currently has a WorldManager adapter, which initializes all of the worlds it is given and starts the world clocks/weather climate clocks etc. The engine server is being modified as well allowing it to become an adapter that starts up and runs when the game starts.

How it works

So how do adapters fit in to the workflow of the engine? Adapters are registered with a modified IGameConfiguration interface. This interface is then given to an IGame when you call Configure() on the game instance. The default IGame implementation (MudGame) pulls all of the adapters out of the configuration class and initializes them when IGame.Initialize() is invoked. It then starts each one of the adapters when the IGame.StartAsync() method is invoked. When IGame.StartAsync() is invoked, an internal game loop is created and the game will forever run. Because of this, adapters must not perform any long running or blocking tasks. If you have to perform a long running operation, such as running a server, spin it up on a worker thread and do the work there.

Example Game setup

I wrote a quick unit test to demonstrate how you can setup a game using adapters.

private async Task TestGameStartup()
{
    // Mocks & Adapters
    IWorldFactory worldFactory = Mock.Of<IWorldFactory>();
    IAdapter server = Mock.Of<AdapterBase>();
    IAdapter worldManager = new WorldManager(worldFactory);

    // Create our game configuration
    IGameConfiguration configuration = new GameConfiguration { Name = "Sample Mud Game", };
    configuration.UseAdapter(server);
    configuration.UseAdapter(worldManager);

    // Setup and run the game.
    IGame game = new MudGame();
    await game.Configure(configuration);
    await game.StartAsync();
}

The above code creates a fake world factory, as an IWorldFactory implementation is required by the WorldManager adapter. I also create a fake server by mocking an adapter base class that the engine provides.

Once my adapters are setup, I create a new GameConfiguration and pass the adapters in to the config using the UseAdapter method. There is also a generic version of this method (UseAdapter<T>).

Lastly, we create a new MudGame, configure the game using our new GameConfiguration instance and then we start the game. Note that game.Initialize() did not get called here. That is because the MudGame class checks if it has been initialized upon starting and initializes itself and all its adapters if that has not happened yet.

At this point, the awaited StartAsync method will not end until something within the engine signals it needs to end, or something calls game.Stop(). It creates an internal game loop and runs.

There is another approach that can be used for those that want a custom game loop.

game.BeginStart(runningGame =>
    {
        Task.Run(() =>
        {
            while (runningGame.IsRunning)
            {
                // Thread.Sleep(1) is not available on Portable Libraries.
                Task.Delay(1).Wait();
            }
        });
    });

This will instead start the game and invoke your callback. This lets you run the game loop in a Task for things like an editor, and not block and UI threads. Your editor or client can still stop the game by invoking game.Stop(). As long as your while loop checks for IsRunning, you're game loop will safely be shut down.

There's a lot of additional new stuff that I couldn't fit in to this post, so I'll work on making more frequent posts with the change that have taken place over the last couple of months.

For now, EOF.

MudDesigner: Configurable stand-alone Server Client

The Mud Designer's Runtime server code has become a pretty solid component. Gone is the old infinite loop and tightly coupling the server all over within the engine runtime. Now the runtime runs completely independent of the server. The server, and the way it will handle processing commands sent from the user, has been abstracted away from the actual player code. This allows the engine runtime to run as a singleplayer game if needed, or move across platforms without worrying if its going on a mobile device with out a server or not.

As a result of this, the actual server client application needed to be revised. The client application (a Win32 Console app) was originally responsible for the following workflow

  • Create Server instance
  • Configure the server properties
  • Create a game instance
  • Configure the game
  • Start the server
    • Provide the server the game
    • Provide the server the player class to use
    • Provide the server all of the dependencies the game and player might need.
  • Listen to game world changes and render them.

This resulted in none of the code being easily shareable across projects. If I wanted to spin up a server in the Design Editor, I'd have to either copy/paste the code or re-write it. Luckily that isn't the case any longer. The actually MudDesigner.Windows.Server.exe server application only needs the following two lines of code:

public static void Main(string[] args)
{
    Task bootstrapTask = new DefaultServerBootstrap().Initialize();
    bootstrapTask.Wait();
}

The application defers all of the server instancing, configuration, game setup and starting of the server to the bootstrap class. The class inherits from ServerBootstrap which is an abstract class. Developers may inherit from the ServerBootstrap so that they can configure the server application startup and dependency model on their own, or use the default bootstrap that ships with the engine.

Bootstrap methods

The ServerBootstrap class has the following abstract methods that children classes must implement.

protected abstract void Run();
protected abstract void ConfigureServices();
protected abstract IServerConfiguration CreateServerConfiguration();
protected abstract IServer CreateServer();
protected abstract IGame CreateGame();
protected abstract void RegisterAllowedSecurityRoles(IEnumerable<ISecurityRole> roles);

Run

This method is responsible for handling the actual game loop. When this method exits, the server application shuts down.

ConfigureServices

This is where any dependency injection requirements can be setup. The Runtime has been built with the assumption that an IoC container of some kind will be used, so objects ask for all of their dependencies to be passed through their constructor. You may opt out of using DI by implementing your own bootstrap class.

CreateServerConfiguration

The Runtime now includes an IServerConfiguration interface that is passed in to the IServer.Start() method invocation. The Start() method uses the IServerConfiguration instance to configure itself. The CreateServerConfiguration method is responsible for creating an instance of a class implementing the IServerConfiguration interface, and returning it. The DefaultServerBootstrap that ships with the engine resolves this through the IoC container.

CreateServer

This method must create an instance of a class implementing the IServer interface and return it. The DefaultServerBootstrap that ships with the engine resolves this through the IoC container.

CreateGame

This method must create an instance of a class implementing the IGame interface and return it. The DefaultServerBootstrap that ships with the engine resolves this through the IoC container.

RegisterAllowdSecurityRoles

The method will be given a collection of ISecurityRole objects. These are all of the roles available for assigning to players. This method allows you to set up how the roles will be fetched when needed. The DefaultServerBootstrap that ships with the engine registers the collection of roles with the IoC container. The roles will be resolved through DI when ever the Runtime engine asks for them.

Conclusion of Bootstrap

The ServerBootstrap class will hopefully help make the code used for the creation, setup and running of the game and its server reusable across multiple applications. I will now be able to use the same bootstrap in the stand-alone server, the Designer toolkit and on a web server.

Notes on Dependency Injection

The MudDesigner.Windows.Server.exe server application uses dependency injection to provide the engine runtime with everything it needs. The engine runtime itself does not make use of dependency injection. If you want to use the engine, with a custom toolchain, you can do so, without being forced to use DI.

Localization Support for ValidatableBase

Localization Support

The included Validation Attributes now all support localization, allowing your failure messages to be presented to the user in the native language for global applications. In order to provide this in a cross-platform manor (iOS, Android and Windows), localization support must be handled with a couple levels of indirection.

Without localization, you would manually specify the FailureMessage like this:

[ValidateObjectHasValue(
    FailureMessage = "Email address must not be blank.",
    ValidationMessageType = typeof(ValidationErrorMessage))]
public string Email
{
    get
    {
        return this.email;
    }

    set
    {
        this.email = value;
        this.OnPropertyChanged("Email");
    }
}

Localization is done by specifying a localization key on your validation rules and omitting the FailureMessage property assignment like the following.

[ValidateObjectHasValue(
    LocalizationKey = "User-Email-Validation-Failure-Cannot-be-blank",
    ValidationMessageType = typeof(ValidationErrorMessage))]
public string Email
{
    get
    {
        return this.email;
    }

    set
    {
        this.email = value;
        this.OnPropertyChanged("Email");
    }
}

When validation is performed, and the Email property is null, a localized failure message will be assigned to the FailureMessage property.

In order to facilitate this, the validation rules fetch an implementation of IValidationLocalizationService from a new Factory, the ValidationLocalizationFactory. The API ships with a Windows App Store supported localization service in the Universal App Shared project.

The factory must provide this service to the validation rules. In order to do that, you must initialize the factory. This is demonstrated in the sample projects as well, in the App.Xaml.cs file.

public App()
{
    this.InitializeComponent();
    this.Suspending += this.OnSuspending;

    ValidationLocalizationFactory.Initialize<ValidationLocalizationService>();
}

The factory is initialized with our Windows App Store version of the localization service included in the sample project. The validation rules will use this service any time that it needs to fetch a localized string.

Another feature of the localization support are fallback messages. While you are developing the application, you often don't have all of the localization done. You can assign a value to both FailureMessage and LocalizationKey. The validation rule will try to fetch a localized string and if none exist, continue to use the specified FailureMessage. If it finds a localized string, it replaces the fallback value with the localized string for you.

The following demonstrates this:

[ValidateObjectHasValue(
    FailureMessage = "Email address can not be blank.", /* fallback if localization does not exist for the key yet */
    LocalizationKey = "User-Email-Validation-Failure-Cannot-be-blank",
    ValidationMessageType = typeof(ValidationErrorMessage))]
public string Email
{
    get
    {
        return this.email;
    }

    set
    {
        this.email = value;
        this.OnPropertyChanged("Email");
    }
}

You can create a custom implementation of IValidationLocalizationService for iOS and Android apps as well. Just initialize the ValidationLocalizationFactory with the Service type for each platform. This allows you to use localized text across all platforms that your app can run on, without making any changes to your models or building custom validation rules per-platform.

Check out the latest build over on the GitHub Repository.