The final update

The last changes and additions have been made to the engine. Let’s take a look at what has happened lately.
To start with, the server no longer transmits all attribute changes to the clients. The reason for this is, of course, that the clients do not need to know of every change. To achieve this, an object’s attribute are split into distributed and regular attributes. Only distributed attributes are synchronized with the client. In order to not inconvenience the developer, only regular attributes have to be created for custom games, as game logic runs on the server. The client mostly deals with objects transformations and graphics, these things are distributed by the engine.
Another big improvement is adding more messages sent by the server to the client. These messages tell the client to do things that it can do on it’s own, while guaranteeing a synchronized result with the server. For example, one such message tells the client to clear all game objects. Previously, this would be done on the server and the server would then send a ‘removeObject’ message to the clients for each removed object. Being able to send just one ‘clearObjects’ message is much better for our network performance.
Client-exclusive objects is another big addition. Such an object will only have a copy on one client, allowing the game to show different information to different players. This is perfect to, for example, create a HUD element.
The engine now also has support for a camera. The camera is client-side, which means that different player can have different views of the game. The camera can be instructed to follow an object.
Image object have been very simple in the past, but now a lot more of Pixi.js power has been unlocked, with new Image object attributes that map directly to Pixi.js sprite attributes. Have fun transforming your images!
Finally, a different form of visual representation has been added, the Text object. This object also uses Pixi and can support the same transforms.

Check back next time and we take a look at a demo game made with Ophelia!

The Ophelia Interface

When we create games with Ophelia, we utilize the Ophelia Interface. To access the interface, we must include the file containing the interface. This is done with Express, like this:

require('../server/ophelia');

Naturally, we will have to adapt this path to where the engine in located relative to our game project.
There are three primary functions to the interface: methods calls, trigger definitions and event callbacks. Methods calls are very straightforward. We invoke a method from the interface that will affect the game state. Trigger definitions we have looked at in the previous post. Event callbacks look like this:

Ophelia.registerCallback('onUpdate', function(data){
	// Code that is invoked with each update
}

Using the above we can put together a file such as this:

var Ophelia = require('../../../../server/ophelia');

/* Objects conrolled by players */
var playerObjects = {};

/* When the game starts */
Ophelia.registerCallback('onGameState', function(data){

	/* Get a list of the players in the game */
	var players = Ophelia.getPlayersInGame();

	/* Assign each player a player object */
	for(var i = 0; i < players.length; i++){
		playerObjects[players[i].id] = Ophelia.createObject('player');
});

/* When a player joins */
Ophelia.registerCallback('onPlayerJoin', function(data){

	var id = data.joiningPlayer.id;

	/* Give the new a player an object */
	playerObjects[id] = Ophelia.createObject('player');
});

/* When a player leaves */
Ophelia.registerCallback('onPlayerLeave', function(data){

	var id = data.leavingPlayer.id;

	/* Remove the leaving player's object */
	Ophelia.removeObject(playerObjects[id]);
});

/* Behavior for player objects */
Ophelia.defineTrigger('onPlayerUpdate', function(data){

	var player = data.updatedObject;

	/* Move the object */
	player.xPosition++;
});

Templates

When we are creating objects for a game, templates are useful. A template is a blueprint for a certain type of object, when we want such an object we can use the same template to always get an equal object. The template will specify which modules that the object use and how the object’s attributes will be configured. Finally, a template can also define scripts, called triggers, linked to local events. When the local event occurs for an object of this type, the trigger is invoked.
Templates in Ophelia ware defined in XML files.
A template looks like this:

<!-- Always start with the Template tag-->
<Template>
	<!-- Give the template a name -->
	<Name>player</Name>
	<!-- Which components does this object use? -->
	<Components>
		<Image>true</Image>
		<Movement>true</Movement>
		<Collision>true</Collision>
	</Components>
	<!-- Which default attribute values does the object have? -->
	<Attributes>
		<name>player</name>
		<imageSrc>blue.bmp</imageSrc>
		<xCollisionSize>20</xCollisionSize>
		<yCollisionSize>20</yCollisionSize>
		<xAnchor>-20</xAnchor>
		<yAnchor>-20</yAnchor>
	</Attributes>
	<!-- Which scripts will trigger on which events? -->
	<Triggers>
		<onCollide>onPlayerCollide</onCollide>
		<onUpdate>onPlayerUpdate</onUpdate>
	</Triggers>
</Template>

To finish defining this object, we also need to define the scripts that make up the template’s triggers. This will be done in JavaScript and can be written anywhere in the project. It looks like this:

Ophelia.defineTrigger('onPlayerCollide', function(data){
	
	var player = data.collidingObject;
	// Code that is invoked when the player collides
});

Ophelia.defineTrigger('onPlayerUpdate', function(data){
	
	var player = data.updatedObject;
	// Code that is invoked when the game updates
});

My very own object module

An important part of Ophelia’s design is the ability for users to define their own object feature modules. But what is an object feature module? It is a relatively small, self-contained module that can add new functionality to game objects. Almost all of the core game object functionality in Ophelia, such as Movement and Collision, have been written in the form of object feature modules.
Let’s take a look at what we need to do to define our own modules:

/* Import the base Object module */
var ObjectModule = require('../../../server/game objects/object module');

/* This contains the interface that will be imported by other files */
module.exports = {
	
	/**
	*	A module must define 'create'
	*	Creates an instance of this module
	*/
	create : function(gameObjects){
		
		/* Create a module 
		/* gameObjects is a reference to the instance of
		/* the gameObjects manager that this module is in */
		var Module = ObjectModule.create(gameObjects);
		
		/**
		*	A module must define 'onAttach'
		*	It is invoked when an object is attached
		*	This is the place to add attributes and events
		*	to objects that attach the module
		*/
		Module.onAttach = function(obj){
			
			/* Register new attributes */
			obj.addAttribute("exampleAttribute", 0, 'float');
			
			/* Register new local events */
			obj.registerEvent('onExampleEvent');
			
			/* Register callbacks to local events */
			obj.registerCallback('onUpdate', function(){
				
			});
		}
		
		/* Return the module */
		return Module;
	}
};

/* We have to register our module with the manager */
GameObjects.registerModule('example module', module.exports);

As we can see, the only requirement is a function ‘create()’ that creates an object module and returns it, an onAttach() method for the module that adds it’s attribute, events and logic, and finally; a call to GameObjects.registerModule() to have the module registered with the engine. Once this is done, we can start using our module, we can even define object templates that include it. We will get to know more about creating your own templates, as well as other content files, in an upcoming post.

Ticking

For many game scenarios, it can be fine to simply send data back and forth as soon as possible, letting the server and connected clients deal with it as they find time to do so.
But sometimes, we want may some actions to be synchronous. Let’s say that there is a larger group of objects that we want to move, all at the same time, retaining their relative distance to each other. In the simple described above, the server would just iterate over the object’s updating their position and notifying clients and it went. This creates a problem; when the number of objects grow, the server might no longer be able to send all the data to the clients during the same client-side frame. This means that on the client, it would look like we first moved half the objects and then the other half on the next frame. Not exactly the synchronous movement we intended.
To solve problems such as this, we need to be able to guarantee that events which occur on the same frame on the server to also be updated for the same client-side frame and for this, Ophelia has a queue system. The queue system works by storing all changes made to game state during a game update, called a ‘tick’. When the server begins to update the game anew, the previous tick is sent to the network module, where it will be distributed to the server. This means that clients will only receive complete ticks of updates, never stray updates to single objects.

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.