SDL 2.0 Tutorial-05: Networking 2

In the last tutorial we finished with a hallow if-statement within our processing loop.

int running = 1;
while(running) {
	int num_rdy = SDLNet_CheckSockets(socket_set, 1000);

	if(num_rdy <= 0) {
		// NOTE: none of the sockets are ready
	} else {
		// NOTE: some number of the sockets are ready
	}
}

If there aren’t any sockets which we need to process (i.e. num_rdy <= 0 ) then we’ll loop over all the currently connected client sockets and perform any tasks which may be pending. This is basically the server’s chance to catch up with game state and perform any security checks. This will work well for a simple TCP game where the server and client need not be in sync the entire time. For something more real-time we would prioritize game state and perhaps use UDP instead.

For now let’s add four wood to the resource count for each connected client.

int ind;
for(ind=0; ind<MAX_SOCKETS; ++ind) {
	if(!clients[ind].in_use) continue;
	clients[ind].amt_wood += 4;
}

Remember this will run every second assuming there aren’t any sockets to be processed (i.e. num_rdy <= 0 ). Why am I saying it’ll run every second? It is because of the following function call:

SDLNet_CheckSockets(socket_set, 1000);

The second argument to this function is 1000, which is the number of milliseconds this function will block while waiting for a socket connection.

Now, if there are sockets ready to be processed we’ll have to check whether it is a client socket or the server socket. If the server socket is ready then we likely have a new client connection. For the client connections we’ll have to check each one until we find the ones which are ready and then process the data that we have received from them.

First let’s handle the case when the server socket is ready:

if(SDLNet_SocketReady(server_socket)) {
	int got_socket = AcceptSocket(next_ind);
	if(!got_socket) {
		num_rdy--;
		continue;
	}

	// NOTE: get a new index
	int chk_count;
	for(chk_count=0; chk_count<MAX_SOCKETS; ++chk_count) {
		if(sockets[(next_ind+chk_count)%MAX_SOCKETS] == NULL) break;
	}

	next_ind = (next_ind+chk_count)%MAX_SOCKETS;
	printf("DB: new connection (next_ind = %d)n", next_ind);

	num_rdy--;
}

Here the function int AcceptSocket(int index); is a shorthand for accepting a new socket connection using sockets[index] and clients[index] . This function returns whether or not the connection was successfully accepted. Here is the function itself:

int AcceptSocket(int index) {
	if(sockets[index]) {
		fprintf(stderr, "ER: Overriding socket at index %d.n", index);
		CloseSocket(index);
	}

	sockets[index] = SDLNet_TCP_Accept(server_socket);
	if(sockets[index] == NULL) return 0;

	clients[index].in_use = 1;
	if(SDLNet_TCP_AddSocket(socket_set, sockets[index]) == -1) {
		fprintf(stderr, "ER: SDLNet_TCP_AddSocket: %sn", SDLNet_GetError());
		exit(-1);
	}

	return 1;
}

The function void CloseSocket(int index); was presented in the previous part of this tutorial. After we accept the new client connection we also update the next_ind variable so that we are ready for the next client connection. As it’s currently written, if a new client connects when the server is full then another client will be kicked to make room for the new connection.

Now let’s process any clients which might have data for us.

int ind;
for(ind=0; (ind<MAX_SOCKETS) && num_rdy; ++ind) {
	if(sockets[ind] == NULL) continue;
	if(!SDLNet_SocketReady(sockets[ind])) continue;

	uint8_t* data;
	uint16_t flag;
	uint16_t length;
				
	data = RecvData(ind, &length, &flag);
	if(data == NULL) {
		num_rdy--;
		continue;
	}

	switch(flag) {
		case FLAG_WOOD_UPDATE: {
			uint16_t offset = 0;
			uint8_t send_data[MAX_PACKET];

			memcpy(send_data+offset, &clients[ind].amt_wood, sizeof(uint8_t));
			offset += sizeof(uint8_t);

			SendData(ind, send_data, offset, FLAG_WOOD_UPDATE);
		} break;

		case FLAG_QUIT: {
			running = 0;
			printf("DB: shutdown by client id: %dn", ind);
		} break;
	}

	free(data);
	num_rdy--;
}

What we are doing here is looping over all the client sockets and checking to see whether a client is connected and whether that connected client has some information ready for us. We also want to make sure that we aren’t working harder than we have to by breaking out of the for-loop when num_rdy == 0 ; because at that point we have processed all the ready client sockets so there is no point in checking any remaining client connections for information.

Once we know that we have a connected socket and that the socket have information ready for us, we fill a buffer with the data they sent us. This is what the uint8_t* RecvData(int index, uint16_t* length, uint16_t* flag); function does for us:

uint8_t* RecvData(int index, uint16_t* length, uint16_t* flag) {
	uint8_t temp_data[MAX_PACKET];
	int num_recv = SDLNet_TCP_Recv(sockets[index], temp_data, MAX_PACKET);

	if(num_recv <= 0) {
		CloseSocket(index);
		const char* err = SDLNet_GetError();
		if(strlen(err) == 0) {
			printf("DB: client disconnectedn");
		} else {
			fprintf(stderr, "ER: SDLNet_TCP_Recv: %sn", err);
		}

		return NULL;
	} else {
		int offset = 0;
		*flag = *(uint16_t*) &temp_data[offset];
		offset += sizeof(uint16_t);

		*length = (num_recv-offset);

		uint8_t* data = (uint8_t*) malloc((num_recv-offset)*sizeof(uint8_t));
		memcpy(data, &temp_data[offset], (num_recv-offset));

		return data;
	}
}

This function gets data from sockets[index] and returns a pointer to a new buffer. The in/out function arguments length and flag are the length of the newly allocated buffer and the packet flag for the information we are receiving from the client.

If the client sends us a request for the amount of wood they current possess (i.e. FLAG_WOOD_UPDATE ) then we reply by sending them a packet with the information they are requesting. To send information to the connected client we use the following shorthand function:

void SendData(int index, uint8_t* data, uint16_t length, uint16_t flag) {
	uint8_t temp_data[MAX_PACKET];

	int offset = 0;
	memcpy(temp_data+offset, &flag, sizeof(uint16_t));
	offset += sizeof(uint16_t);
	memcpy(temp_data+offset, data, length);
	offset += length;

	int num_sent = SDLNet_TCP_Send(sockets[index], temp_data, offset);
	if(num_sent < offset) {
		fprintf(stderr, "ER: SDLNet_TCP_Send: %sn", SDLNet_GetError());
		CloseSocket(index);
	}
}

For debugging purposes the client can also shutdown the server with a FLAG_QUIT . In the next tutorial we will write a simple client which will display the amount of wood the client current has. This amount will continue to increase because the server currently adds four wood each time it enters idle processing. Once we have achieved this we’ll add a “quest” button and timer on the client-side and a few more packet flags allowing the client to request the amount of time left on a quest and to start a quest. The server will check for when a quest completes and will increase the amount of wood for that client as a result.

Till next time!

SDL 2.0 Tutorial-04: Networking 1

For the next few posts I plan to run through some of the basics of TCP communication using the SDLNet library. The goal here is to write a simple “resource” game where the client sends a “quest” request and, after some amount of time has passed, the quest is considered complete and the client’s resource count increases a little. This series will provide an excellent setup for developing a player driven economy type game.

Our first goal will be to write a server which manages all the client connections. To keep things simple we will be working with one single resource; here we are using wood as the resource. We’ll start with a few #define ‘s

//-----------------------------------------------------------------------------
#define MAX_PACKET 0xFF
#define MAX_SOCKETS 0x10

//-----------------------------------------------------------------------------
#define WOOD_WAIT_TIME 5000

//-----------------------------------------------------------------------------
#define FLAG_QUIT 0x0000
#define FLAG_WOOD_UPDATE 0x0010

Here we have three different type of #define ‘s

  • MAX_PACKET and MAX_SOCKETS define the maximum size of the TCP packets we are willing to process and the maximum number of clients we will allow online at once.
  • WOOD_WAIT_TIME is the amount of time in milliseconds to wait before the quest for obtaining additional wood units is complete.
  • FLAG_QUIT and FLAG_WOOD_UPDATE are 16 bit packet flags used to determine the type of packet we are receiving and we then process the rest of the packet’s data based on this flag.

Next we need a structure which we can use to keep all the client’s information separate from one another.

typedef struct {
	int in_use;
	int questing;
	uint8_t amt_wood;
	uint32_t timer_wood;
} Client;

Here we have a few important variables which we need in order to efficiently work with each of the clients which connect to the server. in_use allows us to determine whether the current client is being used by an active socket connection or not. This will prevent us from running checks on client structures which don’t have corresponding network connections. questing is a simple boolean which tells us whether the current client is running a wood fetching quest or not. amt_wood is the amount of the wood resource which the current client has. timer_wood is a timestamp for when the wood fetching quest for this particular client was started – this will help us determine when the wood fetching quest has been completed.

Next we need some global state variables which will make writing our little server a bit easier.

int next_ind = 0;
TCPsocket server_socket;
Client clients[MAX_SOCKETS];
SDLNet_SocketSet socket_set;
TCPsocket sockets[MAX_SOCKETS];

next_ind is the index into the sockets and clients arrays for the next player that will connect to the server. When one client connects next_ind gets updated so that we never overwrite a connected clients information.

First thing we do in int main(int argc, char** argv); is initialize a few SDL components and the SDLNet library.

if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_EVENTS) != 0) {
	fprintf(stderr, "ER: SDL_Init: %sn", SDL_GetError());
	exit(-1);
}

if(SDLNet_Init() == -1) {
	fprintf(stderr, "ER: SDLNet_Init: %sn", SDLNet_GetError());
	exit(-1);
}

Next we’ll want to open the server’s TCP socket on a specific port, here I’m using port 8099. Any function specific information can be found within the SDLNet online API.

IPaddress ip;
if(SDLNet_ResolveHost(&ip, NULL, 8099) == -1) {
	fprintf(stderr, "ER: SDLNet_ResolveHost: %sn", SDLNet_GetError());
	exit(-1);
}

server_socket = SDLNet_TCP_Open(&ip);
if(server_socket == NULL) {
	fprintf(stderr, "ER: SDLNet_TCP_Open: %sn", SDLNet_GetError());
	exit(-1);
}

Finally, before the main processing loop, we will setup an SDLNet socket set and add the server_socket to the socket set so that we may be informed when a net connection is looking to be accepted by the server.

socket_set = SDLNet_AllocSocketSet(MAX_SOCKETS+1);
if(socket_set == NULL) {
	fprintf(stderr, "ER: SDLNet_AllocSocketSet: %sn", SDLNet_GetError());
	exit(-1);
}

if(SDLNet_TCP_AddSocket(socket_set, server_socket) == -1) {
	fprintf(stderr, "ER: SDLNet_TCP_AddSocket: %sn", SDLNet_GetError());
	exit(-1);
}

It’s worth noting that the size of the socket set is MAX_SOCKETS+1 , the plus one accounts for the addition of the server socket. The main processing loop is setup like the following (currently hollow):

int running = 1;
while(running) {
	int num_rdy = SDLNet_CheckSockets(socket_set, 1000);

	if(num_rdy <= 0) {
		// NOTE: none of the sockets are ready
	} else {
		// NOTE: some number of the sockets are ready
	}
}

While the server is running we check for any sockets from the socket set which might have packets for us to process; we check the socket set for a second (1000 milliseconds). If there aren’t any sockets ready to be processed then we use this time to run other various server activities like checking is quests have been completed, etc. If there is some number of sockets ready to be processed then we process those sockets.

In the next tutorial we will fill in these two parts of the if-statement above; this is where the real magic is! To clean up everything after the server is done running we will run the following code.

if(SDLNet_TCP_DelSocket(socket_set, server_socket) == -1) {
	fprintf(stderr, "ER: SDLNet_TCP_DelSocket: %sn", SDLNet_GetError());
	exit(-1);
} SDLNet_TCP_Close(server_socket);

int i;
for(i=0; i<MAX_SOCKETS; ++i) {
	if(sockets[i] == NULL) continue;
	CloseSocket(i);
}

SDLNet_FreeSocketSet(socket_set);
SDLNet_Quit();
SDL_Quit();

The missing piece here is the void CloseSocket(int index); function which is basically just a shorthand for closing a client socket and is stated below:

void CloseSocket(int index) {
	if(sockets[index] == NULL) {
		fprintf(stderr, "ER: Attempted to delete a NULL socket.n");
		return;
	}

	if(SDLNet_TCP_DelSocket(socket_set, sockets[index]) == -1) {
		fprintf(stderr, "ER: SDLNet_TCP_DelSocket: %sn", SDLNet_GetError());
		exit(-1);
	}

	memset(&clients[index], 0x00, sizeof(Client));
	SDLNet_TCP_Close(sockets[index]);
	sockets[index] = NULL;
}

For the next tutorial we’ll pick up from here and fill in the if-statement mentioned earlier. Once the server-side code has been setup to process a simple player request to perform a quest to gather wood, we’ll start working on a simple client-side quest button, quest timer, and resource counter. If there is time or interest we will add persistence to the resource count for each player using SQLite.

Current Projects

I have been really busy with work recently but I’m making some time to discuss my current hobby projects. One of these project is more of a solo gig and the other, I’m hoping, will start with me doing most of the work at first but eventually I’d like other developers to join in and help.

This first project is similar in style to Arthur’s Grove (AG). AG was a little game I released not too long ago but this time I’m stepping up my game a little more now that I’m more comfortable with a lot of the game mechanics I used when developing AG. The worlds are completely different and the stories have nothing to do with each other (not that AG had much of a story anyways) but the game itself will be similar in style because I find myself gravitating towards this style of game a lot.

The tech is also a bit different. Here I’m relying more on the Tiled Map Editor to set collision boundaries and add entity objects into the level. The Tiled Map Editor exports to JSON which I then parse using Duktape. I have been using Duktape for a while and anytime I need to do a bit of scripting on parse Javascript/JSON it is my go-to library!

The second project has recently grown in importance to me since, apparently, Snoxd has been down for whatever reason (most likely due to the lack of contributing community members). As much as I would like to see Snoxd succeed, my feeling is that the private KO server community needs strong developers and not a bunch of “binary hackers.” A community that doesn’t share will never grow and a community that doesn’t support and praise good contributions will never provide the positive reinforcement necessary to keep people contributing. Sometime in the future I’ll write up a more complete article on my feeling towards the KO development community and a direction for where I would like to see the community head towards in the future. But nevertheless, I have been working on a completely new implementation of the KO AIServer architecture using SDL2 and MySQL server. Currently I’m able to spawn in a group of NPCs with this custom code and have the AIServer communicate with the stock 1298 Game server which allows the player to login and see the NPCs. Most of the work here comes from re-implementing the multi-threaded nature of the AIserver using SDL2, along with all the network calls using SDL2 Net. This code is completely cross-platform and will compile on Windows/Mac/Linux.

One very important reason for using MySQL server instead of Microsoft’s SQL server (which the official USKO, as well as all private servers, use) is because if you were to compile the KO servers on Linux you would will need access to a Microsoft SQL server on another remote windows machine. Therefore, no matter what, a machine running Windows is necessary. However, if the database is a MySQL database this isn’t an issue as MySQL can be installed on any prominent operating system. Hell, even a custom coded server running Sqlite would provide more versatility than SQL server (but I digress).

So that’s the sort of stuff I’ve been working with recently. If you have any questions, or would like to help contribute to the OpenKO project, let me know. The long-term idea is to replace each of the KO game servers for the stock 1298 release one at a time. Once this is complete and the binaries are virtually interchangeable with the originally released 1298 private server binaries we will start writing a custom 1298 client using SDL2 and OpenGL. This is the tentative plan, and likely won’t recent full attention until I have finished my PhD and have find a job. But the progress made so far is still quite an accomplishment relative to the stagnation which unfortunately permeates the KO development scene. This will eventually change though because I know there are passionate developer out there and I would love to help be apart of that change.

Knight Online, Repent Online, and the N3Base

Just wanted to share an interesting tidbit of information I stumbled upon. MGame, a Korean MMO developer, worked on two major projects around 2002/2004. One was Knight Online and the other was Repent Online. You can still play Knight Online today and it is possible to play Repent Online but you need a Korean social security number. Anyways, I played Knight Online (KO) a TON when I was 12 or so years old. Today I commonly work on Knight Online private server development in my free time. So while poking through the content files for both KO and Repent Online (RO) I noticed a common trend in the binary formatted content data. A while back I write a tool to help visualize KO game objects using OpenGL, and lo and behold (with minor tweaks) I could get the RO objects to render using my KO tool! Pretty cool!

This might not be too terribly useful but then again it might be neat to add some of the weapons and objects to the KO content as a throwback to Repent Online (a.k.a Legend of Ares).

New Project – “June”

So for whatever reason I recently went on a Youtube binge watching a ton of videos on game design and artistic design. Based on the videos I saw I feel motivated to mix things up a little and try something more creative. There are a ton of point and click adventure games out there but what I have planned is a bit more personal. I am prototyping the idea of turning my own day-to-day experiences into a point and click.

For this game I am also adding music which a lot of my previous projects have been missing, and all the images for the game were taken with my android phone. Music plays a much bigger role in this game because there aren’t really any “game mechanics”. The game mechanic is the experience and how well it draws the play in. Everything I have now has been thrown together quickly just to get a feel for the gameplay, and I’d have to say I like what the prototype has had to offer so far. The images are a bit blurry but linking image to image through an option list of activities is a lot of fun and has opened more of my creative side than any other project so far.

So I will keep with this for a while and see where it takes me!

Duktape to automate Ogre3D scenes

I have been playing around with the Ogre3D graphics engine. Ogre3D has been around for a while but only recently, after getting burnt out on OpenGL, have I started to learn more about how Ogre works. After making my way through their set of beginner tutorials (here) I was ready to start making my own scenes and start building a little prototype. I wanted an easy way to editor my scene without having to recompile everything every time I slightly move something within the scene. Typically this is where most people go out and learn 3Ds Max or Blender or even any of the custom Ogre scene editors. But after spending a day working through the Ogre tutorials I felt like I had experienced enough tutorials for one day.

So, if you are like me, you would like to have another option for manipulating a scene quickly without having to recompile. This is an excellent excuse to embed a scripting language within your C/C++ program! Here is an example of building Ogre scenes using a JSON file and Duktape. Duktape can do more than just parse JSON files, so it is a great addition to your codebase – especially if things like Google’s V8 Engine is overkill for what you are looking to do.

Let’s start by viewing a little test scene I’ve been playing around with.

So this is a basic scene I built using the sample meshes which come with the Ogre SDK. Below is the JSON file which builds this particular scene.

{

"SceneNodes": [
	{
		"Name": "FloorNode",
		"Position": [0.0, -5.0, 0.0]
	},
	{
		"Name": "TestNode",
		"Position": [0.0, 25.0, 0.0]
	}
],

"Entities": [
	{
		"Name": "FloorEntity",
		"Mesh": "floor00.mesh",
		"SceneNode": "FloorNode",
		"CastShadow": false
	},
	{
		"Name": "TestEntity",
		"Mesh": "ogrehead.mesh",
		"SceneNode": "TestNode",
		"CastShadow": true
	}
],

"Lights": [
	{
		"Name": "TestLight",
		"Type": "Point",
		"Position": [250.0, 100.0, 250.0]
	}
]

}

Ogre doesn’t know how to parse this file at all. All the conventions here I define within my code using the Duktape API. Once Duktape parses the JSON file I retrieve the relevant information I need to build my scene using Ogre. First we read in the JSON file as a string so we can later hand it over to Duktape, which will then use it to create a Javascript object.

int strLen = 0;
char* fileStr = NULL;

FILE* file = fopen("../../data/scene/test.json", "r");
if(file == NULL) exit(-1);

char c;
do {
	fread(&c, sizeof(char), 0x01, file);
	fileStr = (char*) realloc(fileStr, ++strLen*sizeof(char));
	fileStr[strLen-1] = c;
} while(!feof(file));

fileStr = (char*) realloc(fileStr, ++strLen*sizeof(char));
fileStr[--strLen] = '';

fclose(file);

So now we have a string pointer, char* fileStr; , which points to the contents of our JSON file. Here you could do any “pre-processing” work that may be needed. Next we need to send this string over to Duktape. It is very simple to get Duktape up and running.

duk_context* mContextJS;
mContextJS = duk_create_heap_default();

duk_push_global_object(mContextJS);

duk_push_string(mContextJS, fileStr);
duk_json_decode(mContextJS, -1);

free(fileStr);

Here we decode the JSON object and free our previously allocated string. From here you can literally do anything with the JSON object. Below is one example but there is really no limit to how complex these scenes could get!

duk_get_prop_string(mContextJS, -1, "SceneNodes");

size_t i;
for(i=0; i<duk_get_length(mContextJS, -1); i++) {
	duk_get_prop_index(mContextJS, -1, i);

	duk_get_prop_string(mContextJS, -1, "Name");
	const char* nodeName = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "Position");
		
	duk_get_prop_index(mContextJS, -1, 0);
	Ogre::Real x = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);
	duk_get_prop_index(mContextJS, -1, 1);
	Ogre::Real y = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);
	duk_get_prop_index(mContextJS, -1, 2);
	Ogre::Real z = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);

	duk_pop(mContextJS);

	/* === */

	Ogre::SceneNode* node;
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode(nodeName);

	node->setPosition(x, y, z);

	/* === */

	duk_pop(mContextJS);
}

duk_pop(mContextJS);

/* === */

duk_get_prop_string(mContextJS, -1, "Entities");

for(i=0; i<duk_get_length(mContextJS, -1); i++) {
	duk_get_prop_index(mContextJS, -1, i);

	duk_get_prop_string(mContextJS, -1, "Name");
	const char* entName = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "Mesh");
	const char* meshFN = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "SceneNode");
	const char* nodeName = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "CastShadow");
	bool castShadow = duk_to_boolean(mContextJS, -1) & 0x01;
	duk_pop(mContextJS);

	/* === */

	Ogre::SceneNode* node = mSceneMgr->getSceneNode(nodeName);
	Ogre::Entity* entity  = mSceneMgr->createEntity(entName, meshFN);

	entity->setCastShadows(castShadow);

	node->attachObject(entity);

	/* === */

	duk_pop(mContextJS);
}

duk_pop(mContextJS);

/* === */

duk_get_prop_string(mContextJS, -1, "Lights");

for(i=0; i<duk_get_length(mContextJS, -1); i++) {
	duk_get_prop_index(mContextJS, -1, i);

	duk_get_prop_string(mContextJS, -1, "Name");
	const char* lightName = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "Type");
	const char* lightType = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "Position");
		
	duk_get_prop_index(mContextJS, -1, 0);
	Ogre::Real x = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);
	duk_get_prop_index(mContextJS, -1, 1);
	Ogre::Real y = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);
	duk_get_prop_index(mContextJS, -1, 2);
	Ogre::Real z = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);

	duk_pop(mContextJS);

	/* === */

	Ogre::Light* light = mSceneMgr->createLight(lightName);

	if(!strcmp(lightType, "Spot"))
		light->setType(Ogre::Light::LT_SPOTLIGHT);
	if(!strcmp(lightType, "Point"))
		light->setType(Ogre::Light::LT_POINT);
	if(!strcmp(lightType, "Directional"))
		light->setType(Ogre::Light::LT_DIRECTIONAL);
	light->setDiffuseColour(1.0, 1.0, 1.0);
	light->setSpecularColour(1.0, 1.0, 1.0);
	light->setPosition(x, y, z);

	/* === */

	duk_pop(mContextJS);
}

duk_pop(mContextJS);

The one line that probably deserves explaining here is duk_to_boolean(mContextJS, -1) & 0x01; , which I did simply to avoid const warnings VC++ was giving about converting from duk_bool_t to bool .

As an added bonus, if we put this code within a void createScene(void); function we can hotload the scene with a single button press (i.e. without having restarting the program). Just add the following to your bool OIS::KeyListener::keyPressed(const OIS::KeyEvent& ke); function’s switch statement.

case OIS::KC_R: {
	mSceneMgr->clearScene();
	createScene();
} break;

Here I’m using the “R”-key to reload my scene.

I much prefer this method to learning a bloated program like 3Ds Max, Blender, or any of the other programs out there. These programs can’t avoid the bloat because they need to be able to perform everything a user needs (and then some). But if you only need the basics they come with way too much overhead to warrant their use.

Hopefully I’ll have more 3D stuff in the future! I’ve never made a 3D game before so this is new territory for me – quite excited!

Arx Libertatis

I have been digging around lately for feature-length games with readily available source code. I really enjoy looking back over great source code and trying understand the underlying structure. Most recently I learned about Arx Fatalis, which is now being developed further by passionate hobby developers under the project name Arx Libertatis. I never even knew this game existed!

Arx Fatalis was originally developed by Arkane Studios, the developers for the more recently popular game Dishonored. I have been making my way through this game in amazement. I am a huge fan of the King’s Field series of game and Arx Fatalis seems to capture a lot of the feelings of isolation that are synonymous with the King’s Field series. The fact that this entire project is open source and under the GNU Version 3 license is just icing on the cake. If you have time, follow this video tutorial and get a version of the source code up and running for yourself! At the very least, get the game off Steam or GOG and enjoy this wondrous experience!