13

I'm developing an Android game in Java which uses Lua scripts. To execute these scripts, I'm using LuaJ with Java's ScriptEngine class. For example...

ScriptEngineManager sem = new ScriptEngineManager();
scriptEngine = sem.getEngineByExtension(".lua");
script = ((Compilable)scriptEngine).compile("some lua here");

However, this is apparently not supported on Android (something to do with android not having a full blown JVM, I read somewhere). Is there a way I can use Lua scripts on Android? Maybe there's a LuaJ alternative? Perhaps there is a way of compiling and executing Lua scripts using LuaJ directly (though I can't see how).

FYI, I see this error when I try to run this code on Android:

05-06 16:12:32.870: E/dalvikvm(17509): Could not find class 'javax.script.ScriptEngineManager', referenced from method unearth.levels.LevelReader.<init>
05-06 16:12:32.870: W/dalvikvm(17509): VFY: unable to resolve new-instance 787 (Ljavax/script/ScriptEngineManager;) in Lunearth/levels/LevelReader;
05-06 16:12:32.870: D/dalvikvm(17509): VFY: replacing opcode 0x22 at 0x0018
05-06 16:12:32.875: E/dalvikvm(17509): Could not find class 'javax.script.Compilable', referenced from method unearth.levels.LevelReader.parseScript
05-06 16:12:32.875: W/dalvikvm(17509): VFY: unable to resolve check-cast 782 (Ljavax/script/Compilable;) in Lunearth/levels/LevelReader;
05-06 16:12:32.875: D/dalvikvm(17509): VFY: replacing opcode 0x1f at 0x0047
05-06 16:12:32.875: W/dalvikvm(17509): VFY: unable to resolve exception class 788 (Ljavax/script/ScriptException;)
05-06 16:12:32.875: W/dalvikvm(17509): VFY: unable to find exception handler at addr 0x51
05-06 16:12:32.875: W/dalvikvm(17509): VFY:  rejected Lunearth/levels/LevelReader;.parseScript (Lorg/w3c/dom/Element;Lunearth/levels/Level;)V
05-06 16:12:32.875: W/dalvikvm(17509): VFY:  rejecting opcode 0x0d at 0x0051
05-06 16:12:32.875: W/dalvikvm(17509): VFY:  rejected Lunearth/levels/LevelReader;.parseScript (Lorg/w3c/dom/Element;Lunearth/levels/Level;)V
05-06 16:12:32.875: W/dalvikvm(17509): Verifier rejected class Lunearth/levels/LevelReader;
05-06 16:12:32.890: W/dalvikvm(17509): threadid=11: thread exiting with uncaught exception (group=0x40c331f8)
05-06 16:12:32.895: E/AndroidRuntime(17509): FATAL EXCEPTION: GLThread 1062
05-06 16:12:32.895: E/AndroidRuntime(17509): java.lang.VerifyError: unearth/levels/LevelReader
05-06 16:12:32.895: E/AndroidRuntime(17509):    at unearth.Game.startGame(Game.java:201)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at unearth.Game.update(Game.java:713)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at unearth.screens.LoadScreen.update(LoadScreen.java:56)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at unearth.UnearthListener.render(UnearthListener.java:71)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:423)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1462)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1216)
05-06 16:12:58.600: D/dalvikvm(17509): GC_CONCURRENT freed 334K, 3% free 16196K/16647K, paused 2ms+6ms

Update:

Maybe this project has some useful code? http://code.google.com/p/android-scripting/

4 Answers 4

13

I found a way of using LuaJ on Android! :-)

The key is to use the LuaJ API directly as opposed to through javax.script. It's a little tricky to figure out as there's no tutorials, so here's a before and after so that others don't have to go through picking through the LuaJ source code and JavaDoc. I really hope this helps someone as it drove me bonkers!

Note: "secretgame" isn't the actual name of my game ;-)

After:

package secretgame.scripting;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import org.luaj.vm2.LuaClosure;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import org.luaj.vm2.lib.jse.JsePlatform;

import secretgame.SecretGameException;
import secretgame.events.EventArgs;
import secretgame.levels.Level;

public class DirectLuaj implements Lua
{
  private final Level level;
  private final ScriptTools scriptTools;
  private final ScriptEvents scriptEvents;
  private int nextId = 0;
  private ArrayList<LuaClosure> scripts = new ArrayList<LuaClosure>();

  public DirectLuaj(Level level, ScriptTools scriptTools,
      ScriptEvents scriptEvents)
  {
    this.level = level;
    this.scriptTools = scriptTools;
    this.scriptEvents = scriptEvents;
  }

  @Override
  public int add(String scriptText) throws SecretGameException
  {
    try {
      InputStream input = new ByteArrayInputStream(scriptText.getBytes());
      Prototype p = LuaC.compile(input, "script");
      LuaValue g = JsePlatform.standardGlobals();
      LuaClosure c = new LuaClosure(p, g);
      scripts.add(c);
    }
    catch (IOException e) {
      throw new SecretGameException("compile failed", e);
    }

    return nextId++;
  }

  @Override
  public void run(int id, EventArgs args) throws SecretGameException
  {
    LuaClosure script = scripts.get(id);

    LuaTable bindings = new LuaTable();

    bindings.set("java", toLua(scriptTools));
    bindings.set("level", toLua(level));
    bindings.set("args", toLua(args));
    bindings.set("events", toLua(scriptEvents));

    script.setfenv(bindings);

    script.call();
  }

  private LuaValue toLua(Object javaValue) {
    return javaValue == null? LuaValue.NIL:
            javaValue instanceof LuaValue? (LuaValue) javaValue:
            CoerceJavaToLua.coerce(javaValue);
  }
}

Before:

package secretgame.scripting;

import java.util.ArrayList;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import secretgame.SecretGameException;
import secretgame.events.EventArgs;
import secretgame.levels.Level;

// sadly this won't work on Android because there's no such thing
// as javax.script in Dalvik ... dumb Android java :|
public class JavaxScriptingLua implements Lua
{
  private ScriptEngine scriptEngine;
  private final Level level;
  private final ScriptTools scriptTools;
  private final ScriptEvents scriptEvents;
  private int nextId = 0;
  private ArrayList<CompiledScript> scripts = new ArrayList<CompiledScript>();

  public JavaxScriptingLua(Level level, ScriptTools scriptTools, ScriptEvents scriptEvents)
  {
    this.level = level;
    this.scriptTools = scriptTools;
    this.scriptEvents = scriptEvents;

    ScriptEngineManager sem = new ScriptEngineManager();
    scriptEngine = sem.getEngineByExtension(".lua");
  }

  public int add(String scriptText) throws SecretGameException
  {
    try {
      CompiledScript script = ((Compilable)scriptEngine).compile(scriptText);
      scripts.add(script);
    }
    catch (ScriptException e) {
      throw new SecretGameException("could not compile lua.", e);
    }

    return nextId++;
  }

  public void run(int id, EventArgs args) throws SecretGameException
  {    
    Bindings bindings = scriptEngine.createBindings();

    bindings.put("java", scriptTools);
    bindings.put("level", level);
    bindings.put("args", args);
    bindings.put("events", scriptEvents);

    try {
      scripts.get(id).eval(bindings);
    }
    catch (ScriptException e) {
      throw new SecretGameException("could not run script.", e);
    }
  }
}
Sign up to request clarification or add additional context in comments.

Comments

4

Instead of LuaJ you can (also) use LuaJava together with Lua compiled for Android using the NDK. This allows Lua full access to whatever is available from Java. Take a look at this example to see how to get started.

Lua 5.1.4 + LuaJava compiled are provided in the libs directory (a single libluajava.so file) if you do not want to fiddle with the NDK, but it is relatively easy to set up anyway.

1 Comment

Kudos on AndroLua but I found a way of using LuaJ on Android, so that way I don't have to re-write my existing code. See my answer for how I solved the problem.
1

To use Lua on Android, you can use JNLua + Lua 5.2 (implemented in C). The only Android port with JSR 223 (javax.script) support is here: https://github.com/danke-sra/jnlua-android

Using this port, you can do what you want:

ScriptEngine scriptEngine = new LuaScriptEngineFactory().getEngine(); scriptEngine.eval("lua statements");

Comments

1

However, this is apparently not supported on Android

Correct.

something to do with android not having a full blown JVM, I read somewhere

Correct. If you look at the JavaDocs, you will see that javax.script does not exist in the Android SDK.

Maybe I'll give this a try: https://github.com/damonkohler/sl4a

That is one option. Another is to embed the Lua interpreter in your app via the NDK, as in this sample application.

1 Comment

Thanks for the answer -- but I managed to get LuaJ working on Android anyway. See my answer if you're interested.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.