summaryrefslogtreecommitdiff
path: root/src/main/java/com/redstoner
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/redstoner')
-rw-r--r--src/main/java/com/redstoner/annotations/AutoRegisterListener.java15
-rw-r--r--src/main/java/com/redstoner/annotations/Commands.java15
-rw-r--r--src/main/java/com/redstoner/annotations/Version.java32
-rw-r--r--src/main/java/com/redstoner/coremods/moduleLoader/ModuleLoader.cmd24
-rw-r--r--src/main/java/com/redstoner/coremods/moduleLoader/ModuleLoader.java752
-rw-r--r--src/main/java/com/redstoner/exceptions/MissingVersionException.java22
-rw-r--r--src/main/java/com/redstoner/exceptions/NonSaveableConfigException.java9
-rw-r--r--src/main/java/com/redstoner/logging/Log4JFilter.java54
-rw-r--r--src/main/java/com/redstoner/logging/PrivateLogManager.java89
-rw-r--r--src/main/java/com/redstoner/misc/BroadcastFilter.java14
-rw-r--r--src/main/java/com/redstoner/misc/CommandHolderType.java13
-rw-r--r--src/main/java/com/redstoner/misc/JsonManager.java149
-rw-r--r--src/main/java/com/redstoner/misc/Main.java40
-rw-r--r--src/main/java/com/redstoner/misc/ModuleInfo.java81
-rw-r--r--src/main/java/com/redstoner/misc/Utils.java203
-rw-r--r--src/main/java/com/redstoner/misc/VersionHelper.java151
-rw-r--r--src/main/java/com/redstoner/misc/mysql/Config.java280
-rw-r--r--src/main/java/com/redstoner/misc/mysql/JSONManager.java107
-rw-r--r--src/main/java/com/redstoner/misc/mysql/MysqlHandler.java115
-rw-r--r--src/main/java/com/redstoner/misc/mysql/MysqlQueryHandler.java33
-rw-r--r--src/main/java/com/redstoner/misc/mysql/elements/ConstraintOperator.java24
-rw-r--r--src/main/java/com/redstoner/misc/mysql/elements/MysqlConstraint.java24
-rw-r--r--src/main/java/com/redstoner/misc/mysql/elements/MysqlDatabase.java90
-rw-r--r--src/main/java/com/redstoner/misc/mysql/elements/MysqlField.java33
-rw-r--r--src/main/java/com/redstoner/misc/mysql/elements/MysqlResult.java16
-rw-r--r--src/main/java/com/redstoner/misc/mysql/elements/MysqlTable.java133
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/MysqlType.java96
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/date/Date.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/date/DateTime.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/date/Time.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/date/TimeStamp.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/date/Year.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/number/BigInt.java12
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/number/Decimal.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/number/Double.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/number/Float.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/number/Int.java16
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/number/MediumInt.java12
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/number/SmallInt.java12
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/number/TinyInt.java12
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/Blob.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/Char.java16
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/Enum.java27
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/LongBlob.java8
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/LongText.java8
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/MediumBlob.java8
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/MediumText.java8
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/Set.java27
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/Text.java10
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/TinyText.java8
-rw-r--r--src/main/java/com/redstoner/misc/mysql/types/text/VarChar.java16
-rw-r--r--src/main/java/com/redstoner/modules/CoreModule.java24
-rw-r--r--src/main/java/com/redstoner/modules/Module.java54
-rw-r--r--src/main/java/com/redstoner/modules/ModuleLogger.java77
54 files changed, 3059 insertions, 0 deletions
diff --git a/src/main/java/com/redstoner/annotations/AutoRegisterListener.java b/src/main/java/com/redstoner/annotations/AutoRegisterListener.java
new file mode 100644
index 0000000..fabdb5f
--- /dev/null
+++ b/src/main/java/com/redstoner/annotations/AutoRegisterListener.java
@@ -0,0 +1,15 @@
+package com.redstoner.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** The auto register annotation, to be put onto Classes that implement listener when you are too lazy to register the events yourself.
+ *
+ * @author Pepich */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Version(major = 1, minor = 0, revision = 1, compatible = 1)
+public @interface AutoRegisterListener
+{}
diff --git a/src/main/java/com/redstoner/annotations/Commands.java b/src/main/java/com/redstoner/annotations/Commands.java
new file mode 100644
index 0000000..537bff0
--- /dev/null
+++ b/src/main/java/com/redstoner/annotations/Commands.java
@@ -0,0 +1,15 @@
+package com.redstoner.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.redstoner.misc.CommandHolderType;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Commands
+{
+ CommandHolderType value();
+}
diff --git a/src/main/java/com/redstoner/annotations/Version.java b/src/main/java/com/redstoner/annotations/Version.java
new file mode 100644
index 0000000..52d5145
--- /dev/null
+++ b/src/main/java/com/redstoner/annotations/Version.java
@@ -0,0 +1,32 @@
+package com.redstoner.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** The Version annotation, to be applied to all Classes that are part of the project.
+ *
+ * @author Pepich */
+@Target(ElementType.TYPE)
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Version
+{
+ /** The major indicator of the version. Will be used for compatibility detection.
+ *
+ * @return the major version as an int */
+ int major();
+
+ int minor();
+
+ int revision();
+
+ /** The compatibility part of the version number. Will be used for compatibility detection.</br>
+ * Set to -1 if it is supposed to be always compatible.</br>
+ * Defaults to 1.
+ *
+ * @return the smallest compatible version as an int. */
+ int compatible() default 1;
+}
diff --git a/src/main/java/com/redstoner/coremods/moduleLoader/ModuleLoader.cmd b/src/main/java/com/redstoner/coremods/moduleLoader/ModuleLoader.cmd
new file mode 100644
index 0000000..4e06bd0
--- /dev/null
+++ b/src/main/java/com/redstoner/coremods/moduleLoader/ModuleLoader.cmd
@@ -0,0 +1,24 @@
+command modules {
+ [empty] {
+ help Lists all modules. Color indicates status: §aENABLED §cDISABLED;
+ perm moduleloader.modules.list;
+ run list;
+ }
+ list {
+ help Lists all modules. Color indicates status: §aENABLED §cDISABLED;
+ perm moduleloader.modules.list;
+ run list;
+ }
+ load [string:name...] {
+ help (Re)-Loads a module. WARNING: Handle with care! This has direct affect on code being executed. This command will temporarily halt the main thread until the class loading operation was completed.;
+ perm moduleloader.modules.admin;
+ run load name;
+ type console;
+ }
+ unload [string:name...] {
+ help Unloads a module. WARNING: Handle with care! This has direct affect on code being executed. This command will temporarily halt the main thread until the class loading operation was completed.;
+ perm moduleloader.modules.admin;
+ run unload name;
+ type console;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/coremods/moduleLoader/ModuleLoader.java b/src/main/java/com/redstoner/coremods/moduleLoader/ModuleLoader.java
new file mode 100644
index 0000000..3886dd1
--- /dev/null
+++ b/src/main/java/com/redstoner/coremods/moduleLoader/ModuleLoader.java
@@ -0,0 +1,752 @@
+package com.redstoner.coremods.moduleLoader;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.configuration.InvalidConfigurationException;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import com.nemez.cmdmgr.Command;
+import com.nemez.cmdmgr.Command.AsyncType;
+import com.nemez.cmdmgr.CommandManager;
+import com.redstoner.annotations.AutoRegisterListener;
+import com.redstoner.annotations.Commands;
+import com.redstoner.annotations.Version;
+import com.redstoner.logging.PrivateLogManager;
+import com.redstoner.misc.Main;
+import com.redstoner.misc.ModuleInfo;
+import com.redstoner.misc.VersionHelper;
+import com.redstoner.modules.CoreModule;
+import com.redstoner.modules.Module;
+import com.redstoner.modules.ModuleLogger;
+
+import net.nemez.chatapi.click.Message;
+
+/** The module loader, mother of all modules. Responsible for loading and taking care of all modules.
+ *
+ * @author Pepich */
+@Version(major = 5, minor = 2, revision = 0, compatible = 5)
+public final class ModuleLoader implements CoreModule
+{
+ private static ModuleLoader instance;
+ private static final HashMap<Module, Boolean> modules = new HashMap<>();
+ private static HashMap<Module, ModuleInfo> moduleInfos = new HashMap<>();
+ private static HashMap<String, List<Module>> categorizes = new HashMap<>();
+ private static URL[] urls;
+ private static URLClassLoader mainLoader;
+ private static HashMap<Module, URLClassLoader> loaders = new HashMap<>();
+ private static File configFile;
+ private static FileConfiguration config;
+ private static boolean debugMode = false;
+ private static HashMap<Module, ModuleLogger> loggers = new HashMap<>();
+
+ private ModuleLoader()
+ {
+ try
+ {
+ config = Main.plugin.getConfig();
+ configFile = new File(Main.plugin.getDataFolder(), "config.yml");
+ urls = new URL[] {(new File(Main.plugin.getDataFolder(), "classes")).toURI().toURL()};
+ mainLoader = new URLClassLoader(urls, this.getClass().getClassLoader());
+ }
+ catch (MalformedURLException e)
+ {
+ System.out.println("Sumtin is wong with ya filesüstem m8. Fix eeeet or I won't werk!");
+ Bukkit.getPluginManager().disablePlugin(Main.plugin);
+ }
+ }
+
+ public static void init()
+ {
+ if (instance == null)
+ instance = new ModuleLoader();
+ ModuleInfo info = new ModuleInfo(ModuleLoader.class.getResourceAsStream("module.info"), instance);
+ moduleInfos.put(instance, info);
+ loggers.put(instance, new ModuleLogger(info.getDisplayName()));
+ CommandManager.registerCommand(ModuleLoader.class.getResourceAsStream("ModuleLoader.cmd"), instance,
+ Main.plugin);
+ }
+
+ public static final void loadFromConfig()
+ {
+ try
+ {
+ if (!configFile.exists())
+ {
+ configFile.getParentFile().mkdirs();
+ configFile.createNewFile();
+ }
+ config.load(configFile);
+ }
+ catch (FileNotFoundException e)
+ {}
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ catch (InvalidConfigurationException e)
+ {
+ configFile.delete();
+ try
+ {
+ configFile.createNewFile();
+ }
+ catch (IOException e1)
+ {
+ e1.printStackTrace();
+ }
+ instance.getLogger().error("Invalid config file! Creating new, blank file!");
+ }
+ List<String> coremods = config.getStringList("coremods");
+ if (coremods == null || coremods.isEmpty())
+ {
+ config.set("coremods", new String[] {"# Add the coremodules here!"});
+ Main.plugin.saveConfig();
+ try
+ {
+ config.save(configFile);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ List<String> autoload = config.getStringList("autoload");
+ if (autoload == null || autoload.isEmpty())
+ {
+ config.set("autoload", new String[] {"# Add the modules here!"});
+ Main.plugin.saveConfig();
+ try
+ {
+ config.save(configFile);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ if (!config.contains("debugMode"))
+ {
+ config.set("debugMode", false);
+ try
+ {
+ config.save(configFile);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ debugMode = config.getBoolean("debugMode");
+ for (String s : coremods)
+ if (!s.startsWith("#"))
+ if (!ModuleLoader.addDynamicModule(s))
+ {
+ instance.getLogger().error("Couldn't autocomplete path for module name: " + s
+ + "! If you're on a case sensitive filesystem, please take note that case correction does not work. Make sure that the classname has proper capitalisation.");
+
+ }
+ for (String s : autoload)
+ if (!s.startsWith("#"))
+ if (!ModuleLoader.addDynamicModule(s))
+ {
+ instance.getLogger().error("Couldn't autocomplete path for module name: " + s
+ + "! If you're on a case sensitive filesystem, please take note that case correction does not work. Make sure that the classname has proper capitalisation.");
+
+ }
+ updateConfig();
+ }
+
+ /** This method enables a specific module. If no module with that name is known to the loader yet it will be added to the list.</br>
+ * This method is deprecated, use enableDynamicModule instead. When using this method, dynamic reloading of the module will not be supported.
+ *
+ * @param clazz The class of the module to be enabled.
+ * @return true, when the module was successfully enabled. */
+ @Deprecated
+ public static final boolean enableModule(Class<? extends Module> clazz)
+ {
+ for (Module module : modules.keySet())
+ {
+ if (module.getClass().equals(clazz))
+ {
+ if (modules.get(module))
+ {
+ instance.getLogger().info("Module was already enabled! Ignoring module.!");
+ return true;
+ }
+ if (module.onEnable())
+ {
+ if (module.getClass().isAnnotationPresent(AutoRegisterListener.class)
+ && (module instanceof Listener))
+ {
+ Bukkit.getPluginManager().registerEvents((Listener) module, Main.plugin);
+ }
+ instance.getLogger().info("Enabled module " + module.getClass().getName());
+ instance.getLogger().info("Loaded module " + module.getClass().getName());
+ modules.put(module, true);
+ return true;
+ }
+ else
+ {
+ instance.getLogger().error("Failed to enable module " + module.getClass().getName());
+ return false;
+ }
+ }
+ }
+ try
+ {
+ Module m = clazz.newInstance();
+ modules.put(m, false);
+ if (m.onEnable())
+ {
+ if (m.getClass().isAnnotationPresent(AutoRegisterListener.class) && (m instanceof Listener))
+ {
+ Bukkit.getPluginManager().registerEvents((Listener) m, Main.plugin);
+ }
+ instance.getLogger().info("Loaded and enabled module " + m.getClass().getName());
+ instance.getLogger().info("Loaded module " + m.getClass().getName());
+ return true;
+ }
+ else
+ {
+ instance.getLogger().error("Failed to enable module " + m.getClass().getName());
+ return false;
+ }
+ }
+ catch (InstantiationException | IllegalAccessException e)
+ {
+ instance.getLogger()
+ .error("Could not add " + clazz.getName() + " to the list, constructor not accessible.");
+ return false;
+ }
+ }
+
+ private static final void enableLoadedModule(Module module, Version oldVersion)
+ {
+ try
+ {
+ InputStream infoFile = null;
+
+ if (VersionHelper.isCompatible(VersionHelper.create(5, 0, 0, 5), module.getClass())) {
+ String basePath = "plugins/ModuleLoader/classes/" + module.getClass().getName().replace(".", "/");
+
+ try {
+ infoFile = new FileInputStream(
+ new File(basePath.substring(0, basePath.lastIndexOf('/')+1) + "module.info"));
+ }
+ catch(Exception e) {
+ infoFile = null;
+ }
+ }
+ ModuleInfo info = new ModuleInfo(infoFile, module);
+
+ moduleInfos.put(module, info);
+
+ String category = info.getCategory();
+ if (!categorizes.containsKey(category))
+ categorizes.put(category, new ArrayList<>(Arrays.asList(module)));
+ else {
+ List<Module> modsInCat = categorizes.get(category);
+ modsInCat.add(module);
+ categorizes.put(category, modsInCat);
+ }
+
+ loggers.put(module, new ModuleLogger(info.getDisplayName()));
+
+
+ if (module.onEnable())
+ {
+ modules.put(module, true);
+ if (VersionHelper.getString(oldVersion).equals("0.0.0.0"))
+ module.firstLoad();
+ else if (!VersionHelper.getVersion(module.getClass()).equals(VersionHelper.getString(oldVersion)))
+ module.migrate(oldVersion);
+ if (VersionHelper.isCompatible(VersionHelper.create(5, 0, 0, 3), module.getClass()))
+ module.postEnable();
+ if (VersionHelper.isCompatible(VersionHelper.create(5, 0, 0, 4), module.getClass()))
+ {
+ Commands ann = module.getClass().getAnnotation(Commands.class);
+ if (ann != null)
+ {
+ switch (ann.value())
+ {
+ case File:
+ File f = new File("plugins/ModuleLoader/classes/"
+ + module.getClass().getName().replace(".", "/") + ".cmd");
+ CommandManager.registerCommand(f, module, Main.plugin);
+ break;
+ case Stream:
+ InputStream stream = module.getClass()
+ .getResourceAsStream(module.getClass().getSimpleName() + ".cmd");
+ CommandManager.registerCommand(stream, module, Main.plugin);
+ case String:
+ CommandManager.registerCommand(module.getCommandString(), module, Main.plugin);
+ break;
+ case None:
+ break;
+ }
+ }
+ }
+ instance.getLogger().info("Loaded module " + module.getClass().getName());
+ if (module.getClass().isAnnotationPresent(AutoRegisterListener.class) && (module instanceof Listener))
+ Bukkit.getPluginManager().registerEvents((Listener) module, Main.plugin);
+ }
+ else
+ instance.getLogger().error("Failed to load module " + module.getClass().getName());
+ }
+ catch (Exception e)
+ {
+ instance.getLogger().error("Failed to load module " + module.getClass().getName());
+ e.printStackTrace();
+ }
+ }
+
+ /** This method lists all modules to the specified CommandSender. The modules will be color coded correspondingly to their enabled status.
+ *
+ * @param sender The person to send the info to, usually the issuer of the command or the console sender.
+ * @return true. */
+ @Command(hook = "list", async = AsyncType.ALWAYS)
+ public boolean listModulesCommand(CommandSender sender)
+ {
+ boolean hasCategorys = hasCategories();
+ Message m = new Message(sender, null);
+ ModuleInfo ml_info = moduleInfos.get(instance);
+
+ m.appendText("§2--=[ ")
+ .appendTextHover("§2" + ml_info.getDisplayName(), ml_info.getModuleInfoHover())
+ .appendText("§2 ]=--\nModules:\n");
+
+ for (String cat: categorizes.keySet()) {
+ if (hasCategorys)
+ m.appendText("\n&7" + cat + ":\n");
+
+ int curModule = 1;
+ List<Module> mods = categorizes.get(cat);
+ for (Module mod : mods) {
+
+ ModuleInfo info = moduleInfos.get(mod);
+ m.appendTextHover((modules.get(mod) ? "§a" : "§c") + info.getDisplayName(), info.getModuleInfoHover());
+
+ if (curModule != mods.size())
+ m.appendText("&7, ");
+ curModule++;
+ }
+ m.appendText("\n");
+
+ }
+ m.send();
+ return true;
+ }
+
+ public static void disableModules()
+ {
+ for (Module module : modules.keySet())
+ {
+ disableModule(module);
+ }
+ }
+
+ public static void disableModule(Module module)
+ {
+ if (modules.get(module))
+ {
+ module.onDisable();
+ if (module.getClass().isAnnotationPresent(AutoRegisterListener.class) && (module instanceof Listener))
+ {
+ HandlerList.unregisterAll((Listener) module);
+ }
+ CommandManager.unregisterAllWithFallback(module.getClass().getSimpleName());
+ PrivateLogManager.unregister(module);
+ try
+ {
+ URLClassLoader loader = loaders.get(module);
+ if (loader != null)
+ loader.close();
+ }
+ catch (IOException e)
+ {}
+ finally
+ {
+ loaders.remove(module);
+ }
+ }
+ }
+
+ @Command(hook = "load")
+ public boolean loadModule(CommandSender sender, String name)
+ {
+ if (!addDynamicModule(name))
+ {
+ instance.getLogger().message(sender, true, "Couldn't autocomplete path for module name: " + name
+ + "! If you're on a case sensitive filesystem, please take note that case correction does not work. Make sure that the classname has proper capitalisation.");
+
+ }
+ updateConfig();
+ return true;
+ }
+
+ @Command(hook = "unload")
+ public boolean unloadModule(CommandSender sender, String name)
+ {
+ if (!removeDynamicModule(name))
+ instance.getLogger().error("Couldn't find module! Couldn't disable nonexisting module!");
+ return true;
+ }
+
+ public static final boolean addDynamicModule(String raw_name)
+ {
+ String[] raw = raw_name.split(" ");
+ String name = raw[0];
+ Version oldVersion;
+ if (raw.length > 1)
+ oldVersion = VersionHelper.getVersion(raw[1]);
+ else
+ oldVersion = VersionHelper.create(0, 0, 0, 0);
+ for (Module m : modules.keySet())
+ {
+ if (m.getClass().getName().equals(name))
+ {
+ instance.getLogger().info(
+ "Found existing module, attempting override. WARNING! This operation will halt the main thread until it is completed.");
+ instance.getLogger()
+ .info("Attempting to load new class definition before disabling and removing the old module");
+ boolean differs = false;
+ instance.getLogger().info("Old class definition: Class@" + m.getClass().hashCode());
+ ClassLoader delegateParent = mainLoader.getParent();
+ Class<?> newClass = null;
+ URLClassLoader cl = new URLClassLoader(urls, delegateParent);
+ try
+ {
+ newClass = cl.loadClass(m.getClass().getName());
+ instance.getLogger().info("Found new class definition: Class@" + newClass.hashCode());
+ differs = m.getClass() != newClass;
+ }
+ catch (ClassNotFoundException e)
+ {
+ instance.getLogger().error("Could not find a class definition, aborting now!");
+ e.printStackTrace();
+ try
+ {
+ cl.close();
+ }
+ catch (IOException e1)
+ {
+ e1.printStackTrace();
+ }
+ return false;
+ }
+ if (!differs)
+ {
+ if (!debugMode)
+ {
+ instance.getLogger().warn(
+ "New class definition equals old definition, are you sure you did everything right?");
+ instance.getLogger().info("Aborting now...");
+ try
+ {
+ cl.close();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ return false;
+ }
+ else
+ instance.getLogger().warn(
+ "New class definition equals old definition, but debugMode is enabled. Loading anyways.");
+ }
+ else
+ instance.getLogger().info("Found new class definition, attempting to instantiate:");
+ Module module = null;
+ try
+ {
+ module = (Module) newClass.newInstance();
+ }
+ catch (InstantiationException | IllegalAccessException e)
+ {
+ instance.getLogger().error("Could not instantiate the module, aborting!");
+ e.printStackTrace();
+ try
+ {
+ cl.close();
+ }
+ catch (IOException e1)
+ {
+ e1.printStackTrace();
+ }
+ return false;
+ }
+ instance.getLogger().info("Instantiated new class definition, checking versions");
+ oldVersion = m.getClass().getAnnotation(Version.class);
+ instance.getLogger().info("Current version: " + VersionHelper.getString(oldVersion));
+ Version newVersion = module.getClass().getAnnotation(Version.class);
+ instance.getLogger().info("Version of remote class: " + VersionHelper.getString(newVersion));
+ if (oldVersion.equals(newVersion))
+ {
+ if (!debugMode)
+ {
+ instance.getLogger().error("Detected equal module versions, " + (debugMode
+ ? " aborting now... Set debugMode to true in your config if you want to continue!"
+ : " continueing anyways."));
+ if (!debugMode)
+ {
+ try
+ {
+ cl.close();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ return false;
+ }
+ }
+ else
+ instance.getLogger()
+ .warn("New version equals old version, but debugMode is enabled. Loading anyways.");
+ }
+ else
+ instance.getLogger().info("Versions differ, disabling old module");
+ disableModule(m);
+ instance.getLogger().info("Disabled module, overriding the implementation");
+ modules.remove(m);
+ categorizes.get(moduleInfos.get(m).getCategory()).remove(m);
+ moduleInfos.remove(m);
+
+ try
+ {
+ if (loaders.containsKey(m))
+ loaders.remove(m).close();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ modules.put(module, false);
+ loaders.put(module, cl);
+ instance.getLogger().info("Successfully updated class definition. Enabling new implementation:");
+ enableLoadedModule(module, oldVersion);
+ return true;
+ }
+ }
+ ClassLoader delegateParent = mainLoader.getParent();
+ URLClassLoader cl = new URLClassLoader(urls, delegateParent);
+ try
+ {
+ Class<?> clazz = cl.loadClass(name);
+ Module module = (Module) clazz.newInstance();
+ modules.put(module, false);
+ loaders.put(module, cl);
+ enableLoadedModule(module, oldVersion);
+ return true;
+ }
+ catch (NoClassDefFoundError | ClassNotFoundException | InstantiationException | IllegalAccessException e)
+ {
+ try
+ {
+ cl.close();
+ }
+ catch (IOException e1)
+ {}
+ if (e instanceof NoClassDefFoundError)
+ {
+ NoClassDefFoundError exception = (NoClassDefFoundError) e;
+ String[] exMessage = exception.getMessage().split(" ");
+ String moduleName = exMessage[exMessage.length - 1]
+ .substring(0, exMessage[exMessage.length - 1].length()
+ - (exMessage[exMessage.length - 1].endsWith(")") ? 1 : 0))
+ .replace("/", ".");
+ if (!moduleName.equalsIgnoreCase(name))
+ {
+ instance.getLogger()
+ .error("Class &e" + moduleName + "&r couldn't be found! Suspecting a missing dependency!");
+ return false;
+ }
+ else
+ instance.getLogger().warn(
+ "Couldn't find class definition, attempting to get proper classname from thrown Exception.");
+ if (addDynamicModule(moduleName))
+ return true;
+ }
+ if (name.endsWith(".class"))
+ {
+ instance.getLogger().warn(
+ "Couldn't find class definition, but path ends with .class -> Attempting again with removed file suffix.");
+ if (addDynamicModule(name.replaceAll(".class$", "")))
+ return true;
+ }
+ if (!name.contains("."))
+ {
+ instance.getLogger().warn(
+ "Couldn't find class definition, suspecting incomplete path. Attempting autocompletion of path by adding a package name and trying again.");
+ if (addDynamicModule(name.toLowerCase() + "." + name))
+ return true;
+ }
+ if (!name.startsWith("com.redstoner.modules.") && name.contains("."))
+ {
+ instance.getLogger().warn(
+ "Couldn't find class definition, suspecting incomplete path. Attempting autocompletion of package name and trying again.");
+ if (addDynamicModule("com.redstoner.modules." + name))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static final boolean removeDynamicModule(String name)
+ {
+ for (Module m : modules.keySet())
+ {
+ if (m.getClass().getName().equals(name))
+ {
+ instance.getLogger().info(
+ "Found existing module, attempting unload. WARNING! This operation will halt the main thread until it is completed.");
+ instance.getLogger().info("Attempting to disable module properly:");
+ disableModule(m);
+ modules.remove(m);
+ categorizes.get(moduleInfos.get(m).getCategory()).remove(m);
+ moduleInfos.remove(m);
+ instance.getLogger().info("Disabled module.");
+ return true;
+ }
+ }
+ if (!name.startsWith("com.redstoner.modules."))
+ {
+ if (name.endsWith(".class"))
+ {
+ instance.getLogger().warn(
+ "Couldn't find class definition, but path ends with .class -> Attempting again with removed file suffix.");
+ if (removeDynamicModule(name.replaceAll(".class$", "")))
+ return true;
+ }
+ if (!name.contains("."))
+ {
+ instance.getLogger().warn(
+ "Couldn't find class definition, suspecting incomplete path. Attempting autocompletion of path by adding a package name and trying again.");
+ if (removeDynamicModule(name.toLowerCase() + "." + name))
+ return true;
+ }
+ if (!name.startsWith("com.redstoner.modules."))
+ {
+ instance.getLogger().warn(
+ "Couldn't find class definition, suspecting incomplete path. Attempting autocompletion of package name and trying again.");
+ if (removeDynamicModule("com.redstoner.modules." + name))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Finds a module by name for other modules to reference it.
+ *
+ * @param name the name of the module. Use the full path if you are not sure about the module's SimpleClassName being unique.
+ * @return the instance of the module or @null it none could be found */
+ public static Module getModule(String name)
+ {
+ for (Module m : modules.keySet())
+ if (m.getClass().getSimpleName().equalsIgnoreCase(name) || m.getClass().getName().equalsIgnoreCase(name))
+ return m;
+ return null;
+ }
+
+ /** Finds a module by name for other modules to reference it.
+ *
+ * @param name the name of the module. Use the full path if you are not sure about the module's SimpleClassName being unique.
+ * @return the instance of the module or @null it none could be found */
+ public static boolean exists(String name)
+ {
+ for (Module m : modules.keySet())
+ if (m.getClass().getSimpleName().equals(name) || m.getClass().getName().equals(name))
+ return true;
+ return false;
+ }
+
+ public static ModuleLogger getModuleLogger(Module module)
+ {
+ return loggers.get(module);
+ }
+
+ public static void updateConfig()
+ {
+ List<String> coremods = config.getStringList("coremods");
+ ArrayList<String> new_coremods = new ArrayList<>();
+ List<String> autoload = config.getStringList("autoload");
+ ArrayList<String> new_autoload = new ArrayList<>();
+
+ for (String s : coremods)
+ {
+ if (s.startsWith("#"))
+ {
+ new_coremods.add(s);
+ }
+ else
+ {
+ s = s.split(" ")[0];
+ try
+ {
+ new_coremods.add(getModule(s).getClass().getName() + " "
+ + VersionHelper.getVersion(getModule(s).getClass()));
+ }
+ catch (Exception e)
+ {
+ new_coremods.add(s + " " + VersionHelper.getString(VersionHelper.create(0, 0, 0, 0)));
+ }
+ }
+ }
+ for (String s : autoload)
+ {
+ if (s.startsWith("#"))
+ {
+ new_autoload.add(s);
+ }
+ else
+ {
+ s = s.split(" ")[0];
+ try
+ {
+ new_autoload.add(getModule(s).getClass().getName() + " "
+ + VersionHelper.getVersion(getModule(s).getClass()));
+ }
+ catch (Exception e)
+ {
+ new_autoload.add(s + " " + VersionHelper.getString(VersionHelper.create(0, 0, 0, 0)));
+ }
+ }
+ }
+
+ config.set("coremods", new_coremods);
+ config.set("autoload", new_autoload);
+ try
+ {
+ config.save(configFile);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public static JavaPlugin getPlugin() {
+ return Main.plugin;
+ }
+
+ public static boolean hasCategories() {
+ return !(categorizes.size() == 1 && categorizes.containsKey("Other"));
+ }
+}
diff --git a/src/main/java/com/redstoner/exceptions/MissingVersionException.java b/src/main/java/com/redstoner/exceptions/MissingVersionException.java
new file mode 100644
index 0000000..62032b6
--- /dev/null
+++ b/src/main/java/com/redstoner/exceptions/MissingVersionException.java
@@ -0,0 +1,22 @@
+package com.redstoner.exceptions;
+
+import com.redstoner.annotations.Version;
+
+/** To be thrown when a module is not annotated with its version. If this gets thrown, then oh boy, you're in trouble now.
+ *
+ * @author Pepich */
+@Version(major = 1, minor = 0, revision = 0, compatible = -1)
+public class MissingVersionException extends Exception
+{
+ private static final long serialVersionUID = 4940161335512222539L;
+
+ public MissingVersionException()
+ {
+ super();
+ }
+
+ public MissingVersionException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/redstoner/exceptions/NonSaveableConfigException.java b/src/main/java/com/redstoner/exceptions/NonSaveableConfigException.java
new file mode 100644
index 0000000..df33bff
--- /dev/null
+++ b/src/main/java/com/redstoner/exceptions/NonSaveableConfigException.java
@@ -0,0 +1,9 @@
+package com.redstoner.exceptions;
+
+public class NonSaveableConfigException extends Exception {
+ private static final long serialVersionUID = -7271481973389455510L;
+
+ public NonSaveableConfigException() {
+ super("This config does not support saving!");
+ }
+}
diff --git a/src/main/java/com/redstoner/logging/Log4JFilter.java b/src/main/java/com/redstoner/logging/Log4JFilter.java
new file mode 100644
index 0000000..1ebed09
--- /dev/null
+++ b/src/main/java/com/redstoner/logging/Log4JFilter.java
@@ -0,0 +1,54 @@
+package com.redstoner.logging;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+import org.apache.logging.log4j.message.Message;
+
+public class Log4JFilter extends AbstractFilter {
+
+ private static final long serialVersionUID = -5594073755007974254L;
+
+ private static Result validateMessage(Message message) {
+ if (message == null) {
+ return Result.NEUTRAL;
+ }
+ return validateMessage(message.getFormattedMessage());
+ }
+
+ private static Result validateMessage(String message) {
+ return PrivateLogManager.isHidden(message)
+ ? Result.DENY
+ : Result.NEUTRAL;
+ }
+
+ @Override
+ public Result filter(LogEvent event) {
+ Message candidate = null;
+ if (event != null) {
+ candidate = event.getMessage();
+ }
+ return validateMessage(candidate);
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
+ return validateMessage(msg);
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String msg, Object... params) {
+ return validateMessage(msg);
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
+ String candidate = null;
+ if (msg != null) {
+ candidate = msg.toString();
+ }
+ return validateMessage(candidate);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/logging/PrivateLogManager.java b/src/main/java/com/redstoner/logging/PrivateLogManager.java
new file mode 100644
index 0000000..e8451e0
--- /dev/null
+++ b/src/main/java/com/redstoner/logging/PrivateLogManager.java
@@ -0,0 +1,89 @@
+package com.redstoner.logging;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.logging.log4j.LogManager;
+
+import com.redstoner.misc.Utils;
+import com.redstoner.modules.Module;
+import com.redstoner.modules.ModuleLogger;
+
+public class PrivateLogManager {
+
+ private static Map<String, Module> registrar = new HashMap<>();
+ private static Map<String, String> commands = new HashMap<>();
+
+ private static final String ISSUED_COMMAND_TEXT = "issued server command: /";
+ private static final int ISSUED_COMMAND_TEXT_LENGTH = ISSUED_COMMAND_TEXT.length();
+
+ private static ModuleLogger logger;
+
+ public static void initialize() {
+ org.apache.logging.log4j.core.Logger logger;
+ logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger();
+ logger.addFilter(new Log4JFilter());
+ PrivateLogManager.logger = new ModuleLogger("PrivateLogManager");
+ }
+
+ public static void register(Module module, String command, String replacement) {
+ command = command.toLowerCase();
+ registrar.put(command, module);
+ commands.put(command, replacement);
+ logger.info(module.getClass().getSimpleName() + " registered &e/" + command
+ + (replacement.equals("")? "&7. Command will not be logged!"
+ : "&7, using replacement, &e" + replacement + "&7."));
+ }
+
+ public static void unregister(Module module) {
+ String unregestered = "";
+ Iterator<Map.Entry<String, Module>> i = registrar.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<String, Module> entry = i.next();
+ if (entry.getValue() == module) {
+ i.remove();
+ commands.remove(entry.getKey());
+ unregestered += "&e" + entry.getKey() + "&7, ";
+ }
+ }
+ if (!unregestered.equals(""))
+ logger.info("Unregistered " + unregestered.substring(0, unregestered.length() - 2) + "&7 for module, " + module.getClass().getSimpleName() + ".");
+ }
+
+ public static void unregister(Module module, String... toRemove) {
+ String unregestered = "";
+ for (int i = 0; i < toRemove.length; i++) {
+ String command = toRemove[i].toLowerCase();
+ registrar.remove(command);
+ if (commands.remove(command) != null)
+ unregestered += "&e" + command + "&7, ";
+ }
+ if (!unregestered.equals(""))
+ logger.info(module.getClass().getSimpleName() + " unregistered " + unregestered.substring(0, unregestered.length() - 2) + "&7.");
+ }
+
+ public static boolean isHidden(String message) {
+ if (message == null)
+ return false;
+
+ int index = message.indexOf(ISSUED_COMMAND_TEXT);
+ if (index == -1)
+ return false;
+
+ String command = message.substring(index + ISSUED_COMMAND_TEXT_LENGTH);
+
+ int spaceIndex = command.indexOf(" ");
+ command = spaceIndex == -1? command.toLowerCase() : command.substring(0, spaceIndex).toLowerCase();
+
+ String replacement = commands.get(command);
+ if (replacement == null)
+ return false;
+ if (replacement.equals(""))
+ return true;
+
+ String player = message.substring(0, message.indexOf(" "));
+ Utils.run(() -> System.out.println(replacement.replace("$s", player)));
+ return true;
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/BroadcastFilter.java b/src/main/java/com/redstoner/misc/BroadcastFilter.java
new file mode 100644
index 0000000..1f0ce04
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/BroadcastFilter.java
@@ -0,0 +1,14 @@
+package com.redstoner.misc;
+
+import org.bukkit.command.CommandSender;
+
+import com.redstoner.annotations.Version;
+
+/** Classes implementing this interface can be used to define a filter for the Utils.broadcast method for sending a message to more than one, but less than all users.
+ *
+ * @author Pepich */
+@Version(major = 1, minor = 0, revision = 0, compatible = 1)
+public interface BroadcastFilter
+{
+ public boolean sendTo(CommandSender recipient);
+}
diff --git a/src/main/java/com/redstoner/misc/CommandHolderType.java b/src/main/java/com/redstoner/misc/CommandHolderType.java
new file mode 100644
index 0000000..7c4383e
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/CommandHolderType.java
@@ -0,0 +1,13 @@
+package com.redstoner.misc;
+
+import com.redstoner.annotations.Version;
+
+/** @author Pepich */
+@Version(major = 4, minor = 0, revision = 0, compatible = -1)
+public enum CommandHolderType
+{
+ Stream,
+ File,
+ String,
+ None
+}
diff --git a/src/main/java/com/redstoner/misc/JsonManager.java b/src/main/java/com/redstoner/misc/JsonManager.java
new file mode 100644
index 0000000..13b51b6
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/JsonManager.java
@@ -0,0 +1,149 @@
+package com.redstoner.misc;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import com.redstoner.annotations.Version;
+
+/** This class provides simple JSON handling, like storing and loading from and to files.
+ *
+ * @author Pepich */
+@Version(major = 1, minor = 0, revision = 2, compatible = -1)
+public class JsonManager
+{
+ private JsonManager()
+ {}
+
+ /** Loads a JSONObject from a file.
+ *
+ * @param source the file to load from.
+ * @return the JSONObject or null if the source does not contain a valid JSONObject. */
+ public static JSONObject getObject(File source)
+ {
+ if (!source.exists())
+ return null;
+ JSONParser parser = new JSONParser();
+ try
+ {
+ FileReader reader = new FileReader(source);
+ Object rawObject = parser.parse(reader);
+ reader.close();
+ JSONObject jsonObject = (JSONObject) rawObject;
+ return jsonObject;
+ }
+ catch (IOException | ParseException e)
+ {}
+ return null;
+ }
+
+ /** Saves a JSONObject to a file. Will create the necessary FileStructure like folders and the file itself.</br>
+ * Note that this operation will be run on a different thread and you do not need to take care of that yourself.
+ *
+ * @param object the JSONObject to save.
+ * @param destination the file to write to. */
+ public static void save(JSONObject object, File destination)
+ {
+ Thread t = new Thread(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ saveSync(object, destination);
+ }
+ });
+ t.start();
+ }
+
+ /** Saves a JSONObject to a file. Will create the necessary FileStructure like folders and the file itself.</br>
+ * Note that this operation will be run on the same thread that you are calling it from!
+ *
+ * @param object the JSONObject to save.
+ * @param destination the file to write to. */
+ public static void saveSync(JSONObject object, File destination)
+ {
+ if (destination.exists())
+ destination.delete();
+ else if (!destination.getParentFile().exists())
+ destination.getParentFile().mkdirs();
+ try
+ {
+ destination.createNewFile();
+ FileWriter writer = new FileWriter(destination);
+ String json_string = object.toJSONString();
+ writer.write(json_string);
+ writer.flush();
+ writer.close();
+ }
+ catch (IOException e)
+ {}
+ }
+
+ /** Loads a JSONArray from a file.
+ *
+ * @param source the file to load from.
+ * @return the JSONArray or null if the source does not contain a valid JSONArray. */
+ public static JSONArray getArray(File source)
+ {
+ if (!source.exists())
+ return null;
+ JSONParser parser = new JSONParser();
+ try
+ {
+ Object rawObject = parser.parse(new FileReader(source));
+ JSONArray jsonArray = (JSONArray) rawObject;
+ return jsonArray;
+ }
+ catch (IOException | ParseException e)
+ {}
+ return null;
+ }
+
+ /** Saves a JSONArray to a file. Will create the necessary FileStructure like folders and the file itself.</br>
+ * Note that this operation will be run on a different thread and you do not need to take care of that yourself.
+ *
+ * @param object the JSONArray to save.
+ * @param destination the file to write to. */
+ public static void save(JSONArray array, File destination)
+ {
+ Thread t = new Thread(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ saveSync(array, destination);
+ }
+ });
+ t.start();
+ }
+
+ /** Saves a JSONArray to a file. Will create the necessary FileStructure like folders and the file itself.</br>
+ * Note that this operation will be run on the same thread that you are calling it from!
+ *
+ * @param object the JSONArray to save.
+ * @param destination the file to write to. */
+ public static void saveSync(JSONArray array, File destination)
+ {
+ if (destination.exists())
+ destination.delete();
+ else if (!destination.getParentFile().exists())
+ destination.getParentFile().mkdirs();
+ try
+ {
+ destination.createNewFile();
+ FileWriter writer = new FileWriter(destination);
+ String json_string = array.toJSONString();
+ writer.write(json_string);
+ writer.flush();
+ writer.close();
+ }
+ catch (IOException e)
+ {}
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/Main.java b/src/main/java/com/redstoner/misc/Main.java
new file mode 100644
index 0000000..40894ee
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/Main.java
@@ -0,0 +1,40 @@
+package com.redstoner.misc;
+
+import org.bukkit.plugin.java.JavaPlugin;
+
+import com.redstoner.annotations.Version;
+import com.redstoner.coremods.moduleLoader.ModuleLoader;
+import com.redstoner.logging.PrivateLogManager;
+import com.redstoner.misc.mysql.MysqlHandler;
+
+import net.nemez.chatapi.ChatAPI;
+
+/** Main class. Duh.
+ *
+ * @author Pepich */
+@Version(major = 5, minor = 1, revision = 0, compatible = -1)
+public class Main extends JavaPlugin
+{
+ public static JavaPlugin plugin;
+
+ @Override
+ public void onEnable()
+ {
+ plugin = this;
+
+ PrivateLogManager.initialize();
+
+ ChatAPI.initialize(this);
+ // Configger.init();
+ MysqlHandler.init();
+ ModuleLoader.init();
+ // Load modules from config
+ ModuleLoader.loadFromConfig();
+ }
+
+ @Override
+ public void onDisable()
+ {
+ ModuleLoader.disableModules();
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/ModuleInfo.java b/src/main/java/com/redstoner/misc/ModuleInfo.java
new file mode 100644
index 0000000..e96e813
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/ModuleInfo.java
@@ -0,0 +1,81 @@
+package com.redstoner.misc;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+import com.redstoner.coremods.moduleLoader.ModuleLoader;
+import com.redstoner.exceptions.MissingVersionException;
+import com.redstoner.modules.Module;
+
+public class ModuleInfo {
+
+ private String simpleName;
+ private String displayName;
+ private String category;
+ private String description;
+ private String version;
+
+ private String warning;
+
+ public ModuleInfo(InputStream descriptor, Module module) {
+ try {
+ InputStreamReader reader = new InputStreamReader(descriptor);
+ FileConfiguration config = YamlConfiguration.loadConfiguration(reader);
+
+ displayName = config.getString("displayName");
+ category = config.getString("category");
+ description = config.getString("description");
+ }
+ catch (Exception e) {
+ warning = "Descriptor file could not be loaded, using the class's name.";
+ }
+
+ simpleName = module.getClass().getSimpleName();
+
+ if (displayName == null)
+ displayName = simpleName;
+
+ if (category == null)
+ category = "Other";
+
+ try {
+ version = VersionHelper.getVersion(module.getClass());
+ } catch (MissingVersionException e) {}
+ }
+
+ public String getSimpleName() {
+ return simpleName;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getWarning() {
+ return warning;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public String getModuleInfoHover() {
+ return "&8&o" + getSimpleName() + "\n"
+ + "&r&e" + (getVersion() == null? "&cVersion Missing" : getVersion())
+ + "&r&9" + (ModuleLoader.hasCategories()? "\n" + getCategory() : "")
+ + "&r&7" + (getDescription() == null? "" : "\n\n" + getDescription());
+ }
+
+
+}
diff --git a/src/main/java/com/redstoner/misc/Utils.java b/src/main/java/com/redstoner/misc/Utils.java
new file mode 100644
index 0000000..e3fd68c
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/Utils.java
@@ -0,0 +1,203 @@
+package com.redstoner.misc;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import com.redstoner.annotations.Version;
+import com.redstoner.coremods.moduleLoader.ModuleLoader;
+
+import net.nemez.chatapi.ChatAPI;
+import net.nemez.chatapi.click.Message;
+
+/** The utils class containing utility functions. Those include but are not limited to sending formatted messages, broadcasts and more.
+ *
+ * @author Pepich */
+@Version(major = 4, minor = 0, revision = 2, compatible = 1)
+public final class Utils
+{
+ /** The @SimpleDateFormat used for getting the current date. */
+ public static SimpleDateFormat dateFormat = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]");
+
+ /** The Pattern for a UUID*/
+ private static final Pattern UUID_pattern = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}");
+ private static final Pattern Class_pattern = Pattern.compile(".*\\.");
+ private static final Pattern NoDolarSign_pattern = Pattern.compile("\\$\\d*");
+
+ /** Hidden constructor. Do not instantiate UTILS classes! :) */
+ private Utils()
+ {}
+
+ /** This method broadcasts a message to all players and console that are allowed by the filter. Set the filter to NULL to broadcast to everyone.</br>
+ * If you want to, you can set a message that will be logged to console. Set to null to not log anything.</br>
+ * You can still allow console in the filter to log the original message.
+ *
+ * @param prefix The prefix for the message. Set to NULL to let it auto generate.
+ * @param message the message to be sent around
+ * @param filter the BroadcastFilter to be applied.</br>
+ * Write a class implementing the interface and pass it to this method, the "sendTo()" method will be called for each recipient.
+ * @param logmessage the log message to appear in console. Set to null to not log this (you can still log the original message by returning true in the filter).
+ * @return the amount of people that received the message. */
+ public static int broadcast(String prefix, String message, BroadcastFilter filter)
+ {
+ if (prefix == null)
+ prefix = "§8[§2" + getCaller() + "§8]: ";
+ if (filter == null)
+ {
+ for (Player p : Bukkit.getOnlinePlayers())
+ p.sendMessage(prefix + message);
+ Bukkit.getConsoleSender().sendMessage(prefix + message);
+ return Bukkit.getOnlinePlayers().size() + 1;
+ }
+ else
+ {
+ int count = 0;
+ for (Player p : Bukkit.getOnlinePlayers())
+ if (filter.sendTo(p))
+ {
+ p.sendMessage(prefix + message);
+ count++;
+ }
+ if (filter.sendTo(Bukkit.getConsoleSender()))
+ {
+ Bukkit.getConsoleSender().sendMessage(prefix + message);
+ count++;
+ }
+ return count;
+ }
+ }
+
+ /** This method broadcasts a message to all players and console that are allowed by the filter. Set the filter to NULL to broadcast to everyone.</br>
+ * If you want to, you can set a message that will be logged to console. Set to null to not log anything.</br>
+ * You can still allow console in the filter to log the original message.
+ *
+ * @param prefix The prefix for the message. Set to NULL to let it auto generate.
+ * @param message the message to be sent around
+ * @param filter the BroadcastFilter to be applied.</br>
+ * Write a class implementing the interface and pass it to this method, the "sendTo()" method will be called for each recipient.
+ * @param logmessage the log message to appear in console. Set to null to not log this (you can still log the original message by returning true in the filter).
+ */
+ public static int broadcast(String prefix, Message message, BroadcastFilter filter)
+ {
+ if (prefix == null)
+ prefix = "§8[§2" + getCaller() + "§8]: ";
+ if (filter == null)
+ {
+ for (Player p : Bukkit.getOnlinePlayers())
+ ChatAPI.createMessage(p).appendText(prefix).appendMessage(message).send();
+ Bukkit.getConsoleSender().sendMessage(prefix + message.getRawMessage());
+ return Bukkit.getOnlinePlayers().size() + 1;
+ }
+ else
+ {
+ int count = 0;
+ for (Player p : Bukkit.getOnlinePlayers())
+ if (filter.sendTo(p))
+ {
+ ChatAPI.createMessage(p).appendText(prefix).appendMessage(message).send();
+ count++;
+ }
+ if (filter.sendTo(Bukkit.getConsoleSender()))
+ {
+ Bukkit.getConsoleSender().sendMessage(prefix + message.getRawMessage());
+ count++;
+ }
+ return count;
+ }
+ }
+
+ /** This method will find the next parent caller and return their class name, omitting package names.
+ *
+ * @return the Name of the calling class. */
+ private static final String getCaller()
+ {
+ StackTraceElement[] stackTrace = (new Exception()).getStackTrace();
+ String classname = "Utils";
+ for (int i = 0; classname.equals("Utils"); i++)
+ {
+ classname = Class_pattern.matcher(stackTrace[i].getClassName()).replaceAll("");
+ }
+ return classname;
+ }
+
+ /** This method will find the next parent caller and return their class name, omitting package names.
+ *
+ * @param directCaller used to prevent this method from returning the caller itself. Null if supposed to be ignored.
+ * @return the name of the calling class. */
+ public static final String getCaller(String... directCaller)
+ {
+ if (directCaller == null || directCaller.length == 0)
+ return getCaller();
+ StackTraceElement[] stackTrace = (new Exception()).getStackTrace();
+ String classname = "Utils";
+ List<String> callers = Arrays.asList(directCaller);
+ for (int i = 0; callers.contains(classname) || classname.equals("Utils"); i++)
+ {
+ classname = Class_pattern.matcher(stackTrace[i].getClassName()).replaceAll("");
+ }
+ classname = NoDolarSign_pattern.matcher(classname).replaceAll("");
+ return classname;
+ }
+
+ /** Provides a uniform way of getting the date for all modules.
+ *
+ * @return The current date in the format "[dd-mm-yyyy hh:mm:ss]" */
+ public static String getDate()
+ {
+ Date date = new Date(System.currentTimeMillis());
+ return dateFormat.format(date);
+ }
+
+ /** Provides a uniform way of getting the (display)name of a @CommandSender.
+ *
+ * @param sender The @CommandSender to get the name of.
+ * @return The DisplayName of the @CommandSender or if not a @Player, the name in blue. */
+ public static String getName(CommandSender sender)
+ {
+ if (sender instanceof Player)
+ return ((Player) sender).getDisplayName();
+ else
+ return "§9" + sender.getName();
+ }
+
+ /** Provides a uniform way of getting the UUID of a @CommandSender.
+ *
+ * @param sender The @CommandSender to get the UUID of.
+ * @return The UUID of the @CommandSender or if not a player, "CONSOLE" in blue. */
+ public static String getID(CommandSender sender)
+ {
+ String id;
+ if (sender instanceof Player)
+ id = ((Player) sender).getUniqueId().toString();
+ else
+ id = "CONSOLE";
+ return id;
+ }
+
+ /** Checks if the string is a UUID.
+ *
+ * @param toCheck String to check.
+ * @return if the string is a UUID.
+ */
+ public static boolean isUUID(String toCheck)
+ {
+ return UUID_pattern.matcher(toCheck).matches();
+ }
+
+ public static void run(Runnable r) {
+ run(r, 0);
+ }
+
+ public static void run(Runnable r, int delay) {
+ Bukkit.getScheduler().scheduleSyncDelayedTask(ModuleLoader.getPlugin(), r, delay);
+ }
+
+
+
+}
diff --git a/src/main/java/com/redstoner/misc/VersionHelper.java b/src/main/java/com/redstoner/misc/VersionHelper.java
new file mode 100644
index 0000000..e4a9403
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/VersionHelper.java
@@ -0,0 +1,151 @@
+package com.redstoner.misc;
+
+import java.lang.annotation.Annotation;
+
+import com.redstoner.annotations.Version;
+import com.redstoner.exceptions.MissingVersionException;
+
+/** This class can be used to compare modules against the loader version or against each other to prevent dependency issues.
+ *
+ * @author Pepich */
+@Version(major = 2, minor = 1, revision = 3, compatible = 0)
+public final class VersionHelper
+{
+ private VersionHelper()
+ {}
+
+ /** Checks two classes versions for compatibility.
+ *
+ * @param base The API to compare to.
+ * @param module The module to compare.
+ * @return true, when the module is up to date with the API, or the API supports outdated modules.
+ * @throws MissingVersionException When one of the parameters is not annotated with a @Version annotation. */
+ public static boolean isCompatible(Class<?> api, Class<?> module) throws MissingVersionException
+ {
+ if (!api.isAnnotationPresent(Version.class))
+ throw new MissingVersionException("The API is not annotated with a version.");
+ if (!module.isAnnotationPresent(Version.class))
+ throw new MissingVersionException("The module is not annotated with a version.");
+ Version apiVersion = api.getAnnotation(Version.class);
+ Version moduleVersion = module.getAnnotation(Version.class);
+ return isCompatible(apiVersion, moduleVersion);
+ }
+
+ /** Checks two classes versions for compatibility.
+ *
+ * @param base The API to compare to.
+ * @param module The module to compare.
+ * @return true, when the module is up to date with the API, or the API supports outdated modules.
+ * @throws MissingVersionException When one of the parameters is not annotated with a @Version annotation. */
+ public static boolean isCompatible(Version apiVersion, Class<?> module) throws MissingVersionException
+ {
+ if (!module.isAnnotationPresent(Version.class))
+ throw new MissingVersionException("The module is not annotated with a version.");
+ Version moduleVersion = module.getAnnotation(Version.class);
+ return isCompatible(apiVersion, moduleVersion);
+ }
+
+ /** Checks two classes versions for compatibility.
+ *
+ * @param base The API to compare to.
+ * @param module The module to compare.
+ * @return true, when the module is up to date with the API, or the API supports outdated modules.
+ * @throws MissingVersionException When one of the parameters is not annotated with a @Version annotation. */
+ public static boolean isCompatible(Class<?> api, Version moduleVersion) throws MissingVersionException
+ {
+ if (!api.isAnnotationPresent(Version.class))
+ throw new MissingVersionException("The API is not annotated with a version.");
+ Version apiVersion = api.getAnnotation(Version.class);
+ return isCompatible(apiVersion, moduleVersion);
+ }
+
+ /** Checks two versions for compatibility.
+ *
+ * @param base The API version to compare to.
+ * @param module The module version to compare.
+ * @return true, when the module is up to date with the API, or the API supports outdated modules. */
+ public static boolean isCompatible(Version apiVersion, Version moduleVersion)
+ {
+ if (apiVersion.major() >= moduleVersion.compatible())
+ return true;
+ if (apiVersion.compatible() == -1)
+ return false;
+ if (apiVersion.compatible() <= moduleVersion.major())
+ return true;
+ return false;
+ }
+
+ /** Returns the version of a given class as a String.
+ *
+ * @param clazz The class to grab the version number from.
+ * @return The version number of the class in format major.minor.revision.compatible.
+ * @throws MissingVersionException If the class is not annotated with @Version. */
+ public static String getVersion(Class<?> clazz) throws MissingVersionException
+ {
+ if (!clazz.isAnnotationPresent(Version.class))
+ throw new MissingVersionException("The given class is not associated with a version.");
+ Version ver = clazz.getAnnotation(Version.class);
+ return getString(ver);
+ }
+
+ /** Returns the String representation of a version.
+ *
+ * @param ver The version to be represented.
+ * @return The String representation. */
+ public static String getString(Version ver)
+ {
+ return ver.major() + "." + ver.minor() + "." + ver.revision() + "." + ver.compatible();
+ }
+
+ public static Version getVersion(String ver)
+ {
+ String[] raw = ver.split("\\.");
+ if (raw.length != 4)
+ return null;
+ return VersionHelper.create(Integer.parseInt(raw[0]), Integer.parseInt(raw[1]), Integer.parseInt(raw[2]),
+ Integer.parseInt(raw[3]));
+ }
+
+ /** This method creates a new Version to use for compatibility checks.
+ *
+ * @param major The major version
+ * @param minor The minor version
+ * @param revision The revision
+ * @param compatible The compatibility tag
+ * @return */
+ public static Version create(int major, int minor, int revision, int compatible)
+ {
+ return new Version()
+ {
+ @Override
+ public Class<? extends Annotation> annotationType()
+ {
+ return Version.class;
+ }
+
+ @Override
+ public int revision()
+ {
+ return revision;
+ }
+
+ @Override
+ public int minor()
+ {
+ return minor;
+ }
+
+ @Override
+ public int major()
+ {
+ return major;
+ }
+
+ @Override
+ public int compatible()
+ {
+ return compatible;
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/Config.java b/src/main/java/com/redstoner/misc/mysql/Config.java
new file mode 100644
index 0000000..519b20a
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/Config.java
@@ -0,0 +1,280 @@
+package com.redstoner.misc.mysql;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import com.redstoner.exceptions.NonSaveableConfigException;
+import com.redstoner.misc.Main;
+
+public class Config
+{
+ private File file;
+ private JSONObject config;
+ private JSONParser parser;
+
+ public Config()
+ {
+ file = null;
+ parser = new JSONParser();
+ config = new JSONObject();
+ }
+
+ public Config(JSONObject config)
+ {
+ this.file = null;
+ this.parser = new JSONParser();
+ this.config = config;
+ }
+
+ private Config(File file) throws IOException, ParseException
+ {
+ this.file = file;
+ parser = new JSONParser();
+ if (file.exists())
+ {
+ config = loadConfig(file);
+ }
+ else
+ {
+ config = new JSONObject();
+ }
+ }
+
+ public static final Config getConfig(String fileName) throws IOException, ParseException
+ {
+ return new Config(new File(Main.plugin.getDataFolder(), fileName));
+ }
+
+ public static final Config getConfig(File file) throws IOException, ParseException
+ {
+ return new Config(file);
+ }
+
+ private JSONObject loadConfig(File file) throws IOException, ParseException
+ {
+ FileReader reader = new FileReader(file);
+ JSONObject object = (JSONObject) parser.parse(reader);
+ reader.close();
+ return object;
+ }
+
+ @Override
+ public String toString()
+ {
+ return config.toJSONString();
+ }
+
+ public JSONObject asObject()
+ {
+ return config;
+ }
+
+ public void save() throws IOException, NonSaveableConfigException
+ {
+ if (file == null)
+ {
+ throw new NonSaveableConfigException();
+ }
+ PrintWriter writer = new PrintWriter(file);
+ writer.write(config.toJSONString());
+ writer.close();
+ }
+
+ public void refresh() throws IOException, ParseException, NonSaveableConfigException
+ {
+ if (file == null)
+ {
+ throw new NonSaveableConfigException();
+ }
+ loadConfig(file);
+ }
+
+ public void setFile(String fileName)
+ {
+ file = new File(Main.plugin.getDataFolder(), fileName);
+ }
+
+ public void setFile(File file)
+ {
+ this.file = file;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void put(String key, String value)
+ {
+ config.put(key, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void put(String key, List<String> value)
+ {
+ JSONArray array = new JSONArray();
+ for (String entry : value)
+ {
+ array.add(entry);
+ }
+ config.put(key, array);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void putArray(String key, JSONArray value)
+ {
+ config.put(key, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void put(String key, Map<String, String> value)
+ {
+ JSONObject object = new JSONObject();
+ for (String valKey : value.keySet())
+ {
+ String valVal = value.get(valKey);
+ object.put(valKey, valVal);
+ }
+ config.put(key, object);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void put(String key, JSONObject value)
+ {
+ config.put(key, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void putAll(Map<String, String> entry)
+ {
+ for (String key : entry.keySet())
+ {
+ String value = entry.get(key);
+ config.put(key, value);
+ }
+ }
+
+ public boolean containsKey(String key)
+ {
+ return config.containsKey(key);
+ }
+
+ public String get(String key)
+ {
+ if (containsKey(key))
+ {
+ Object value = config.get(key);
+ if (value instanceof String)
+ {
+ return (String) value;
+ }
+ }
+ return null;
+ }
+
+ public String getOrDefault(String key, String defaultValue)
+ {
+ if (containsKey(key))
+ {
+ Object value = config.get(key);
+ if (value instanceof String)
+ {
+ return (String) value;
+ }
+ return null;
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<String> getList(String key)
+ {
+ if (containsKey(key))
+ {
+ Object value = config.get(key);
+ if (value instanceof JSONArray)
+ {
+ JSONArray array = (JSONArray) value;
+ List<String> output = new ArrayList<String>();
+ for (String entry : (String[]) array.toArray(new String[0]))
+ {
+ output.add(entry);
+ }
+ return output;
+ }
+ }
+ return null;
+ }
+
+ public JSONArray getArray(String key)
+ {
+ if (containsKey(key))
+ {
+ Object value = config.get(key);
+ if (value instanceof JSONArray)
+ {
+ JSONArray array = (JSONArray) value;
+ return array;
+ }
+ }
+ return null;
+ }
+
+ public Map<String, String> getMap(String key)
+ {
+ if (containsKey(key))
+ {
+ Object value = config.get(key);
+ if (value instanceof JSONObject)
+ {
+ JSONObject object = (JSONObject) value;
+ @SuppressWarnings("unchecked")
+ Set<Map.Entry<String, String>> entrySet = object.entrySet();
+ Map<String, String> output = new HashMap<String, String>();
+ for (Map.Entry<String, String> entry : entrySet)
+ {
+ output.put(entry.getKey(), entry.getValue());
+ }
+ return output;
+ }
+ }
+ return null;
+ }
+
+ public JSONObject getObject(String key)
+ {
+ if (containsKey(key))
+ {
+ Object value = config.get(key);
+ if (value instanceof JSONObject)
+ {
+ JSONObject object = (JSONObject) value;
+ return object;
+ }
+ }
+ return null;
+ }
+
+ public void remove(String key)
+ {
+ config.remove(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Set<Entry<String, String>> getAll()
+ {
+ return config.entrySet();
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/JSONManager.java b/src/main/java/com/redstoner/misc/mysql/JSONManager.java
new file mode 100644
index 0000000..ae248d5
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/JSONManager.java
@@ -0,0 +1,107 @@
+package com.redstoner.misc.mysql;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import com.redstoner.misc.Main;
+
+public class JSONManager
+{
+ public static Map<Serializable, Serializable> getConfiguration(String fileName)
+ {
+ File file = new File(Main.plugin.getDataFolder(), fileName);
+ if (!file.exists())
+ {
+ try
+ {
+ PrintWriter writer = new PrintWriter(file.getAbsolutePath(), "UTF-8");
+ writer.println("{}");
+ writer.close();
+ }
+ catch (FileNotFoundException | UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ try
+ {
+ return loadMap(file);
+ }
+ catch (IOException | ParseException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static void saveConfiguration(Map<Serializable, Serializable> config, String fileName)
+ {
+ try
+ {
+ saveMap(new File(Main.plugin.getDataFolder(), fileName), config);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void saveList(File file, List<Serializable> entries) throws IOException
+ {
+ JSONArray array = new JSONArray();
+ array.addAll(entries);
+ FileWriter writer = new FileWriter(file);
+ writer.write(array.toJSONString());
+ writer.close();
+ }
+
+ public static List<Serializable> loadList(File file) throws IOException, ParseException
+ {
+ FileReader read = new FileReader(file);
+ List<Serializable> entries = new ArrayList<>();
+ JSONArray array = (JSONArray) new JSONParser().parse(read);
+ for (Object o : array)
+ {
+ entries.add((Serializable) o);
+ }
+ return entries;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void saveMap(File file, Map<Serializable, Serializable> entries) throws IOException
+ {
+ JSONObject map = new JSONObject();
+ map.putAll(entries);
+ FileWriter writer = new FileWriter(file);
+ writer.write(map.toJSONString());
+ writer.close();
+ }
+
+ public static Map<Serializable, Serializable> loadMap(File file) throws IOException, ParseException
+ {
+ FileReader reader = new FileReader(file);
+ JSONObject map = (JSONObject) new JSONParser().parse(reader);
+ Map<Serializable, Serializable> entries = new HashMap<>();
+ for (Object o : map.keySet())
+ {
+ entries.put((Serializable) o, (Serializable) map.get(o));
+ }
+ return entries;
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/MysqlHandler.java b/src/main/java/com/redstoner/misc/mysql/MysqlHandler.java
new file mode 100644
index 0000000..909d276
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/MysqlHandler.java
@@ -0,0 +1,115 @@
+package com.redstoner.misc.mysql;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.json.simple.parser.ParseException;
+
+import com.redstoner.misc.Main;
+import com.redstoner.misc.mysql.elements.MysqlDatabase;
+
+public class MysqlHandler
+{
+ public static MysqlHandler INSTANCE;
+ private String url, username, password;
+
+ public MysqlHandler(String hostname, int port, String username, String password)
+ {
+ this.url = "jdbc:mysql://" + hostname + ":" + port + "/";
+ this.username = username;
+ this.password = password;
+ }
+
+ public static void init()
+ {
+ Map<Serializable, Serializable> mysqlCredentials = new HashMap<>();
+ File mysqlCredentialsFile = new File(Main.plugin.getDataFolder(), "mysqlCredentials.json");
+ if (mysqlCredentialsFile.exists())
+ {
+ try
+ {
+ mysqlCredentials = JSONManager.loadMap(mysqlCredentialsFile);
+ }
+ catch (IOException | ParseException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ else
+ {
+ Bukkit.getConsoleSender().sendMessage(
+ ChatColor.RED + "MySQL config does not exist, creating an example one, things might (will) break!");
+ mysqlCredentials.put("hostname", "localhost");
+ mysqlCredentials.put("port", "3306");
+ mysqlCredentials.put("username", "your username here");
+ mysqlCredentials.put("password", "your password here");
+ try
+ {
+ JSONManager.saveMap(mysqlCredentialsFile, mysqlCredentials);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ String hostname = (String) mysqlCredentials.get("hostname");
+ int port = Integer.valueOf((String) mysqlCredentials.get("port"));
+ String username = (String) mysqlCredentials.get("username");
+ String password = (String) mysqlCredentials.get("password");
+ INSTANCE = new MysqlHandler(hostname, port, username, password);
+ }
+
+ private Connection getConnection(String databaseName) throws IllegalStateException
+ {
+ Connection connection = null;
+ try
+ {
+ connection = DriverManager.getConnection(url + databaseName, username, password);
+ }
+ catch (SQLException e)
+ {
+ throw new IllegalStateException("Cannot connect to the database!", e);
+ }
+ return connection;
+ }
+
+ public MysqlDatabase getDatabase(String databaseName)
+ {
+ return new MysqlDatabase(getConnection(databaseName));
+ }
+
+ public List<MysqlDatabase> getDatabases()
+ {
+ try
+ {
+ List<MysqlDatabase> databases = new ArrayList<>();
+ Connection connection = DriverManager.getConnection(url.substring(0, url.length()), username, password);
+ DatabaseMetaData metadata = connection.getMetaData();
+ ResultSet queryResults = metadata.getCatalogs();
+ while (queryResults.next())
+ {
+ String databaseName = queryResults.getString("TABLE_CAT");
+ databases.add(new MysqlDatabase(getConnection(databaseName)));
+ }
+ connection.close();
+ return databases;
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/MysqlQueryHandler.java b/src/main/java/com/redstoner/misc/mysql/MysqlQueryHandler.java
new file mode 100644
index 0000000..f89a08a
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/MysqlQueryHandler.java
@@ -0,0 +1,33 @@
+package com.redstoner.misc.mysql;
+
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class MysqlQueryHandler {
+ public static ResultSet queryResult(Connection connection, String query) {
+ try {
+ Statement statement = connection.createStatement();
+ ResultSet results = statement.executeQuery(query);
+
+ return results;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static boolean queryNoResult(Connection connection, String query) {
+ try {
+ CallableStatement statement = connection.prepareCall(query);
+ statement.execute();
+
+ return true;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/elements/ConstraintOperator.java b/src/main/java/com/redstoner/misc/mysql/elements/ConstraintOperator.java
new file mode 100644
index 0000000..45cb33c
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/elements/ConstraintOperator.java
@@ -0,0 +1,24 @@
+package com.redstoner.misc.mysql.elements;
+
+public enum ConstraintOperator {
+ LESS_THAN, GREATER_THAN, EQUAL, NOT_EQUAL, LESS_THAN_OR_EQUAL, GREATER_THAN_OR_EQUAL;
+
+ public String toString() {
+ switch (this) {
+ case LESS_THAN:
+ return "<";
+ case GREATER_THAN:
+ return ">";
+ case EQUAL:
+ return "=";
+ case NOT_EQUAL:
+ return "!=";
+ case LESS_THAN_OR_EQUAL:
+ return "<=";
+ case GREATER_THAN_OR_EQUAL:
+ return ">=";
+ default:
+ return "=";
+ }
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/elements/MysqlConstraint.java b/src/main/java/com/redstoner/misc/mysql/elements/MysqlConstraint.java
new file mode 100644
index 0000000..d651344
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/elements/MysqlConstraint.java
@@ -0,0 +1,24 @@
+package com.redstoner.misc.mysql.elements;
+
+public class MysqlConstraint {
+ private String fieldName, value;
+ private ConstraintOperator operator;
+
+ public MysqlConstraint(String fieldName, ConstraintOperator operator, String value) {
+ this.fieldName = fieldName;
+ this.operator = operator;
+ this.value = value;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public ConstraintOperator getOperator() {
+ return operator;
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/elements/MysqlDatabase.java b/src/main/java/com/redstoner/misc/mysql/elements/MysqlDatabase.java
new file mode 100644
index 0000000..91c0fe4
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/elements/MysqlDatabase.java
@@ -0,0 +1,90 @@
+package com.redstoner.misc.mysql.elements;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.redstoner.misc.mysql.MysqlQueryHandler;
+
+public class MysqlDatabase {
+ private Connection connection;
+
+ public MysqlDatabase(Connection connection) {
+ this.connection = connection;
+ }
+
+ public String getName() {
+ try {
+ return connection.getCatalog();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public MysqlTable getTable(String name) {
+ return new MysqlTable(this, name);
+ }
+
+ public boolean createTable(String name, MysqlField... description) {
+ return MysqlQueryHandler.queryNoResult(connection, "CREATE TABLE `" + name + "` " + getDescription(description) + ";");
+ }
+
+ public boolean createTableIfNotExists(String name, MysqlField... description) {
+ return MysqlQueryHandler.queryNoResult(connection, "CREATE TABLE IF NOT EXISTS `" + name + "` " + getDescription(description) + ";");
+ }
+
+ public boolean dropTable(String name) {
+ return MysqlQueryHandler.queryNoResult(connection, "DROP TABLE `" + name + "`;");
+ }
+
+ public boolean drop() {
+ return MysqlQueryHandler.queryNoResult(connection, "DROP DATABASE `" + getName() + "`;");
+ }
+
+ public List<MysqlTable> getTables() {
+ try {
+ List<MysqlTable> tables = new ArrayList<>();
+ DatabaseMetaData metadata = connection.getMetaData();
+ ResultSet queryResults = metadata.getTables(null, null, "%", null);
+
+ while (queryResults.next()) {
+ tables.add(new MysqlTable(this, queryResults.getString(3)));
+ }
+
+ return tables;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ protected Connection getConnection() {
+ return connection;
+ }
+
+ private String getDescription(MysqlField... description) {
+ String desc = "(";
+
+ for (int i = 0; i < description.length; i++) {
+ String nil = "";
+
+ if (description[i].canBeNull()) {
+ nil = " NOT NULL";
+ }
+
+ desc += "`" + description[i].getName() + "` " + description[i].getType().getName() + nil;
+
+ if (i < description.length - 1) {
+ desc += ",";
+ }
+ }
+
+ desc += ")";
+
+ return desc;
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/elements/MysqlField.java b/src/main/java/com/redstoner/misc/mysql/elements/MysqlField.java
new file mode 100644
index 0000000..61cba2e
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/elements/MysqlField.java
@@ -0,0 +1,33 @@
+package com.redstoner.misc.mysql.elements;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class MysqlField {
+ private String name;
+ private MysqlType type;
+ private boolean canBeNull;
+
+ public MysqlField(String name, MysqlType type, boolean canBeNull) {
+ this.name = name;
+ this.type = type;
+ this.canBeNull = canBeNull;
+ }
+
+ public MysqlField(String name, String type, boolean canBeNull) {
+ this.name = name;
+ this.type = MysqlType.getTypeFromString(type);
+ this.canBeNull = canBeNull;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public MysqlType getType() {
+ return type;
+ }
+
+ public boolean canBeNull() {
+ return canBeNull;
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/elements/MysqlResult.java b/src/main/java/com/redstoner/misc/mysql/elements/MysqlResult.java
new file mode 100644
index 0000000..6db0769
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/elements/MysqlResult.java
@@ -0,0 +1,16 @@
+package com.redstoner.misc.mysql.elements;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class MysqlResult {
+ private ResultSet results;
+
+ public MysqlResult(ResultSet results) {
+ this.results = results;
+ }
+
+ public Object getObject(int columnIndex, Class<?> type) throws SQLException {
+ return results.getObject(columnIndex, type);
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/elements/MysqlTable.java b/src/main/java/com/redstoner/misc/mysql/elements/MysqlTable.java
new file mode 100644
index 0000000..6656fcd
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/elements/MysqlTable.java
@@ -0,0 +1,133 @@
+package com.redstoner.misc.mysql.elements;
+
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.redstoner.misc.mysql.MysqlQueryHandler;
+
+public class MysqlTable
+{
+ private MysqlDatabase database;
+ private String name;
+
+ public MysqlTable(MysqlDatabase database, String name)
+ {
+ this.database = database;
+ this.name = name;
+ }
+
+ public String getName()
+ {
+ return this.name;
+ }
+
+ public MysqlField[] describe()
+ {
+ try
+ {
+ List<MysqlField> description = new ArrayList<>();
+ DatabaseMetaData metadata = database.getConnection().getMetaData();
+ ResultSet queryResults = metadata.getColumns(null, null, name, null);
+ while (queryResults.next())
+ {
+ description.add(new MysqlField(queryResults.getString(4),
+ queryResults.getString(6).split(" ")[0] + "(" + queryResults.getString(7) + ")",
+ queryResults.getBoolean(11)));
+ }
+ return description.toArray(new MysqlField[0]);
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public boolean insert(String... values)
+ {
+ MysqlField[] description = describe();
+ if (values.length > 0 && values.length == description.length)
+ {
+ String val = "(\"" + String.join("\",\"", values) + "\")";
+ return MysqlQueryHandler.queryNoResult(database.getConnection(),
+ "INSERT INTO `" + name + "` VALUES " + val + ";");
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public Object[] get(String fieldName, MysqlConstraint... constraints)
+ {
+ ResultSet results = MysqlQueryHandler.queryResult(database.getConnection(),
+ "SELECT " + fieldName + " FROM `" + name + "`" + getConstraints(constraints) + ";");
+ List<Object> resObj = new ArrayList<>();
+ try
+ {
+ while (results.next())
+ {
+ resObj.add(results.getObject(1));
+ }
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ return new Object[0];
+ }
+ return resObj.toArray(new Object[0]);
+ }
+
+ public Object[] get(String statement)
+ {
+ ResultSet results = MysqlQueryHandler.queryResult(database.getConnection(), statement);
+ List<Object> resObj = new ArrayList<>();
+ try
+ {
+ while (results.next())
+ {
+ resObj.add(results.getObject(1));
+ }
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ return new Object[0];
+ }
+ return resObj.toArray(new Object[0]);
+ }
+
+ public boolean delete(MysqlConstraint... constraints)
+ {
+ return MysqlQueryHandler.queryNoResult(database.getConnection(),
+ "DELETE FROM `" + name + "`" + getConstraints(constraints) + ";");
+ }
+
+ public boolean drop()
+ {
+ return MysqlQueryHandler.queryNoResult(database.getConnection(), "DROP TABLE `" + name + "`;");
+ }
+
+ private String getConstraints(MysqlConstraint... constraints)
+ {
+ String cons = "";
+ if (constraints.length > 0)
+ {
+ cons += " WHERE ";
+ for (int i = 0; i < constraints.length; i++)
+ {
+ MysqlConstraint constraint = constraints[i];
+ cons += constraint.getFieldName() + constraint.getOperator().toString() + "\"" + constraint.getValue()
+ + "\"";
+ if (i < constraints.length - 1)
+ {
+ cons += " AND ";
+ }
+ }
+ }
+ return cons;
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/MysqlType.java b/src/main/java/com/redstoner/misc/mysql/types/MysqlType.java
new file mode 100644
index 0000000..86413f9
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/MysqlType.java
@@ -0,0 +1,96 @@
+package com.redstoner.misc.mysql.types;
+
+import com.redstoner.misc.mysql.types.date.Date;
+import com.redstoner.misc.mysql.types.date.DateTime;
+import com.redstoner.misc.mysql.types.date.Time;
+import com.redstoner.misc.mysql.types.date.TimeStamp;
+import com.redstoner.misc.mysql.types.date.Year;
+import com.redstoner.misc.mysql.types.number.BigInt;
+import com.redstoner.misc.mysql.types.number.Decimal;
+import com.redstoner.misc.mysql.types.number.Double;
+import com.redstoner.misc.mysql.types.number.Float;
+import com.redstoner.misc.mysql.types.number.Int;
+import com.redstoner.misc.mysql.types.number.MediumInt;
+import com.redstoner.misc.mysql.types.number.SmallInt;
+import com.redstoner.misc.mysql.types.number.TinyInt;
+import com.redstoner.misc.mysql.types.text.Blob;
+import com.redstoner.misc.mysql.types.text.Char;
+import com.redstoner.misc.mysql.types.text.Enum;
+import com.redstoner.misc.mysql.types.text.LongBlob;
+import com.redstoner.misc.mysql.types.text.LongText;
+import com.redstoner.misc.mysql.types.text.MediumBlob;
+import com.redstoner.misc.mysql.types.text.MediumText;
+import com.redstoner.misc.mysql.types.text.Set;
+import com.redstoner.misc.mysql.types.text.Text;
+import com.redstoner.misc.mysql.types.text.TinyText;
+import com.redstoner.misc.mysql.types.text.VarChar;
+
+public abstract class MysqlType
+{
+ public abstract String getName();
+
+ public static MysqlType getTypeFromString(String type)
+ {
+ String[] splitType = type.split("\\(");
+ String toSwitch = splitType[0].toUpperCase();
+ String value = "";
+ if (type.contains("(") && type.endsWith(")"))
+ {
+ value = splitType[1].substring(0, splitType[1].length() - 1);
+ }
+ switch (toSwitch)
+ {
+ case "CHAR":
+ return new Char(Integer.valueOf(value));
+ case "ENUM":
+ return new Enum(value.replaceAll("'", "").split(","));
+ case "VARCHAR":
+ return new VarChar(Integer.valueOf(value));
+ case "SET":
+ return new Set(value.replaceAll("'", "").split(","));
+ case "BLOB":
+ return new Blob();
+ case "TEXT":
+ return new Text();
+ case "MEDIUMBLOB":
+ return new MediumBlob();
+ case "LONGBLOB":
+ return new LongBlob();
+ case "TINYTEXT":
+ return new TinyText();
+ case "MEDIUMTEXT":
+ return new MediumText();
+ case "LONGTEXT":
+ return new LongText();
+ case "INT":
+ return new Int(Integer.valueOf(value));
+ case "TINYINT":
+ return new TinyInt(Integer.valueOf(value));
+ case "SMALLINT":
+ return new SmallInt(Integer.valueOf(value));
+ case "MEDIUMINT":
+ return new MediumInt(Integer.valueOf(value));
+ case "BIGINT":
+ return new BigInt(Integer.valueOf(value));
+ case "BIT":
+ return new TinyInt(1);
+ case "FLOAT":
+ return new Float();
+ case "DOUBLE":
+ return new Double();
+ case "DECIMAL":
+ return new Decimal();
+ case "DATE":
+ return new Date();
+ case "DATETIME":
+ return new DateTime();
+ case "TIME":
+ return new Time();
+ case "TIMESTAMP":
+ return new TimeStamp();
+ case "YEAR":
+ return new Year();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/date/Date.java b/src/main/java/com/redstoner/misc/mysql/types/date/Date.java
new file mode 100644
index 0000000..9943333
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/date/Date.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.date;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Date extends MysqlType {
+ @Override
+ public String getName() {
+ return "DATE";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/date/DateTime.java b/src/main/java/com/redstoner/misc/mysql/types/date/DateTime.java
new file mode 100644
index 0000000..b4d9623
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/date/DateTime.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.date;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class DateTime extends MysqlType {
+ @Override
+ public String getName() {
+ return "DATETIME";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/date/Time.java b/src/main/java/com/redstoner/misc/mysql/types/date/Time.java
new file mode 100644
index 0000000..4ead72c
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/date/Time.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.date;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Time extends MysqlType {
+ @Override
+ public String getName() {
+ return "TIME";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/date/TimeStamp.java b/src/main/java/com/redstoner/misc/mysql/types/date/TimeStamp.java
new file mode 100644
index 0000000..56205af
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/date/TimeStamp.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.date;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class TimeStamp extends MysqlType {
+ @Override
+ public String getName() {
+ return "TIMESTAMP";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/date/Year.java b/src/main/java/com/redstoner/misc/mysql/types/date/Year.java
new file mode 100644
index 0000000..4c2ce1a
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/date/Year.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.date;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Year extends MysqlType {
+ @Override
+ public String getName() {
+ return "YEAR";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/number/BigInt.java b/src/main/java/com/redstoner/misc/mysql/types/number/BigInt.java
new file mode 100644
index 0000000..71086fd
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/number/BigInt.java
@@ -0,0 +1,12 @@
+package com.redstoner.misc.mysql.types.number;
+
+public class BigInt extends Int {
+ public BigInt(int maxSize) {
+ super(maxSize);
+ }
+
+ @Override
+ public String getName() {
+ return "BIG" + super.getName();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/number/Decimal.java b/src/main/java/com/redstoner/misc/mysql/types/number/Decimal.java
new file mode 100644
index 0000000..4e4dbb6
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/number/Decimal.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.number;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Decimal extends MysqlType {
+ @Override
+ public String getName() {
+ return "DECIMAL";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/number/Double.java b/src/main/java/com/redstoner/misc/mysql/types/number/Double.java
new file mode 100644
index 0000000..b4b1dda
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/number/Double.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.number;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Double extends MysqlType {
+ @Override
+ public String getName() {
+ return "DOUBLE";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/number/Float.java b/src/main/java/com/redstoner/misc/mysql/types/number/Float.java
new file mode 100644
index 0000000..ea3047e
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/number/Float.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.number;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Float extends MysqlType {
+ @Override
+ public String getName() {
+ return "FLOAT";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/number/Int.java b/src/main/java/com/redstoner/misc/mysql/types/number/Int.java
new file mode 100644
index 0000000..4256f7b
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/number/Int.java
@@ -0,0 +1,16 @@
+package com.redstoner.misc.mysql.types.number;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Int extends MysqlType {
+ private int maxSize;
+
+ public Int(int maxSize) {
+ this.maxSize = maxSize;
+ }
+
+ @Override
+ public String getName() {
+ return "INT(" + maxSize + ")";
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/number/MediumInt.java b/src/main/java/com/redstoner/misc/mysql/types/number/MediumInt.java
new file mode 100644
index 0000000..fbcb0f4
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/number/MediumInt.java
@@ -0,0 +1,12 @@
+package com.redstoner.misc.mysql.types.number;
+
+public class MediumInt extends Int {
+ public MediumInt(int maxSize) {
+ super(maxSize);
+ }
+
+ @Override
+ public String getName() {
+ return "MEDIUM" + super.getName();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/number/SmallInt.java b/src/main/java/com/redstoner/misc/mysql/types/number/SmallInt.java
new file mode 100644
index 0000000..01bf97d
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/number/SmallInt.java
@@ -0,0 +1,12 @@
+package com.redstoner.misc.mysql.types.number;
+
+public class SmallInt extends Int {
+ public SmallInt(int maxSize) {
+ super(maxSize);
+ }
+
+ @Override
+ public String getName() {
+ return "SMALL" + super.getName();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/number/TinyInt.java b/src/main/java/com/redstoner/misc/mysql/types/number/TinyInt.java
new file mode 100644
index 0000000..63ad700
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/number/TinyInt.java
@@ -0,0 +1,12 @@
+package com.redstoner.misc.mysql.types.number;
+
+public class TinyInt extends Int {
+ public TinyInt(int maxSize) {
+ super(maxSize);
+ }
+
+ @Override
+ public String getName() {
+ return "TINY" + super.getName();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/Blob.java b/src/main/java/com/redstoner/misc/mysql/types/text/Blob.java
new file mode 100644
index 0000000..d56ee45
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/Blob.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.text;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Blob extends MysqlType {
+ @Override
+ public String getName() {
+ return "BLOB";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/Char.java b/src/main/java/com/redstoner/misc/mysql/types/text/Char.java
new file mode 100644
index 0000000..ece068c
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/Char.java
@@ -0,0 +1,16 @@
+package com.redstoner.misc.mysql.types.text;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Char extends MysqlType {
+ private int size;
+
+ public Char(int size) {
+ this.size = size;
+ }
+
+ @Override
+ public String getName() {
+ return "CHAR(" + size + ")";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/Enum.java b/src/main/java/com/redstoner/misc/mysql/types/text/Enum.java
new file mode 100644
index 0000000..e68476d
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/Enum.java
@@ -0,0 +1,27 @@
+package com.redstoner.misc.mysql.types.text;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Enum extends MysqlType {
+ private String[] possibleValues;
+
+ public Enum(String... possibleValues) {
+ this.possibleValues = possibleValues;
+ }
+
+ @Override
+ public String getName() {
+ String name = "ENUM(";
+
+ for (int i = 0; i < possibleValues.length; i++) {
+ name += "'" + possibleValues[i] + "'";
+
+ if (i != possibleValues.length - 1) {
+ name += ",";
+ }
+ }
+
+ return name + ")";
+ }
+
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/LongBlob.java b/src/main/java/com/redstoner/misc/mysql/types/text/LongBlob.java
new file mode 100644
index 0000000..802caed
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/LongBlob.java
@@ -0,0 +1,8 @@
+package com.redstoner.misc.mysql.types.text;
+
+public class LongBlob extends Blob {
+ @Override
+ public String getName() {
+ return "LONG" + super.getName();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/LongText.java b/src/main/java/com/redstoner/misc/mysql/types/text/LongText.java
new file mode 100644
index 0000000..5149ed2
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/LongText.java
@@ -0,0 +1,8 @@
+package com.redstoner.misc.mysql.types.text;
+
+public class LongText extends Text {
+ @Override
+ public String getName() {
+ return "LONG" + super.getName();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/MediumBlob.java b/src/main/java/com/redstoner/misc/mysql/types/text/MediumBlob.java
new file mode 100644
index 0000000..6ae25a7
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/MediumBlob.java
@@ -0,0 +1,8 @@
+package com.redstoner.misc.mysql.types.text;
+
+public class MediumBlob extends Blob {
+ @Override
+ public String getName() {
+ return "MEDIUM" + super.getName();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/MediumText.java b/src/main/java/com/redstoner/misc/mysql/types/text/MediumText.java
new file mode 100644
index 0000000..a0f6525
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/MediumText.java
@@ -0,0 +1,8 @@
+package com.redstoner.misc.mysql.types.text;
+
+public class MediumText extends Text {
+ @Override
+ public String getName() {
+ return "MEDIUM" + super.getName();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/Set.java b/src/main/java/com/redstoner/misc/mysql/types/text/Set.java
new file mode 100644
index 0000000..4e12ce6
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/Set.java
@@ -0,0 +1,27 @@
+package com.redstoner.misc.mysql.types.text;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Set extends MysqlType {
+ private String[] possibleValues;
+
+ public Set(String... possibleValues) {
+ this.possibleValues = possibleValues;
+ }
+
+ @Override
+ public String getName() {
+ String name = "SET(";
+
+ for (int i = 0; i < possibleValues.length; i++) {
+ name += "'" + possibleValues[i] + "'";
+
+ if (i != possibleValues.length - 1) {
+ name += ",";
+ }
+ }
+
+ return name + ")";
+ }
+
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/Text.java b/src/main/java/com/redstoner/misc/mysql/types/text/Text.java
new file mode 100644
index 0000000..7d1a38c
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/Text.java
@@ -0,0 +1,10 @@
+package com.redstoner.misc.mysql.types.text;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class Text extends MysqlType {
+ @Override
+ public String getName() {
+ return "TEXT";
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/TinyText.java b/src/main/java/com/redstoner/misc/mysql/types/text/TinyText.java
new file mode 100644
index 0000000..e78160c
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/TinyText.java
@@ -0,0 +1,8 @@
+package com.redstoner.misc.mysql.types.text;
+
+public class TinyText extends Text {
+ @Override
+ public String getName() {
+ return "TINY" + super.getName();
+ }
+}
diff --git a/src/main/java/com/redstoner/misc/mysql/types/text/VarChar.java b/src/main/java/com/redstoner/misc/mysql/types/text/VarChar.java
new file mode 100644
index 0000000..cb28ad1
--- /dev/null
+++ b/src/main/java/com/redstoner/misc/mysql/types/text/VarChar.java
@@ -0,0 +1,16 @@
+package com.redstoner.misc.mysql.types.text;
+
+import com.redstoner.misc.mysql.types.MysqlType;
+
+public class VarChar extends MysqlType {
+ private int maxSize;
+
+ public VarChar(int maxSize) {
+ this.maxSize = maxSize;
+ }
+
+ @Override
+ public String getName() {
+ return "VARCHAR(" + maxSize + ")";
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/redstoner/modules/CoreModule.java b/src/main/java/com/redstoner/modules/CoreModule.java
new file mode 100644
index 0000000..9f71557
--- /dev/null
+++ b/src/main/java/com/redstoner/modules/CoreModule.java
@@ -0,0 +1,24 @@
+package com.redstoner.modules;
+
+import com.redstoner.annotations.Version;
+
+/** This class shall be used for "CoreModules", which are acting on a lower level than modules and are also exempted from being disabled or reloaded on the go.</br>
+ * Please note that CoreModules will not be known to the ModuleLoader itself!</br>
+ * Examples are the ModuleLoader and the Debugger.
+ *
+ * @author Pepich */
+@Version(major = 2, minor = 0, revision = 0, compatible = -1)
+public interface CoreModule extends Module
+{
+ /** Core modules don't need to be enabled. */
+ @Override
+ public default boolean onEnable()
+ {
+ return true;
+ }
+
+ /** Core modules don't need to be disabled. */
+ @Override
+ public default void onDisable()
+ {}
+}
diff --git a/src/main/java/com/redstoner/modules/Module.java b/src/main/java/com/redstoner/modules/Module.java
new file mode 100644
index 0000000..1c89e15
--- /dev/null
+++ b/src/main/java/com/redstoner/modules/Module.java
@@ -0,0 +1,54 @@
+package com.redstoner.modules;
+
+import com.redstoner.annotations.Version;
+import com.redstoner.coremods.moduleLoader.ModuleLoader;
+
+/** Interface for the Module class. Modules must always have an empty constructor to be invoked by the ModuleLoader.
+ *
+ * @author Pepich */
+@Version(major = 4, minor = 0, revision = 0, compatible = 0)
+public interface Module
+{
+ /** Will be called when the module gets enabled. */
+ public default boolean onEnable()
+ {
+ return true;
+ }
+
+ /** This methods gets called after all modules were enabled, please use this method to register commands and similar. <br/>
+ * It will only get called if and only if the module was successfully enabled. */
+ public default void postEnable()
+ {}
+
+ /** Will be called when the module gets disabled. */
+ public default void onDisable()
+ {}
+
+ /** Gets called on registration of the module, when this option is selected for command registration
+ *
+ * @return The String used for the CommandManager to register the commands. */
+ public default String getCommandString()
+ {
+ return null;
+ }
+
+ public default ModuleLogger getLogger()
+ {
+ return ModuleLoader.getModuleLogger(this);
+ }
+
+ /** This method gets run the very first time a module gets loaded. You can use this to set up file structures or background data. */
+ public default void firstLoad()
+ {}
+
+ /** This method gets run every time a module gets loaded and its version has changed.
+ *
+ * @param old The version of the previous module. */
+ public default void migrate(Version old)
+ {}
+
+ default void setPrefix(final String name)
+ {
+ getLogger().setName(name);
+ }
+}
diff --git a/src/main/java/com/redstoner/modules/ModuleLogger.java b/src/main/java/com/redstoner/modules/ModuleLogger.java
new file mode 100644
index 0000000..11d71b6
--- /dev/null
+++ b/src/main/java/com/redstoner/modules/ModuleLogger.java
@@ -0,0 +1,77 @@
+package com.redstoner.modules;
+
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+
+import com.redstoner.annotations.Version;
+
+import net.nemez.chatapi.ChatAPI;
+import net.nemez.chatapi.click.Message;
+
+@Version(major = 4, minor = 0, revision = 0, compatible = -1)
+public class ModuleLogger
+{
+ public static final String PREFIX_WARN = "§8[§eWARN§8]:§7 ";
+ public static final String PREFIX_ERROR = "§8[§cERROR§8]:§7 ";
+ public static final String PREFIX_INFO = "§8[§fINFO§8]:§7 ";
+
+ private String name;
+
+ public ModuleLogger(final String name)
+ {
+ this.name = name;
+ }
+
+ public void info(final String message)
+ {
+ Bukkit.getConsoleSender().sendMessage(PREFIX_INFO + getPrefix() + ChatAPI.colorify(null, message));
+ }
+
+ public void warn(final String message)
+ {
+ Bukkit.getConsoleSender().sendMessage(PREFIX_WARN + getPrefix() + ChatAPI.colorify(null, message));
+ }
+
+ public void error(final String message)
+ {
+ Bukkit.getConsoleSender().sendMessage(PREFIX_ERROR + getPrefix() + ChatAPI.colorify(null, message));
+ }
+
+ public void message(final CommandSender recipient, final String... message)
+ {
+ message(recipient, false, message);
+ }
+
+ public void message(final CommandSender recipient, final boolean error, final String... message)
+ {
+ Message m = new Message(recipient, null);
+ if (message.length == 1)
+ m.appendText(getPrefix(error) + message[0]);
+ else
+ {
+ m.appendText(getHeader());
+ m.appendText("&7" + String.join("\n&7", message));
+ }
+ m.send();
+ }
+
+ public String getPrefix()
+ {
+ return getPrefix(false);
+ }
+
+ public String getPrefix(final boolean error)
+ {
+ return "§8[§" + (error ? 'c' : '2') + name + "§8]§7 ";
+ }
+
+ public String getHeader()
+ {
+ return "§2--=[ " + name + " ]=--\n";
+ }
+
+ protected final void setName(final String name)
+ {
+ this.name = name;
+ }
+}