diff --git a/assets/config.csv b/assets/config.csv index 4847ae2..c84d561 100644 --- a/assets/config.csv +++ b/assets/config.csv @@ -82,7 +82,7 @@ "Localized Strings","BoopInfo","Kitty will react with a counter" "Localized Strings","BoopMultiple","%s booped several others - %s!" "Localized Strings","BoopPerson","%s booped %s!" -"Localized Strings","BoopStandard","Woah! %s booped me! That's %s total!" +"Localized Strings","BoopStandard","Woah! %s booped me! That's %s total! " "Localized Strings","CatchInfo","Play catch with yourself or with a friend with @!" "Localized Strings","ChangeIndicatorChanged","Indicator changed to `%s`" "Localized Strings","ChangeIndicatorError","Please specify a letter or symbol to use!" @@ -185,7 +185,7 @@ "Localized Strings","LeaderboardInfo","Shows you the 10 users with the most beans on the server" "Localized Strings","LeaderboardTitle","Server Leaderboard" "Localized Strings","MapHeight","" -"Localized Strings","MapInfo","Generates a map! You can pass additional information if you want with the flags `-s`. If one of the fields isn't provided, its default will be used. Note that adjusting the width and height impacts the map outcomes.nn**Defaults**nDefault Seed: Random,nDefault Width: 35(max %s),nDefault Height: 25(max %s)nn**Key**nLand: ~nMountain: â–²nBeach: " +"Localized Strings","MapInfo","Generates a map! You can pass additional information if you want with the flags `-s`. If one of the fields isn't provided, its default will be used. Note that adjusting the width and height impacts the map outcomes.\n\n**Defaults**\nDefault Seed: Random,\nDefault Width: 35(max %s),\nDefault Height: 25(max %s)\n\n**Key**\nLand: ~\nMountain: \u25B2\nBeach: =\nTrees: \u00A5\nVolcano: #" "Localized Strings","MapInvalid","Invalid arguments provided!" "Localized Strings","MapSeed","Seed" "Localized Strings","MapVersion","Using mapgen v0.1" diff --git a/assets/plugins/forbiddenword.lua b/assets/plugins/forbiddenword.lua index d9417c3..b94064b 100644 --- a/assets/plugins/forbiddenword.lua +++ b/assets/plugins/forbiddenword.lua @@ -8,6 +8,7 @@ function plugin(message, user) hours = ((timedif / (60*60)) % 24); mins = ((timedif / (60)) % 60); secs = timedif % 60; + return "*GASP!!!* \n" .. user.name .. " said **THE** word! It's been " .. round(days) .. " days, " .. round(hours) .. " hours, " .. round(mins) .. " minutes, and " .. round(secs) .. " seconds since the last time!"; end diff --git a/src/core/ConfigCSV.java b/src/core/ConfigCSV.java index 7b644d0..f4280ff 100644 --- a/src/core/ConfigCSV.java +++ b/src/core/ConfigCSV.java @@ -14,8 +14,10 @@ import java.util.Vector; import com.opencsv.CSVReader; import com.opencsv.CSVReaderBuilder; import com.opencsv.CSVWriter; +import com.opencsv.RFC4180ParserBuilder; import utils.GlobalLog; +import utils.StringUtils; public class ConfigCSV { @@ -41,7 +43,7 @@ public class ConfigCSV return new CSVWriter(writer, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.DEFAULT_QUOTE_CHARACTER, - '\\', + CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END); } @@ -60,7 +62,7 @@ public class ConfigCSV } Reader reader = Files.newBufferedReader(Paths.get(filepath)); - CSVReader csvReader = new CSVReaderBuilder(reader).withSkipLines(1).build(); + CSVReader csvReader = new CSVReaderBuilder(reader).withSkipLines(1).withCSVParser(new RFC4180ParserBuilder().build()).build(); fileContents.clear(); // Read contents. As for the line number, we not only start counting at 1, we also skip the header. @@ -74,7 +76,7 @@ public class ConfigCSV continue; } - ConfigItem parsedLine = new ConfigItem(record[0], record[1], record[2]); + ConfigItem parsedLine = new ConfigItem(StringUtils.unEscape(record[0]), StringUtils.unEscape(record[1]), StringUtils.unEscape(record[2])); fileContents.add(parsedLine); ++line; } diff --git a/src/core/ConfigItem.java b/src/core/ConfigItem.java index 5e73eb5..7f556d2 100644 --- a/src/core/ConfigItem.java +++ b/src/core/ConfigItem.java @@ -1,5 +1,7 @@ package core; +import utils.StringUtils; + // Everything in here must be a string type. This class does not // perform any parsing past naming columns, and just holds the raw data. public class ConfigItem @@ -30,7 +32,7 @@ public class ConfigItem public String[] getAll() { - return new String[] {type, key, value}; + return new String[] {StringUtils.reEscape(type), StringUtils.reEscape(key), StringUtils.reEscape(value) }; } public String toString() diff --git a/src/core/lua/Plugin.java b/src/core/lua/Plugin.java index 640d2eb..5348c30 100644 --- a/src/core/lua/Plugin.java +++ b/src/core/lua/Plugin.java @@ -26,12 +26,14 @@ public class Plugin public Plugin(Path filepath) { this.filepath = filepath; - - - StringBuilder contentBuilder = new StringBuilder(); - + reRead(); + } + + public void reRead() + { try { + StringBuilder contentBuilder = new StringBuilder(); Stream stream = Files.lines(filepath, StandardCharsets.UTF_8); stream.forEach(str -> contentBuilder.append(str).append("\n")); stream.close(); diff --git a/src/core/lua/PluginManager.java b/src/core/lua/PluginManager.java index f77203c..e6e1570 100644 --- a/src/core/lua/PluginManager.java +++ b/src/core/lua/PluginManager.java @@ -1,33 +1,31 @@ package core.lua; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.stream.Stream; + import dataStructures.KittyUser; +import utils.GlobalLog; +import utils.LogFilter; +import utils.io.DirectoryMonitor; // Reads in, handles, and manipulates plugins. Plugins are loaded in the order they appear in the folder. public class PluginManager { // Variables - public final String pluginFolder; public ArrayList plugins; + public DirectoryMonitor directoryMonitor; // Constructor public PluginManager(String folder) { - this.pluginFolder = folder; + this.directoryMonitor = new DirectoryMonitor(folder); plugins = new ArrayList(); try { - try (Stream paths = Files.walk(Paths.get(this.pluginFolder))) - { - paths.filter(Files::isRegularFile).forEach((path)->{ addPlugin(path); }); - } + directoryMonitor.getCurrentFiles().forEach((file) -> addPlugin(file.path)); } catch(Exception e) { @@ -35,6 +33,43 @@ public class PluginManager } } + public void update() + { + directoryMonitor.update( + (addedFile) -> + { + addPlugin(addedFile.path); + + GlobalLog.log(LogFilter.Plugin, "Found new plugin at " + addedFile.path); + }, + (updatedFile) -> + { + for(Plugin plugin : plugins) + { + if(plugin.filepath.toString().equalsIgnoreCase(updatedFile.path.toString())) + { + plugin.reRead(); + break; + } + } + + GlobalLog.log(LogFilter.Plugin, "A plugin was updated " + updatedFile.path); + }, + (deletedFile) -> + { + for(int i = plugins.size() - 1; i >= 0; i--) + { + Plugin plugin = plugins.get(i); + + if(plugin.filepath.toString().equalsIgnoreCase(deletedFile.path.toString())) + plugins.remove(i); + } + + GlobalLog.log(LogFilter.Plugin, "Plugin deleted at " + deletedFile.path); + }); + } + + // Registers a plugin based on the path public void addPlugin(Path path) { plugins.add(new Plugin(path)); @@ -66,6 +101,8 @@ public class PluginManager 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 a69aba9..06b562b 100644 --- a/src/main/Main.java +++ b/src/main/Main.java @@ -86,7 +86,8 @@ public class Main extends ListenerAdapter RPManager.instance.addLine(channel, user, input); // Run any pre-emptive upkeep we need to - Superintendent.perCommandUpkeepPre(); + if(!Superintendent.perCommandUpkeepPre(pluginManager)) + return; // Attempt to spin up a command. If the command doesn't exist. // Run plugins right before invoking the commands but after all other setup. @@ -111,6 +112,7 @@ public class Main extends ListenerAdapter } // Run any upkeep in post we need to - Superintendent.perCommandUpkeepPost(kittyCore, databaseManager); + if(!Superintendent.perCommandUpkeepPost(kittyCore, databaseManager)) + return; } } \ No newline at end of file diff --git a/src/main/Superintendent.java b/src/main/Superintendent.java index 1b49ecf..427b02f 100644 --- a/src/main/Superintendent.java +++ b/src/main/Superintendent.java @@ -6,6 +6,7 @@ import core.Config; import core.DatabaseManager; import core.RPManager; import core.Stats; +import core.lua.PluginManager; import dataStructures.KittyChannel; import dataStructures.KittyCore; import dataStructures.KittyGuild; @@ -71,11 +72,13 @@ public class Superintendent // Only called once per command. Good for lazily updating. // Happens just before the command / plugin runs. - public static boolean perCommandUpkeepPre() + public static boolean perCommandUpkeepPre(PluginManager pluginManager) { // Upkeep the config file monitoring Config.instance.upkeep(); + pluginManager.update(); + // Return if we succeeded or not. At this time, we always succeed. return true; } diff --git a/src/utils/StringUtils.java b/src/utils/StringUtils.java index 04c3dec..2da6f75 100644 --- a/src/utils/StringUtils.java +++ b/src/utils/StringUtils.java @@ -1,29 +1,18 @@ package utils; +import org.apache.commons.lang3.StringEscapeUtils; + +@SuppressWarnings("deprecation") public class StringUtils { public static String unEscape(String str) { - str = str.replaceAll("\\\\b", "\b"); - str = str.replaceAll("\\\\n", "\n"); - str = str.replaceAll("\\\\t", "\t"); - str = str.replaceAll("\\\\r", "\r"); - str = str.replaceAll("\\\\f", "\f"); - str = str.replaceAll("\\\\\"", "\""); - str = str.replaceAll("\\\\\\\\\\\\", "\\\\"); - - return str; + return StringEscapeUtils.unescapeJava(str); } public static String reEscape(String str) { - str = str.replaceAll("\\\b", "\\\\\\b"); - str = str.replaceAll("\\\n", "\\\\\\n"); - str = str.replaceAll("\\\t", "\\\\\\t"); - str = str.replaceAll("\\\r", "\\\\\\r"); - str = str.replaceAll("\\\f", "\\\\\\f"); - - return str; + return StringEscapeUtils.escapeJava(str); } // Finds first whitespace in the string