diff --git a/.classpath b/.classpath index b116255..7c63137 100644 --- a/.classpath +++ b/.classpath @@ -13,5 +13,6 @@ + diff --git a/lib/luaj-jse-3.0.1.jar b/lib/luaj-jse-3.0.1.jar new file mode 100644 index 0000000..e125860 Binary files /dev/null and b/lib/luaj-jse-3.0.1.jar differ diff --git a/src/core/ObjectBuilderFactory.java b/src/core/ObjectBuilderFactory.java index e4f806d..ada6602 100644 --- a/src/core/ObjectBuilderFactory.java +++ b/src/core/ObjectBuilderFactory.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.concurrent.Semaphore; import commands.*; +import core.lua.PluginManager; import dataStructures.*; import net.dv8tion.jda.core.entities.Emote; import net.dv8tion.jda.core.entities.Member; @@ -38,6 +39,9 @@ public class ObjectBuilderFactory // RPManger for tracking RP system private static RPManager rpManager; + // Plugin manager + private static PluginManager pluginManager; + // Localization classes - these are singletons, but should be initialized before almost all other // things so their inclusion in the factory is to ensure they're started at the correct time. @SuppressWarnings("unused") private static LocStrings locStrings; @@ -408,6 +412,16 @@ public class ObjectBuilderFactory return rpManager; } + public static PluginManager ConstructPluginManager() + { + LazyInit(); + + if(pluginManager == null) + pluginManager = new PluginManager("./plugins/"); + + return pluginManager; + } + public static Integer GetGuildCount() { synchronized(guildCache) { diff --git a/src/core/lua/Plugin.java b/src/core/lua/Plugin.java index 4dc989a..9053ef6 100644 --- a/src/core/lua/Plugin.java +++ b/src/core/lua/Plugin.java @@ -1,8 +1,34 @@ package core.lua; -// To promote flexibility, plugins are lua file with predefined callbacks. -// They are executed between the initial built-in preprocessing and the command parsing. +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +//To promote flexibility, plugins are lua file with predefined callbacks. +//They are executed between the initial built-in preprocessing and the command parsing. public class Plugin { + public Path filepath; + public String contents; + public Plugin(Path filepath) + { + this.filepath = filepath; + + StringBuilder contentBuilder = new StringBuilder(); + + try + { + Stream stream = Files.lines(filepath, StandardCharsets.UTF_8); + stream.forEach(str -> contentBuilder.append(str).append("\n")); + stream.close(); + contents = contentBuilder.toString(); + } + catch (IOException e) + { + PluginLog.Error(e.getMessage()); + } + } } diff --git a/src/core/lua/PluginLoader.java b/src/core/lua/PluginLoader.java new file mode 100644 index 0000000..35db1a0 --- /dev/null +++ b/src/core/lua/PluginLoader.java @@ -0,0 +1,42 @@ +package core.lua; + +import org.luaj.vm2.Globals; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.lib.jse.JsePlatform; + +// This class wraps the lua layer - provides all the processing and function +// calls to the lua binding. +public class PluginLoader +{ + private Globals globals; + + public PluginLoader() + { + globals = JsePlatform.standardGlobals(); + PluginLog.Log("Created new PluginLoader lua environment"); + } + + public String Process(Plugin toProcess, String args) + { + globals.load(toProcess.contents).call(); + LuaValue pluginFunc = globals.get("plugin"); + + String output = null; + + try + { + LuaValue res = pluginFunc.call(LuaValue.valueOf(args)); + + if(res.isnil()) + output = null; + else + output = res.toString(); + } + catch(Exception e) + { + output = null; + } + + return output; + } +} diff --git a/src/core/lua/PluginLog.java b/src/core/lua/PluginLog.java new file mode 100644 index 0000000..8b084c7 --- /dev/null +++ b/src/core/lua/PluginLog.java @@ -0,0 +1,11 @@ +package core.lua; + +import utils.GlobalLog; +import utils.LogFilter; + +public class PluginLog +{ + public static void Log(String s) { GlobalLog.Log(LogFilter.Plugin, s); } + public static void Warn(String s) { GlobalLog.Warn(LogFilter.Plugin, s); } + public static void Error(String s) { GlobalLog.Error(LogFilter.Plugin, s); } +} diff --git a/src/core/lua/PluginManager.java b/src/core/lua/PluginManager.java index 3cc08cf..07937fe 100644 --- a/src/core/lua/PluginManager.java +++ b/src/core/lua/PluginManager.java @@ -1,8 +1,64 @@ package core.lua; -// Reads in, handles, and manipulates plugins. Plugins are -// loaded in the order they appear in the folder. +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.function.Consumer; +import java.util.stream.Stream; + +//Reads in, handles, and manipulates plugins. Plugins are +//loaded in the order they appear in the folder. public class PluginManager { + public PluginLoader pluginLoader; + public final String pluginFolder; + public ArrayList plugins; + public void AddPlugin(Path path) + { + plugins.add(new Plugin(path)); + } + + public PluginManager(String folder) + { + this.pluginFolder = folder; + plugins = new ArrayList(); + this.pluginLoader = new PluginLoader(); + + try + { + try (Stream paths = Files.walk(Paths.get(this.pluginFolder))) + { + paths.filter(Files::isRegularFile).forEach((path)->{ AddPlugin(path); }); + } + } + catch(Exception e) + { + PluginLog.Error(e.getMessage()); + } + } + + public String CallAll(String input) + { + for(int i = 0; i < plugins.size(); ++i) + { + Plugin script = plugins.get(i); + String out = pluginLoader.Process(script, input); + + if(out != null) + { + PluginLog.Log("Executed plugin at " + script.filepath); + return out; + } + } + + return null; + } + + public void PrintAll() + { + for(int i = 0; i < plugins.size(); ++i) + PluginLog.Log(plugins.get(i).contents.toString()); + } } diff --git a/src/main/Main.java b/src/main/Main.java index 1ca3707..5d79ca8 100644 --- a/src/main/Main.java +++ b/src/main/Main.java @@ -2,6 +2,7 @@ package main; import javax.security.auth.login.LoginException; import core.*; +import core.lua.PluginManager; import dataStructures.KittyChannel; import dataStructures.KittyGuild; import dataStructures.KittyRole; @@ -22,14 +23,15 @@ import net.dv8tion.jda.core.*; @SuppressWarnings("unused") public class Main extends ListenerAdapter { - // Variables and stuff + // Variables and bot specific objects private static JDA kitty; - private static CommandManager commandManager; - private static CommandEnabler commandEnabler; private static DatabaseManager databaseManager; + private static CommandEnabler commandEnabler; + private static CommandManager commandManager; private static Stats stats; private static RPManager rpManager; - + private static PluginManager pluginManager; + // Main test location public static void main(String[] args) throws InterruptedException, LoginException, Exception { @@ -37,8 +39,9 @@ public class Main extends ListenerAdapter databaseManager = ObjectBuilderFactory.ConstructDatabaseManager(); commandEnabler = ObjectBuilderFactory.ConstructCommandEnabler(); commandManager = ObjectBuilderFactory.ConstructCommandManager(commandEnabler); - rpManager = ObjectBuilderFactory.ConstructRPManager(); stats = ObjectBuilderFactory.ConstructStats(commandManager); + rpManager = ObjectBuilderFactory.ConstructRPManager(); + pluginManager = ObjectBuilderFactory.ConstructPluginManager(); // Bot startup kitty = new JDABuilder(AccountType.BOT).setToken(Ref.TestToken).buildBlocking(); @@ -61,19 +64,25 @@ public class Main extends ListenerAdapter // Specialized uncached objects Response response = new Response(event, kitty); UserInput input = new UserInput(event, guild); - + // Tweak object construction as necessary if(!PostProcessSetup(event, user, guild, channel, response, input)) return; - + // Track beans! - user.ChangeBeans(1); + user.ChangeBeans(1); // RP logging system RPManager.instance.addLine(channel, user, input); - // Issue the command - commandManager.InvokeOnNewThread(guild, channel, user, input, response); + // Run plugins right before invoking the commands but after all other setup + String output = pluginManager.CallAll(input.message); + + // Spin up the command if no plugins ran. Otherwise, send a response. + if(output == null) + commandManager.InvokeOnNewThread(guild, channel, user, input, response); + else + response.Call(output); // Run any upkeep we need to PerCommandUpkeep(); diff --git a/src/utils/LogFilter.java b/src/utils/LogFilter.java index 22742fe..c0ad6e3 100644 --- a/src/utils/LogFilter.java +++ b/src/utils/LogFilter.java @@ -3,7 +3,7 @@ package utils; public enum LogFilter { // Assign numbers as flags, so we can | ('or') them together as necessary - Debug(0), Command(1), Core(2), Util(4), Database(8), Response(16), Network(32), Strings(64); + Debug(0), Command(1), Core(2), Util(4), Database(8), Response(16), Network(32), Strings(64), Plugin(128); private final int value; private LogFilter(int value)