diff --git a/lib/groovy-2.4.11.jar b/lib/groovy-2.4.11.jar new file mode 100644 index 000000000..ccf798021 Binary files /dev/null and b/lib/groovy-2.4.11.jar differ diff --git a/pom.xml b/pom.xml index 96a0d35cc..23c607e91 100644 --- a/pom.xml +++ b/pom.xml @@ -53,6 +53,11 @@ lz4 1.3.0 + + org.codehaus.groovy + groovy + 2.4.11 + diff --git a/scripts/commands/generic/addFriend.groovy b/scripts/commands/generic/addFriend.groovy new file mode 100644 index 000000000..59c5b3cda --- /dev/null +++ b/scripts/commands/generic/addFriend.groovy @@ -0,0 +1,15 @@ +import intents.chat.ChatAvatarRequestIntent +import resources.objects.SWGObject +import resources.player.Player +import services.galaxy.GalacticManager + +static def execute(GalacticManager galacticManager, Player player, SWGObject target, String args) { + if (args == null) + return + + def name = args.split(" ")[0].toLowerCase(Locale.ENGLISH) + if (name == null) + return + + new ChatAvatarRequestIntent(player, name, ChatAvatarRequestIntent.RequestType.FRIEND_ADD_TARGET).broadcast() +} \ No newline at end of file diff --git a/scripts/commands/generic/cmdTip.groovy b/scripts/commands/generic/cmdTip.groovy new file mode 100644 index 000000000..f88902da5 --- /dev/null +++ b/scripts/commands/generic/cmdTip.groovy @@ -0,0 +1,32 @@ +import resources.commands.ICmdCallback +import resources.objects.SWGObject +import resources.player.AccessLevel +import resources.player.Player +import services.galaxy.GalacticManager +import utilities.IntentFactory + +class CmdTip implements ICmdCallback { + void execute(GalacticManager galacticManager, Player player, SWGObject target, String args) { + if (player.getAccessLevel() == AccessLevel.PLAYER) { + IntentFactory.sendSystemMessage(player, "Unable to access /tip command - currently reserved for admins") + return + } + def argSplit = args.split(" ") + if (argSplit.length < 2) { + IntentFactory.sendSystemMessage(player, "Invalid Arguments: " + args) + return + } + def creature = player.getCreatureObject() + if (argSplit[0] == "bank") + creature.setBankBalance(creature.getBankBalance() + Long.valueOf(argSplit[1])) + else if (argSplit[0] == "cash") + creature.setCashBalance(creature.getCashBalance() + Long.valueOf(argSplit[1])) + else + IntentFactory.sendSystemMessage(player, "Unknown Destination: " + argSplit[0]) + } +} + +def execute(GalacticManager galacticManager, Player player, SWGObject target, String args) { + println("Hello World!") + new CmdTip().execute(galacticManager, player, target, args) +} \ No newline at end of file diff --git a/src/main/ProjectSWG.java b/src/main/ProjectSWG.java index ee44f64ec..9daa264f1 100644 --- a/src/main/ProjectSWG.java +++ b/src/main/ProjectSWG.java @@ -27,6 +27,18 @@ ***********************************************************************************/ package main; +import com.projectswg.common.concurrency.Delay; +import com.projectswg.common.control.IntentManager; +import com.projectswg.common.debug.Log; +import com.projectswg.common.debug.Log.LogLevel; +import com.projectswg.common.debug.log_wrapper.ConsoleLogWrapper; +import com.projectswg.common.debug.log_wrapper.FileLogWrapper; +import intents.server.ServerStatusIntent; +import resources.Galaxy.GalaxyStatus; +import resources.control.ServerStatus; +import resources.server_info.DataManager; +import services.CoreManager; + import java.io.File; import java.io.IOException; import java.lang.Thread.State; @@ -36,19 +48,6 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import com.projectswg.common.concurrency.Delay; -import com.projectswg.common.control.IntentManager; -import com.projectswg.common.debug.Log; -import com.projectswg.common.debug.Log.LogLevel; -import com.projectswg.common.debug.log_wrapper.ConsoleLogWrapper; -import com.projectswg.common.debug.log_wrapper.FileLogWrapper; - -import intents.server.ServerStatusIntent; -import resources.Galaxy.GalaxyStatus; -import resources.control.ServerStatus; -import resources.server_info.DataManager; -import services.CoreManager; - public class ProjectSWG { private static ProjectSWG server; diff --git a/src/resources/commands/ICmdCallback.java b/src/resources/commands/ICmdCallback.java index 79deddb63..20d627521 100644 --- a/src/resources/commands/ICmdCallback.java +++ b/src/resources/commands/ICmdCallback.java @@ -32,5 +32,5 @@ import resources.player.Player; import services.galaxy.GalacticManager; public interface ICmdCallback { - public void execute(GalacticManager galacticManager, Player player, SWGObject target, String args); + void execute(GalacticManager galacticManager, Player player, SWGObject target, String args); } diff --git a/src/resources/commands/callbacks/QaToolCmdCallback.java b/src/resources/commands/callbacks/QaToolCmdCallback.java index 7d66e3dee..72fcedf08 100644 --- a/src/resources/commands/callbacks/QaToolCmdCallback.java +++ b/src/resources/commands/callbacks/QaToolCmdCallback.java @@ -27,13 +27,11 @@ package resources.commands.callbacks; -import java.io.FileNotFoundException; -import java.util.Map; - import com.projectswg.common.data.location.Location; import com.projectswg.common.data.location.Terrain; import com.projectswg.common.debug.Log; - +import groovy.util.ResourceException; +import groovy.util.ScriptException; import intents.chat.ChatBroadcastIntent; import intents.experience.ExperienceIntent; import intents.network.CloseConnectionIntent; @@ -58,6 +56,8 @@ import services.objects.StaticItemService.ObjectCreationHandler; import services.player.PlayerManager; import utilities.Scripts; +import java.util.Map; + /** * Created by Waverunner on 8/19/2015 */ @@ -94,7 +94,7 @@ public class QaToolCmdCallback implements ICmdCallback { case "details": try { Scripts.invoke("commands/helper/qatool/details", "sendDetails", player, target, args.split(" ")); - } catch (FileNotFoundException ex) { + } catch (ResourceException | ScriptException e) { Log.e("sendDetails qatool script not found!"); } break; diff --git a/src/resources/radial/Radials.java b/src/resources/radial/Radials.java index 4ae182acd..2e812e2a4 100644 --- a/src/resources/radial/Radials.java +++ b/src/resources/radial/Radials.java @@ -27,16 +27,16 @@ ***********************************************************************************/ package resources.radial; -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; - import com.projectswg.common.debug.Log; - +import groovy.util.ResourceException; +import groovy.util.ScriptException; import resources.objects.SWGObject; import resources.player.Player; import utilities.Scripts; +import java.util.ArrayList; +import java.util.List; + public class Radials { private static final String SCRIPT_PREFIX = "radial/"; @@ -45,7 +45,7 @@ public class Radials { List options = new ArrayList<>(); try { Scripts.invoke(SCRIPT_PREFIX + script, "getOptions", options, player, target, args); - } catch (FileNotFoundException ex) { + } catch (ResourceException | ScriptException e) { Log.w("Couldn't retrieve radial options from %s for object %s because the script couldn't be found", SCRIPT_PREFIX + script, target); } return options; @@ -54,7 +54,7 @@ public class Radials { public static void handleSelection(String script, Player player, SWGObject target, RadialItem selection, Object ... args) { try { Scripts.invoke(SCRIPT_PREFIX + script, "handleSelection", player, target, selection, args); - } catch (FileNotFoundException ex) { + } catch (ResourceException | ScriptException e) { Log.w("Can't handle selection %s on object %s because the script couldn't be found"); } } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 1389b2b43..26cb8defe 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -27,20 +27,13 @@ ***********************************************************************************/ package services.commands; -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Stream; - import com.projectswg.common.concurrency.PswgBasicScheduledThread; import com.projectswg.common.control.Service; import com.projectswg.common.data.CRC; import com.projectswg.common.debug.Assert; import com.projectswg.common.debug.Log; - +import groovy.util.ResourceException; +import groovy.util.ScriptException; import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; @@ -54,6 +47,13 @@ import services.commands.buff.BuffData; import services.commands.buff.BuffMap; import utilities.Scripts; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + public class BuffService extends Service { /* @@ -326,7 +326,7 @@ public class BuffService extends Service { } else { try { Scripts.invoke("buffs/callback" + callback, callback, creature); - } catch (FileNotFoundException ex) { + } catch (ResourceException | ScriptException e) { Log.w("Callback script %s doesn't exist - buff %s won't behave as expected", callback, buffData.getName()); } } diff --git a/src/services/commands/CommandLauncher.java b/src/services/commands/CommandLauncher.java index c3d813f31..822f1d36b 100644 --- a/src/services/commands/CommandLauncher.java +++ b/src/services/commands/CommandLauncher.java @@ -27,15 +27,10 @@ ***********************************************************************************/ package services.commands; -import java.io.FileNotFoundException; -import java.util.HashMap; -import java.util.Map; -import java.util.PriorityQueue; -import java.util.Queue; - import com.projectswg.common.concurrency.PswgScheduledThreadPool; import com.projectswg.common.debug.Log; - +import groovy.util.ResourceException; +import groovy.util.ScriptException; import intents.chat.ChatBroadcastIntent; import intents.chat.ChatCommandIntent; import network.packets.swg.zone.object_controller.CommandQueueDequeue; @@ -52,6 +47,11 @@ import resources.server_info.DataManager; import services.galaxy.GalacticManager; import utilities.Scripts; +import java.util.HashMap; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Queue; + public class CommandLauncher { private final Map> combatQueueMap; @@ -180,8 +180,11 @@ public class CommandLauncher { } } else { try { - Scripts.invoke("commands/generic/" + command.getDefaultScriptCallback(), "executeCommand", enqueued.getGalacticManager(), player, enqueued.getTarget(), enqueued.getRequest().getArguments()); - } catch (FileNotFoundException ex) { + Scripts.invoke("commands/generic/" + command.getDefaultScriptCallback(), "execute", enqueued.getGalacticManager(), player, enqueued.getTarget(), enqueued.getRequest().getArguments()); + } catch (ResourceException e) { + // Script doesn't exist + } catch (ScriptException e) { + Log.a(e); } } } diff --git a/src/services/sui/SuiService.java b/src/services/sui/SuiService.java index 181bc2272..2a5cd1d92 100644 --- a/src/services/sui/SuiService.java +++ b/src/services/sui/SuiService.java @@ -27,16 +27,10 @@ ***********************************************************************************/ package services.sui; -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import com.projectswg.common.control.Service; import com.projectswg.common.debug.Log; - +import groovy.util.ResourceException; +import groovy.util.ScriptException; import intents.network.GalacticPacketIntent; import intents.sui.SuiWindowIntent; import network.packets.Packet; @@ -51,6 +45,12 @@ import resources.sui.SuiComponent; import resources.sui.SuiEvent; import utilities.Scripts; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + public class SuiService extends Service { private final Map> windows; @@ -135,7 +135,7 @@ public class SuiService extends Service { String script = window.getCallbackScript(callback); try { Scripts.invoke(script, callback, player, player.getCreatureObject(), event, parameters); - } catch (FileNotFoundException ex) { + } catch (ResourceException | ScriptException e) { Log.e("Callback script %s not found", script); } } else if (window.hasJavaCallback(callback)) { diff --git a/src/utilities/Scripts.java b/src/utilities/Scripts.java index e8ebb0cb7..6fce85e2d 100644 --- a/src/utilities/Scripts.java +++ b/src/utilities/Scripts.java @@ -27,71 +27,83 @@ ***********************************************************************************/ package utilities; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; +import groovy.lang.Binding; +import groovy.lang.Script; +import groovy.util.GroovyScriptEngine; +import groovy.util.ResourceException; +import groovy.util.ScriptException; +import main.ProjectSWG; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; - -import com.projectswg.common.debug.Log; +import java.io.IOException; public class Scripts { - - private static final String SCRIPTS; - private static final String EXTENSION; - private static final ScriptEngine ENGINE; - private static final Invocable INVOCABLE; - - static { - ScriptEngineManager engineManager = new ScriptEngineManager(); - SCRIPTS = "scripts/"; - EXTENSION = ".js"; - ENGINE = engineManager.getEngineByName("nashorn"); - INVOCABLE = (Invocable) ENGINE; - if (ENGINE == null) { - Log.e("ScriptEngine is null!"); - } else { - try { - ENGINE.put("intentFactory", new IntentFactory()); - ENGINE.eval("var RadialOption = Java.type('resources.radial.RadialOption')"); - ENGINE.eval("var RadialItem = Java.type('resources.radial.RadialItem')"); - ENGINE.eval("var Log = Java.type('com.projectswg.common.debug.Log')"); - ENGINE.eval("var SuiWindow = Java.type('resources.sui.SuiWindow')"); - ENGINE.eval("var SuiButtons = Java.type('resources.sui.SuiButtons')"); - ENGINE.eval("var SuiEvent = Java.type('resources.sui.SuiEvent')"); - } catch (Throwable t) { - Log.e(t); - } - } - } - - // Prevents instantiation. - private Scripts() {} - - /** - * @param script name of the script, relative to the scripts folder. - * @param function name of the specific function within the script. - * @param args to pass to the function. - * @return whatever the function returns. If the function doesn't have a return statement, this method returns {@code null}. - * If an exception occurs, {@code null} is returned. - * @throws java.io.FileNotFoundException if the script file wasn't found - */ - @SuppressWarnings("unchecked") - public static T invoke(String script, String function, Object... args) throws FileNotFoundException { - try { - ENGINE.eval(new InputStreamReader(new FileInputStream(SCRIPTS + script + EXTENSION), StandardCharsets.UTF_8)); - return (T) INVOCABLE.invokeFunction(function, args); - } catch (ScriptException | NoSuchMethodException t) { - Log.e("Error invoking script: " + script + " with function: " + function); - Log.e(" Args: " + Arrays.toString(args)); - Log.e(t); - return null; - } - } - + private static GroovyScriptEngine groovyEngine; + + static { + try { + groovyEngine = new GroovyScriptEngine("scripts/"); + } catch (IOException e) { + e.printStackTrace(); + } + + if (groovyEngine == null) + throw new ProjectSWG.CoreException("Could not load the Groovy Script Engine!"); + } + + /** + * Invokes a method from the provided Groovy Script. + * @param scriptName name of the script, relative to the scripts folder. + * @param method name of the specific method within the script. + * @param args to pass to the method. + * @return expected return type of the script. If the method doesn't have a return statement, this method returns {@code null}. + * If an exception occurs, {@code null} is returned. + * @throws java.io.FileNotFoundException if the script file wasn't found + */ + @SuppressWarnings("unchecked") + public static T invoke(String scriptName, String method, Object... args) throws ResourceException, ScriptException { + Script script = getScript(formatScriptName(scriptName)); + return (script != null) ? (T) script.invokeMethod(method, args) : null; + } + + /** + * Creates a binding from an array of variables. The name of the variable should lead the instance. An example would be: + *
  {@code setupScriptVariables("variableNameOne", variableOne, "variableNameTwo", variableTwo);} + * @param variables an array of variables. Variable names should lead the instance of the variable. + */ + public static Binding createBindings(Object... variables) { + Binding binding = new Binding(); + + for (int i = 0; i < variables.length; i++) { + if (!(variables[i] instanceof String)) + continue; + + binding.setVariable((String) variables[i], variables[i++]); + } + + return binding; + } + + /** + * Creates the Groovy Script and returns it. The method uses the {@link GroovyScriptEngine}'s createScript method. + * A new {@link Binding} is passed to the created {@link Script} to ensure thread safety, providing a new instance of the generated {@link Script} for the accessing thread. + * @param scriptName Name of the script to load + * @return a unique instance of {@link Script} + */ + public static Script getScript(String scriptName) throws ResourceException, ScriptException { + return getScript(scriptName, new Binding()); + } + + /** + * Creates the Groovy Script and returns it. The method uses the {@link GroovyScriptEngine}'s createScript method. + * @param scriptName name of the script to load + * @param binding the binding instance to use + * @return an instance of the obtained Groovy Script + */ + public static Script getScript(String scriptName, Binding binding) throws ResourceException, ScriptException { + return groovyEngine.createScript(formatScriptName(scriptName), binding); + } + + private static String formatScriptName(String name) { + return name.endsWith(".groovy") ? name : name + ".groovy"; + } }