/*
* Copyright (C) 2012 maartenl
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mmud.scripting;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.logging.Logger;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import mmud.database.entities.game.DisplayInterface;
import mmud.exceptions.MudException;
import mmud.scripting.entities.Room;
/**
* Can run javascript source code. The methods call specific javascript
* functions in the source code.
*
* @author maartenl
*/
public class RunScript
{
private static final Logger itsLog = Logger.getLogger(RunScript.class.getName());
private Persons persons;
private Rooms rooms;
private Items items;
private World world;
public RunScript(Persons persons, Rooms rooms, Items items, World world)
{
if (persons == null)
{
throw new NullPointerException("persons was null.");
}
if (rooms == null)
{
throw new NullPointerException("rooms was null.");
}
if (world == null)
{
throw new NullPointerException("world was null.");
}
this.persons = persons;
this.rooms = rooms;
this.world = world;
this.items = items;
}
/**
* Runs a specific function called "function command(person, command)".
*
* Basically it calls a specific deputy defined "command".
*
* @param person the person issuing forth the command.
* @param command the command, a string.
* @param sourceCode the source code, javascript, a string.
* @return false if failed, true if successful
* @throws ScriptException if an error occurred in the javascript
* @throws NoSuchMethodException if the function cannot be found,
* @throws java.lang.IllegalAccessException
* @throws java.lang.InstantiationException
* @throws java.lang.reflect.InvocationTargetException
*/
public DisplayInterface run(mmud.database.entities.characters.Person person, String command, String sourceCode) throws ScriptException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException
{
itsLog.entering(this.getClass().getName(), "run(" + person.getName() + ", " + command + ")");
final Invocable inv = initialiseScriptEngine(sourceCode);
final Object result = inv.invokeFunction("command", new mmud.scripting.entities.Person(person), command);
if (result != null)
{
return new DisplayInterface()
{
@Override
public String getMainTitle() throws MudException
{
try
{
return (String) inv.invokeMethod(result, "getTitle");
} catch (ScriptException | NoSuchMethodException ex)
{
throw new MudException(ex);
}
}
@Override
public String getImage() throws MudException
{
try
{
return (String) inv.invokeMethod(result, "getImage");
} catch (ScriptException | NoSuchMethodException ex)
{
throw new MudException(ex);
}
}
@Override
public String getBody() throws MudException
{
try
{
return (String) inv.invokeMethod(result, "getBody");
} catch (ScriptException | NoSuchMethodException ex)
{
throw new MudException(ex);
}
}
};
}
return null;
}
/**
* Runs a specific function called "function event(person)".
*
* Basically it calls a specific deputy defined "event".
*
* @param person the event needs to be executed with this person as the focus.
* @param sourceCode the source code, javascript, a string.
* @return false if failed, true if successful
* @throws ScriptException if an error occurred in the javascript
* @throws NoSuchMethodException if the function cannot be found,
* @throws java.lang.IllegalAccessException
* @throws java.lang.InstantiationException
* @throws java.lang.reflect.InvocationTargetException
*/
public boolean run(mmud.database.entities.characters.Person person, String sourceCode) throws ScriptException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException
{
itsLog.entering(this.getClass().getName(), "run(" + person.getName() + ")");
Invocable inv = initialiseScriptEngine(sourceCode);
// invoke the global function named "hello"
Boolean result = (Boolean) inv.invokeFunction("event", new mmud.scripting.entities.Person(person));
return result == null ? false : result;
}
/**
* Runs a specific function called "function event(room)".
*
* Basically it calls a specific deputy defined "event".
*
* @param room the event needs to be executed with this room as the focus.
* @param sourceCode the source code, javascript, a string.
* @return false if failed, true if successful
* @throws ScriptException if an error occurred in the javascript
* @throws NoSuchMethodException if the function cannot be found,
* @throws java.lang.IllegalAccessException
* @throws java.lang.InstantiationException
* @throws java.lang.reflect.InvocationTargetException
*/
public boolean run(mmud.database.entities.game.Room room, String sourceCode) throws ScriptException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException
{
itsLog.entering(this.getClass().getName(), "run(" + room.getId() + ")");
Invocable inv = initialiseScriptEngine(sourceCode);
// invoke the global function named "hello"
Boolean result = (Boolean) inv.invokeFunction("event", new Room(room));
return result == null ? false : result;
}
/**
* Runs a specific function called "function event()". This is a generic
* event without a focus.
*
* @param sourceCode the source code, javascript, a string.
* @return false if failed, true if successful
* @throws ScriptException if an error occurred in the javascript
* @throws NoSuchMethodException if the function cannot be found,
* @throws java.lang.IllegalAccessException
* @throws java.lang.InstantiationException
* @throws java.lang.reflect.InvocationTargetException
*/
public boolean run(String sourceCode) throws ScriptException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException
{
itsLog.entering(this.getClass().getName(), "run()");
Invocable inv = initialiseScriptEngine(sourceCode);
// invoke the global function named "hello"
Boolean result = (Boolean) inv.invokeFunction("event");
return result == null ? false : result;
}
private Invocable initialiseScriptEngine(String sourceCode) throws ScriptException, IllegalAccessException, InstantiationException, InvocationTargetException
{
itsLog.entering(this.getClass().getName(), "initialiseScriptEngine");
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByExtension("js");
if (engine == null)
{
itsLog.warning("Javascript engine missing!");
List<ScriptEngineFactory> engineFactories = factory.getEngineFactories();
itsLog.warning("Available scripting engines:");
for (ScriptEngineFactory engines : engineFactories)
{
itsLog.warning(engines.getEngineName());
}
throw new MudException("Javascript engine missing!");
}
// expose persons and rooms object as variable to script
engine.put("persons", persons);
engine.put("rooms", rooms);
engine.put("items", items);
engine.put("world", world);
// engine.eval("print('Hello, World')");
engine.eval(sourceCode);
Invocable inv = (Invocable) engine;
return inv;
}
}