Tag Archives: Duktape

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.

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!