diff --git a/.idea/libraries/Maven__com_github_TBMCPlugins_ButtonCore_Towny_master_v1_0_g8d3b6b6_296.xml b/.idea/libraries/Maven__com_github_TBMCPlugins_ButtonCore_Towny_master_248b0d8d0a_1.xml similarity index 55% rename from .idea/libraries/Maven__com_github_TBMCPlugins_ButtonCore_Towny_master_v1_0_g8d3b6b6_296.xml rename to .idea/libraries/Maven__com_github_TBMCPlugins_ButtonCore_Towny_master_248b0d8d0a_1.xml index 8991a67..0d3e00e 100644 --- a/.idea/libraries/Maven__com_github_TBMCPlugins_ButtonCore_Towny_master_v1_0_g8d3b6b6_296.xml +++ b/.idea/libraries/Maven__com_github_TBMCPlugins_ButtonCore_Towny_master_248b0d8d0a_1.xml @@ -1,13 +1,13 @@ - + - + - + - + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_github_milkbowl_VaultAPI_master_c8cb88f27a_1.xml b/.idea/libraries/Maven__com_github_milkbowl_VaultAPI_master_431c5273c2_1.xml similarity index 55% rename from .idea/libraries/Maven__com_github_milkbowl_VaultAPI_master_c8cb88f27a_1.xml rename to .idea/libraries/Maven__com_github_milkbowl_VaultAPI_master_431c5273c2_1.xml index b5aaa43..19b24a3 100644 --- a/.idea/libraries/Maven__com_github_milkbowl_VaultAPI_master_c8cb88f27a_1.xml +++ b/.idea/libraries/Maven__com_github_milkbowl_VaultAPI_master_431c5273c2_1.xml @@ -1,13 +1,13 @@ - + - + - + - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 0b24af5..fc07820 100755 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -4,6 +4,7 @@ + diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BuildConfigUpdater/BuildConfigUpdater.iml b/BuildConfigUpdater/BuildConfigUpdater.iml index 4902d56..73696cd 100644 --- a/BuildConfigUpdater/BuildConfigUpdater.iml +++ b/BuildConfigUpdater/BuildConfigUpdater.iml @@ -12,7 +12,6 @@ - diff --git a/BuildConfigUpdater/src/main/java/BCUMain.java b/BuildConfigUpdater/src/main/java/BCUMain.java index bca7209..07666d3 100644 --- a/BuildConfigUpdater/src/main/java/BCUMain.java +++ b/BuildConfigUpdater/src/main/java/BCUMain.java @@ -1,4 +1,4 @@ -import buttondevteam.lib.PluginUpdater; +import buttondevteam.component.updater.PluginUpdater; import java.util.List; import java.util.stream.Collectors; diff --git a/ButtonCore/lombok.config b/ButtonCore/lombok.config new file mode 100644 index 0000000..d959b09 --- /dev/null +++ b/ButtonCore/lombok.config @@ -0,0 +1 @@ +lombok.var.flagUsage = ALLOW diff --git a/ButtonCore/pom.xml b/ButtonCore/pom.xml index 3750f03..2bbce2d 100755 --- a/ButtonCore/pom.xml +++ b/ButtonCore/pom.xml @@ -72,6 +72,26 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + diff --git a/ButtonCore/src/main/java/buttondevteam/component/commands/CommandComponent.java b/ButtonCore/src/main/java/buttondevteam/component/commands/CommandComponent.java new file mode 100644 index 0000000..b85ad34 --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/component/commands/CommandComponent.java @@ -0,0 +1,15 @@ +package buttondevteam.component.commands; + +import buttondevteam.lib.architecture.Component; + +public class CommandComponent extends Component { //TODO: Do we just move everything here? + @Override + public void enable() { + + } + + @Override + public void disable() { + + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/component/restart/RestartComponent.java b/ButtonCore/src/main/java/buttondevteam/component/restart/RestartComponent.java new file mode 100644 index 0000000..8184b8a --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/component/restart/RestartComponent.java @@ -0,0 +1,20 @@ +package buttondevteam.component.restart; + +import buttondevteam.core.PrimeRestartCommand; +import buttondevteam.core.ScheduledRestartCommand; +import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.chat.TBMCChatAPI; + +public class RestartComponent extends Component { + @Override + public void enable() { + //TODO: Permissions for the commands + TBMCChatAPI.AddCommand(getPlugin(), ScheduledRestartCommand.class); + TBMCChatAPI.AddCommand(getPlugin(), PrimeRestartCommand.class); + } + + @Override + public void disable() { + + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/PluginUpdater.java b/ButtonCore/src/main/java/buttondevteam/component/updater/PluginUpdater.java similarity index 97% rename from ButtonCore/src/main/java/buttondevteam/lib/PluginUpdater.java rename to ButtonCore/src/main/java/buttondevteam/component/updater/PluginUpdater.java index ca758f8..ce2e993 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/PluginUpdater.java +++ b/ButtonCore/src/main/java/buttondevteam/component/updater/PluginUpdater.java @@ -1,5 +1,6 @@ -package buttondevteam.lib; +package buttondevteam.component.updater; +import buttondevteam.lib.TBMCCoreAPI; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -136,7 +137,7 @@ public class PluginUpdater { public static List GetPluginNames() { List ret = new ArrayList<>(); try { - String resp = TBMCCoreAPI.DownloadString("https://api.github.com/orgs/TBMCPlugins/repos"); + String resp = TBMCCoreAPI.DownloadString("https://api.github.com/orgs/" + "TBMCPlugins" + "/repos"); //TODO: PluginUpdater JsonArray arr = new JsonParser().parse(resp).getAsJsonArray(); for (JsonElement obj : arr) { JsonObject jobj = obj.getAsJsonObject(); diff --git a/ButtonCore/src/main/java/buttondevteam/component/updater/PluginUpdaterComponent.java b/ButtonCore/src/main/java/buttondevteam/component/updater/PluginUpdaterComponent.java new file mode 100644 index 0000000..2425631 --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/component/updater/PluginUpdaterComponent.java @@ -0,0 +1,16 @@ +package buttondevteam.component.updater; + +import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.chat.TBMCChatAPI; + +public class PluginUpdaterComponent extends Component { + @Override + public void enable() { + TBMCChatAPI.AddCommand(getPlugin(), UpdatePluginCommand.class); + } + + @Override + public void disable() { //TODO: Unregister commands and such + + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/core/UpdatePluginCommand.java b/ButtonCore/src/main/java/buttondevteam/component/updater/UpdatePluginCommand.java similarity index 94% rename from ButtonCore/src/main/java/buttondevteam/core/UpdatePluginCommand.java rename to ButtonCore/src/main/java/buttondevteam/component/updater/UpdatePluginCommand.java index ba5b309..ee006c4 100755 --- a/ButtonCore/src/main/java/buttondevteam/core/UpdatePluginCommand.java +++ b/ButtonCore/src/main/java/buttondevteam/component/updater/UpdatePluginCommand.java @@ -1,6 +1,6 @@ -package buttondevteam.core; +package buttondevteam.component.updater; -import buttondevteam.lib.PluginUpdater; +import buttondevteam.core.MainPlugin; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.TBMCCommandBase; diff --git a/ButtonCore/src/main/java/buttondevteam/core/ComponentCommand.java b/ButtonCore/src/main/java/buttondevteam/core/ComponentCommand.java new file mode 100644 index 0000000..a83037b --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/core/ComponentCommand.java @@ -0,0 +1,30 @@ +package buttondevteam.core; + +import buttondevteam.lib.chat.CommandClass; +import buttondevteam.lib.chat.TBMCCommandBase; +import org.bukkit.command.CommandSender; + +@CommandClass(modOnly = true) +public class ComponentCommand extends TBMCCommandBase { + @Override + public boolean OnCommand(CommandSender sender, String alias, String[] args) { + if (args.length < 2) + return false; + switch (args[0]) { + case "enable": + break; + case "disable": + break; + case "list": + break; + default: + return false; + } + return true; + } + + @Override + public String[] GetHelpText(String alias) { + return new String[0]; + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/core/ComponentManager.java b/ButtonCore/src/main/java/buttondevteam/core/ComponentManager.java new file mode 100644 index 0000000..0c3c51b --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/core/ComponentManager.java @@ -0,0 +1,57 @@ +package buttondevteam.core; + +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.Component; +import lombok.val; + +public final class ComponentManager { + private ComponentManager() {} + + private static boolean componentsEnabled = false; + + /** + * This flag is used to enable components registered after the others were enabled. + * @return Whether already registered components have been enabled + */ + public static boolean areComponentsEnabled() { return componentsEnabled; } + + /** + * Enables components based on a configuration - any component registered afterwards will be also enabled + */ + public static void enableComponents() { + //Component.getComponents().values().stream().filter(c->cs.getConfigurationSection(c.getClass().getSimpleName()).getBoolean("enabled")).forEach(c-> { + Component.getComponents().values().stream().filter(c -> c.shouldBeEnabled().get()).forEach(c -> { + try { + Component.setComponentEnabled(c, true); + } catch (Exception e) { + TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.getClass().getSimpleName(), e); + } + }); + componentsEnabled = true; + } + + /** + * Disables all components that are enabled + */ + public static void disableComponents() { + Component.getComponents().values().stream().filter(Component::isEnabled).forEach(c -> { + try { + Component.setComponentEnabled(c, false); + } catch (Exception e) { + TBMCCoreAPI.SendException("Failed to disable one of the components: " + c.getClass().getSimpleName(), e); + } + }); + componentsEnabled = false; + } + + /** + * Will also return false if the component is not registered. + * + * @param cl The component class + * @return Whether the component is registered and enabled + */ + public static boolean isEnabled(Class cl) { + val c = Component.getComponents().get(cl); + return c != null && c.isEnabled(); + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/core/MainPlugin.java b/ButtonCore/src/main/java/buttondevteam/core/MainPlugin.java index ef0768f..b3a4aaa 100755 --- a/ButtonCore/src/main/java/buttondevteam/core/MainPlugin.java +++ b/ButtonCore/src/main/java/buttondevteam/core/MainPlugin.java @@ -1,7 +1,10 @@ package buttondevteam.core; -import buttondevteam.lib.PluginUpdater; +import buttondevteam.component.restart.RestartComponent; +import buttondevteam.component.updater.PluginUpdater; +import buttondevteam.component.updater.PluginUpdaterComponent; import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.Component; import buttondevteam.lib.chat.Channel; import buttondevteam.lib.chat.ChatRoom; import buttondevteam.lib.chat.Color; @@ -47,9 +50,9 @@ public class MainPlugin extends JavaPlugin { setupPermissions(); Test = getConfig().getBoolean("test", true); saveConfig(); - TBMCChatAPI.AddCommand(this, UpdatePluginCommand.class); - TBMCChatAPI.AddCommand(this, ScheduledRestartCommand.class); - TBMCChatAPI.AddCommand(this, PrimeRestartCommand.class); + Component.registerComponent(this, new PluginUpdaterComponent()); + Component.registerComponent(this, new RestartComponent()); + ComponentManager.enableComponents(); TBMCChatAPI.AddCommand(this, MemberCommand.class); TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this); ChromaGamerBase.addConverter(commandSender -> Optional.ofNullable(commandSender instanceof ConsoleCommandSender || commandSender instanceof BlockCommandSender @@ -82,6 +85,7 @@ public class MainPlugin extends JavaPlugin { @Override public void onDisable() { + ComponentManager.disableComponents(); logger.info("Saving player data..."); TBMCPlayerBase.savePlayers(); logger.info("Player data saved."); diff --git a/ButtonCore/src/main/java/buttondevteam/core/PlayerListener.java b/ButtonCore/src/main/java/buttondevteam/core/PlayerListener.java index af8220e..be7c10b 100755 --- a/ButtonCore/src/main/java/buttondevteam/core/PlayerListener.java +++ b/ButtonCore/src/main/java/buttondevteam/core/PlayerListener.java @@ -1,7 +1,7 @@ package buttondevteam.core; import buttondevteam.lib.TBMCSystemChatEvent; -import buttondevteam.lib.chat.IDiscordSender; +import buttondevteam.lib.chat.IFakePlayer; import buttondevteam.lib.player.TBMCPlayerBase; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -43,7 +43,7 @@ public class PlayerListener implements Listener { if (PrimeRestartCommand.isLoud()) Bukkit.broadcastMessage("§cNobody is online anymore. Restarting."); Bukkit.spigot().restart(); - } else if (!(event.getPlayer() instanceof IDiscordSender) && System.nanoTime() - 10 * 1000000000L - lasttime > 0) { //Ten seconds passed since last reminder + } else if (!(event.getPlayer() instanceof IFakePlayer) && System.nanoTime() - 10 * 1000000000L - lasttime > 0) { //Ten seconds passed since last reminder lasttime = System.nanoTime(); if (PrimeRestartCommand.isLoud()) Bukkit.broadcastMessage(ChatColor.DARK_RED + "The server will restart as soon as nobody is online."); diff --git a/ButtonCore/src/main/java/buttondevteam/lib/TBMCChatEvent.java b/ButtonCore/src/main/java/buttondevteam/lib/TBMCChatEvent.java index 3c44345..f304cde 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/TBMCChatEvent.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/TBMCChatEvent.java @@ -1,8 +1,9 @@ package buttondevteam.lib; import buttondevteam.lib.chat.Channel; -import buttondevteam.lib.player.ChromaGamerBase; +import buttondevteam.lib.chat.ChatMessage; import lombok.Getter; +import lombok.experimental.Delegate; import org.bukkit.command.CommandSender; import org.bukkit.event.HandlerList; @@ -16,28 +17,19 @@ import javax.annotation.Nullable; */ @Getter public class TBMCChatEvent extends TBMCChatEventBase { - public TBMCChatEvent(CommandSender sender, ChromaGamerBase user, Channel channel, String message, int score, boolean fromcmd, String groupid) { - super(channel, message, score, groupid); - this.sender = sender; - this.fromcmd = fromcmd; - this.ignoreSenderPermissions = false; - this.user = user; - } - - public TBMCChatEvent(CommandSender sender, ChromaGamerBase user, Channel channel, String message, int score, boolean fromcmd, String groupid, boolean ignoreSenderPermissions) { - super(channel, message, score, groupid); - this.sender = sender; - this.user = user; - this.fromcmd = fromcmd; - this.ignoreSenderPermissions = ignoreSenderPermissions; + public TBMCChatEvent(Channel channel, ChatMessage cm, Channel.RecipientTestResult rtr) { + super(channel, cm.getMessage(), rtr.score, rtr.groupID); + this.cm = cm; } private static final HandlerList handlers = new HandlerList(); - private final CommandSender sender; - private final boolean fromcmd; - private final boolean ignoreSenderPermissions; - private final ChromaGamerBase user; + @Delegate //<-- Backwards compatibility + private ChatMessage cm; + + private boolean isIgnoreSenderPermissions() { + return cm.getPermCheck() != cm.getSender(); + } // TODO: Message object with data? /** @@ -45,7 +37,7 @@ public class TBMCChatEvent extends TBMCChatEventBase { */ @Override public boolean shouldSendTo(CommandSender sender) { - if (isIgnoreSenderPermissions() && sender.equals(this.sender)) + if (isIgnoreSenderPermissions() && sender.equals(this.cm.getSender())) return true; //Allow sending the message no matter what return super.shouldSendTo(sender); } @@ -55,7 +47,7 @@ public class TBMCChatEvent extends TBMCChatEventBase { */ @Override public int getMCScore(CommandSender sender) { - if (isIgnoreSenderPermissions() && sender.equals(this.sender)) + if (isIgnoreSenderPermissions() && sender.equals(this.cm.getSender())) return getScore(); //Send in the correct group no matter what return super.getMCScore(sender); } @@ -66,7 +58,7 @@ public class TBMCChatEvent extends TBMCChatEventBase { @Nullable @Override public String getGroupID(CommandSender sender) { - if (isIgnoreSenderPermissions() && sender.equals(this.sender)) + if (isIgnoreSenderPermissions() && sender.equals(this.cm.getSender())) return getGroupID(); //Send in the correct group no matter what return super.getGroupID(sender); } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/TBMCCoreAPI.java b/ButtonCore/src/main/java/buttondevteam/lib/TBMCCoreAPI.java index 76498d5..e114196 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/TBMCCoreAPI.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/TBMCCoreAPI.java @@ -1,5 +1,6 @@ package buttondevteam.lib; +import buttondevteam.component.updater.PluginUpdater; import buttondevteam.core.MainPlugin; import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.potato.DebugPotato; diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java new file mode 100644 index 0000000..c37a529 --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java @@ -0,0 +1,52 @@ +package buttondevteam.lib.architecture; + +import buttondevteam.lib.TBMCCoreAPI; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public abstract class ButtonPlugin extends JavaPlugin { + private final HashMap> datamap = new HashMap<>(); + private ConfigurationSection section; + + protected abstract void pluginEnable(); + + protected abstract void pluginDisable(); + + @Override + public void onEnable() { + section = getConfig().getConfigurationSection("global"); + if (section == null) section = getConfig().createSection("global"); + try { + pluginEnable(); + } catch (Exception e) { + TBMCCoreAPI.SendException("Error while enabling plugin " + getName() + "!", e); + } + } + + @Override + public void onDisable() { + try { + pluginDisable(); + } catch (Exception e) { + TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e); + } + } + + /** + * @see IHaveConfig#getData(Map, ConfigurationSection, String, Object) + */ + protected ConfigData getData(String path, T def) { + return IHaveConfig.getData(datamap, section, path, def); + } + + /** + * @see IHaveConfig#getData(Map, ConfigurationSection, String, Object, Function, Function) + */ + protected ConfigData getData(String path, T def, Function getter, Function setter) { + return IHaveConfig.getData(datamap, section, path, def, getter, setter); + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/Component.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/Component.java new file mode 100644 index 0000000..4380812 --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/Component.java @@ -0,0 +1,221 @@ +package buttondevteam.lib.architecture; + +import buttondevteam.core.ComponentManager; +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException; +import buttondevteam.lib.chat.TBMCChatAPI; +import buttondevteam.lib.chat.TBMCCommandBase; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.var; +import lombok.val; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.event.Listener; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * Configuration is based on class name + */ +public abstract class Component { + private static HashMap, Component> components = new HashMap<>(); + + @Getter + private boolean enabled = false; + @Getter(value = AccessLevel.PROTECTED) + @NonNull + private JavaPlugin plugin; + @NonNull + private ConfigurationSection config; + + public ConfigData shouldBeEnabled() { + return getData("enabled", true); + } + + private HashMap> datamap = new HashMap<>(); + + /** + * @see IHaveConfig#getData(Map, ConfigurationSection, String, Object) + */ + protected ConfigData getData(String path, T def) { + return IHaveConfig.getData(datamap, config, path, def); + } + + /** + * @see IHaveConfig#getData(Map, ConfigurationSection, String, Object, Function, Function) + */ + protected ConfigData getData(String path, T def, Function getter, Function setter) { + return IHaveConfig.getData(datamap, config, path, def, getter, setter); + } + + /** + * Registers a component checking it's dependencies and calling {@link #register(JavaPlugin)}.
+ * Make sure to register the dependencies first.
+ * The component will be enabled automatically, regardless of when it was registered. + * + * @param component The component to register + * @return Whether the component is registered successfully (it may have failed to enable) + */ + public static boolean registerComponent(JavaPlugin plugin, Component component) { + return registerUnregisterComponent(plugin, component, true); + } + + /** + * Unregisters a component by calling {@link #unregister(JavaPlugin)}.
+ * Make sure to unregister the dependencies last. + * + * @param componentClass The component class to unregister + * @return Whether the component is unregistered successfully (it also got disabled) + */ + public static boolean unregisterComponent(JavaPlugin plugin, Class componentClass) { + val component = components.get(componentClass); + if (component == null) + return false; //Failed to load + return registerUnregisterComponent(plugin, component, false); + } + + public static boolean registerUnregisterComponent(JavaPlugin plugin, Component component, boolean register) { + try { + val metaAnn = component.getClass().getAnnotation(ComponentMetadata.class); + if (metaAnn != null) { + Class[] dependencies = metaAnn.depends(); + for (val dep : dependencies) { + if (!components.containsKey(dep)) { + plugin.getLogger().warning("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + " as a required dependency is missing/disabled: " + dep.getSimpleName()); + return false; + } + } + } + if (register) { + component.plugin = plugin; + var compconf = plugin.getConfig().getConfigurationSection("components"); + if (compconf == null) compconf = plugin.getConfig().createSection("components"); + component.config = compconf.getConfigurationSection(component.getClassName()); + if (component.config == null) component.config = compconf.createSection(component.getClassName()); + component.register(plugin); + components.put(component.getClass(), component); + if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled().get()) { + try { //Enable components registered after the previous ones getting enabled + setComponentEnabled(component, true); + return true; + } catch (Exception | NoClassDefFoundError e) { + TBMCCoreAPI.SendException("Failed to enable component " + component.getClassName() + "!", e); + return true; + } + } + return true; + } else { + if (component.enabled) { + try { + component.disable(); + component.enabled = false; + } catch (Exception | NoClassDefFoundError e) { + TBMCCoreAPI.SendException("Failed to disable component " + component.getClassName() + "!", e); + return false; //If failed to disable, won't unregister either + } + } + component.unregister(plugin); + components.remove(component.getClass()); + return true; + } + } catch (Exception e) { + TBMCCoreAPI.SendException("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + "!", e); + return false; + } + } + + /** + * Registers a component checking it's dependencies and calling {@link #register(JavaPlugin)}.
+ * Make sure to register the dependencies first. + * + * @param component The component to register + */ + public static void setComponentEnabled(Component component, boolean enabled) throws UnregisteredComponentException { + if (!components.containsKey(component.getClass())) + throw new UnregisteredComponentException(component); + if (component.enabled = enabled) + component.enable(); + else + component.disable(); + } + + /** + * Returns the currently registered components
+ * + * @return The currently registered components + */ + public static Map, Component> getComponents() { + return Collections.unmodifiableMap(components); + } + + /** + * Registers the module, when called by the JavaPlugin class. + * This gets fired when the plugin is enabled. Use {@link #enable()} to register commands and such. + * + * @param plugin Plugin object + */ + protected void register(JavaPlugin plugin) { + } + + /** + * Unregisters the module, when called by the JavaPlugin class. + * This gets fired when the plugin is disabled. + * Do any cleanups needed within this method. + * + * @param plugin Plugin object + */ + protected void unregister(JavaPlugin plugin) { + } + + /** + * Enables the module, when called by the JavaPlugin class. Call + * registerCommand() and registerListener() within this method.
+ * To access the plugin, use {@link #getPlugin()}. + */ + protected abstract void enable(); + + /** + * Disables the module, when called by the JavaPlugin class. Do + * any cleanups needed within this method. + * To access the plugin, use {@link #getPlugin()}. + */ + protected abstract void disable(); + + /** + * Registers a TBMCCommand to the plugin. Make sure to add it to plugin.yml and use {@link buttondevteam.lib.chat.CommandClass}. + * + * @param plugin Main plugin responsible for stuff + * @param commandBase Custom coded command class + */ + protected void registerCommand(JavaPlugin plugin, TBMCCommandBase commandBase) { + TBMCChatAPI.AddCommand(plugin, commandBase); + } + + /** + * Registers a Listener to this plugin + * + * @param plugin Main plugin responsible for stuff + * @param listener The event listener to register + * @return The provided listener + */ + protected Listener registerListener(JavaPlugin plugin, Listener listener) { + TBMCCoreAPI.RegisterEventsForExceptions(listener, plugin); + return listener; + } + + private String getClassName() { + Class enclosingClass = getClass().getEnclosingClass(); + String className; + if (enclosingClass != null) { + className = (enclosingClass.getName()); + } else { + className = (getClass().getName()); + } + return className; + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ComponentMetadata.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ComponentMetadata.java new file mode 100644 index 0000000..6519fca --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ComponentMetadata.java @@ -0,0 +1,12 @@ +package buttondevteam.lib.architecture; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ComponentMetadata { + Class[] depends(); +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java new file mode 100644 index 0000000..b023664 --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java @@ -0,0 +1,48 @@ +package buttondevteam.lib.architecture; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.function.Function; + +/** + * Use the getter/setter constructor if {@link T} isn't a primitive type or String.
+ * Use {@link Component#getData(String, Object)} or {@link ButtonPlugin#getData(String, Object)} to get an instance. + */ +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +public class ConfigData { //TODO: Save after a while + private final ConfigurationSection config; + private final String path; + private final T def; + /** + * The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)} + */ + private Function getter; + /** + * The result should be a primitive type or string that can be retrieved correctly later + */ + private Function setter; + + @SuppressWarnings("unchecked") + public T get() { + Object val = config.get(path, def); + if (getter != null) { + T hmm = getter.apply(val); + if (hmm == null) hmm = def; //Set if the getter returned null + return hmm; + } + return (T) val; + } + + public void set(T value) { + Object val; + if (setter != null) + val = setter.apply(value); + else val = value; + config.set(path, val); + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java new file mode 100644 index 0000000..77ad752 --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java @@ -0,0 +1,45 @@ +package buttondevteam.lib.architecture; + +import org.bukkit.configuration.ConfigurationSection; + +import java.util.Map; +import java.util.function.Function; + +/** + * Members of this interface should be protected (access level) + */ +final class IHaveConfig { + private IHaveConfig() {} + + /** + * This method overload should only be used with primitves or String. + * + * @param path The path in config to use + * @param def The value to use by default + * @param The type of this variable (only use primitives or String) + * @return The data object that can be used to get or set the value + */ + @SuppressWarnings("unchecked") + protected static ConfigData getData(Map> datamap, ConfigurationSection config, String path, T def) { + ConfigData data = datamap.get(path); + if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def)); + return (ConfigData) data; + } + + /** + * This method overload may be used with any class. + * + * @param path The path in config to use + * @param def The value to use by default + * @param getter A function that converts a primitive representation to the correct value + * @param setter A function that converts a value to a primitive representation + * @param The type of this variable (can be any class) + * @return The data object that can be used to get or set the value + */ + @SuppressWarnings("unchecked") + protected static ConfigData getData(Map> datamap, ConfigurationSection config, String path, T def, Function getter, Function setter) { + ConfigData data = datamap.get(path); + if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, getter, setter)); + return (ConfigData) data; + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/exceptions/UnregisteredComponentException.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/exceptions/UnregisteredComponentException.java new file mode 100644 index 0000000..b230fad --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/exceptions/UnregisteredComponentException.java @@ -0,0 +1,10 @@ +package buttondevteam.lib.architecture.exceptions; + +import buttondevteam.lib.architecture.Component; + +public class UnregisteredComponentException extends Exception { + + public UnregisteredComponentException(Component component) { + super("The component '" + component.getClass().getSimpleName() + "' isn't registered!"); + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/Channel.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/Channel.java index c18c557..bddc0f3 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/Channel.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/Channel.java @@ -34,7 +34,7 @@ public class Channel { /** * Filters both the sender and the targets */ - public final Function filteranderrormsg; + private final Function filteranderrormsg; private static final List channels = new ArrayList<>(); @@ -87,10 +87,7 @@ public class Channel { * Note: Errors are sent to the sender automatically */ public int getMCScore(CommandSender sender) { - if (filteranderrormsg == null) - return SCORE_SEND_OK; - RecipientTestResult result = filteranderrormsg.apply(sender); - return result.errormessage == null ? result.score : SCORE_SEND_NOPE; + return getRTR(sender).score; //No need to check if there was an error } /** @@ -100,10 +97,13 @@ public class Channel { */ @Nullable public String getGroupID(CommandSender sender) { + return getRTR(sender).groupID; //No need to check if there was an error + } + + public RecipientTestResult getRTR(CommandSender sender) { if (filteranderrormsg == null) - return GROUP_EVERYONE; - RecipientTestResult result = filteranderrormsg.apply(sender); - return result.errormessage == null ? result.groupID : null; + return new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE); + return filteranderrormsg.apply(sender); } public static List getChannels() { @@ -143,9 +143,10 @@ public class Channel { } public static class RecipientTestResult { - public String errormessage = null; - public int score = SCORE_SEND_NOPE; // Anything below 0 is "never send" - public String groupID = null; + public final String errormessage; + public final int score; // Anything below 0 is "never send" + public final String groupID; + public static final RecipientTestResult ALL = new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE); /** * Creates a result that indicates an error @@ -154,6 +155,8 @@ public class Channel { */ public RecipientTestResult(String errormessage) { this.errormessage = errormessage; + this.score = SCORE_SEND_NOPE; + this.groupID = null; } /** @@ -163,8 +166,10 @@ public class Channel { * @param groupID The ID of the target group. */ public RecipientTestResult(int score, String groupID) { + if (score < 0) throw new IllegalArgumentException("Score must be non-negative!"); this.score = score; this.groupID = groupID; + this.errormessage = null; } } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/ChatMessage.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/ChatMessage.java index 99b16b8..8496cbc 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/ChatMessage.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/ChatMessage.java @@ -4,39 +4,54 @@ import buttondevteam.lib.player.ChromaGamerBase; import lombok.Builder; import lombok.Getter; import lombok.NonNull; +import lombok.Setter; import org.bukkit.command.CommandSender; @Builder @Getter public class ChatMessage { - /** - * The sender which sends the message. - */ - private final CommandSender sender; - /** - * The Chroma user which sends the message. - */ - private final ChromaGamerBase user; - /** - * The message to send as the user. - */ - private final String message; - /** - * Indicates whether the message comes from running a command (like /tableflip). Implemented to be used from Discord. - */ - private boolean fromCommand; - /** - * The sender which we should check for permissions. Same as {@link #sender} by default. - */ - private CommandSender permCheck; + /** + * The sender which sends the message. + */ + private final CommandSender sender; + /** + * The Chroma user which sends the message. + */ + private final ChromaGamerBase user; + /** + * The message to send as the user. + */ + @Setter + private String message; + /** + * Indicates whether the message comes from running a command (like /tableflip). Implemented to be used from Discord. + */ + private boolean fromCommand; + /** + * The sender which we should check for permissions. Same as {@link #sender} by default. + */ + private CommandSender permCheck; + /** + * The origin of the message, "Minecraft" or "Discord" for example. May be displayed to the user.
+ * This is the user class capitalized folder name. + */ + private final String origin; - private static ChatMessageBuilder builder() { - return new ChatMessageBuilder(); - } + /** + * The sender which we should check for permissions. Same as {@link #sender} by default. + * + * @return The perm check or the sender + */ + public CommandSender getPermCheck() { + return permCheck == null ? sender : permCheck; + } - @NonNull - public static ChatMessageBuilder builder(CommandSender sender, ChromaGamerBase user, String message) { - return builder().sender(sender).user(user).message(message); - } + private static ChatMessageBuilder builder() { + return new ChatMessageBuilder(); + } + @NonNull + public static ChatMessageBuilder builder(CommandSender sender, ChromaGamerBase user, String message) { + return builder().sender(sender).user(user).message(message).origin(user.getFolder().substring(0, 1).toUpperCase() + user.getFolder().substring(1)); + } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/ChatRoom.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/ChatRoom.java index 8a1e75d..9819d7c 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/ChatRoom.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/ChatRoom.java @@ -15,11 +15,11 @@ public class ChatRoom extends Channel { public void joinRoom(CommandSender sender) { usersInRoom.add(sender); - TBMCChatAPI.SendSystemMessage(this, 0, "everyone", sender.getName() + " joined the room"); + TBMCChatAPI.SendSystemMessage(this, RecipientTestResult.ALL, sender.getName() + " joined the room"); } public void leaveRoom(CommandSender sender) { usersInRoom.remove(sender); - TBMCChatAPI.SendSystemMessage(this, 0, "everyone", sender.getName() + " left the room"); + TBMCChatAPI.SendSystemMessage(this, RecipientTestResult.ALL, sender.getName() + " left the room"); } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/IDiscordSender.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/IFakePlayer.java similarity index 71% rename from ButtonCore/src/main/java/buttondevteam/lib/chat/IDiscordSender.java rename to ButtonCore/src/main/java/buttondevteam/lib/chat/IFakePlayer.java index fd41c90..4f9318c 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/IDiscordSender.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/IFakePlayer.java @@ -7,6 +7,6 @@ import org.bukkit.command.CommandSender; * @author Norbi * */ -public interface IDiscordSender extends CommandSender { +public interface IFakePlayer extends CommandSender { } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCChatAPI.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCChatAPI.java index b3efcb2..86d1f10 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCChatAPI.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCChatAPI.java @@ -35,7 +35,7 @@ public class TBMCChatAPI { /** * Returns messages formatted for Minecraft chat listing the subcommands of the command. - * + * * @param command * The command which we want the subcommands of * @param sender @@ -49,7 +49,7 @@ public class TBMCChatAPI { /** * Returns messages formatted for Minecraft chat listing the subcommands of the command.
* Returns a header if subcommands were found, otherwise returns an empty array. - * + * * @param command * The command which we want the subcommands of * @param sender @@ -228,17 +228,18 @@ public class TBMCChatAPI { public static boolean SendChatMessage(ChatMessage cm, Channel channel) { if (!Channel.getChannels().contains(channel)) throw new RuntimeException("Channel " + channel.DisplayName + " not registered!"); - val permcheck = cm.getPermCheck() == null ? cm.getSender() : cm.getPermCheck(); + val permcheck = cm.getPermCheck(); RecipientTestResult rtr = getScoreOrSendError(channel, permcheck); int score = rtr.score; - if (score == -1 || rtr.groupID == null) + if (score == Channel.SCORE_SEND_NOPE || rtr.groupID == null) return true; TBMCChatPreprocessEvent eventPre = new TBMCChatPreprocessEvent(cm.getSender(), channel, cm.getMessage()); Bukkit.getPluginManager().callEvent(eventPre); if (eventPre.isCancelled()) return true; + cm.setMessage(eventPre.getMessage()); TBMCChatEvent event; - event = new TBMCChatEvent(cm.getSender(), cm.getUser(), channel, eventPre.getMessage(), score, cm.isFromCommand(), rtr.groupID, permcheck != cm.getSender()); + event = new TBMCChatEvent(channel, cm, rtr); Bukkit.getPluginManager().callEvent(event); return event.isCancelled(); } @@ -248,29 +249,25 @@ public class TBMCChatAPI { * * @param channel * The channel to send to - * @param score - * The score to use to find the group - use 0 if the channel doesn't have scores + * @param rtr + * The score&group to use to find the group - use {@link RecipientTestResult#ALL} if the channel doesn't have scores * @param message * The message to send * @return The event cancelled state */ - public static boolean SendSystemMessage(Channel channel, int score, String groupid, String message) { + public static boolean SendSystemMessage(Channel channel, RecipientTestResult rtr, String message) { if (!Channel.getChannels().contains(channel)) throw new RuntimeException("Channel " + channel.DisplayName + " not registered!"); - TBMCSystemChatEvent event = new TBMCSystemChatEvent(channel, message, score, groupid); + TBMCSystemChatEvent event = new TBMCSystemChatEvent(channel, message, rtr.score, rtr.groupID); Bukkit.getPluginManager().callEvent(event); return event.isCancelled(); } private static RecipientTestResult getScoreOrSendError(Channel channel, CommandSender sender) { - if (channel.filteranderrormsg == null) - return new RecipientTestResult(0, "everyone"); - else { - RecipientTestResult result = channel.filteranderrormsg.apply(sender); - if (result.errormessage != null) - sender.sendMessage("§c" + result.errormessage); - return result; - } + RecipientTestResult result = channel.getRTR(sender); + if (result.errormessage != null) + sender.sendMessage("§c" + result.errormessage); + return result; } /** diff --git a/ButtonCore/src/main/java/buttondevteam/lib/player/ChromaGamerBase.java b/ButtonCore/src/main/java/buttondevteam/lib/player/ChromaGamerBase.java index 3ab07b6..9d0a3a4 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/player/ChromaGamerBase.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/player/ChromaGamerBase.java @@ -8,6 +8,7 @@ import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; +import javax.annotation.Nullable; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -203,6 +204,7 @@ public abstract class ChromaGamerBase implements AutoCloseable { * @return The player as a {@link T} object or null if not having an account there */ @SuppressWarnings("unchecked") + @Nullable public T getAs(Class cl) { // TODO: Provide a way to use TBMCPlayerBase's loaded players if (cl.getSimpleName().equals(getClass().getSimpleName())) return (T) this; diff --git a/ButtonCore/src/main/java/buttondevteam/lib/player/UserClass.java b/ButtonCore/src/main/java/buttondevteam/lib/player/UserClass.java index 9abfce6..0e375aa 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/player/UserClass.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/player/UserClass.java @@ -13,7 +13,7 @@ import java.lang.annotation.*; @Inherited public @interface UserClass { /** - * Indicates which folder should the player files be saved in. + * Indicates which folder should the player files be saved in. Must be lowercase. */ String foldername(); } diff --git a/ButtonProcessor/pom.xml b/ButtonProcessor/pom.xml index a7e5999..54981ad 100755 --- a/ButtonProcessor/pom.xml +++ b/ButtonProcessor/pom.xml @@ -23,6 +23,14 @@ -proc:none + + org.apache.maven.plugins + maven-surefire-plugin + + false + + +