OK, I'm so sorry for my delay in responding! Ok, so I think we have two main issues here.
- I think you may misunderstand the actual level of overhead involved in introspection and
- when I talk about a "wrapper", and you talk about a level of effort similar to writing a scripting language from scratch, it's clear that we aren't talking about the same thing
So let me just cover the introspection thing first.
Zukero wrote:It comes into play at pretty much every word. A simple line like player.addQuestProgress(new QuestProgress("quest_one", 10) would translates roughly to :
Code: Select all
Object param1 = "quest_one";
Object param2 = 10;
Object param3 = Class.forName("QuestProgress").newInstance(new Object[]{param1, param2});
Method m = player.getClass().getMethod("addQuestProgress", param3.getClass());
m.invoke(player, Object[]{param3});
And I removed the long list of try-catch blocks.
OK, so this makes sense to me and if Java were a purely interpreted or statically compiled language, I would say "well screw that!" However, it is a dynamically compiled language which gives it a distinct advantage in situations like this: it has the data at the time it compiles. Normally when a JVM executes a method, it will interpret it. This is actually a loose statement, what really happens is usually quite complex, depends upon several factors (how much CPU the programs threads are using and how much is available for background compiling, etc.) and is also vendor-specific to the JVM. But pretty much, if it's only going to run the method once or twice, it's going to interpret it. However, if it runs the function several times, it's going to optimize it. If it sees that a function is often called with the same data, it will create an optimized version of that function for that specific data set.
This can leave the JVM with several different compiled versions of a function, which can often take up more memory. The nice thing about this facet of the JIT in relation to introspection is that it can just eliminate the introspection entirely and just return the java method, field, class or object directly. Thus, if we ran some performance tests on this, I would bet it would end up surprisingly fast because the entire snippet above could end up being optimized as player.addQuestProgress(new QuestProgress(param1, param2)), moreso if it's called often or the JVM has been running for a non-trivial amount of time. Of course, this is where you insert the disclaimers "Actual mileage may vary. Void where prohibited.", etc. It depends upon the quality of the JVM and I would suspect that Google, with 50 billion-ish cash on hand has invested in writing a good JVM. Heck, they even decided to break with the standard to eliminate the one bottleneck that we've been able to criticize the JVM design with: being stack-based instead of register-based.
In reality though, this lua function would be represented as a object of a type from the Lua library who's "executeFunction()" method (or whatever it's called) would be the one most likely to receive these optimizations.
Zukero wrote:The C++ wrappers for Lua are indeed very efficient, but this is Java, and actually, it is more the interpreter that suffers than the wrappers.
Beirlis wrote:
The second thing that I have to wonder is that even if the introspection overhead is significant, is there a way to extend the pure-java Lua library with custom code to eliminate the introspection overhead (for objects of known types) and if so, would that be less effort?
IMHO, that would be even more work, as you'd have to a lot of regression testing, and try to understand the often optimized code, and the amount of code to write would be quite similar to the one needed to do it from scratch. Moreover, you would need to add special checks in the langugage to treat the special class differently than unknown ones, which would make using the unkown ones even worse.
So this is where I don't think we're talking about the same thing. I guess I'm going to have to actually dig into the java Lua implementations to confirm that the mechanisms that I would *assume* any "bolt-on" type of scripting language should have are indeed available. The idea is that you write some custom Java code to export some custom Lua objects to bypass the introspection. To me, your original example demonstrates the flexibility of a well-implemented bolt-on scripting language, by utilizing introspection at the cost of speed, code compactness, etc. So very,
very loosely (since I don't know a thing about the pure-java Lua library yet), I would think this would happen something like this:
Code: Select all
package com.gpl.rpg.AndorsTrail.lua;
import org.lua.or.whatever;
class ATLuaInterface extends SomeLuaClassThatAllowsYouToDefineAndExportObjectsIntoLua {
private static ATLuaInterface singleton = null; // unless you'll have more than one lua environment at a time.
public static void exportClass(SomeLuaEnvironemntClass lua) throws SomeLuaException {
if (singleton != null)
throw new Something();
lua.exportClass() // describe this class type to the Lua environment and construct an ATLuaInterface type in Lua that connects to this one.
singleton = new ATLuaInterface();
lua.exportObject(singleton);
}
public int someMethods() throws Whatever {...}
public com.gpl.rpg.AndorsTrail.World getWorld() throws Whatever {...}
// elsewhere you export the World class similarly so that no introspection is required
}
Again, I need to dig into the java Lua implementations to make sure that I'm not talking in vapor-ware, "pie in the sky" "I want a pony!"-isms. (yes, "I want a pony!"-isms is a plural noun to me

)
Zukero wrote:Beirlis wrote:
Designing a scripting language is no small endeavor. Designing and implementing one is an even greater task. Designing and implementing a good scripting language is exponentially more so. The idea of using "off-the-shelf" libraries in projects is to keep us focused on our project and spending less time on implementing the underpinnings. Mature scripting languages have faced the test of time and been debugged, debated, refined and improved many times over. I have to wonder if this is the design anti-pattern known as NIH ("Not Invented Here") -- one that I've fallen into a number of times in the past.
Actually, I WANTED to use an exisiting language, but simply found nothing suitable. Lua & Jython on android all use the same "android script" project, itself relying heavily on introspection. I also looked at beanshell, which is rather nice from a user perspective, but once again, implementation is based on introspection. What's more, a language is just a norm, what needs to have faced the tests of time & volume is the interpreter, and in this regard, the android versions of Lua & Jython are still very young, and not heavily used.
Designing and implementing a language is actually rather easy, using the right tools (I'm using JavaCC, similar in many ways to Yacc or Bison, except JavaCC is LL(k) while Yacc and Bison are LALR()).
Well, lexicographic parsers is admittedly not my forte and I've only worked with yacc/bison on a trivial level. None the less, I'm talking about a full-feature robust language with lots of libraries to add support for
JUNK like splitting and manipulating strings and arrays, and doing the common types of things that you'll find in the assortment of Lua libraries that others have written over the last 20 years. I seriously don't consider designing and implementing such a full-featured scripting language anything near trivial and anybody who does I would have to say is naive.
Zukero wrote:The hard part IMHO is building all the in-game hooks, especially in terms of what triggers a script, and how do you exchange data between Java and script. I am currently expanding the possibilities in this regard, as well as allowing wider access to Java objects' fields & methods, but generally providing a more natural API, hiding the technical details.
w00t! I agree with you for once!!

While having the ability to directly access the game objects *can* be good sometimes, it's certainly too darn complex and we often don't want to do that because we expect those writing scripts to not understand the nuances of our objects or the impact of changes they make on the game. Best if it can be made so that it's hard to crash the game from the script. Indeed, that is challenging because we design and implement the classes of the game for a different purpose than what we want exposed in the scripting environment.
So even if it turns out that Lua, Jython or some such is indeed a viable and attractive alternative to an AT-specific language and I manage to convince you guys of that, then it sounds like there is still a lot of value in your current endeavour that could be ported.
EDIT: Ok, so this is definitely possible for simple Lua functions, I'm not yet certain about doing so for Lua objects (complete with methods, etc.):
http://www.keplerproject.org/luajava/API/index.html
EDIT #2: Here we go, it looks like kahlua will do what I'm talking about. And even better, they use annotations!
https://code.google.com/p/kahlua/wiki/K ... ct_methods. So instead of my example above, it would be something more like:
Code: Select all
package com.gpl.rpg.AndorsTrail.lua;
import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.model.actor.Player;
//import se.krka.kahlua.*;
@LuaClass
public class AT {
private ControllerContext controller;
private WorldContext world;
private Player player;
@LuaConstructor(name="NewAT")
@Desc("returns a new Andor's Trail Interface object")
public AT() {
// populate fields
}
@LuaMethod(name="GetPlayer", global=false)
public Player healPlayer() {
return player; // Player also has to have annotations for exposed methods and fields.
}
// or a more practical, but limited method
@LuaMethod(name="HealPlayer", global=false)
public void healPlayer(@Desc("range 0-10000") int amount) {
player.hp += amount;
}
}