Neverwinter Nights is now Groovy


Neverwinter Nights is now Groovy

One of the things I do in my spare time is write the scripts for a couple Neverwinter Nights modules that I run. My current module is dynamically driven by using NWNX2 to call out to MySQL . Today I integrated Groovy into so I can call Groovy from the game server.

The default scripting language of Neverwinter Nights does not include things to access native methods or files although it does now have a way to store things to a simple database (key based, not SQL). What the clever authors of NWNX2 did was to extend the server against its will. Their library is included as a dynamic library LD_PRELOAD. When the server is started this preloaded library searches for the entry point to the scripting languages object property system. They then — using some assembly code — redirect the call to their own function, examine the call, determine if they should handle it, and if not, send the call to its normal destination. The first version only included a hard-coded MySQL outcall system that was awesome, but not easily extensible. The most recent version though has a C++ plugin API that lets you add your own plugin.

So what was my motive for building a Groovy Plugin for NWN? Well, there are a few things that I do in the server that can only be done outside the scripting language but are not database related. Right now what I do is run a separate Java process to poll a command queue in the database and execute the commands that it finds there. This is pretty painful because you have to have more than one process running and if you poll too often you can bog down the system but if don’t pool frequently the operations I need to perform won’t get done fast enough. Obviously this isn’t the best way to do this. What would be better would be some way to call out to Java code from the server directly. But, I didn’t want to hard code calls to Java in there. Enter Groovy.

Groovy gives me the best of both worlds, its a simple way to call Java code, its dynamic, terse, and I know the internals of it pretty well, so I knew that I could embed it without much trouble. I modeled the embedding API on my GroovyServlet implementation for automatic reload on modify and other niceties.

So now that I have my goal in mind, I started to look at the plugin API. I’ve not programmed a lot in C++ since 1994 or so, but I guess I understand well enough since, because that wasn’t a big deal. There were a few problems with library loading and other such things but on the whole the plugin API is pretty simple. You extend a C++ class, implement an OnCreate and OnRequest method, drop the .so in the directory of the server, modify the config file (which you get access to), and go on your way. Getting a skeletal plugin up and running didn’t take long. The next step was to refresh my memory about the JNI invocation API and have it call out to my Groovy embedding API. The biggest problems there were trying to figure out things like: If I call NewStringUTF do I need to call ReleaseStringUTFChars? (no)¬† It eventually stopped seg faulting. Here is what the .h file looks like for my plugin to give you an idea of the difficulty (not much):

#ifndef NWNXJava_h_
#define NWNXJava_h_
#include <jni.h>
#include “NWNXBase.h”

class CNWNXGroovy : public CNWNXBase
{
 public:
 CNWNXGroovy();
 virtual ~CNWNXGroovy();

 bool OnCreate(gline *nwnxConfig, const char *LogDir=NULL);
 char* OnRequest(char *gameObject, char* Request, char* Parameters);
 
 protected:
 void ExecuteGroovyScript(char* script, char* argument);
 void ReturnScriptResult(char* results);
 
 private:
 char* pGameObject;
 JNIEnv *env;
 JavaVM *jvm;
 jobject gse;
 jclass cls;
 char currentResults[1024];
};
#endif

As you can see, I’ve got a couple of methods that do the heavy lifting, a couple of methods that do the initialization and dispatch, and some fields that keep around. The lack of GC really hurts me deep down and I would like to get rid of that hard-coded number in there, but I’ll do that later when I write a Java plugin API for the NWNX2 system so that you never have to write C++ plugins again :)

Now I write a script in my module that look like this:

void GroovyInit() {
 // Placeholder for ODBC persistence
 string sMemory;
 int i;
 for (i = 0; i < 8; i++) // reserve 8*128 bytes
 sMemory += “………………………………………………………………………………………………………………..”;

 SetLocalString(GetModule(), “GROOVY!MEMORY”, sMemory);
}

string GroovyScript(string script) {
 SetLocalString(GetModule(), “NWNX!GROOVY!” + GetStringUpperCase(script), “!”);
 SetLocalString(GetModule(), “NWNX!GROOVY!RESULT”, GetLocalString(GetModule(), “GROOVY!MEMORY”));
 return GetLocalString(GetModule(), “NWNX!GROOVY!RESULT”);
}

string GroovyScript2(string script, string argument) {
 SetLocalString(GetModule(), “NWNX!GROOVY!” + GetStringUpperCase(script), argument);
 SetLocalString(GetModule(), “NWNX!GROOVY!RESULT”, GetLocalString(GetModule(), “GROOVY!MEMORY”));
 return GetLocalString(GetModule(), “NWNX!GROOVY!RESULT”);
}

To understand this better you basically need to look at the plugin implementation, but in a nutshell, the NWNX2 code intercepts the SetLocalString() functions that start “NWNX!”, dispatch them to the plugin.so that matches the next token, with the parameter passed being the value in the set function. Additionally, since the parameter that gets passed in is the actual char* array from the server code you can just write over it, that’s why you see me Setting and the immediately Getting. The first method includes a non-empty string as the argument because NWNX2 doesn’t pass on calls with empty string arguments. It may be a bug with their code, I’m going to look at it. Now you also see where the 1024 came from, it is the size of the parameter that is passed in that we use as a buffer.

If anyone actually wants this thing, I might package it up for you.