Content is king

Now that games can be created with Ophelia, it is time to talk about content. All games need content, without it they are only logic and code!

Content in Ophelia comes in three types: Templates, Levels and Images. Templates are XML files that define objects. All objects are created from a template. The template specifies what components the object will have, what default values it’s attributes will have and which events that objects of this type will respond to as well as which triggers that will execute on these events.
Levels are defined by XML files too. A level file contains a list of all object that are in that level when it loads. if any of those objects have default values that differ from the object’s template, those values will be defined along with the object. It also contains values for other properties a level might have, such as a name or a background image.
Images are exactly what you would expect.

On the server, content is imported automatically if it is placed in the correct folder. This is accomplished with this snippet of code:

// Get files in folder
var filesystem = require("fs");
var results = [];
		
// Parse files
filesystem.readdirSync(folder).forEach(function(file) {
			
	// handle content here
});

This finds all the files in a specified folder, letting us import all templates and levels this way. Image do not have to be gathered like this, as the templates will define which images we need.

On the client, we don’t need access to the level content files, but we still need images and templates. Images don’t need explicit management (as mentioned above), but we need access to the templates. This is accomplished by serving the path to the templates folder for the client and then sending each client a list of the templates used by the hosted game.

Paradigm Shift, part 2

The server-client infrastructure in Ophelia now looks like this: The server, an express application, manages connections from different client. Each client can choose to host a game on the server or join an already hosted game. Once a game is in progress, it’s logic will be executed on the server. Whenever something happens that needs to be displayed on the client, such as an object being created, the server sends a message to the clients who update their state to match. The only thing sent to the server by the clients is user input.

The game logic on the server is written in the form of triggers; callbacks to events. All user scripts will start their execution on an event. See the previous post on Events for more information about this.

User’s create their own application by creating JavaScript file and starting the Ophelia server from it. After that, any game code can be defined. No custom code is required for the clients to run the game.

Paradigm Shift, part 1

It has been quiet lately on this blog, and that is because of two very busy weeks.

Last Friday the first usable build of Ophelia was complete. To try it out, I created a game, a simple version of pong. Immediately, it was apparent that there was a major issue. All the game logic (strictly client side scripts), demanded attention to be paid to whether they ran on a host or a client, which completely contradicts the stated goal of the project. First, I looked at whether this could be fixed by providing a system for automatically determining what should happen on host and on client, but this was only applicable on internal engine functions (where it is was already applied). When a user wanted to, in Pong for example, create a paddle on the left for player 1 and one on the right for player 2, they had to check whether they were player 1 or 2 and create the right object locally. This was then distributed to the other client. But that requires running two separate scripts! Alternatively, the user could create all objects on the host, but now this script must only execute on the host.

Looking at these problems, I decided that a retooling of the engine’s structure was necessary to meet the projects goals. This time, the end user’s interface should be the primary focus. However, there is only so much project time, so a complete rewrite is out of the question.

The most effective solution was to place game logic on the server, instead on the client. This turns the clients into shell applications that only send input and receive data about what they should render on the screen. With this structure, game logic can now be written to only run in one place, the server, treating the player’s as abstract objects in the game.

Converting the design of Ophelia to this structure is very easy in theory, most systems will work exactly the same, only in a different place. But we have to remember that Ophelia’s server is an Express application, so attention also had to be payed to how one such application is put together to run user scripts.

Continue reading in part to find out what Ophelia’s new structure looks like!

An eventful day

Events are very important in Ophelia’s structure, because they are our way to ensure that clients run synchronously. If we run all the game code in response to events, then all clients will be in sync as long as no event message in lost on the network. But in order to code a whole game in the form of event triggers we have to place requirements on the event system. It has to be flexible and expansive, allowing users to listen to all kinds of events, and also define their own. Let’s take a look at how this is implemented in Ophelia.

There are two types of events in Ophelia; global and local events. Global events are game-wide, when they occur, it affects all objects. To listen to a global events, we simply register a callback function to the desired event and that function will be called when the event triggers.
Local events meanwhile, are events that only happen to one object, for example, an object being removed from the stage. To listen to these events, we have to register our callback on the object, as each object will contain their own list of callbacks and trigger them independently. Note that we can still register any function to be a callback for a local event, it doesn’t have to be related to the object in any way.

Java script permits us a very simple implementation of these concepts, as seen below. In this example, ‘event’ is a string used to represent the event, such as “onUpdate”.

var Events = {

	callbacks : {},

	// Register a function to be called 
	// when the event occurs
	registerCallback : function(event, callback){

		Events.callbacks[event].push(callback);
	},

	// Deregister a function to no longer be
	// called when the event occurs
	deregisterCallback : function(event, callback){

		var i = Events.callbacks[event].indexOf(callback);

		Events.callbacks[event].splice(i, 1);
	},

	// Trigger an event, calling all callbacks
	triggerEvent : function(event, eventData){

		var len = Events.callbacks[event].length;

		for(var i = 0; i < len; i++){

			Events.callbacks[event][i](eventData);
		}
	},
	
	// Register a new event, this must be done
	// before callbacks can be registered on it
	registerEvent : function(eventName){

		Events.callbacks[eventName] = [];
	}

};

This design relies on two key concepts in Javascript. Firstly, functions can be saved in an array and executed as we iterate over this array. Secondly, properties can be defined on an object during runtime, letting us define new events dynamically.

Objectivity Part 2: Synchronization

We already covered the design of objects locally, but how will the synchronization work between clients? Now that we have the tools to create a network, we can undertake this task.

To achieve synchronization, we will have to support three kinds of object-related messages in our client-server protocol:

First, we need a message that tells all connected clients to create the same object and a message that tells all connected clients about a change to an object that exists and is presumed to be in sync.

Finally, a message that fully copies an object and it’s state from one client to another. This is crucial, because we will need it both to re-synchronize objects if they end up out of sync for any reason (such as a lost message) and to allow a client connecting into a game in progress to catch up on the current state of the game.

To create the last message, we need a good way to define the complete change of state an object has undergone from it’s default state. JavaScript allows us to do this easily by enumerating an object’s properties:

for(var property in objState)
	obj[property] = objState[property]

In this snippet, we assume that the changes to an object is stored in ‘objState’ . We update the properties of ‘obj’ by enumerating any properties of ‘objState’. This means that we can use this to handle any properties, as long as they exist, and that we can handle as few or as many as needed.

A new demo demonstrating object synchronization is available in the repo Examples/Network/Network Objects Example.

The right tools for the job: Socket.io & Node.js

More than a render engine, there is one dependency we critically need: something to handle the network connection! This is expertly handled by two powerful libraries called Socket.io and Node.js.

Socket.io helps us connect to other computers over a socket. It also aids with messaging and events on that socket.

Node.js is a very large and diverse library for building network applications. Through the node package manager, we can install additional frameworks that build upon Node.js.

Here, we particularly need Express, which is a framework for web applications.

With these powerful tools, a working network application is not far away!

In the Network folder of the Examples repo, you can find an example of one such application; a simple chat application.

To test the example, you will need to install Node.js. Then, follow the instructions in the README.txt file.

The right tools for the job: PIXI.js

In order for this project to be able to focus on the network multi-player development, we will need strong dependancies that can deal with other aspects of a game. One such aspect it graphics, and to handle it, I have selected PIXI.js.

PIXI.js is a complete render engine for HTML and javascript. It supports webGL, but will automatically revert to drawing on the regular canvas if we find ourself in a context that doesn’t support webGL. It is very fast and has lot’s useful tools and features. What else could we ask for?

Getting started with PIXI.js is extremly simple, check out this snippet:

// Get the renderer
var renderer = PIXI.autoDetectRenderer(640, 480);

// ...and append it to the document
document.body.appendChild(renderer.view);

// Create stage
var stage = new PIXI.Stage(0x000000);

// Create sprite
var sprite = new PIXI.Sprite(PIXI.Texture.fromImage("simple.bmp"));

// ...and add it to the stage
stage.addChild(sprite);

function paint() {
	// Request that we paint again when the browser does
	requestAnimFrame(paint);

	// Render the stage
	renderer.render(stage);
}

// Paint the first frame!
paint();

This will create a single sprite and render it each frame. The stage is the parent to all sprites and also serves as a solid color background.

Objectivity

The design of game objects is a very important part of Ophelia. We need objects that fulfill a lot of different specifications. Let us look at these, one at a time and see how the design of Ophelia will deal with them.

We will start with one of the most important:

“Objects need to be able to synchronize their internal state with their clones on other connected clients without demanding additional work from the developer who is creating an application with the engine.”

This looks tricky at first. When coding the behavior of their objects, developers are bound to modify the internal state of the object constantly. How can we detect this change? We could test if anything has changed, but the cost of doing this would quickly add up as more objects, or properties of objects, are  added to the game. We could demand that users only modify the state of objects in response to certain events and then we can just test which properties that changed after this event. But now we have begun to restrict the user, which we didn’t want to do.

Luckily, javascript comes to the rescue. Instead of defining our objects’ state with regular properties, we can define them with the defineProperty() method. This allows us to create custom setters and getters for the properties, while retaining the same interface.

In the example below, we store the actual value of these properties inside a private member object, to ensure that they cannot be accessed directly.
Of course, our setter could contain any code we want. This is the place where we will handle object synchronization.

function GameObject(){

   var properties = {};

   this.addProperty= function(name, initialValue){

      properties[name] = initialValue;

      Object.defineProperty(this, name, {

         configureable : true,

         enumerable : true,

         get : function(){
            return properties[name];
         },

         set : function(value){
            properties[name] = value;
         }
      });
   }
};

For a functioning example for how this can be used, look at the Object Properties example in the Examples repo.