Thursday, 28 February 2013

Java Scripting?

Well to "relax" I did some reading up on JavaScript from Java ... and to be honest it's "bloody awesome". I've done plugin languages and extensions before in C and Java so i'm no stranger to the binding issue, but this makes it almost "zero work" if you already have well defined objects - which should be a goal anyway.

Anyway, it's tidy and 'included', which is nice - no wonder Oracle is trying to modularise Java, it's getting big.

As one might know from reading this blog i'm not a huge fan of writing applications in JavaScript ... but it does have it's uses and this one seems like a perfect fit.

One of the last major components of the original Dusk I haven't already gutted is the scripting language - but it's now on the chopping block too. One reason is the code is pretty opaque and difficult to maintain, another is that the 'scripts' are the whole game, the finally it is a reasonable enough implementation (compact pre-compiled code) and still works. But the thought of extending it to support planned new features is a bit unpleasant.

I think breaking the whole existing part-game is worth it in the long run. And efficiency isn't much worth if it isn't usable - and computers are somewhat faster than they were 15 years ago.

It's also what I would call a "marketing" or "management" feature: JavaScript has a huge following and a ton of resources, whereas trying to learn the arcane and obtuse syntax of Dusk's script engine is not going to be high on anyone's list of things to do before they die.

I played with it a bit more and realised you can basically do everything you could do in Java from any JavaScript script, including file and network i/o ... so it looks like i'll have to delve into SecurityManager. I've always avoided it as the documentation is obtuse and bereft of any useful examples - and we all know how complexity is such a winner when security is at issue. I'll also have to work out using it in a multi-threaded context, if that's possible.

Well I suppose life wasn't meant to be easy.

Time passes.

Ahh on threading it looks like everything has to be from the same doPrivileged() call. I chose to create a threadpool and submit jobs to that, and use Future to get the results if required. Seems to be a reasonable compromise. I've implemented a watchdog timer too, but because the ScriptEngine has no way of interrupting or stopping execution (should be something on the Context?) I'm forced to use the dangerous "Thread.stop()". Well what can you do eh? I guess it's more than a "few lines of code", but on the other hand I have a nice easy to use - and probably safe - script mechanism.

This deserves more of a write-up but I was supposed to have an early minute today so it will have to wait.

Update: I had more of a look at scripting including the SecurityManager aspects and documenting the current script system and how it's used.

Unfortunately the sandboxing turns out to be much more involved than I wanted. I can fairly easily do a basic sandboxing such as preventing disk and network i/o outside of prescribed locations (no thanks to the documentation; it's unreadable shit), but beyond that things get quite tricky. One reason is the rhino engine itself needs to do things like reflection in order to execute any script.

I experimented with some nasty stuff like scanning the stack-trace for 'legal' accesses, but I dunno, it seems complex enough that I wouldn't trust it. So for the moment my initial solution will stick with the simple high-level solution of restricting file and network access and put some trust that the players that reach a high enough level to create scrips that will behave themselves - as one did in the past.

I've still yet to come up with the solution i want for how the scripts should be structured. I've thought about everything from a simple global-context scripts to having the entire mob persistent state exist within local variables on objects within the JavaScript engine and all scripts merged into a single pre-compiled programme. Something in-between will probably be the result - as usual - but I need to implement at least a couple of ideas and see how they work in practice. One desires as much flexibility and simplicity as possible, but you can't maximise both at the same time. I initially thought of simply having global-state inline code, but that doesn't allow the use of 'return'. So now i'm thinking more of defining a function within the code that matches the filename. This also allows pre-compilation. And certain objects such as things or items require a number of related scripts, so it probably makes sense to wrap them in an object and just call methods on that.

The existing script language is pretty weird. Prefix notation for operators and effectively everything is just a function (which parses itself - the syntax is not parse-able without semantic information). Might take a while to grok and convert the scripts - a compiler which converts the scripts probably isn't worth the effort. One detail I hadn't previously been aware of is the number of general player commands that are implemented as scripts - and the complexity thereof. Most of the other scripts are fairly simple or at least straightforward. It's not an absolute necessity to convert all of them or even any of them - but having an existing game working is a good test-bed.

There are also some scripts which don't really do much and simply define global "game-theme" behaviour - yet need to run very often. I will look into whether these can be instead be implemented by Java code and supplied at run-time via a plugin/extension mechanism. This still allows for customised behaviour without recompiling the whole application, together with better space and time performance.

No comments: