Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (492)
Games in Android Showcase (112)
games submitted by our members
Games in WIP (556)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  AI Scripting for Players?  (Read 10867 times)
0 Members and 1 Guest are viewing this topic.
Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Posted 2009-10-19 21:16:40 »

I am thinking about adding AI scripting functionality to my game (http://www.ageofconquest.com). The goal is to let players create their own AI for the game. The game is turn-based, so the AI script will be called every time a new game turn is calculated.

AI Input:
- a game object
AI Output:
- a list of actions to execute on the game
Constraints:
- Required: AI Script can be executed in Java (platform independent)
- Required: prevent malicious code (e.g. prevent access to file system etc.)
- Desired: limit AI functionality to just reading the game object and creating actions
- Desired: AI editor with syntax coloring (what's good?)

What's the best strategy to create AI scripting for players? Use an existing language? Which one? Or roll my own?

Let me know if my question is not clear. Thanks  Smiley






Offline Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #1 - Posted 2009-10-19 22:00:54 »

Probably the easiest (for you) is to launch a separate sandboxed JVM and kill it after a second, if it hasn't terminated yet. You simply serialize some of your game objects, and load them in 'their' JVM (as read-only as it gets, no matter what they write/modify, it won't alter your real game JVM).

It might sound stupid, but it's like 30 minutes to implement, and that's it! Compare that to anything else.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #2 - Posted 2009-10-19 22:57:45 »

How about launching the code in a separate thread instead of a sandboxed JVM? I would kill the thread if it doesn't finish after a second (like you proposed for the JVM). I can pass in a copy of the game object. It wouldn't matter if the AI would modify the game object copy.

Now, how can a prevent the AI code to access the file system/doing malicious stuff? If I load the AI script via a modified ClassLoader, would I be able to prevent the bad from happening?

How about the following for a start Huh
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
...
// Contains the actual Java source code for the AI
String code = ...;

// this is the copy of our game
Game gameCopy = game.copy();

// execute script in separate monitored thread
Thread scriptRunner = new Thread() {
  public void run() {
    // compile and load script via custom class loader
   Script script = ScriptClassLoader.compileAndLoad(code);

    // obtain the actions
   ActionList actions = script.createActions(gameCopy);
   
    // store the actions
   database.store(actions);
  }
}
scriptRunner.start();

// wait for 1 second script to terminate
Thread.sleep(1000);
if (scriptRunner.isActive()) {
  scriptRunner.interrupt();
}
...


Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #3 - Posted 2009-10-20 00:38:29 »

Actually, Riven, for your TinyCode competition, what did you do to prevent people from hacking your server?

TinyCode Competition
http://www.java-gaming.org/topics/tinycode-competition-trial/17591/30/view.html

Offline Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #4 - Posted 2009-10-20 06:08:21 »

 Smiley I didn't allow any capticals in the sourcecode (also checking unicodes -- \uHEXX) and I stop()ed the Thread after 100ms. I remember Abuse came up with some obscure class in the JRE that started with a lowercase character, that did something really bad, and I limited the max source length.

So... not really a useful strategy to you, unfortunately.

FYI:   scriptRunner.interrupt(); does nothing. you really must use stop() which will not properly release mutexes.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #5 - Posted 2009-10-20 07:32:22 »

http://www.java-gaming.org/topics/scripting-for-games-using-java/21136/msg/172468/view.html#msg172468

Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #6 - Posted 2009-10-20 09:19:44 »

In regards to security: did you try to use the SecureClassLoader in Java when loading and executing classes? I think it would limit access to system resources for the class???

In regards to Pnuts: it looks cool, but how is it possible to pass in values/objects?

Offline Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #7 - Posted 2009-10-20 12:57:57 »

In regards to security: did you try to use the SecureClassLoader in Java when loading and executing classes? I think it would limit access to system resources for the class???

Well, if you look at the classes in java.io.* and java.net.*, you'll see that those permissions are only checked if there is a SecurityManager installed. That SecurityManager will interfere with your game too. Hence the idea of launching an external (sandboxed) JVM.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline DzzD
« Reply #8 - Posted 2009-10-20 17:44:37 »

maybe a stupid idea...dono....  cant you use a custom class loader (force it to the user script) ? and only allow safe classes by checking if it is in your safe classes a list ? dont know hw it is possible but for example your script could only allow getSafeClass("Name") and prevent any Blabla.something() or new Something(), just a search and replace in the user source.

EDIT:

something like :

"Something." => not enabled if Something not in safe list
"new "=> not enabled
getClass().newInstance() => replaced at compil time by getSafeClassInstance();

Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #9 - Posted 2009-10-20 20:10:47 »

I dug deep and found the AccessController:
http://java.sun.com/j2se/1.5.0/docs/api/java/security/AccessController.html

Here is how it would look:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
   somemethod() {
         AccessController.doPrivileged(new PrivilegedAction() {
              public Object run() {
                 // Code goes here. Any permission checks within this
                // run method will require that the intersection of the
                // callers protection domain and the snapshot's
                // context have the desired permission.
             }
         }, context);
         ...normal code here...
   }

context is AccessControlContext with the access permissions.

I think that should do it? The context would be very limited to prevent access to file system etc. Or is my assumption wrong?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline zingbat

Senior Member




Java games rock!


« Reply #10 - Posted 2009-10-20 20:17:28 »

What i tested some days ago was to use Janino to dynamically compile scripts and run them with a SecurityManager that only permits using your game classes. you can't hide classes but you can prevent moders from using them and with Janino you can tell it to compile only class bodies and prevent the use of the import instruction.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #11 - Posted 2009-10-20 20:22:54 »

What i tested some days ago was to use Janino to dynamically compile scripts and run them with a SecurityManager that only permits using your game classes.
You can't do that with a SecurityManager.

you can't hide classes but you can prevent moders from using them and with Janino you can tell it to compile only class bodies and prevent the use of the import instruction.
You can use fully qualified class names.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #12 - Posted 2009-10-20 21:21:03 »

In regards to Pnuts: it looks cool, but how is it possible to pass in values/objects?
1  
2  
3  
4  
5  
Package pkg = new Package();
pkg.set("someVariable".intern(), "any java object");
pkg.setConstant("someConstant".intern(), "any java object");
Context context = new Context(pkg);
Pnuts.load(scriptStreamUrlReaderOrString, context);


You can control whether scripts are interpreted, compiled, cached, etc with context.setImplementation:
http://pnuts.org/apidoc/pnuts/lang/PnutsImpl.html
Also, context.setVerbose is useful, and I like to call context.setWriter(new PrintWriter(System.out, true)); (the default PrintWriter doesn't autoflush for whatever reason).

You can also pass in Pnuts functions in addition to objects:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
pkg.setConstant("moo".intern(), new PnutsFunction () {
   public boolean defined (int nargs) {
      return nargs == 1;
   }

   protected Object exec (Object[] args, Context context) {
      if (!defined(args.length)) undefined(args, context);
      String someString = (String)args[0];
      // Do stuff and return stuff.
     return null;
   }

   public String toString () {
      return "function moo(String)";
   }
});

Another way to provide functionality to your scripts is the Pnuts module system. It is powerful and easy to use.
http://pnuts.org/1.2.1/snapshot/20070724/doc/lang.html#modules

Here is how you'd use a modified classloader with Pnuts, as kingaschi mentioned:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
context.setClassLoader(new ClassLoader() {
   protected synchronized Class<? > loadClass (String name, boolean resolve) throws ClassNotFoundException {
      if (name.startsWith("com.example.your.game.")) {
         Class c = getParent().loadClass(name);
         if (resolve) resolveClass(c);
         return c;
      }
      throw new ClassNotFoundException("Unable to load class: " + name);
   }
});


That seems a pretty secure way to prevent malicious classes from being loaded, but I would definitely hesitate to claim it is bulletproof without a lot more thought.


Offline Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #13 - Posted 2009-10-20 22:33:01 »

From your point in your initial post:
- Required: prevent malicious code (e.g. prevent access to file system etc.)

So I assume the code will be running on the server, or in remote clients.



Where ever your potentially malicious code is running, would you really think the best solution is to 'plug all security holes you find' ? You'll be surprised how much effort people put into trying to find a vulnerability. In the end it is your game, and it is your responsibility that nobody gets they files corrupted or their creditcard number stolen.

My advice: play it safe and launch 1 sandboxed JVM for each AI player. In that sandboxed JVM you can kill threads each turn (if needed), for each player.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #14 - Posted 2009-10-20 23:42:35 »

1  
2  
3  
4  
5  
Package pkg = new Package();
pkg.set("someVariable".intern(), "any java object");
pkg.setConstant("someConstant".intern(), "any java object");
Context context = new Context(pkg);
Pnuts.load(scriptStreamUrlReaderOrString, context);

Thanks, that looks like this could work?

Let me see if I understand it correctly?
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
// 1. retrieve the game object to manipulate
Game game = ...

// 2. obtain Pnuts script (throws ParseException for syntax errors)
Pnuts script = Pnuts.parse(sourceCode);

// 3. execute script on game object
Package pkg = new Package();
pkg.set("game".intern(), game);
Context context = new Context(pkg);
script.run(context);


Question: script.run can manipulate the game object I passed in??? I.e. after the script is done, the game object has changed?

Offline SimonH
« Reply #15 - Posted 2009-10-20 23:56:34 »

Or roll my own?
I did a serious 3 month project on dynamic scripting (ie an agent's script changes depending on its circumstances) and I found that home-brew was best. No compiler security issues, easier to read script (because it's dedicated to the context), and no WTFs from rhino, lua, pnuts &c...


People make games and games make people
Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #16 - Posted 2009-10-21 00:22:14 »

script.run can manipulate the game object I passed in??? I.e. after the script is done, the game object has changed?
If your game object has methods that can be called to change the internal state of the object, then yes, the script could have modified it. Either don't provide those methods or make a copy. It is possible to intercept method invocations and other things using a Configuration and context.setConfiguration. This might be used in addition to the classloader.

For simpler scripting there is fscript:
http://fscript.sourceforge.net/

Riven makes a good point. How are you going to be certain you are really secure? If mostly secure is good enough, the classloader is probably fine.

Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #17 - Posted 2009-10-21 01:34:59 »

Quote
If your game object has methods that can be called to change the internal state of the object, then yes, the script could have modified it.
Yes, that's what I actually want! Seems like Pnuts is the way to go. So, my assumptions from above how to create and run a script are basically correct?

I also checked FScript which looks nice but is probably too limited? Then rolling my own would take lots of time to implement. My goal isn't to create a scripting language but to add one to my game. Using an existing solution is probably the most feasible solution.

Once thing I noted about Pnuts is, it doesn't have any updates since 2007? Is there an official forum for it too?


Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #18 - Posted 2009-10-21 02:57:10 »

Yeah, the code you outlined should do what you expect.

Honestly, you could use any JVM scripting language (Rhino, JRuby, Jython, Groovy, blah). I like Pnuts a lot though! It has nice syntax that doesn't stray too far from Java (compared to the ridiculousness in Groovy). I also like that the Pnuts type system is the Java type system, many languages converted to run on the JVM come with their own type system.

Development on Pnuts started in 1997. It is stable and actively maintained. The java.net Pnuts mailing list is the best way to ask questions or discuss bugs. Any bugs found are usually fixed with 24 hours. You might check out this page:
http://pnuts.org/articles/pnutsHighlights.html

Offline Wrist Radar

Junior Newbie





« Reply #19 - Posted 2010-02-06 12:01:09 »

What's the best strategy to create AI scripting for players? Use an existing language? Which one? Or roll my own?
hi! I don't know if someone already told you, but if JavaScript is ok for your purposes you can use Java Scripting technologies: http://java.sun.com/javase/6/docs/technotes/guides/scripting/index.html

I'm making a game that uses it.

code example:
1  
2  
3  
4  
String scriptCode = ... ; // contains JavaScript code
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript");
scriptEngine.put("out", System.out);
scriptEngine.eval(scriptCode)


script example:
1  
out.println("Hello, world!");


very easy Smiley
Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #20 - Posted 2010-02-06 22:38:13 »

I am aware of JavaScript for Java. As I am targeting Java 5, scripting is not available there yet. Also, the current version doesn't prevent people from executing malicious code. I am planning to run it on my server, so limiting script execution is quite important!

Anyhow, I am going with Pnuts. I got everything to work nicely  Grin

Offline Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #21 - Posted 2010-02-06 22:42:35 »

I am aware of JavaScript for Java. As I am targeting Java 5, scripting is not available there yet. Also, the current version doesn't prevent people from executing malicious code. I am planning to run it on my server, so limiting script execution is quite important!

Anyhow, I am going with Pnuts. I got everything to work nicely  Grin

Rhino is independent of Java 6 scripting APIs, and with a proper ClassShutter implementation, it's as secure as it gets. I bet the players will find JavaScript easier than Pnuts. But heck, I should mind my own business!

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline DzzD
« Reply #22 - Posted 2010-02-06 23:04:26 »

I am aware of JavaScript for Java. As I am targeting Java 5, scripting is not available there yet. Also, the current version doesn't prevent people from executing malicious code. I am planning to run it on my server, so limiting script execution is quite important!

Anyhow, I am going with Pnuts. I got everything to work nicely  Grin
did not kneew about Pnuts, It look really great

@Riven : your sig is every days more funny Smiley, what next ? oldness of the user ? credit card number  ?  Grin

Offline noblemaster

JGO Ninja


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #23 - Posted 2010-02-07 00:12:53 »

Yeah, Rhino might work!  Grin

Anyhow, Pnuts is very close to Java, so I'll stick with it for now. Also, I already implemented & tested Pnuts, so I'll defer other scripting languages to later...

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Nickropheliac (16 views)
2014-08-31 22:59:12

TehJavaDev (23 views)
2014-08-28 18:26:30

CopyableCougar4 (33 views)
2014-08-22 19:31:30

atombrot (42 views)
2014-08-19 09:29:53

Tekkerue (41 views)
2014-08-16 06:45:27

Tekkerue (35 views)
2014-08-16 06:22:17

Tekkerue (26 views)
2014-08-16 06:20:21

Tekkerue (37 views)
2014-08-16 06:12:11

Rayexar (73 views)
2014-08-11 02:49:23

BurntPizza (49 views)
2014-08-09 21:09:32
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!