Scripting with Nashorn

I’ve recently discovered a Java 8 JavaScript engine called Nashorn, has anyone thought of using it for scripting?

I believe the people working on Skylimit Tycoon are, though I could be wrong.

1 Like

I looked into it once. It didn’t fit my (extremely specific) needs, but for 99% of uses it would probably be a decent or very good fit for devs who like scripting with JS. If you’re leery of adding dependencies to your project it also has the advantage of being built into JDK 8. Personally I’d be more inclined to use either Groovy or Jython as my scripting engine since I’m not keen on JS, but that’s purely a matter of taste.

ok, cool

Even java6 or something has javascript.
See “jsr Script engine”, its a Interface which all Script Engines implement so you can swap them out at real time.

Or use groovy, you can even Compile java with it.

I recommend this over javascript…

Says the guy with a web background :stuck_out_tongue:
But if you follow the jsr you can use all Script Engines anyway.

Or write groovy as if it was js. But I like to be able to Script in Java when it comes to complicated things

Well exactly…

Also, one nice distinction is that groovy classes actually ARE Java classes. A groovy class can implement a Java interface and just be handed around. No special adapting needed. A groovy class also can be reflected just like a Java class, etc… because it IS a Java class.

That’s what they mean when they say “Groovy is Java”. Javascript, while nice, can never really have that advantage.

1 Like

I’m using it. It’s great :frog:

It’s the same with nashorn.

1 Like

Yes, @DannyJo and I use it for Skylimit Tycoon when we want to do callbacks etc. We think it would make it easier for people to mod the game later, than exposing Java code.

I think it works quite well and there are quite many people that are able to write JavaScript.

The performance is good once you’ve imported all your scripts, since they are compiled into byte code and are executed as normal Java code.

Some example code:

public static ScriptEngine createScriptEngine() {
    return new NashornScriptEngineFactory().getScriptEngine("-strict", "--no-java");
}

public static void loadScript(final ScriptEngine scriptEngine, final InputStream inputStream) throws IOException {
        try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
            scriptEngine.eval(reader);
        } catch (ScriptException e) {
            throw new IOException(e);
        }
}

public static <C> C getInterface(final ScriptEngine scriptEngine, final String objectName, final Class<C> clazz)
        throws ScriptException {
    final Invocable invocable = (Invocable) scriptEngine;

    final Object def = scriptEngine.get(objectName);
    if (def == null) {
        throw new ScriptException("Missing object " + objectName);
    }

    final C object = invocable.getInterface(def, clazz);

    if (object == null) {
        throw new ScriptException("Failed to create instance for " + objectName);
    }

    return object;
}

Using an interface like

public interface ScenarioCallback {
    public boolean isLevelUnlocked(int level);
}

Javascript code in scenario.js

var Scenario1 = new Object();

Scenario1.isLevelUnlocked = function(level) {
    return true;
};

Use callbacks from Java

ScriptEngine scriptEngine = createScriptEngine();

loadScript(scriptEngine, getClass().getResourceAsStream("/scenario.js"));

ScenarioCallback scenarioCallback = getInterface(scriptEngine, "Scenario1", ScenarioCallback.class);

if(scenarioCallback.isLevelUnlocked(7)) {
    // You win
}
2 Likes

By the way, what I mean by “groovy is Java”…

Foo.groovy

class Foo {
    String bar;
}

MyClass.java

Foo foo = new Foo();
foo.setBar("Hello, world.");

…I’m pretty sure you cannot do that in Javascript but it would be cool to be wrong.

With exceptions.

MyVar var = new MyVar (); wont work because of some property Problems (i raised an issue at the groovy issue tracker, it’s by Design)

Weird… it’s never been an issue for me. I’ll have to check to see if I still have code like that and if it’s broken now.

If you want to i can look for it.
The issue is if you dont use def, something might get misinterpreted.

I now remember though that this is only for camelCase Class Names which you wont have.

Edit: https://issues.apache.org/jira/browse/GROOVY-8044

Personally I wrote my own scripting stuff using yaml mark up.

Respectfully, I’m trying really hard to think of any good reason to do this other than “because I wanted to see if I could”. If that’s the reason then good job. It’s a tricky problem.

But I can’t recommend that approach to anyone generally.

While we could argue the merits of different approaches, a hand-built scripting language based on yaml easily ranks pretty high on the “bad ideas” list. All of the downsides of rolling your own scripting language along with the downsides of the extra object abstraction of yaml.

So far all of the “But it’s…” style objections I can think of have much better answers.

Maybe it’s too many years modding games where those game devs decided to “roll their own” scripting… that turned into a buggy unsupportable mess for those of us trying to mod with it. It’s a sort subject for me. We’re already in Java that’s got full blown scripting literally built right into it. Two lines of code to run a script.

Edit: shout out to any fellow Neverwinter Nights modders who know what I’m talking about…

Afaik it’s not possible, but you can extend java classes or implement interfaces and then pass the implementation back to java, that is usually what you do in scripting rather than creating arbitrary classes.

It always depends on the use case, I think.
I know Paul hates XML “but it’s” sometimes just about appropriate to be used.

For example when you have some hiearchical structure (e.g. when you would modify a skill tree where each skill can evolve into several child trees, or even for some logic: If you don’t want an arbitrary script to be run you can roll some “own language” which can define some control blocks which your logic will then run. This might also be desired for some kind of trees, which you don’t want to re-iterate each frame (by following the if’s) but building up a tree and tracking the state internally.)

But yeah for some real modding interfaces with full access there is nothing like interacting with the code as if it was yours. Especially the ability to simply subclass things to only modify a partial behavior and such.

But we derailed. Use the JSR as you can see in Kecons Code and then you can use multiple engines with the same code. Or use Groovy/Kotlin for “real” code (without stuff like getInterface, …)

In general, I agree with you… but I can chime in for one “why.” I’m designing a massively distributed online environment. Different “chunks” of a single, massive, contiguous world run on different servers. Entities in the world are scripted, and any entity may cross between different servers at any point in time. To make matters worse, entity scripts may be edited by users at runtime, so strict sandboxing (including memory and processor use limiting) is an absolute must. With the right AST transforms, I may have been able to compile Groovy code that would fit these requirements (I may explore this option again - developing scripting engines is a pain), but for now I have two different scripting engines in the works, both of which are designed with the strict sandboxing I mentioned in mind. One is currently functional (albeit not yet thoroughly tested), the other (which runs a significantly more advanced scripting language) is still being designed. The trouble with traditional scripting engines is that it’s notoriously difficult to deal with memory limits and suspending and resuming execution at arbitrary points in the code. Both are possible to do with (extensive) bytecode instrumentation or AST transforms, but at the end of the day I decided that it would be easier, safer, and less error-prone to write my own bytecode interpreters that support arbitrary suspend/resume of execution and memory limiting. Both engines, when complete, will be able to take a script running in an infinite loop, schedule it fairly with other scripts, keep memory usage within a specified bound, and transfer the running script between servers.

That being said… developing one’s own scripting engine is a tremendously arduous undertaking. 99.999% of projects would probably be far better off using one of the existing ones. I just got to be the lucky sucker with a project that falls in the 0.001% of projects that need very special attention. :wink: