This code is an attempt to add generic scripting to the jME project, through hooks into the GameConsole class, as well as creating Spatial controllers that are implemented through scripts.
By default the ScriptCommandProcessor will evaluate whatever text you enter in the scripting language set (By default Pnuts). You can register objects from your application to be available to the scripts by calling the register(string, obj). IE:
GameConsole console = new GameConsole(KeyInput.KEY_GRAVE, rows, true); ScriptCommandProcessor scriptProcessor = new ScriptCommandProcessor(console); scriptProcessor.register("console", console); scriptProcessor.register("game", game); scriptProcessor.register("state", debug); scriptProcessor.register("box", box);
The ScriptCommandProcessor provides the following highlevel commands commands:
For example in the TestScriptConsole.java we register the box to the ScriptCommandProcessor:
...
scriptProcessor.register("box", box);
...
Then on the console we type:
addController box spinController.js
This will add a ScriptController implemented by the spinController.js file (It implements it by defining a “function update(time)” function):
importPackage(Packages.com.jme.math); var incr = 1.0; var rotVec = new Vector3f(0.5, 0.5, 0.0); var rotQuat = new Quaternion(); var angle = 0.0; /** * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 678 $ * $Id: spinController.js 678 2007-03-31 23:41:40Z dougnukem $ * $HeadURL: https://captiveimagination.com/svn/public/cigame/trunk/src/resources/scripts/js/spinController.js $ */ function update(time) { if(console!=null) console.log("time="+time); else System.out.println("time="+time); //update the angle angle = angle + incr; if (angle > 360) { angle = 0; } rotQuat.fromAngleNormalAxis(angle * FastMath.DEG_TO_RAD, rotVec); //controlled is special binding referencing the Spatial object being controlled. controlled.setLocalRotation(rotQuat); //print('spinController update: ' + angle); }
http://jme-game-console.googlecode.com/svn/trunk/spinController.js
Then to remove it the user can type in the console:
removeController box
/** * */ package test; import com.captiveimagination.game.console.*; import com.captiveimagination.game.console.command.*; import com.captiveimagination.game.console.script.ScriptCommandProcessor; import com.jme.bounding.*; import com.jme.image.*; import com.jme.input.*; import com.jme.math.*; import com.jme.scene.shape.*; import com.jme.scene.state.*; import com.jme.util.*; import com.jmex.game.*; import com.jmex.game.state.*; /** * @author ddaniels * */ public class TestScriptConsole { public String exit() { return "I would exit, but I don't know how."; } public void quit() { System.exit(0); } public static void main(String[] args) throws Exception { StandardGame game = new StandardGame("Test Script Console"); game.getSettings().clear(); game.start(); TextureState ts = game.getDisplay().getRenderer().createTextureState(); Texture t = TextureManager.loadTexture(TestScriptConsole.class.getClassLoader().getResource("jmetest/data/texture/cloud_land.jpg"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear); t.setWrap(Texture.WrapMode.Repeat); ts.setTexture(t); Box box = new Box("Box", new Vector3f(), 10.0f, 10.0f, 10.0f); box.setModelBound(new BoundingBox()); box.updateModelBound(); box.setRenderState(ts); BasicGameState debug = new BasicGameState("Basic"); debug.getRootNode().attachChild(box); debug.getRootNode().updateRenderState(); GameStateManager.getInstance().attachChild(debug); debug.setActive(true); int rows = 5; GameConsole console = new GameConsole(KeyInput.KEY_GRAVE, rows, true); // GameConsole console = new GameConsole(KeyInput.KEY_GRAVE, rows, true, // new float[]{8, 8, 8, 8}, // new Vector2f(100, -16), // new Vector2f(100, GameConsole.calculateHeight(rows) + 16), // DisplaySystem.getDisplaySystem().getWidth() - 200); BasicCommandProcessor processor1 = new BasicCommandProcessor(); processor1.registerCommand(new TestScriptConsole()); //ScriptCommandProcessor automatically registers itself to the "script" mode ScriptCommandProcessor scriptProcessor = new ScriptCommandProcessor(console); scriptProcessor.register("console", console); scriptProcessor.register("game", game); scriptProcessor.register("state", debug); //scriptProcessor.importPackage("com.jme.math.Vector3f"); scriptProcessor.register("box", box); JavaCommandProcessor processor2 = new JavaCommandProcessor(console); processor2.register("console", console); processor2.register("game", game); processor2.register("state", debug); processor2.importPackage("com.jme.math.Vector3f"); processor2.register("box", box); console.registerCommandProcessor("command", processor1); console.registerCommandProcessor("java", processor2); GameStateManager.getInstance().attachChild(console); console.setActive(true); } }
http://jme-game-console.googlecode.com/svn/trunk/test/test/TestScriptConsole.java
/** * */ package com.captiveimagination.game.console.script; import java.util.HashMap; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptException; import javax.script.SimpleBindings; import com.captiveimagination.game.console.GameConsole; import com.captiveimagination.game.console.command.CommandProcessor; import com.captiveimagination.game.script.Script; import com.jme.input.KeyInput; /** * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 685 $ * $Id: ScriptCommandProcessor.java 685 2007-04-03 23:46:57Z dougnukem $ * $HeadURL: svn://captiveimagination.com/public/cigame/trunk/src/com/captiveimagination/game/console/script/ScriptCommandProcessor.java $ */ public class ScriptCommandProcessor implements CommandProcessor { public static final int DEFAULT_ROWS = 5; public static final String SCRIPT_MODE_CMD = "script"; private GameConsole gameConsole ; private ScriptEvalCommandProcessor sp; private HashMap<String, CommandProcessor> highLevelCommands = new HashMap<String, CommandProcessor>(); public ScriptCommandProcessor(GameConsole console) { gameConsole = console; sp = new ScriptEvalCommandProcessor(console); //User types "mode script" to enter scripting mode. gameConsole.registerCommandProcessor(SCRIPT_MODE_CMD, this); //Use the default high level commands. setHighLevelCommands(getDefaultHighLevelCommands(console)); } /** * Creates the default high level commands. * * TODO: Populate this using some kind of external data. * * @return */ private HashMap<String, CommandProcessor> getDefaultHighLevelCommands(GameConsole console) { HashMap<String, CommandProcessor> defaultCommands = new HashMap<String, CommandProcessor>(); SetScriptLanguageCommand setScriptCmd = new SetScriptLanguageCommand(console, this); ScriptFileExecuteCommand scriptExecCmd = new ScriptFileExecuteCommand(console, this); AttachScriptControllerCommand scriptAttachController = new AttachScriptControllerCommand(console, this); //COMMAND create setLang <language> command defaultCommands.put(setScriptCmd.getCommandText(), setScriptCmd); //COMMAND create file <script.ext> command defaultCommands.put(scriptExecCmd.getCommandText(), scriptExecCmd); //COMMAND addController <Spatial> <scriptFile.ext> command //Has a list of commands for different functions, like adding and removing script controllers. for(String cmd : scriptAttachController.getCommands()) { defaultCommands.put(cmd, scriptAttachController); } return defaultCommands; } private GameConsole getConsole() { return gameConsole; } /** * Adds the following CommandProcessors to be handled by this CommandProcessor. * @param newHighLevelCommands */ public void setHighLevelCommands(HashMap<String, CommandProcessor> newHighLevelCommands) { this.highLevelCommands.putAll(newHighLevelCommands); } /** * Clears the high level commands. * */ public void clearHighLevelCommands() { this.highLevelCommands.clear(); } /* (non-Javadoc) * @see com.captiveimagination.game.console.command.CommandProcessor#execute(java.lang.String, com.captiveimagination.game.console.GameConsole) */ public void execute(String command, GameConsole console) { //Pull off the first argument split by the spaces String firstArg = command.split("\\s")[0]; if(isHighLevelCommand(firstArg)) { processHighLevelCommand(firstArg, command, console); } else { //Delegate to the script evaluator sp.execute(command, console); } setPrompt(console); } /** * @param console */ private void setPrompt(GameConsole console) { String curLang = sp.getLanguage(); console.setPrompt(curLang+">"); //sp.getScriptFactory().getEngineName(); //console.log("<Script Language: "+curLang+">"); } /** * Checks if the executing command should be handled * by a high level registered CommandProcessor. * * @param cmd * @return */ private boolean isHighLevelCommand(String cmd) { return highLevelCommands.containsKey(cmd); } /** * Handles a high level registered command by delegating to a registered * CommandProcessor. * * @param cmd */ private void processHighLevelCommand(String cmd, String params, GameConsole console) { highLevelCommands.get(cmd).execute(params, console); } /** * Allows the Script Command processor to bind objects from your * application to the script engine so that scripts can access them. * * @param string * @param console */ public void register(String string, Object objVal) { sp.register(string, objVal); } /** * Sets the prompt to display the scripting language on activate. * @see com.captiveimagination.game.console.command.CommandProcessor#onModeActivate(com.captiveimagination.game.console.GameConsole) */ public void onModeActivate(GameConsole console) { this.setPrompt(console); } /** * @param language */ public boolean setLanguage(String language) { return sp.setLanguage(language); } /** * @param file * @throws ScriptException */ public void runScriptFile(String file) throws ScriptException { sp.runScriptFile(file); } /** * @return */ public String getAvailableLanguagesText() { String langs = ""; Object [] languages = this.sp.getLanguages(); for(Object language : languages) { langs += language.toString() + " "; } return langs; } public Object getRegisteredObject(String object) { // TODO Auto-generated method stub return sp.getRegisteredObject(object); } public Script getScript(String scriptFile) throws ScriptException { // TODO Auto-generated method stub return sp.runScriptFile(scriptFile); } /** * @return */ public Bindings getRegisteredObjects() { // TODO Auto-generated method stub return sp.getRegisteredObjects(); } }
/** * */ package com.captiveimagination.game.console.script; import java.util.HashMap; import java.util.MissingResourceException; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.SimpleBindings; import javax.xml.crypto.NoSuchMechanismException; import com.captiveimagination.game.console.GameConsole; import com.captiveimagination.game.console.command.CommandProcessor; import com.captiveimagination.game.script.Script; /** * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 1136 $ * $Id: ScriptEvalCommandProcessor.java 1136 2007-08-18 02:21:47Z mhicks $ * $HeadURL: svn://captiveimagination.com/public/cigame/trunk/src/com/captiveimagination/game/console/script/ScriptEvalCommandProcessor.java $ */ public class ScriptEvalCommandProcessor implements CommandProcessor { private static final String DEFAULT_LANGUAGE = "Pnuts"; private static final ScriptEngineManager scriptManager = new ScriptEngineManager(); private static HashMap<String, ScriptEngineFactory> availableLanguages = new HashMap<String, ScriptEngineFactory>(); // Initialize the available languages static { for (ScriptEngineFactory f : scriptManager.getEngineFactories()) { availableLanguages.put(f.getLanguageName(), f); } } private ScriptEngineFactory curScriptFactory; private ScriptEngine engine; private GameConsole gConsole; private Bindings n = new SimpleBindings(); /** * Command processor that will execute an arbitrary script. Or evaluate a * script on the fly. * * @param console */ public ScriptEvalCommandProcessor(GameConsole console) { gConsole = console; if(availableLanguages.containsKey(DEFAULT_LANGUAGE)) { setLanguage(DEFAULT_LANGUAGE); } else { if(availableLanguages.size()>0) { for(String language : availableLanguages.keySet()) { //Just give me the first one that works. if(setLanguage(language)) { break; } } } //We've got a problem! else { throw new NoSuchMechanismException("Script engines cannot be found " + "make sure you have the script engine libraries on your classpath!!"); } } } public boolean setLanguage(String language) { if(availableLanguages.containsKey(language)) { try { //Attempt to retrieve the engine factory. //Will fail if script implmentation libraries aren't on //classpath. //See: https://scripting.dev.java.net/ engine = availableLanguages.get(language).getScriptEngine(); } catch(Error e) { gConsole.log("Attempt to retrieve script implementation factory for: " + language + " has failed. You must have both the script engine "+ "and script implementation on classpath see: https://scripting.dev.java.net/"); return false; } //Set the factory to the correct value, now that we know it is valid. curScriptFactory = availableLanguages.get(language); //Pass in the key, value pair bindings engine.setBindings(n, ScriptContext.ENGINE_SCOPE); return true; } //Language could not be found return false; } /* * (non-Javadoc) * * @see com.captiveimagination.game.console.command.CommandProcessor#execute(java.lang.String, * com.captiveimagination.game.console.GameConsole) */ public void execute(String command, GameConsole console) { try { engine.eval(command); } catch (ScriptException e) { Logger.getLogger("cigame").log(Level.SEVERE, "Error while evaluating script command:\n"+command+"\n", e); console.log("ERROR: " + e.getMessage()); } } public Script runScriptFile(String file) throws ScriptException { Script sc = new Script(file); sc.setBindings(n); sc.eval(); return sc; } public void register(String name, Object val) { n.put(name, val); } /** * @return */ public String getLanguage() { return curScriptFactory.getLanguageName(); } public ScriptEngineFactory getScriptFactory() { return curScriptFactory; } /* (non-Javadoc) * @see com.captiveimagination.game.console.command.CommandProcessor#onModeActivate(com.captiveimagination.game.console.GameConsole) */ public void onModeActivate(GameConsole console) { //Do nothing. } /** * @return */ public Object[] getLanguages() { Object[] languages = this.availableLanguages.keySet().toArray(); return languages; } public Object getRegisteredObject(String object) { // TODO Auto-generated method stub return n.get(object); } /** * @return */ public Bindings getRegisteredObjects() { // TODO Auto-generated method stub return n; } }
/** * */ package com.captiveimagination.game.console.script; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.ScriptException; import com.captiveimagination.game.console.GameConsole; import com.captiveimagination.game.console.command.CommandProcessor; import com.captiveimagination.game.script.Script; /** * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 1136 $ * $Id: ScriptFileExecuteCommand.java 1136 2007-08-18 02:21:47Z mhicks $ * $HeadURL: svn://captiveimagination.com/public/cigame/trunk/src/com/captiveimagination/game/console/script/ScriptFileExecuteCommand.java $ * */ public class ScriptFileExecuteCommand implements CommandProcessor{ private GameConsole console; private static final String CMD = "file"; private static final String USAGE ="USAGE: " + CMD + " <file.ext>" ; private ScriptCommandProcessor scrCmdProcessor; /** * @param console * @param processor */ public ScriptFileExecuteCommand(GameConsole console, ScriptCommandProcessor scp) { this.console = console; scrCmdProcessor = scp; console.registerCommandProcessor(getCommandText(), this); //scrCmdProcessor = processor; } /* (non-Javadoc) * @see com.captiveimagination.game.console.command.CommandProcessor#execute(java.lang.String, com.captiveimagination.game.console.GameConsole) */ public void execute(String command, GameConsole console) { //Split on spaces String args[] = command.split("\\s"); if(args.length!=2) { console.log("ERROR:" + USAGE); } String file = args[1]; try { scrCmdProcessor.runScriptFile(file); } catch (ScriptException e) { console.log("ERROR evaluating script " + file); Logger.getLogger("cigame").log(Level.SEVERE, "Console couldn't evaluate the script " + file, e); } } public String getCommandText() { return CMD; } /* (non-Javadoc) * @see com.captiveimagination.game.console.command.CommandProcessor#onModeActivate(com.captiveimagination.game.console.GameConsole) */ public void onModeActivate(GameConsole console) { } }
/** * */ package com.captiveimagination.game.console.script; import java.util.logging.Level; import java.util.logging.Logger; import com.captiveimagination.game.console.GameConsole; import com.captiveimagination.game.console.command.CommandProcessor; /** * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 1136 $ * $Id: SetScriptLanguageCommand.java 1136 2007-08-18 02:21:47Z mhicks $ * $HeadURL: svn://captiveimagination.com/public/cigame/trunk/src/com/captiveimagination/game/console/script/SetScriptLanguageCommand.java $ * */ public class SetScriptLanguageCommand implements CommandProcessor{ private GameConsole console; private ScriptCommandProcessor scrCmdProcessor; private static final String CMD = "lang"; private static final String USAGE ="USAGE: " + CMD + " <language>" ; /** * @param console * @param processor */ public SetScriptLanguageCommand(GameConsole console, ScriptCommandProcessor processor) { this.console = console; scrCmdProcessor = processor; console.registerCommandProcessor(getCommandText(), this); } /* (non-Javadoc) * @see com.captiveimagination.game.console.command.CommandProcessor#execute(java.lang.String, com.captiveimagination.game.console.GameConsole) */ public void execute(String command, GameConsole console) { //Split on spaces String args[] = command.split("\\s"); if(args.length!=2) { console.log("ERROR:" + USAGE); console.log(this.scrCmdProcessor.getAvailableLanguagesText()); Logger.getLogger("cigame").log(Level.INFO, this.scrCmdProcessor.getAvailableLanguagesText()); } else { String language = args[1]; boolean success = scrCmdProcessor.setLanguage(language); if(!success) { console.log("ERROR failed to set:" + USAGE); console.log(this.scrCmdProcessor.getAvailableLanguagesText()); Logger.getLogger("cigame").log(Level.INFO, this.scrCmdProcessor.getAvailableLanguagesText()); } } } public String getCommandText() { return CMD; } /* (non-Javadoc) * @see com.captiveimagination.game.console.command.CommandProcessor#onModeActivate(com.captiveimagination.game.console.GameConsole) */ public void onModeActivate(GameConsole console) { } }
package com.captiveimagination.game.script; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.Bindings; import javax.script.Compilable; import javax.script.CompiledScript; import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.SimpleBindings; /** * * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 1136 $ * $Id: Script.java 1136 2007-08-18 02:21:47Z mhicks $ * $HeadURL: svn://captiveimagination.com/public/cigame/trunk/src/com/captiveimagination/game/script/Script.java $ */ public class Script { private static final ScriptEngineManager scriptManager = new ScriptEngineManager(); private ScriptEngine engine; private String scriptFileName; private String suffix; private StringBuffer scriptContents = new StringBuffer(); private CompiledScript scr = null; private Invocable inv = null; private Object evalReturnObject = null; /** * Indicates if the script has already been evaluated. */ private boolean evaluated = false; private Bindings n = new SimpleBindings(); public Script(String scriptFile) { Compilable eng = null; scriptFileName = scriptFile; initEngine(); try { /** * If script is compiliable we compile it. */ if (engine instanceof Compilable) { // Compile script eng = (Compilable) engine; BufferedReader fr = new BufferedReader(new FileReader(scriptFileName)); String line; while((line=fr.readLine()) != null) { scriptContents.append(line+"\n"); } scr = eng.compile(new FileReader(scriptFileName)); engine = scr.getEngine(); } //set engine scope namespace engine.setBindings(n, ScriptContext.ENGINE_SCOPE); //evaluate(); } catch (FileNotFoundException fnf) { Logger.getLogger("cigame").log( Level.SEVERE, "Script file not found: " +scriptFileName, fnf); } catch (ScriptException se) { Logger.getLogger("cigame").log( Level.SEVERE, "Script exception: " +scriptFileName, se); } catch (IOException e) { Logger.getLogger("cigame").log( Level.SEVERE, "Script read error: " +scriptFileName, e); } if(engine instanceof Invocable) { inv = (Invocable) engine; } } /** * Evaluate the script. * * NOTE: This will revaluate the script even if it has already been * evaluated. * * @return */ public Object eval() throws ScriptException { try { evaluate(); evaluated = true; } catch (ScriptException e) { // TODO Auto-generated catch block Logger.getLogger("cigame").log(Level.SEVERE, "Error evaluating the script " + this.scriptFileName, e); throw e; } return evalReturnObject; } public static void main(String[] args) { // TODO Auto-generated method stub if (args.length < 2) { System.out .println("Invalid parameters, Usage : java " + ScriptEngine.class.getName() + " <scriptFile> <function>"); System.exit(1); } Script s = new Script(args[0]); String function = args[1]; Object result = s.invoke(function); System.out.println("Script<" + args[0] + ">." + args[1] + "() result: \n" + result); } public Object invoke(String function) { return invoke(function, null); } public <T> T getInterface(Class<T> clasz) throws ScriptException { evaluate(); return inv.getInterface(clasz); } public Object invoke(String function, Object... args) { Object result = null; try { evaluate(); result = inv.invokeFunction(function, args); } catch (ScriptException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return result; } private void initEngine() { String suffix = scriptFileName.substring(scriptFileName .lastIndexOf(".") + 1); engine = scriptManager.getEngineByExtension(suffix); } private Object evaluate() throws ScriptException { if (!evaluated) { // Evaluate compiled script if (scr != null) { evalReturnObject = scr.eval(); } // evaluate interpreted script else { evalReturnObject = engine.eval(this.scriptFileName); } evaluated = true; } return evalReturnObject; } /** * @param n2 */ public void setBindings(Bindings n2) { //push more bindings into the script registry n.putAll(n2); } /** * @return */ public String getScriptFilePath() { return scriptFileName; } /** * @return */ public String getScriptText() { return scriptContents.toString(); } }
http://jme-game-console.googlecode.com/svn/trunk/src/com/captiveimagination/game/script/Script.java
package com.captiveimagination.game.control.script; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptException; import javax.script.SimpleBindings; import com.captiveimagination.game.script.Script; import com.jme.scene.Controller; import com.jme.scene.Spatial; import com.jme.scene.TriMesh; /** * A jME Spatial Controller that runs a JSR 223 Script on update. * @author Doug Daniels * @version $Revision: 1136 $ * $Id: ScriptController.java 1136 2007-08-18 02:21:47Z mhicks $ * */ public class ScriptController extends Controller { private IScriptController script; private Spatial mesh; private Bindings bindings = new SimpleBindings();; private static final String UPDATE = "update"; private Script scriptController; /** * This is the name of the object that can be accessed in the script. */ private static final String CONTROLLED_OBJECT_ID = "controlled"; public ScriptController(String scriptFile, Spatial m) throws ScriptException { mesh = m; scriptController = new Script(scriptFile); //Put the Spatial into the script's global variables as variable "controlled" bindings.put(CONTROLLED_OBJECT_ID, mesh); scriptController.setBindings(bindings); // Script can only retrieve interface objects then can be // wrapped to jME interface. IScriptController controller = scriptController .getInterface(IScriptController.class); this.script = controller; Logger.getLogger("cigame").log( Level.INFO, "Creating ScriptController from script:\n"+ scriptController.getScriptFilePath() + "\n" + "contents:\n" + scriptController.getScriptText() ); } public ScriptController(IScriptController script, Spatial m){ this.script = script; mesh = m; } @Override public void update(float time) { try { //Delegate the update to the script script.update(time); } catch(Exception e) { Logger.getLogger("cigame").log( Level.SEVERE, "ERROR script controller. Removing script controller from Spatial:", e); //Remove it mesh.removeController(this); } } public void register(String key, Object val) { bindings.put(key, val); scriptController.setBindings(bindings); } /** * @param registeredObjects */ public void registerAll(Bindings registeredObjects) { bindings.putAll(registeredObjects); scriptController.setBindings(bindings); } }
package com.captiveimagination.game.control.script; /** * Provides an interface implementing the jME Controller methods. * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 678 $ * $Id: IScriptController.java 678 2007-03-31 23:41:40Z dougnukem $ * $HeadURL: svn://captiveimagination.com/public/cigame/trunk/src/com/captiveimagination/game/control/script/IScriptController.java $ */ public interface IScriptController { public void update(float time); }
package com.captiveimagination.game.console.script; import java.io.File; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import com.captiveimagination.game.console.GameConsole; import com.captiveimagination.game.console.command.CommandProcessor; import com.captiveimagination.game.control.script.ScriptController; import com.jme.scene.Spatial; /** * * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 1133 $ * $Id: AttachScriptControllerCommand.java 1133 2007-08-13 20:31:12Z mhicks $ * $HeadURL: svn://captiveimagination.com/public/cigame/trunk/src/com/captiveimagination/game/console/script/AttachScriptControllerCommand.java $ */ public class AttachScriptControllerCommand implements CommandProcessor { private GameConsole console; private ScriptCommandProcessor scrCmdProcessor; private static final String CMD = "addController"; private static final String CMD_REMOVE = "removeController"; // we expect 3 arguments private static final int NUM_ADD_ARGS = 3; private static final int NUM_REMOVE_ARGS = 2; private static final String USAGE = "USAGE: " + CMD + " <Object> <script.ext>"; private static final String USAGE_REMOVE = "USAGE: " + CMD_REMOVE + " <Object>"; /** * @param console * @param processor */ public AttachScriptControllerCommand(GameConsole console, ScriptCommandProcessor processor) { this.console = console; scrCmdProcessor = processor; console.registerCommandProcessor(getCommandText(), this); } /* * (non-Javadoc) * * @see com.captiveimagination.game.console.command.CommandProcessor#execute(java.lang.String, * com.captiveimagination.game.console.GameConsole) */ public void execute(String command, GameConsole console) { // Split on spaces String args[] = command.split("\\s"); // Removes all attached ScriptController's if (args[0].equals(CMD_REMOVE) && args.length == NUM_REMOVE_ARGS) { String object = args[1]; Object controllee = scrCmdProcessor.getRegisteredObject(object); if (!(controllee instanceof Spatial)) { console.log("ERROR object not Spatial:" + USAGE_REMOVE); } else { // TODO allow parameter to only remove a certain script // controller given it's filename Spatial spatialBeingControlled = (Spatial) controllee; // Need to clone it so that we don't concurrently remove it // while iterating for (Object c : (ArrayList) spatialBeingControlled .getControllers().clone()) { // Only remove attached ScriptController's if (c instanceof ScriptController) spatialBeingControlled .removeController((ScriptController) c); } } } else if (args[0].equals(CMD) && args.length == NUM_ADD_ARGS) { String object = args[1]; String scriptFileLoc = args[2]; Object controllee = scrCmdProcessor.getRegisteredObject(object); File scriptFile = new File(scriptFileLoc); if (!(controllee instanceof Spatial) || !scriptFile.exists()) { console .log("ERROR object not Spatial or scriptfile doesn't exist:" + USAGE); } else { try { Spatial spatialBeingControlled = (Spatial) controllee; // The Script Controller will bind the Spatial to ID "controller" // so that scripts have access to Spatial ScriptController sc = new ScriptController(scriptFileLoc, spatialBeingControlled); sc.registerAll(this.scrCmdProcessor.getRegisteredObjects()); if (sc != null) { spatialBeingControlled.addController(sc); } else { console .log("ERROR Script was not a valid Controller see documentation" + USAGE); } } catch (javax.script.ScriptException e) { console.log("ERROR eval script check LOG:" + USAGE); Logger.getLogger("cigame").log(Level.SEVERE, "ERROR eval script", e); } } } else { console.log("ERROR:" + USAGE); } } public String[] getCommands() { return new String[] { CMD, CMD_REMOVE }; } public String getCommandText() { return CMD; } /* * (non-Javadoc) * * @see com.captiveimagination.game.console.command.CommandProcessor#onModeActivate(com.captiveimagination.game.console.GameConsole) */ public void onModeActivate(GameConsole console) { } }
importPackage(Packages.com.jme.math); var incr = 1.0; var rotVec = new Vector3f(0.5, 0.5, 0.0); var rotQuat = new Quaternion(); var angle = 0.0; /** * @author <a href="mailto:Daniels.Douglas@gmail.com">Doug Daniels</a> * @version $Revision: 678 $ * $Id: spinController.js 678 2007-03-31 23:41:40Z dougnukem $ * $HeadURL: https://captiveimagination.com/svn/public/cigame/trunk/src/resources/scripts/js/spinController.js $ */ function update(time) { if(console!=null) console.log("time="+time); else System.out.println("time="+time); //update the angle angle = angle + incr; if (angle > 360) { angle = 0; } rotQuat.fromAngleNormalAxis(angle * FastMath.DEG_TO_RAD, rotVec); //controlled is special binding referencing the Spatial object being controlled. controlled.setLocalRotation(rotQuat); //print('spinController update: ' + angle); }
http://jme-game-console.googlecode.com/svn/trunk/spinController.js
/** * */ package com.captiveimagination.game.console; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintStream; import java.util.*; import com.captiveimagination.game.spatial.DialogBox; import com.captiveimagination.game.util.TextureLoader; import com.captiveimagination.game.console.command.*; import com.captiveimagination.game.control.*; import com.jme.image.*; import com.jme.image.Texture.MagnificationFilter; import com.jme.input.*; import com.jme.math.Vector2f; import com.jme.renderer.*; import com.jme.scene.*; import com.jme.scene.Spatial.TextureCombineMode; import com.jme.scene.state.*; import com.jme.scene.state.BlendState.DestinationFunction; import com.jme.scene.state.BlendState.SourceFunction; import com.jme.scene.state.BlendState.TestFunction; import com.jme.system.*; import com.jme.util.*; import com.jmex.game.*; import com.jmex.game.state.*; import com.jmex.model.collada.schema.string; /** * @author Matthew D. Hicks * */ public class GameConsole extends BasicGameState implements KeyInputListener { private static final long serialVersionUID = 1L; private static final String FONT_LOCATION = "/com/jme/app/defaultfont.tga"; public static String errTag = "<Error> :"; private BlendState as; private TextureState font; private int key; private Text[] rows; private Text entry; private Text cursor; private LinkedList<String> history; private LinkedList<String> commandHistory; private boolean echo; private Spatial backdrop; private int maxHistory; private int maxCommandHistory; private StringBuffer cursorBuffer; private StringBuffer current; private int cursorPosition; private int historyPosition; private int commandHistoryPosition; private float targetOnState; private FloatSpring spring; private String currentMode; private HashMap<String, CommandProcessor> modes; private List<GameConsoleListener> listeners; private float[] borders; private Vector2f onPosition; private Vector2f offPosition; private boolean on; private float width; public static int RIGHT = 0; public static int TOP = 1; public static int LEFT = 2; public static int BOTTOM = 3; private float yPromptPosition; private Text prompt; private String promptString; private boolean useSystemOut = false; PipedInputStream pin; PipedInputStream errPin; PipedOutputStream pout; PipedOutputStream errPout; PrintStream out; PrintStream errOut; BufferedReader in; BufferedReader errIn; public GameConsole(int key, int rows, boolean echo) { this(key, rows, echo, new float[]{0, 20, 0, 0}, new Vector2f(0, 0), new Vector2f(0, calculateHeight(rows) + 5), DisplaySystem.getDisplaySystem().getWidth()); } public GameConsole(int key, int rows, boolean echo, float[] borders, Vector2f onPosition, Vector2f offPosition, float width) { super("GameConsole"); this.key = key; this.rows = new Text[rows]; this.history = new LinkedList<String>(); commandHistory = new LinkedList<String>(); this.echo = echo; maxHistory = 250; maxCommandHistory = 250; commandHistoryPosition = -1; this.borders = borders; this.onPosition = onPosition; this.offPosition = offPosition; this.width = width; listeners = new ArrayList<GameConsoleListener>(); init(); prompt.setTextColor(ColorRGBA.red); entry.setTextColor(ColorRGBA.green); cursor.setTextColor(ColorRGBA.green); } private void init() { modes = new HashMap<String, CommandProcessor>(); getRootNode().setRenderQueueMode(Renderer.QUEUE_ORTHO); cursorBuffer = new StringBuffer(40); cursorBuffer.append("_"); current = new StringBuffer(40); //Create default backdrop backdrop = createDialogBackdrop(); getRootNode().attachChild(backdrop); spring = new FloatSpring(400); //Start switched off, positioned off spring.setPosition(0); this.targetOnState = 0; this.on = false; // Create Text as = DisplaySystem.getDisplaySystem().getRenderer().createBlendState(); as.setBlendEnabled(true); as.setSourceFunction(/*AlphaState.SB_SRC_ALPHA*/ BlendState.SourceFunction.SourceAlpha); as.setDestinationFunction(/*AlphaState.DB_ONE*/ BlendState.DestinationFunction.One); as.setTestEnabled(true); as.setTestFunction(/*AlphaState.TF_GREATER*/ TestFunction.GreaterThan); as.setEnabled(true); font = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); font.setTexture(TextureManager.loadTexture(StandardGame.class.getResource(FONT_LOCATION), /*Texture.MM_LINEAR*/ Texture.MinificationFilter.BilinearNearestMipMap, /*Texture.FM_LINEAR*/ MagnificationFilter.Bilinear)); font.setEnabled(true); float y = DisplaySystem.getDisplaySystem().getHeight() - 18.0f; for (int i = 0; i < rows.length; i++) { rows[i] = createText("Console Line" + i, "", 10.0f, y); y -= 14.0f; } entry = createText("Console Entry", "", 10.0f, y); cursor = createText("Cursor", cursorBuffer.toString(), 0.0f, y); yPromptPosition = y; //Initialize prompt prompt = createText("Prompt", "", 0.0f, yPromptPosition); prompt.getLocalTranslation().x = -2.0f; setPrompt(">"); // getRootNode().setCullMode(Spatial.CULL_ALWAYS); getRootNode().updateRenderState(); KeyInput.get().addListener(this); //getRootNode().getLocalTranslation().y = backdropHeight; } private void setupOutput() { try { pin = new PipedInputStream(); pout = new PipedOutputStream(pin); out = new PrintStream(pout); in = new BufferedReader(new InputStreamReader(pin)); System.setOut(out); errPin = new PipedInputStream(); errPout = new PipedOutputStream(errPin); errOut = new PrintStream(pout); errIn = new BufferedReader(new InputStreamReader(pin)); System.setErr(errOut); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public boolean isOpen() { return on; } public static float calculateHeight(int rows) { return ((rows + 1) * 14.0f) + 10.0f; } public void update(float time) { super.update(time); spring.update(targetOnState, time); float onNess = spring.getPosition(); getRootNode().getLocalTranslation().x = onPosition.x * onNess + offPosition.x * (1-onNess); getRootNode().getLocalTranslation().y = onPosition.y * onNess + offPosition.y * (1-onNess); if(useSystemOut) { updateOut(); } } private void updateOut() { try { if(in.ready()) { String instr = in.readLine(); this.log(instr); } if(errIn.ready()) { this.logErr(errIn.readLine()); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void registerCommandProcessor(String name, CommandProcessor processor) { name = name.toLowerCase(); if (currentMode == null) { currentMode = name; processor.onModeActivate(this); } modes.put(name, processor); } public void setPrompt(String promptStr) { this.promptString = promptStr; prompt.print(promptString); cursor.getLocalTranslation().x = prompt.getWidth(); entry.getLocalTranslation().x = prompt.getWidth(); //getRootNode().updateRenderState(); } private Text createText(String name, String value, float xPosition, float yPosition) { Text text = new Text(name, value); text.setTextureCombineMode(/*TextureState.REPLACE*/ TextureCombineMode.Replace); text.setRenderState(as); text.setRenderState(font); text.updateGeometricState(0.0f, true); text.setLocalTranslation(xPosition, yPosition, 0.0f); getRootNode().attachChild(text); return text; } private Spatial createDialogBackdrop() { //Make default texture float opacityAmount = 1.0f; Texture dialogTexture = TextureLoader.loadUncompressedTexture("resources/dialogArea.png"); //Make the dialog box DialogBox box = new DialogBox("box", dialogTexture); box.setDimension(width + borders[LEFT] + borders[RIGHT], calculateHeight(rows.length) + borders[TOP] + borders[BOTTOM]); box.setLocalTranslation(-borders[LEFT], DisplaySystem.getDisplaySystem().getRenderer().getHeight() - calculateHeight(rows.length) - borders[BOTTOM], 0.0f); MaterialState materialState = DisplaySystem.getDisplaySystem().getRenderer().createMaterialState(); materialState.setAmbient(new ColorRGBA(0.0f, 0.0f, 0.0f, opacityAmount)); materialState.setDiffuse(new ColorRGBA(0.1f, 0.5f, 0.8f, opacityAmount)); materialState.setSpecular(new ColorRGBA(1.0f, 1.0f, 1.0f, opacityAmount)); materialState.setShininess(128.0f); materialState.setEmissive(new ColorRGBA(0.0f, 0.0f, 0.0f, opacityAmount)); materialState.setEnabled(true); materialState.setMaterialFace(MaterialState.MaterialFace.FrontAndBack); box.setRenderState(materialState); box.updateRenderState(); //Create alpha state to blend using alpha BlendState bas = DisplaySystem.getDisplaySystem().getRenderer().createBlendState(); bas.setBlendEnabled(true); //bas.setSrcFunction(AlphaState.SB_SRC_ALPHA); bas.setSourceFunction(SourceFunction.SourceAlpha); //bas.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA); bas.setDestinationFunction(DestinationFunction.OneMinusSourceAlpha); bas.setTestFunction(TestFunction.GreaterThan); bas.setTestEnabled(true); bas.setEnabled(true); //set box render state box.setRenderState(bas); box.updateRenderState(); box.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT); //return the box return box; } public void type(char character) { if (Character.isISOControl(character)) return; current.insert(cursorPosition++, character); cursorBuffer.insert(0, ' '); entry.print(current); cursor.print(cursorBuffer); } public void setText(String text) { current.delete(0, current.length()); current.append(text); cursorBuffer.delete(0, cursorBuffer.length()); cursorBuffer.append("_"); for (int i = 0; i < text.length()-1; i++) { cursorBuffer.insert(0, ' '); } cursorPosition = text.length(); entry.print(current); cursor.print(cursorBuffer); } public void backspace() { if ((current.length() > 0) && (cursorPosition > 0)) { current.deleteCharAt(--cursorPosition); cursorBuffer.deleteCharAt(0); entry.print(current); cursor.print(cursorBuffer); } } public void enter() { if (current.length() > 0) { String s = current.toString(); if (echo) log(s); execute(s); if (commandHistory.size() >= maxCommandHistory) { commandHistory.poll(); } commandHistory.add(s); current.delete(0, current.length()); cursorBuffer.delete(0, cursorBuffer.length()); cursorBuffer.append("_"); cursorPosition = 0; commandHistoryPosition = -1; entry.print(current); cursor.print(cursorBuffer); } } public void execute(String command) { CommandProcessor processor = modes.get(currentMode); if (command.startsWith("mode")) { if (command.length() > "mode ".length()) { String mode = command.substring("mode ".length()); mode = mode.toLowerCase(); processor = modes.get(mode); if (processor != null) { log("Mode Changed: " + mode); currentMode = mode; processor.onModeActivate(this); } else { logErr("Unknown Mode: " + mode); } } else { StringBuffer buffer = new StringBuffer(); for (String mode : modes.keySet()) { if (buffer.length() > 0) buffer.append(", "); buffer.append(mode); } log("Current Mode: " + currentMode + " (Available: " + buffer + ")"); } return; } if (processor != null) { processor.execute(command, this); } else { logErr("No CommandProcessor set."); } } public void log(String message) { if (history.size() >= maxHistory) { history.poll(); } history.add(message); if (historyPosition != 0) historyPosition++; updateHistory(); } public void logErr(String message) { if (history.size() >= maxHistory) { history.poll(); } history.add(errTag + message); if (historyPosition != 0) historyPosition++; updateHistory(); } public void updateHistory() { // Update history display int position = history.size() - rows.length - historyPosition; for (int i = 0; i < rows.length; i++) { if (position < 0) { rows[i].print(""); } else { if(history.get(position).startsWith(errTag)) { rows[i].setTextColor(ColorRGBA.red); history.get(position).replaceAll(errTag, ""); } else { rows[i].setTextColor(ColorRGBA.green); } rows[i].print(history.get(position)); } position++; } } public void saveLog(String location) { PrintStream out; try { out = new PrintStream(location); for(int i=0; i < history.size(); i++) { out.println(history.get(i)); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block this.logErr("Could not save the Log, file not found"); } } public void moveUpInHistory() { if (historyPosition < history.size() - rows.length) { historyPosition++; updateHistory(); } } public void moveDownInHistory() { if (historyPosition > 0) { historyPosition--; updateHistory(); } } public boolean moveCursorLeft() { if (cursorPosition > 0) { cursorPosition--; cursorBuffer.deleteCharAt(0); cursor.print(cursorBuffer); return true; } return false; } public void moveCursorHome() { while (moveCursorLeft()); } public boolean moveCursorRight() { if (cursorPosition < current.length()) { cursorPosition++; cursorBuffer.insert(0, ' '); cursor.print(cursorBuffer); return true; } return false; } public void moveCursorEnd() { while (moveCursorRight()); } public void moveUpInCommandHistory() { if (commandHistory.size() > 0) { if (commandHistoryPosition == 0) return; if (commandHistoryPosition == -1) { commandHistoryPosition = commandHistory.size(); } commandHistoryPosition--; setText(commandHistory.get(commandHistoryPosition)); } } public void moveDownInCommandHistory() { if (commandHistory.size() > 0) { if (commandHistoryPosition == commandHistory.size() - 1) { commandHistoryPosition = -1; setText(""); return; } if (commandHistoryPosition == -1) return; commandHistoryPosition++; setText(commandHistory.get(commandHistoryPosition)); } } public void onKey(char character, int keyCode, boolean pressed) { if (pressed) { if (keyCode == key) { on = !on; targetOnState = on ? 1 : 0; for (GameConsoleListener listener : listeners) { if (on) { listener.consoleActivated(); } else { listener.consoleDeactivated(); } } } else if (!on) { // We're disabled, we're not going to listen return; } else if (keyCode == KeyInput.KEY_BACK) { backspace(); } else if (keyCode == KeyInput.KEY_RETURN) { enter(); } else if (keyCode == KeyInput.KEY_PGUP) { moveUpInHistory(); } else if (keyCode == KeyInput.KEY_PGDN) { moveDownInHistory(); } else if (keyCode == KeyInput.KEY_LEFT) { moveCursorLeft(); } else if (keyCode == KeyInput.KEY_RIGHT) { moveCursorRight(); } else if (keyCode == KeyInput.KEY_UP) { moveUpInCommandHistory(); } else if (keyCode == KeyInput.KEY_DOWN) { moveDownInCommandHistory(); } else if (keyCode == KeyInput.KEY_HOME) { moveCursorHome(); } else if (keyCode == KeyInput.KEY_END) { moveCursorEnd(); } else { type(character); } } } public boolean addListener(GameConsoleListener listener) { return listeners.add(listener); } public boolean removeListener(GameConsoleListener listener) { return listeners.remove(listener); } public void setUseSystemOut(boolean useSystemOut) { this.useSystemOut = useSystemOut; if(useSystemOut) { setupOutput(); } } public boolean usesSystemOut() { return useSystemOut; } }
package com.captiveimagination.game.console.command; import com.captiveimagination.game.console.*; public interface CommandProcessor { public void execute(String command, GameConsole console); public void onModeActivate(GameConsole console); }
package test; import java.net.URL; import java.util.logging.Level; import javax.script.ScriptException; import jmetest.renderer.TestEnvMap; import com.captiveimagination.game.control.script.ScriptController; import com.jme.app.SimpleGame; import com.jme.image.Texture; import com.jme.input.KeyBindingManager; import com.jme.input.KeyInput; import com.jme.math.Vector3f; import com.jme.scene.Node; import com.jme.scene.TriMesh; import com.jme.scene.shape.Box; import com.jme.scene.state.CullState; import com.jme.scene.state.TextureState; import com.jme.util.TextureManager; public class TestScriptController extends SimpleGame { private TriMesh mesh; //private static String rotateScript = "scripts/groovy/spinController.groovy"; private static String rotateScript = "resources/scripts/js/spinController.js"; /** * Entry point for the test, * * @param args */ public static void main(String[] args) { TestScriptController app = new TestScriptController(); //app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG); app.start(); } /** * @see com.jme.app.SimpleGame#initGame() */ protected void simpleInitGame() { display.setTitle("Testing Script"); lightState.setEnabled(false); CullState cull = display.getRenderer().createCullState(); //cull.setCullHint(CullState.Face.Back); cull.setCullFace(CullState.Face.Back); // we set a cull state to hide the back of our batches, "proving" they // are camera facing. rootNode.setRenderState(cull); initBoxes(); initScripts(); /* try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } */ } private void initBoxes() { mesh = new Box("box", new Vector3f(0,0,0), 1,1,1); TextureState ts = display.getRenderer().createTextureState(); Texture t0 = TextureManager.loadTexture( TestEnvMap.class.getClassLoader().getResource( "jmetest/data/images/Monkey.jpg"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear); t0.setWrap(Texture.WrapMode.Repeat); ts.setTexture(t0); mesh.setRenderState(ts); mesh.updateRenderState(); rootNode.attachChild(mesh); } private void initScripts() { URL scriptURL = TestScriptController.class.getClassLoader().getResource(rotateScript); try { ScriptController sc = new ScriptController(scriptURL.getPath(), mesh); mesh.addController(sc); } catch (ScriptException e) { } //Add the script to the mesh //mesh.addController(sc); } }
http://jme-game-console.googlecode.com/svn/trunk/test/test/TestScriptController.java