diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 56dd862..3fb39be 100755 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -46,17 +46,17 @@ - + - + diff --git a/ButtonCore/src/main/java/buttondevteam/core/CommandCaller.java b/ButtonCore/src/main/java/buttondevteam/core/CommandCaller.java deleted file mode 100755 index e93d386..0000000 --- a/ButtonCore/src/main/java/buttondevteam/core/CommandCaller.java +++ /dev/null @@ -1,107 +0,0 @@ -package buttondevteam.core; - -import buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.lib.chat.TBMCChatAPI; -import buttondevteam.lib.chat.TBMCCommandBase; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.PluginCommand; -import org.bukkit.plugin.java.JavaPlugin; - -import java.util.Arrays; - -public class CommandCaller implements CommandExecutor { - - private CommandCaller() { - } - - private static CommandCaller instance; - - public static void RegisterCommand(TBMCCommandBase cmd) throws Exception { - if (instance == null) - instance = new CommandCaller(); - String[] topcmd = new String[1]; //Holds out param - PluginCommand pc = getPluginCommand(cmd, topcmd); - pc.setExecutor(instance); - String[] helptext = cmd.GetHelpText(topcmd[0]); - if (helptext == null || helptext.length == 0) - throw new Exception("Command " + cmd.GetCommandPath() + " has no help text!"); - pc.setUsage(helptext.length > 1 ? helptext[1] : helptext[0]); - } - - @Override - public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) { - StringBuilder path = new StringBuilder(command.getName().toLowerCase()); - for (String arg : args) - path.append(" ").append(arg); - TBMCCommandBase cmd = TBMCChatAPI.GetCommands().get(path.toString()); - int argc = 0; - String[] subcmds = null; - while (cmd == null && (subcmds = TBMCChatAPI.GetSubCommands(path.toString(), sender)).length == 0 && path.toString().contains(" ")) { - path = new StringBuilder(path.substring(0, path.toString().lastIndexOf(' '))); - argc++; - cmd = TBMCChatAPI.GetCommands().get(path.toString()); - } - if (cmd == null) { - if (subcmds.length > 0) //Subcmds will always have value here (see assignment above) - sender.sendMessage(subcmds); - else { - final String errormsg = "§cYou don't have access to any of this command's subcommands or it doesn't have any."; - sender.sendMessage(errormsg); - } - return true; - } - if (cmd.isModOnly() && (MainPlugin.permission != null ? !MainPlugin.permission.has(sender, "tbmc.admin") : !sender.isOp())) { - sender.sendMessage("§cYou need to be a mod to use this command."); - return true; - } - final String[] cmdargs = args.length > 0 ? Arrays.copyOfRange(args, args.length - argc, args.length) : args; - try { - if (!cmd.OnCommand(sender, alias, cmdargs)) { - if (cmd.GetHelpText(alias) == null) { - sender.sendMessage("Wrong usage, but there's no help text! Error is being reported to devs."); - throw new NullPointerException("GetHelpText is null for comand /" + cmd.GetCommandPath()); - } else - sender.sendMessage(cmd.GetHelpText(alias)); - } - } catch (Exception e) { - TBMCCoreAPI.SendException("Failed to execute command /" + cmd.GetCommandPath() + " with arguments " - + Arrays.toString(cmdargs), e); - } - return true; - } - - public static void UnregisterCommand(TBMCCommandBase cmd) throws Exception { - PluginCommand pc = getPluginCommand(cmd, null); - pc.setExecutor(null); //Sets the executor to this plugin - } - - /** - * Gets the plugin command from the TBMC command. - * - * @param cmd The TBMC command - * @param out_topcmd An array with at least 1 elements or null - * @return The Bukkit plugin command - an exception is generated if null - * @throws Exception If the command isn't set up properly (or a different error) - */ - public static PluginCommand getPluginCommand(TBMCCommandBase cmd, String[] out_topcmd) throws Exception { - String topcmd = cmd.GetCommandPath(); - if (topcmd == null) - throw new Exception("Command " + cmd.getClass().getSimpleName() + " has no command path!"); - if (cmd.getPlugin() == null) - throw new Exception("Command " + cmd.GetCommandPath() + " has no plugin!"); - int i; - if ((i = topcmd.indexOf(' ')) != -1) // Get top-level command - topcmd = topcmd.substring(0, i); - if (out_topcmd != null && out_topcmd.length > 0) - out_topcmd[0] = topcmd; - { - PluginCommand pc = ((JavaPlugin) cmd.getPlugin()).getCommand(topcmd); - if (pc == null) - throw new Exception("Top level command " + topcmd + " not registered in plugin.yml for plugin: " - + cmd.getPlugin().getName()); - return pc; - } - } -} diff --git a/ButtonCore/src/main/java/buttondevteam/core/ComponentCommand.java b/ButtonCore/src/main/java/buttondevteam/core/ComponentCommand.java index 10b4b16..55fbf6f 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/ComponentCommand.java +++ b/ButtonCore/src/main/java/buttondevteam/core/ComponentCommand.java @@ -23,21 +23,33 @@ public class ComponentCommand extends ICommand2MC { getManager().addParamConverter(Plugin.class, arg -> Bukkit.getPluginManager().getPlugin(arg), "Plugin not found!"); } - @Subcommand + @Subcommand(helpText = { + "Enable component", + "Temporarily enables a component. If you want to permanently enable a component, change it's 'enabled' config option.\"" + }) public boolean enable(CommandSender sender, Plugin plugin, String component) { - if (plugin instanceof ButtonPlugin) - ((ButtonPlugin) plugin).justReload(); - else + if (plugin instanceof ButtonPlugin) { + if (!((ButtonPlugin) plugin).justReload()) { + sender.sendMessage("§cCouldn't reload config, check console."); + return true; + } + } else plugin.reloadConfig(); //Reload config so the new config values are read - All changes are saved to disk on disable return enable_disable(sender, plugin, component, true); } - @Subcommand + @Subcommand(helpText = { + "Disable component", + "Temporarily disables a component. If you want to permanently disable a component, change it's 'enabled' config option." + }) public boolean disable(CommandSender sender, Plugin plugin, String component) { return enable_disable(sender, plugin, component, false); } - @Subcommand + @Subcommand(helpText = { + "List components", + "Lists all of the registered Chroma components" + }) public boolean list(CommandSender sender, @Command2.OptionalArg String plugin) { sender.sendMessage("§6List of components:"); Component.getComponents().values().stream().filter(c -> plugin == null || c.getPlugin().getName().equalsIgnoreCase(plugin)) //If plugin is null, don't check diff --git a/ButtonCore/src/main/java/buttondevteam/core/MainPlugin.java b/ButtonCore/src/main/java/buttondevteam/core/MainPlugin.java index d5b690b..7557932 100755 --- a/ButtonCore/src/main/java/buttondevteam/core/MainPlugin.java +++ b/ButtonCore/src/main/java/buttondevteam/core/MainPlugin.java @@ -47,6 +47,7 @@ import java.util.logging.Logger; public class MainPlugin extends ButtonPlugin { public static MainPlugin Instance; public static Permission permission; + @Nullable public static Essentials ess; private Logger logger; @@ -60,19 +61,38 @@ public class MainPlugin extends ButtonPlugin { @Setter private boolean chatHandlerEnabled = true; + /** + * Sets whether the plugin should write a list of installed plugins in a txt file. + * It can be useful if some other software needs to know the plugins. + */ private ConfigData writePluginList() { return getIConfig().getData("writePluginList", false); } + /** + * The chat format to use for messages from other platforms if Chroma-Chat is not installed. + */ ConfigData chatFormat() { return getIConfig().getData("chatFormat", "[{origin}|" + "{channel}] <{name}> {message}"); } + /** + * Print some debug information. + */ public ConfigData test() { - return getIConfig().getData("test", true); + return getIConfig().getData("test", false); } + /* + * By default, the plugin uses Vault for all command permission checks, but this can have issues (with PEX for example) where default permissions aren't granted. + * When this setting is off, the plugin uses Bukkit's built-in way of handling permissions, which usually works fine for players. + * You can also grant chroma.command.* to each player (mod-only commands need another permission, chroma.mod). + */ + /*public ConfigData useVaultForCommands() { + return getIConfig().getData("useVaultForCommands", true); + }*/ + @Override public void pluginEnable() { // Logs "Plugin Enabled", registers commands @@ -90,7 +110,8 @@ public class MainPlugin extends ButtonPlugin { Component.registerComponent(this, new ChannelComponent()); Component.registerComponent(this, new RandomTPComponent()); Component.registerComponent(this, new MemberComponent()); - Component.registerComponent(this, new SpawnComponent()); + if (Bukkit.getPluginManager().isPluginEnabled("Multiverse-Core")) + Component.registerComponent(this, new SpawnComponent()); if (Bukkit.getPluginManager().isPluginEnabled("Towny")) //It fails to load the component class otherwise Component.registerComponent(this, new TownyComponent()); if (Bukkit.getPluginManager().isPluginEnabled("Votifier") && economy != null) @@ -124,7 +145,8 @@ public class MainPlugin extends ButtonPlugin { TBMCCoreAPI.SendException("Failed to write plugin list!", e); } } - ess = Essentials.getPlugin(Essentials.class); + if (getServer().getPluginManager().isPluginEnabled("Essentials")) + ess = Essentials.getPlugin(Essentials.class); logger.info(pdf.getName() + " has been Enabled (V." + pdf.getVersion() + ") Test: " + test().get() + "."); } diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/members/MemberComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/members/MemberComponent.java index 5ea098a..0c13f10 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/members/MemberComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/members/MemberComponent.java @@ -2,6 +2,7 @@ package buttondevteam.core.component.members; import buttondevteam.core.MainPlugin; import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.architecture.ComponentMetadata; import buttondevteam.lib.architecture.ConfigData; import org.bukkit.Statistic; import org.bukkit.event.EventHandler; @@ -18,6 +19,7 @@ import static buttondevteam.core.MainPlugin.permission; /** * Allows giving a 'member' group over some time elapsed OR played. */ +@ComponentMetadata(enabledByDefault = false) public class MemberComponent extends Component implements Listener { /** * The permission group to give to the player @@ -62,11 +64,15 @@ public class MemberComponent extends Component implements Listener { if (permission != null && !permission.playerInGroup(event.getPlayer(), memberGroup().get()) && (new Date(event.getPlayer().getFirstPlayed()).toInstant().plus(registeredForDays().get(), ChronoUnit.DAYS).isBefore(Instant.now()) || event.getPlayer().getStatistic(playtime.getKey()) > playtime.getValue() * playedHours().get())) { - if (permission.playerAddGroup(null, event.getPlayer(), memberGroup().get())) { - event.getPlayer().sendMessage("§bYou are a member now. YEEHAW"); - MainPlugin.Instance.getLogger().info("Added " + event.getPlayer().getName() + " as a member."); - } else { - MainPlugin.Instance.getLogger().warning("Failed to assign the member role! Please make sure the member group exists or disable the component if it's unused."); + try { + if (permission.playerAddGroup(null, event.getPlayer(), memberGroup().get())) { + event.getPlayer().sendMessage("§bYou are a member now. YEEHAW"); + MainPlugin.Instance.getLogger().info("Added " + event.getPlayer().getName() + " as a member."); + } else { + MainPlugin.Instance.getLogger().warning("Failed to assign the member role! Please make sure the member group exists or disable the component if it's unused."); + } + } catch (UnsupportedOperationException e) { + MainPlugin.Instance.getLogger().warning("Failed to assign the member role! Groups are not supported by the permissions implementation."); } } } diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTP.java b/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTP.java index 755a32a..e841d76 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTP.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTP.java @@ -1,16 +1,20 @@ package buttondevteam.core.component.randomtp; -import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; -import buttondevteam.lib.chat.TBMCChatAPI; -import buttondevteam.lib.chat.TBMCCommandBase; +import buttondevteam.lib.chat.ICommand2MC; import org.bukkit.*; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import java.util.logging.Logger; + // @formatter:off -@SuppressWarnings("FieldCanBeLocal")@CommandClass -public class RandomTP extends TBMCCommandBase +@SuppressWarnings("FieldCanBeLocal")@CommandClass(helpText = { + "§6---- Random Teleport ----", + "Teleport player to random location within world border. Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players." +}) +public class RandomTP extends ICommand2MC { private final int radius = 70; //set how far apart the five teleport positions are @@ -51,37 +55,23 @@ public class RandomTP extends TBMCCommandBase /*================================================================================================*/ - public void onEnable(Component component) + public void onEnable(RandomTPComponent component) { - System.out.println("Adding command"); - TBMCChatAPI.AddCommand(component, this); - - System.out.println("Getting world"); world = Bukkit.getWorld("World"); - System.out.println("Getting border"); border = world.getWorldBorder(); - System.out.println("Getting new location"); - System.out.println("Success: "+newLocation()); //TODO: It takes 10-30 seconds to find a location (newLocation() was there) - } - - /*================================================================================================*/ - - public String[] GetHelpText(String alias) - { - return new String[] - { - "§6---- Random Teleport ----", - "Teleport player to random location within world border. Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players." - }; + Logger logger = component.getPlugin().getLogger(); + logger.info("Getting new location"); + if(border.getSize() > 100000) + logger.warning("World border is wide, it may take a minute..."); + logger.info("Success: "+newLocation()); } /*================================================================================================*/ - - public boolean OnCommand(CommandSender sender, String command, String[] args) + + @Command2.Subcommand + public boolean def(CommandSender sender, Player player) { - if (args.length == 0) return false; - - if (sender.isOp()) return rtp(Bukkit.getPlayer(args[0])); + if (sender.isOp()) return rtp(player); else sender.sendMessage("§7 hmm, " + sender.getName() + "... " + sender.getName() + "... nope, no operator permissions."); diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTPComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTPComponent.java index 0f1fb5e..c5a0a30 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTPComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTPComponent.java @@ -2,15 +2,20 @@ package buttondevteam.core.component.randomtp; import buttondevteam.core.MainPlugin; import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.architecture.ComponentMetadata; /** * Teleport player to random location within world border. * Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players. + * Author: github.com/iiegit */ +@ComponentMetadata(enabledByDefault = false) public class RandomTPComponent extends Component { @Override protected void enable() { - new RandomTP().onEnable(this); //It registers it's command + var rtp = new RandomTP(); + registerCommand(rtp); + rtp.onEnable(this); } @Override diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/restart/PrimeRestartCommand.java b/ButtonCore/src/main/java/buttondevteam/core/component/restart/PrimeRestartCommand.java index 389da82..c2b56c8 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/restart/PrimeRestartCommand.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/restart/PrimeRestartCommand.java @@ -1,48 +1,43 @@ package buttondevteam.core.component.restart; import buttondevteam.core.component.channel.Channel; +import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; +import buttondevteam.lib.chat.ICommand2MC; import buttondevteam.lib.chat.TBMCChatAPI; -import buttondevteam.lib.chat.TBMCCommandBase; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -@CommandClass(path = "primerestart", modOnly = true) +@CommandClass(path = "primerestart", modOnly = true, helpText = { + "§6---- Prime restart ----", // + "Restarts the server as soon as nobody is online.", // + "To be loud, type something after, like /primerestart lol (it doesn't matter what you write)", // + "To be silent, don't type anything" // +}) @RequiredArgsConstructor -public class PrimeRestartCommand extends TBMCCommandBase { +public class PrimeRestartCommand extends ICommand2MC { private final RestartComponent component; - @Override - public boolean OnCommand(CommandSender sender, String alias, String[] args) { - loud = args.length > 0; - if (Bukkit.getOnlinePlayers().size() > 0) { - sender.sendMessage("§bPlayers online, restart delayed."); - if (loud) + + public void def(CommandSender sender, @Command2.TextArg @Command2.OptionalArg String somethingrandom) { + loud = somethingrandom != null; + if (Bukkit.getOnlinePlayers().size() > 0) { + sender.sendMessage("§bPlayers online, restart delayed."); + if (loud) TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", component.getRestartBroadcast()); - plsrestart = true; - } else { - sender.sendMessage("§bNobody is online. Restarting now."); - if (loud) + plsrestart = true; + } else { + sender.sendMessage("§bNobody is online. Restarting now."); + if (loud) TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online. Restarting server.", component.getRestartBroadcast()); - Bukkit.spigot().restart(); - } - return true; - } + Bukkit.spigot().restart(); + } + } @Getter private static boolean plsrestart = false; @Getter private static boolean loud = false; - - @Override - public String[] GetHelpText(String alias) { - return new String[]{ // - "§6---- Prime restart ----", // - "Restarts the server as soon as nobody is online.", // - "To be loud, type something after, like /primerestart lol (it doesn't matter what you write)", // - "To be silent, don't type anything" // - }; - } } diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/restart/RestartComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/restart/RestartComponent.java index d0104a0..83df9f5 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/restart/RestartComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/restart/RestartComponent.java @@ -20,7 +20,7 @@ public class RestartComponent extends Component implements Listener @Override public void enable() { registerCommand(new ScheduledRestartCommand(this)); - TBMCChatAPI.AddCommand(this, new PrimeRestartCommand(this)); + registerCommand(new PrimeRestartCommand(this)); registerListener(this); restartBroadcast = TBMCSystemChatEvent.BroadcastTarget.add("restartCountdown"); } diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/spawn/SpawnComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/spawn/SpawnComponent.java index 12cc43b..7ad07aa 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/spawn/SpawnComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/spawn/SpawnComponent.java @@ -6,6 +6,7 @@ import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.ICommand2MC; +import com.earth2me.essentials.Trade; import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; @@ -13,10 +14,15 @@ import com.onarandombox.MultiverseCore.MultiverseCore; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.plugin.messaging.PluginMessageListener; import java.io.*; +import java.math.BigDecimal; +/** + * Provides a /spawn command that works with BungeeCord. Make sure to set up on each server. + */ public class SpawnComponent extends Component implements PluginMessageListener { @Override protected void enable() { @@ -69,7 +75,7 @@ public class SpawnComponent extends Component implements PluginMessa } /** - * Set to empty if this server is the target. + * The BungeeCord server that has the spawn. Set to empty if this server is the target. */ private ConfigData targetServer() { return getConfig().getData("targetServer", ""); @@ -86,8 +92,16 @@ public class SpawnComponent extends Component implements PluginMessa @Command2.Subcommand public void def(Player player) { if (targetServer().get().length() == 0) { - player.sendMessage("§bTeleporting to spawn."); - player.teleport(spawnloc); + player.sendMessage("§bTeleporting to spawn..."); + try { + if (MainPlugin.ess != null) + MainPlugin.ess.getUser(player).getTeleport() + .teleport(spawnloc, new Trade(BigDecimal.ZERO, MainPlugin.ess), PlayerTeleportEvent.TeleportCause.COMMAND); + else + player.teleport(spawnloc); + } catch (Exception e) { + player.sendMessage("§cFailed to teleport: " + e); + } return; } ByteArrayDataOutput out = ByteStreams.newDataOutput(); diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/towny/TownyComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/towny/TownyComponent.java index 3d8966d..a6fdda4 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/towny/TownyComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/towny/TownyComponent.java @@ -31,7 +31,9 @@ public class TownyComponent extends Component { */ public static void renameInTowny(String oldName, String newName) { if (!ComponentManager.isEnabled(TownyComponent.class)) - return; TownyUniverse tu = Towny.getPlugin(Towny.class).getTownyUniverse(); + return; + Bukkit.getLogger().info("Renaming" + oldName + " in Towny to " + newName); + TownyUniverse tu = Towny.getPlugin(Towny.class).getTownyUniverse(); Resident resident = tu.getResidentMap().get(oldName.toLowerCase()); //The map keys are lowercase if (resident == null) { Bukkit.getLogger().warning("Resident not found - couldn't rename in Towny."); @@ -42,6 +44,7 @@ public class TownyComponent extends Component { } else try { tu.getDataSource().renamePlayer(resident, newName); //Fixed in Towny 0.91.1.2 + Bukkit.getLogger().info("Renaming done."); } catch (AlreadyRegisteredException e) { TBMCCoreAPI.SendException("Failed to rename resident, there's already one with this name.", e); } catch (NotRegisteredException e) { diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/updater/PluginUpdaterComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/updater/PluginUpdaterComponent.java index 3ea44c8..4ed8f63 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/updater/PluginUpdaterComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/updater/PluginUpdaterComponent.java @@ -2,15 +2,16 @@ package buttondevteam.core.component.updater; import buttondevteam.core.MainPlugin; import buttondevteam.lib.architecture.Component; -import buttondevteam.lib.chat.TBMCChatAPI; +import buttondevteam.lib.architecture.ComponentMetadata; /** - * Downloads plugin updates built from their source using JitPack - older code + * Downloads plugin updates built from their source using JitPack - doesn't work anymore */ +@ComponentMetadata(enabledByDefault = false) public class PluginUpdaterComponent extends Component { //TODO: Config @Override public void enable() { - TBMCChatAPI.AddCommand(this, new UpdatePluginCommand()); + registerCommand(new UpdatePluginCommand()); } @Override diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/updater/UpdatePluginCommand.java b/ButtonCore/src/main/java/buttondevteam/core/component/updater/UpdatePluginCommand.java index 0569e89..d236be5 100755 --- a/ButtonCore/src/main/java/buttondevteam/core/component/updater/UpdatePluginCommand.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/updater/UpdatePluginCommand.java @@ -2,40 +2,41 @@ package buttondevteam.core.component.updater; import buttondevteam.core.MainPlugin; import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; -import buttondevteam.lib.chat.TBMCCommandBase; +import buttondevteam.lib.chat.ICommand2MC; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; +import java.lang.reflect.Method; + @CommandClass(modOnly = true) -public class UpdatePluginCommand extends TBMCCommandBase { - @Override - public boolean OnCommand(CommandSender sender, String alias, String[] args) { - Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> { - if (args.length == 0) { - sender.sendMessage("Downloading plugin names..."); - boolean first = true; - for (String plugin : PluginUpdater.GetPluginNames()) { - if (first) { - sender.sendMessage("§6---- Plugin names ----"); - first = false; - } - sender.sendMessage("- " + plugin); - } - } else { - TBMCCoreAPI.UpdatePlugin(args[0], sender, args.length == 1 ? "master" : args[1]); - } - }); - return true; +public class UpdatePluginCommand extends ICommand2MC { + public void def(CommandSender sender, @Command2.OptionalArg String plugin, @Command2.OptionalArg String branch) { + Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> { + if (plugin == null) { + sender.sendMessage("Downloading plugin names..."); + boolean first = true; + for (String plugin2 : PluginUpdater.GetPluginNames()) { + if (first) { + sender.sendMessage("§6---- Plugin names ----"); + first = false; + } + sender.sendMessage("- " + plugin2); + } + } else { + TBMCCoreAPI.UpdatePlugin(plugin, sender, branch == null ? "master" : branch); + } + }); } @Override - public String[] GetHelpText(String alias) { - return new String[] { // - "§6---- Update plugin ----", // - "This command downloads the latest version of a TBMC plugin from GitHub", // - "To update a plugin: /" + alias + " ", // - "To list the plugin names: /" + alias // + public String[] getHelpText(Method method, Command2.Subcommand ann) { + return new String[]{ // + "§6---- Update plugin ----", // + "This command downloads the latest version of a custom plugin from GitHub", // + "To update a plugin: add its name", // + "To list the plugin names: don't type a name" // }; } } diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/votifier/VotifierComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/votifier/VotifierComponent.java index 94ffe55..6329e03 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/votifier/VotifierComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/votifier/VotifierComponent.java @@ -2,6 +2,7 @@ package buttondevteam.core.component.votifier; import buttondevteam.core.MainPlugin; import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.architecture.ComponentMetadata; import buttondevteam.lib.architecture.ConfigData; import com.vexsoftware.votifier.model.Vote; import com.vexsoftware.votifier.model.VotifierEvent; @@ -15,6 +16,7 @@ import org.bukkit.event.EventHandler; * Do not use (EULA) */ @RequiredArgsConstructor +@ComponentMetadata(enabledByDefault = false) public class VotifierComponent extends Component { private final Economy economy; @@ -39,11 +41,11 @@ public class VotifierComponent extends Component { getPlugin().getLogger().info("Vote: " + vote); org.bukkit.OfflinePlayer op = Bukkit.getOfflinePlayer(vote.getUsername()); Player p = Bukkit.getPlayer(vote.getUsername()); - if (op != null) { + /*if (op != null) { economy.depositPlayer(op, rewardAmount().get()); } if (p != null) { p.sendMessage("§bThanks for voting! $50 was added to your account."); - } + }*/ } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java index d22f192..b16b447 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java @@ -1,34 +1,41 @@ package buttondevteam.lib.architecture; +import buttondevteam.buttonproc.HasConfig; import buttondevteam.core.ComponentManager; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.chat.Command2MC; -import buttondevteam.lib.chat.TBMCChatAPI; import lombok.AccessLevel; import lombok.Getter; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; +import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.Arrays; import java.util.Optional; import java.util.Stack; -@HasConfig +@HasConfig(global = true) public abstract class ButtonPlugin extends JavaPlugin { @Getter private static Command2MC command2MC = new Command2MC(); @Getter(AccessLevel.PROTECTED) private IHaveConfig iConfig; + private CommentedConfiguration yaml; @Getter(AccessLevel.PROTECTED) private IHaveConfig data; //TODO - private boolean loaded = false; /** * Used to unregister components in the right order - and to reload configs */ @Getter private Stack> componentStack = new Stack<>(); + ; protected abstract void pluginEnable(); @@ -45,7 +52,10 @@ public abstract class ButtonPlugin extends JavaPlugin { @Override public final void onEnable() { - loadConfig(); + if (!loadConfig()) { + getLogger().warning("Please fix the issues and restart the server to load the plugin."); + return; + } try { pluginEnable(); } catch (Exception e) { @@ -55,10 +65,14 @@ public abstract class ButtonPlugin extends JavaPlugin { IHaveConfig.pregenConfig(this, null); } - private void loadConfig() { - var section = super.getConfig().getConfigurationSection("global"); - if (section == null) section = super.getConfig().createSection("global"); + private boolean loadConfig() { + var config = getConfig(); + if (config == null) + return false; + var section = config.getConfigurationSection("global"); + if (section == null) section = config.createSection("global"); iConfig = new IHaveConfig(section, this::saveConfig); + return true; } @Override @@ -67,9 +81,10 @@ public abstract class ButtonPlugin extends JavaPlugin { pluginPreDisable(); ComponentManager.unregComponents(this); pluginDisable(); - saveConfig(); + if (ConfigData.saveNow(getConfig())) + getLogger().info("Saved configuration changes."); iConfig = null; //Clearing the hashmap is not enough, we need to update the section as well - TBMCChatAPI.RemoveCommands(this); + //TBMCChatAPI.RemoveCommands(this); - TODO } catch (Exception e) { TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e); } @@ -88,15 +103,50 @@ public abstract class ButtonPlugin extends JavaPlugin { } public boolean justReload() { - if (loaded && ConfigData.saveNow(getConfig())) { - getLogger().warning("Saved pending configuration changes to the file, didn't reload (try again)."); + if (yaml != null && ConfigData.saveNow(getConfig())) { + getLogger().warning("Saved pending configuration changes to the file, didn't reload. Apply your changes again."); return false; } - super.reloadConfig(); - loaded = true; //Needed because for the first time it uses reloadConfig() to load it + var file = new File(getDataFolder(), "config.yml"); + var yaml = new CommentedConfiguration(file); + if (file.exists()) { + try { + yaml.load(file); + } catch (IOException | InvalidConfigurationException e) { + getLogger().warning("Failed to load config! Check for syntax errors."); + e.printStackTrace(); + return false; + } + } + this.yaml = yaml; + var res = getTextResource("configHelp.yml"); + if (res == null) + return true; + var yc = YamlConfiguration.loadConfiguration(res); + for (var kv : yc.getValues(true).entrySet()) + if (kv.getValue() instanceof String) + yaml.addComment(kv.getKey(), Arrays.stream(((String) kv.getValue()).split("\n")) + .map(str -> "# " + str.trim()).toArray(String[]::new)); return true; } + @Override + public FileConfiguration getConfig() { + if (yaml == null) + justReload(); //TODO: If it fails to load, it'll probably throw an NPE + return yaml; + } + + @Override + public void saveConfig() { + try { + if (yaml != null) + yaml.save(); + } catch (Exception e) { + TBMCCoreAPI.SendException("Failed to save config", e); + } + } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ConfigOpts { diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/CommentedConfiguration.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/CommentedConfiguration.java new file mode 100644 index 0000000..62cafb2 --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/CommentedConfiguration.java @@ -0,0 +1,228 @@ +package buttondevteam.lib.architecture; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.file.YamlConstructor; +import org.bukkit.configuration.file.YamlRepresenter; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.representer.Representer; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; + +/** + * A copy of Towny's CommentedConfiguration: https://github.com/TownyAdvanced/Towny/blob/master/src/com/palmergames/bukkit/config/CommentedConfiguration.java + * Modified to remove dependency on the FileMgmt class + * + * @author dumptruckman & Articdive + */ +public class CommentedConfiguration extends YamlConfiguration { //TODO: Remove FileMgmt dependency + private HashMap comments; + private File file; + + private final DumperOptions yamlOptions = new DumperOptions(); + private final Representer yamlRepresenter = new YamlRepresenter(); + private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions); + + public CommentedConfiguration(File file) { + + super(); + comments = new HashMap<>(); + this.file = file; + } + + public boolean load() { + + boolean loaded = true; + + try { + this.load(file); + } catch (InvalidConfigurationException | IOException e) { + loaded = false; + } + + return loaded; + } + + public void save() throws IOException { + + boolean saved = true; + + // Save the config just like normal + try { + this.save(file); + + } catch (Exception e) { + saved = false; + } + + // if there's comments to add and it saved fine, we need to add comments + if (!comments.isEmpty() && saved) { + // String array of each line in the config file + String[] yamlContents = Files.readAllLines(file.toPath()).toArray(new String[0]); + + // This will hold the newly formatted line + StringBuilder newContents = new StringBuilder(); + // This holds the current path the lines are at in the config + String currentPath = ""; + // This flags if the line is a node or unknown text. + boolean node; + // The depth of the path. (number of words separated by periods - 1) + int depth = 0; + + // Loop through the config lines + for (String line : yamlContents) { + // If the line is a node (and not something like a list value) + if (line.contains(": ") || (line.length() > 1 && line.charAt(line.length() - 1) == ':')) { + + // This is a node so flag it as one + node = true; + + // Grab the index of the end of the node name + int index; + index = line.indexOf(": "); + if (index < 0) { + index = line.length() - 1; + } + // If currentPath is empty, store the node name as the currentPath. (this is only on the first iteration, i think) + if (currentPath.isEmpty()) { + currentPath = line.substring(0, index); + } else { + // Calculate the whitespace preceding the node name + int whiteSpace = 0; + for (int n = 0; n < line.length(); n++) { + if (line.charAt(n) == ' ') { + whiteSpace++; + } else { + break; + } + } + // Find out if the current depth (whitespace * 2) is greater/lesser/equal to the previous depth + if (whiteSpace / 2 > depth) { + // Path is deeper. Add a . and the node name + currentPath += "." + line.substring(whiteSpace, index); + depth++; + } else if (whiteSpace / 2 < depth) { + // Path is shallower, calculate current depth from whitespace (whitespace / 2) and subtract that many levels from the currentPath + int newDepth = whiteSpace / 2; + for (int i = 0; i < depth - newDepth; i++) { + currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), ""); + } + // Grab the index of the final period + int lastIndex = currentPath.lastIndexOf("."); + if (lastIndex < 0) { + // if there isn't a final period, set the current path to nothing because we're at root + currentPath = ""; + } else { + // If there is a final period, replace everything after it with nothing + currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), ""); + currentPath += "."; + } + // Add the new node name to the path + currentPath += line.substring(whiteSpace, index); + // Reset the depth + depth = newDepth; + } else { + // Path is same depth, replace the last path node name to the current node name + int lastIndex = currentPath.lastIndexOf("."); + if (lastIndex < 0) { + // if there isn't a final period, set the current path to nothing because we're at root + currentPath = ""; + } else { + // If there is a final period, replace everything after it with nothing + currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), ""); + currentPath += "."; + } + //currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), ""); + currentPath += line.substring(whiteSpace, index); + + } + + } + + } else { + node = false; + } + + if (node) { + // If there's a comment for the current path, retrieve it and flag that path as already commented + String comment = comments.get(currentPath); + + if (comment != null) { + // Add the comment to the beginning of the current line + line = comment + System.getProperty("line.separator") + line + System.getProperty("line.separator"); + } else { + // Add a new line as it is a node, but has no comment + line += System.getProperty("line.separator"); + } + } + // Add the (modified) line to the total config String + if (!node) { + newContents.append(line).append(System.getProperty("line.separator")); + } else { + newContents.append(line); + } + } + + /* + * Due to a Bukkit Bug with the Configuration + * we just need to remove any extra comments at the start of a file. + */ + while (newContents.toString().startsWith(" " + System.getProperty("line.separator"))) { + newContents = new StringBuilder(newContents.toString().replaceFirst(" " + System.getProperty("line.separator"), "")); + } + Files.write(file.toPath(), newContents.toString().getBytes()); + } + } + + /** + * Adds a comment just before the specified path. The comment can be + * multiple lines. An empty string will indicate a blank line. + * + * @param path Configuration path to add comment. + * @param commentLines Comments to add. One String per line. + */ + public void addComment(String path, String... commentLines) { + + StringBuilder commentstring = new StringBuilder(); + StringBuilder leadingSpaces = new StringBuilder(); + for (int n = 0; n < path.length(); n++) { + if (path.charAt(n) == '.') { + leadingSpaces.append(" "); + } + } + for (String line : commentLines) { + if (!line.isEmpty()) { + line = leadingSpaces + line; + } else { + line = " "; + } + if (commentstring.length() > 0) { + commentstring.append(System.getProperty("line.separator")); + } + commentstring.append(line); + } + comments.put(path, commentstring.toString()); + } + + @Override + public String saveToString() { + yamlOptions.setIndent(options().indent()); + yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + yamlOptions.setWidth(10000); + yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + + String dump = yaml.dump(getValues(false)); + + + if (dump.equals(BLANK_CONFIG)) { + dump = ""; + } + + return dump; + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/Component.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/Component.java index 582102f..c0b7b63 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/Component.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/Component.java @@ -1,11 +1,10 @@ package buttondevteam.lib.architecture; +import buttondevteam.buttonproc.HasConfig; import buttondevteam.core.ComponentManager; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException; import buttondevteam.lib.chat.ICommand2MC; -import buttondevteam.lib.chat.TBMCChatAPI; -import buttondevteam.lib.chat.TBMCCommandBase; import lombok.Getter; import lombok.NonNull; import lombok.val; @@ -16,13 +15,14 @@ import org.bukkit.plugin.java.JavaPlugin; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Collectors; /** * Configuration is based on class name */ -@HasConfig //Used for obtaining javadoc +@HasConfig(global = false) //Used for obtaining javadoc public abstract class Component { private static HashMap, Component> components = new HashMap<>(); @@ -37,7 +37,7 @@ public abstract class Component { private @Getter IHaveConfig data; //TODO public final ConfigData shouldBeEnabled() { - return config.getData("enabled", true); + return config.getData("enabled", Optional.ofNullable(getClass().getAnnotation(ComponentMetadata.class)).map(ComponentMetadata::enabledByDefault).orElse(true)); } /** @@ -141,8 +141,7 @@ public abstract class Component { //System.out.println("Done enabling "+component.getClassName()); } else { component.disable(); - component.plugin.saveConfig(); - TBMCChatAPI.RemoveCommands(component); + //TBMCChatAPI.RemoveCommands(component); - TODO } } @@ -212,15 +211,6 @@ public abstract class Component { ButtonPlugin.getCommand2MC().registerCommand(commandBase); } - /** - * Registers a TBMCCommand to the component. Make sure to add it to plugin.yml and use {@link buttondevteam.lib.chat.CommandClass}. - * - * @param commandBase Custom coded command class - */ - protected final void registerCommand(TBMCCommandBase commandBase) { - TBMCChatAPI.AddCommand(this, commandBase); - } - /** * Registers a Listener to this component * diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ComponentMetadata.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ComponentMetadata.java index a182df8..5d12c91 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ComponentMetadata.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ComponentMetadata.java @@ -9,4 +9,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) public @interface ComponentMetadata { Class[] depends() default {}; + + boolean enabledByDefault() default true; } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java index 4cd683c..2725866 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java @@ -15,7 +15,7 @@ import org.bukkit.scheduler.BukkitTask; import java.lang.reflect.Array; import java.util.HashMap; import java.util.List; -import java.util.Objects; +import java.util.function.BiFunction; import java.util.function.Function; /** @@ -32,7 +32,7 @@ public class ConfigData { @Getter @Setter(AccessLevel.PACKAGE) private String path; - private final T def; + protected final T def; private final Object primitiveDef; private final Runnable saveAction; /** @@ -48,10 +48,6 @@ public class ConfigData { * The config value should not change outside this instance */ private T value; - /** - * Whether the default value is saved in the yaml - */ - private boolean saved = false; //This constructor is needed because it sets the getter and setter ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function getter, Function setter, Runnable saveAction) { @@ -84,27 +80,35 @@ public class ConfigData { @SuppressWarnings("unchecked") public T get() { if (value != null) return value; //Speed things up - Object val = config == null ? null : config.get(path); //config==null: testing - if (val == null) { + Object val; + if (config == null || !config.isSet(path)) { //Call set() if config == null val = primitiveDef; - } - if (!saved && Objects.equals(val, primitiveDef)) { //String needs .equals() if ((def == null || this instanceof ReadOnlyConfigData) && config != null) //In Discord's case def may be null - config.set(path, primitiveDef); + setInternal(primitiveDef); //If read-only then we still need to save the default value so it can be set else set(def); //Save default value - def is always set - saved = true; - } + } else + val = config.get(path); //config==null: testing + if (val == null) //If it's set to null explicitly + val = primitiveDef; + BiFunction convert = (_val, _def) -> { + if (_def instanceof Number) //If we expect a number + if (_val instanceof Number) + _val = ChromaUtils.convertNumber((Number) _val, + (Class) _def.getClass()); + else + _val = _def; //If we didn't get a number, return default (which is a number) + else if (_val instanceof List && _def != null && _def.getClass().isArray()) + _val = ((List) _val).toArray((T[]) Array.newInstance(_def.getClass().getComponentType(), 0)); + return _val; + }; if (getter != null) { + val = convert.apply(val, primitiveDef); T hmm = getter.apply(val); if (hmm == null) hmm = def; //Set if the getter returned null return hmm; } - if (val instanceof Number && def != null) - val = ChromaUtils.convertNumber((Number) val, - (Class) def.getClass()); - if (val instanceof List && def != null && def.getClass().isArray()) - val = ((List) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0)); + val = convert.apply(val, def); return value = (T) val; //Always cache, if not cached yet } @@ -115,20 +119,23 @@ public class ConfigData { if (setter != null && value != null) val = setter.apply(value); else val = value; - if (config != null) { - config.set(path, val); - if (!saveTasks.containsKey(config.getRoot())) { - synchronized (saveTasks) { - saveTasks.put(config.getRoot(), new SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> { - synchronized (saveTasks) { - saveTasks.remove(config.getRoot()); - saveAction.run(); - } - }, 100), saveAction)); - } + if (config != null) + setInternal(val); + this.value = value; + } + + private void setInternal(Object val) { + config.set(path, val); + if (!saveTasks.containsKey(config.getRoot())) { + synchronized (saveTasks) { + saveTasks.put(config.getRoot(), new SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> { + synchronized (saveTasks) { + saveTasks.remove(config.getRoot()); + saveAction.run(); + } + }, 100), saveAction)); } } - this.value = value; } @AllArgsConstructor @@ -138,12 +145,14 @@ public class ConfigData { } public static boolean saveNow(Configuration config) { - SaveTask st = saveTasks.get(config); - if (st != null) { - st.task.cancel(); - saveTasks.remove(config); - st.saveAction.run(); - return true; + synchronized (saveTasks) { + SaveTask st = saveTasks.get(config); + if (st != null) { + st.task.cancel(); + saveTasks.remove(config); + st.saveAction.run(); + return true; + } } return false; } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java index 2a73e16..18b8097 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java @@ -139,6 +139,21 @@ public final class IHaveConfig { return (ConfigData) data; } + /** + * This method overload should only be used with primitves or String. + * + * @param path The path in config to use + * @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") + public ListConfigData getListData(String path) { + ConfigData data = datamap.get(path); + if (data == null) + datamap.put(path, data = new ListConfigData<>(config, path, new ListConfigData.List(), saveAction)); + return (ListConfigData) data; + } + /** * Generates the config YAML. * diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ListConfigData.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ListConfigData.java new file mode 100644 index 0000000..3e89172 --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ListConfigData.java @@ -0,0 +1,125 @@ +package buttondevteam.lib.architecture; + +import lombok.val; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +public class ListConfigData extends ConfigData> { + @SuppressWarnings("unchecked") + ListConfigData(ConfigurationSection config, String path, List def, Runnable saveAction) { + super(config, path, def, new ArrayList<>(def), list -> { + var l = new List<>((ArrayList) list); + l.listConfig = def.listConfig; + return l; + }, ArrayList::new, saveAction); + def.listConfig = this; //Can't make the List class non-static or pass this in the super() constructor + } + + public static class List extends ArrayList { + private ListConfigData listConfig; + + public List(@NotNull Collection c) { + super(c); + } + + public List() { + } + + private void update() { + listConfig.set(this); //Update the config model and start save task if needed + } + + @Override + public T set(int index, T element) { + T ret = super.set(index, element); + update(); + return ret; + } + + @Override + public boolean add(T t) { + val ret = super.add(t); + update(); + return ret; + } + + @Override + public void add(int index, T element) { + super.add(index, element); + update(); + } + + @Override + public T remove(int index) { + T ret = super.remove(index); + update(); + return ret; + } + + @Override + public boolean remove(Object o) { + val ret = super.remove(o); + update(); + return ret; + } + + @Override + public boolean addAll(Collection c) { + val ret = super.addAll(c); + update(); + return ret; + } + + @Override + public boolean addAll(int index, Collection c) { + val ret = super.addAll(index, c); + update(); + return ret; + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + super.removeRange(fromIndex, toIndex); + update(); + } + + @Override + public boolean removeAll(Collection c) { + val ret = super.removeAll(c); + update(); + return ret; + } + + @Override + public boolean retainAll(Collection c) { + val ret = super.retainAll(c); + update(); + return ret; + } + + @Override + public boolean removeIf(Predicate filter) { + val ret = super.removeIf(filter); + update(); + return ret; + } + + @Override + public void replaceAll(UnaryOperator operator) { + super.replaceAll(operator); + update(); + } + + @Override + public void sort(Comparator c) { + super.sort(c); + update(); + } + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2.java index 562de54..3983f61 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2.java @@ -135,7 +135,7 @@ public abstract class Command2 try { handleCommandAsync(sender, commandline, sd, subcommand, sync); } catch (Exception e) { - TBMCCoreAPI.SendException("Command execution failed for sender " + sender + " and message " + commandline, e); + TBMCCoreAPI.SendException("Command execution failed for sender " + sender.getName() + "(" + sender.getClass().getCanonicalName() + ") and message " + commandline, e); } }); return true; //We found a method @@ -144,6 +144,17 @@ public abstract class Command2 } //Needed because permission checking may load the (perhaps offline) sender's file which is disallowed on the main thread + + /** + * Handles a command asynchronously + * + * @param sender The command sender + * @param commandline The command line the sender sent + * @param sd The subcommand data + * @param subcommand The subcommand text + * @param sync Whether the command was originally sync + * @throws Exception If something's not right + */ public void handleCommandAsync(TP sender, String commandline, SubcommandData sd, String subcommand, boolean sync) throws Exception { if (sd.method == null || sd.command == null) { //Main command not registered, but we have subcommands sender.sendMessage(sd.helpText); @@ -247,7 +258,7 @@ public abstract class Command2 public abstract void registerCommand(TC command); - protected void registerCommand(TC command, char commandChar) { + protected void registerCommand(TC command, @SuppressWarnings("SameParameterValue") char commandChar) { val path = command.getCommandPath(); int x = path.indexOf(' '); val mainPath = commandChar + path.substring(0, x == -1 ? path.length() : x); @@ -277,7 +288,7 @@ public abstract class Command2 if (ht != null) { val subcommand = commandChar + path + //Add command path (class name by default) (method.getName().equals("def") ? "" : " " + method.getName().replace('_', ' ').toLowerCase()); //Add method name, unless it's 'def' - ht = getHelpText(method, ht, subcommand); + ht = getParameterHelp(method, ht, subcommand); subcommands.put(subcommand, new SubcommandData<>(method, command, ht)); //Result of the above (def) is that it will show the help text scmdHelpList.add(subcommand); nosubs = false; @@ -299,7 +310,7 @@ public abstract class Command2 } } - private String[] getHelpText(Method method, String[] ht, String subcommand) { + private String[] getParameterHelp(Method method, String[] ht, String subcommand) { val str = method.getDeclaringClass().getResourceAsStream("/commands.yml"); if (str == null) TBMCCoreAPI.SendException("Error while getting command data!", new Exception("Resource not found!")); diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MC.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MC.java index 561befc..2f52c69 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MC.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MC.java @@ -6,6 +6,7 @@ import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.server.TabCompleteEvent; @@ -16,7 +17,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; -import java.util.UUID; import java.util.function.Function; public class Command2MC extends Command2 implements Listener { @@ -26,7 +26,7 @@ public class Command2MC extends Command2 implemen var perm = "chroma.command." + command.getCommandPath().replace(' ', '.'); if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset Bukkit.getPluginManager().addPermission(new Permission(perm, - modOnly(command) ? PermissionDefault.OP : PermissionDefault.TRUE)); //Allow commands by default, unless it's mod only - TODO: Test + PermissionDefault.TRUE)); //Allow commands by default, it will check mod-only for (val method : command.getClass().getMethods()) { if (!method.isAnnotationPresent(Subcommand.class)) continue; String pg = permGroup(command, method); @@ -34,7 +34,6 @@ public class Command2MC extends Command2 implemen perm = "chroma." + pg; if (Bukkit.getPluginManager().getPermission(perm) == null) //It may occur multiple times Bukkit.getPluginManager().addPermission(new Permission(perm, - //pg.equals(Subcommand.MOD_GROUP) ? PermissionDefault.OP : PermissionDefault.TRUE)); //Allow commands by default, unless it's mod only PermissionDefault.OP)); //Do not allow any commands that belong to a group } } @@ -50,16 +49,18 @@ public class Command2MC extends Command2 implemen boolean p = true; String[] perms = { "chroma.command." + command.getCommandPath().replace(' ', '.'), - (pg = permGroup(command, method)).length() > 0 ? "chroma." + pg : null, - modOnly(command) ? "tbmc.admin" : null + (pg = permGroup(command, method)).length() > 0 ? "chroma." + pg : null }; for (String perm : perms) { if (perm != null) { if (p) { //Use OfflinePlayer to avoid fetching player data if (sender instanceof OfflinePlayer) - p = MainPlugin.permission.playerHas(null, (OfflinePlayer) sender, perm); + p = MainPlugin.permission.playerHas(sender instanceof Player ? ((Player) sender).getLocation().getWorld().getName() : null, (OfflinePlayer) sender, perm); else - p = MainPlugin.permission.playerHas(null, Bukkit.getOfflinePlayer(new UUID(0, 0)), perm); + p = false; //Use sender's method + //System.out.println("playerHas " + perm + ": " + p); + //System.out.println("hasPermission: " + sender.hasPermission(perm)); + if (!p) p = sender.hasPermission(perm); } else break; //If any of the permissions aren't granted then don't allow } } @@ -67,17 +68,7 @@ public class Command2MC extends Command2 implemen } /** - * Returns true if this class or any of the superclasses are mod only. - * - * @param command The command to check - * @return Whether the command is mod only - */ - private boolean modOnly(ICommand2MC command) { - return getAnnForValue(command.getClass(), CommandClass.class, CommandClass::modOnly, false); - } - - /** - * Returns true if this class or any of the superclasses are mod only. + * Returns the first group found in the hierarchy starting from the command method or the mod group if any of the superclasses are mod only. * * @param method The subcommand to check * @return The permission group for the subcommand or empty string @@ -87,6 +78,8 @@ public class Command2MC extends Command2 implemen if (sc != null && sc.permGroup().length() > 0) { return sc.permGroup(); } + if (getAnnForValue(command.getClass(), CommandClass.class, CommandClass::modOnly, false)) + return Subcommand.MOD_GROUP; return getAnnForValue(command.getClass(), CommandClass.class, CommandClass::permGroup, ""); } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MCSender.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MCSender.java index 059d953..18df19c 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MCSender.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MCSender.java @@ -17,4 +17,9 @@ public class Command2MCSender implements Command2Sender { public void sendMessage(String[] message) { sender.sendMessage(message); } + + @Override + public String getName() { + return sender.getName(); + } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2Sender.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2Sender.java index ebb2b66..9da1b30 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2Sender.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2Sender.java @@ -4,4 +4,6 @@ public interface Command2Sender { //We don't need the 'extras' of CommandSender void sendMessage(String message); void sendMessage(String[] message); + + String getName(); } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/ICommand2.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/ICommand2.java index b55a85a..d0a202a 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/ICommand2.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/ICommand2.java @@ -73,7 +73,7 @@ public abstract class ICommand2 { String path = getClass().getAnnotation(CommandClass.class).path(), prevpath = path = path.length() == 0 ? getFromClass.apply(getClass()) : path; for (Class cl = getClass().getSuperclass(); cl != null - && !cl.getPackage().getName().equals(TBMCCommandBase.class.getPackage().getName()); cl = cl + && !cl.getPackage().getName().equals(ICommand2MC.class.getPackage().getName()); cl = cl .getSuperclass()) { // String newpath; if (!cl.isAnnotationPresent(CommandClass.class) diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/OptionallyPlayerCommandBase.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/OptionallyPlayerCommandBase.java deleted file mode 100755 index d5095ee..0000000 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/OptionallyPlayerCommandBase.java +++ /dev/null @@ -1,29 +0,0 @@ -package buttondevteam.lib.chat; - -import buttondevteam.lib.TBMCCoreAPI; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -public abstract class OptionallyPlayerCommandBase extends TBMCCommandBase { - public boolean OnCommand(Player player, String alias, String[] args) { - if (getClass().isAnnotationPresent(OptionallyPlayerCommandClass.class) - && getClass().getAnnotation(OptionallyPlayerCommandClass.class).playerOnly()) - TBMCCoreAPI.SendException("Error while executing command " + getClass().getSimpleName() + "!", - new Exception( - "The PlayerCommand annotation is present and set to playerOnly but the Player overload isn't overriden!")); - return true; - } - - @Override - public boolean OnCommand(CommandSender sender, String alias, String[] args) { - if (sender instanceof Player) - return OnCommand((Player) sender, alias, args); - if (!getClass().isAnnotationPresent(OptionallyPlayerCommandClass.class) - || !getClass().getAnnotation(OptionallyPlayerCommandClass.class).playerOnly()) - TBMCCoreAPI.SendException("Error while executing command " + getClass().getSimpleName() + "!", - new Exception( - "Command class doesn't override the CommandSender overload and no PlayerCommandClass annotation is present or playerOnly is false!")); - sender.sendMessage("§cYou need to be a player to use this command."); - return true; - } -} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/OptionallyPlayerCommandClass.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/OptionallyPlayerCommandClass.java deleted file mode 100755 index 1850df2..0000000 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/OptionallyPlayerCommandClass.java +++ /dev/null @@ -1,16 +0,0 @@ -package buttondevteam.lib.chat; - -import java.lang.annotation.*; - -/** - * Only needed to use with {@link OptionallyPlayerCommandBase} command classes - * - * @author NorbiPeti - * - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Inherited -public @interface OptionallyPlayerCommandClass { - boolean playerOnly(); -} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/PlayerCommandBase.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/PlayerCommandBase.java deleted file mode 100755 index 3b30ff5..0000000 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/PlayerCommandBase.java +++ /dev/null @@ -1,16 +0,0 @@ -package buttondevteam.lib.chat; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -public abstract class PlayerCommandBase extends TBMCCommandBase { - public abstract boolean OnCommand(Player player, String alias, String[] args); - - @Override - public final boolean OnCommand(CommandSender sender, String alias, String[] args) { - if (sender instanceof Player) - return OnCommand((Player) sender, alias, args); - sender.sendMessage("§cYou need to be a player to use this command."); - return true; - } -} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCChatAPI.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCChatAPI.java index b6b160b..421515b 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCChatAPI.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCChatAPI.java @@ -1,250 +1,19 @@ package buttondevteam.lib.chat; -import buttondevteam.core.CommandCaller; -import buttondevteam.core.MainPlugin; import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel.RecipientTestResult; -import buttondevteam.lib.*; -import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.ChromaUtils; +import buttondevteam.lib.TBMCChatEvent; +import buttondevteam.lib.TBMCChatPreprocessEvent; +import buttondevteam.lib.TBMCSystemChatEvent; import lombok.val; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; -import org.reflections.Reflections; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.util.ClasspathHelper; -import org.reflections.util.ConfigurationBuilder; -import java.lang.reflect.Modifier; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.Consumer; import java.util.function.Supplier; public class TBMCChatAPI { - - private static final HashMap commands = new HashMap<>(); - - public static HashMap GetCommands() { - return commands; - } - - /** - * Returns messages formatted for Minecraft chat listing the subcommands of the command. - * - * @param command The command which we want the subcommands of - * @param sender The sender for permissions - * @return The subcommands - */ - public static String[] GetSubCommands(TBMCCommandBase command, CommandSender sender) { - return GetSubCommands(command.GetCommandPath(), sender); - } - - /** - * 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 The sender for permissions - * @return The subcommands - */ - public static String[] GetSubCommands(String command, CommandSender sender) { - ArrayList cmds = new ArrayList(); - Consumer addToCmds = cmd -> { - if (cmds.size() == 0) - cmds.add("§6---- Subcommands ----"); - cmds.add(cmd); - }; - for (Entry cmd : TBMCChatAPI.GetCommands().entrySet()) { - if (cmd.getKey().startsWith(command + " ")) { - if (cmd.getValue().isPlayerOnly() && !(sender instanceof Player)) - continue; - if (cmd.getValue().isModOnly() && (MainPlugin.permission != null ? !MainPlugin.permission.has(sender, "tbmc.admin") : !sender.isOp())) - continue; - int ind = cmd.getKey().indexOf(' ', command.length() + 2); - if (ind >= 0) { - String newcmd = cmd.getKey().substring(0, ind); - if (!cmds.contains("/" + newcmd)) - addToCmds.accept("/" + newcmd); - } else - addToCmds.accept("/" + cmd.getKey()); - } - } - return cmds.toArray(new String[0]); //Apparently it's faster to use an empty array in modern Java - } - - /** - *

- * This method adds a plugin's commands to help and sets their executor. - *

- *

- *

- * The command classes have to have a constructor each with no parameters - *

- * The command must be registered in the caller plugin's plugin.yml. Otherwise the plugin will output a messsage to console. - *

- *

- * Using this method after the server is done loading will have no effect. - *

- * - * @param plugin The caller plugin - * @param acmdclass A command's class to get the package name for commands. The provided class's package and subpackages are scanned for commands. - */ - public static synchronized void AddCommands(JavaPlugin plugin, Class acmdclass) { - plugin.getLogger().info("Registering commands from " + acmdclass.getPackage().getName()); - Reflections rf = new Reflections(new ConfigurationBuilder() - .setUrls(ClasspathHelper.forPackage(acmdclass.getPackage().getName(), - plugin.getClass().getClassLoader())) - .addUrls( - ClasspathHelper.forClass(OptionallyPlayerCommandBase.class, - OptionallyPlayerCommandBase.class.getClassLoader()), - ClasspathHelper.forClass(PlayerCommandBase.class, PlayerCommandBase.class.getClassLoader())) // http://stackoverflow.com/questions/12917417/using-reflections-for-finding-the-transitive-subtypes-of-a-class-when-not-all - .addClassLoader(plugin.getClass().getClassLoader()).addScanners(new SubTypesScanner())); - Set> cmds = rf.getSubTypesOf(TBMCCommandBase.class); - for (Class cmd : cmds) { - try { - if (!cmd.getPackage().getName().startsWith(acmdclass.getPackage().getName())) - continue; // It keeps including the commands from here - if (Modifier.isAbstract(cmd.getModifiers())) - continue; - TBMCCommandBase c = cmd.newInstance(); - c.plugin = plugin; - CommandCaller.RegisterCommand(c); //Will check for nulls - commands.put(c.GetCommandPath(), c); - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occured while registering command " + cmd.getName(), e); - } - } - } - - /** - *

- * This method adds a plugin's command to help and sets it's executor. They will be automatically unregistered on plugin disable. - *

- *

- * The command must be registered in the caller plugin's plugin.yml. Otherwise the plugin will output a messsage to console. - *

- *

- * Using this method after the server is done loading will have no effect. - *

- * - * @param plugin The caller plugin - * @param thecmdclass The command's class to create it (because why let you create the command class) - */ - public static void AddCommand(JavaPlugin plugin, Class thecmdclass, Object... params) { - // plugin.getLogger().info("Registering command " + thecmdclass.getSimpleName() + " for " + plugin.getName()); - try { - TBMCCommandBase c; - if (params.length > 0) - c = thecmdclass.getConstructor(Arrays.stream(params).map(Object::getClass).toArray(Class[]::new)) - .newInstance(params); - else - c = thecmdclass.newInstance(); - c.plugin = plugin; - CommandCaller.RegisterCommand(c); //Will check for nulls - commands.put(c.GetCommandPath(), c); - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occured while registering command " + thecmdclass.getSimpleName(), e); - } - } - - /** - *

- * This method adds a plugin's command to help and sets its executor. They will be automatically unregistered on plugin disable. - *

- *

- * The command must be registered in the caller plugin's plugin.yml. Otherwise the plugin will output a message to console. - *

- *

- * Using this method after the server is done loading will have no effect. - *

- * - * @param plugin The caller plugin - * @param cmd The command to add - */ - public static void AddCommand(JavaPlugin plugin, TBMCCommandBase cmd) { - try { - if (plugin == null) throw new IllegalArgumentException("The plugin is null!"); - if (cmd == null) throw new IllegalArgumentException("The command is null!"); - cmd.plugin = plugin; - CommandCaller.RegisterCommand(cmd); //Checks for other nulls - commands.put(cmd.GetCommandPath(), cmd); - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occured while registering command " + (cmd == null ? "n u l l" : cmd.GetCommandPath()), e); - } - } - - /** - *

- * This method adds a plugin's command to help and sets its executor. They will be automatically unregistered on component disable. - *

- *

- * The command must be registered in the caller plugin's plugin.yml. Otherwise the plugin will output a message to console. - *

- *

- * Using this method after the server is done loading will have no effect. - *

- * - * @param component The caller component - * @param cmd The command to add - */ - public static void AddCommand(Component component, TBMCCommandBase cmd) { - try { - if (component == null) throw new IllegalArgumentException("The component is null!"); - if (cmd == null) throw new IllegalArgumentException("The command is null!"); - cmd.plugin = component.getPlugin(); - cmd.component = component; - CommandCaller.RegisterCommand(cmd); //Checks for other nulls - commands.put(cmd.GetCommandPath(), cmd); - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occured while registering command " + (cmd == null ? "n u l l" : cmd.GetCommandPath()), e); - } - } - - /** - * Removes all commands from the plugin - * - * @param plugin The plugin to remove commands from - */ - public static void RemoveCommands(JavaPlugin plugin) { - commands.values().removeIf(c -> { - try { - if (c.plugin == plugin) { - CommandCaller.UnregisterCommand(c); - return true; //Remove - } - return false; - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occured while unregistering commands for " + plugin.getName(), e); - return true; //Remove if it couldn't get the plugin command - } - }); - } - - /** - * Removes all commands from the component - * - * @param component The component to remove commands from - */ - public static void RemoveCommands(Component component) { - commands.values().removeIf(c -> { - try { - if (c.component == component) { - CommandCaller.UnregisterCommand(c); - return true; //Remove - } - return false; - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occured while unregistering commands for " + component.getClass().getSimpleName(), e); - return true; //Remove if it couldn't get the plugin command - } - }); - } - /** * Sends a chat message to Minecraft. Make sure that the channel is registered with {@link #RegisterChatChannel(Channel)}.
* This will also send the error message to the sender, if they can't send the message. diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCCommandBase.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCCommandBase.java deleted file mode 100755 index dbd3b9f..0000000 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCCommandBase.java +++ /dev/null @@ -1,111 +0,0 @@ -package buttondevteam.lib.chat; - -import buttondevteam.lib.architecture.Component; -import javassist.Modifier; -import lombok.Getter; -import org.bukkit.command.CommandSender; -import org.bukkit.plugin.Plugin; - -import java.util.function.Function; - -/** - * Extend this class to create new TBMCCommand and use {@link TBMCChatAPI#AddCommand(org.bukkit.plugin.java.JavaPlugin, TBMCCommandBase)} to add it. Note: The command path (command name - * and subcommand arguments) will be the class name by default, removing any "command" from it. To change it (especially for subcommands), use the path field in the {@link CommandClass} annotation. - * - * @author Norbi - * - */ -@TBMCCommandEnforcer -public abstract class TBMCCommandBase { - - public TBMCCommandBase() { - path = getcmdpath(); - modonly = ismodonly(); - } - - public abstract boolean OnCommand(CommandSender sender, String alias, String[] args); - - public abstract String[] GetHelpText(String alias); - - private final String path; - - /** - * The command's path, or name if top-level command.
- * For example:
- * "u admin updateplugin" or "u" for the top level one
- * The path must be lowercase!
- * Abstract classes with no {@link CommandClass} annotations will be ignored. - * - * @return The command path, which is the command class name by default (removing any "command" from it) - Change via the {@link CommandClass} annotation - */ - public final String GetCommandPath() { - return path; - } - - private String getcmdpath() { - if (!getClass().isAnnotationPresent(CommandClass.class)) - throw new RuntimeException( - "No @CommandClass annotation on command class " + getClass().getSimpleName() + "!"); - Function, String> getFromClass = cl -> cl.getSimpleName().toLowerCase().replace("commandbase", "") // <-- ... - .replace("command", ""); - String path = getClass().getAnnotation(CommandClass.class).path(), - prevpath = path = path.length() == 0 ? getFromClass.apply(getClass()) : path; - for (Class cl = getClass().getSuperclass(); cl != null - && !cl.getPackage().getName().equals(TBMCCommandBase.class.getPackage().getName()); cl = cl - .getSuperclass()) { // - String newpath; - if (!cl.isAnnotationPresent(CommandClass.class) - || (newpath = cl.getAnnotation(CommandClass.class).path()).length() == 0 - || newpath.equals(prevpath)) { - if ((Modifier.isAbstract(cl.getModifiers()) && !cl.isAnnotationPresent(CommandClass.class)) - || cl.getAnnotation(CommandClass.class).excludeFromPath()) // <-- - continue; - newpath = getFromClass.apply(cl); - } - path = (prevpath = newpath) + " " + path; - } - return path; - } - - Plugin plugin; // Used By TBMCChatAPI - - public final Plugin getPlugin() { // Used by CommandCaller (ButtonChat) - return plugin; - } - - public final boolean isPlayerOnly() { - return this instanceof PlayerCommandBase || - (this instanceof OptionallyPlayerCommandBase && - (!getClass().isAnnotationPresent(OptionallyPlayerCommandClass.class) - || getClass().getAnnotation(OptionallyPlayerCommandClass.class).playerOnly())); - } - - private final boolean modonly; - - /** - * Returns true if this class' or any superclass' modOnly property is set to true. - */ - public final boolean isModOnly() { - return modonly; - } - - private boolean ismodonly() { - if (!getClass().isAnnotationPresent(CommandClass.class)) - throw new RuntimeException( - "No @CommandClass annotation on command class " + getClass().getSimpleName() + "!"); - boolean modOnly = getClass().getAnnotation(CommandClass.class).modOnly(); - for (Class cl = getClass().getSuperclass(); cl != null - && !cl.getPackage().getName().equals(TBMCCommandBase.class.getPackage().getName()); cl = cl - .getSuperclass()) { // - if (cl.isAnnotationPresent(CommandClass.class) && !modOnly - && cl.getAnnotation(CommandClass.class).modOnly()) { - modOnly = true; - break; - } - } - return modOnly; - } - - @Getter - Component component; //May be null -} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCCommandEnforcer.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCCommandEnforcer.java deleted file mode 100644 index b644679..0000000 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/TBMCCommandEnforcer.java +++ /dev/null @@ -1,14 +0,0 @@ -package buttondevteam.lib.chat; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.CLASS) -@Target(ElementType.TYPE) -@Inherited -public @interface TBMCCommandEnforcer { - -} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/player/TBMCPlayerBase.java b/ButtonCore/src/main/java/buttondevteam/lib/player/TBMCPlayerBase.java index 0093c4e..f6caff0 100755 --- a/ButtonCore/src/main/java/buttondevteam/lib/player/TBMCPlayerBase.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/player/TBMCPlayerBase.java @@ -36,7 +36,7 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { /** * Use from a method with the name of the key. For example, use flair() for the enclosing method to save to and load from "flair" - * + * * @return A data object with methods to get and set */ @Override @@ -46,7 +46,7 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { /** * Use from a method with the name of the key. For example, use flair() for the enclosing method to save to and load from "flair" - * + * * @return A data object with methods to get and set */ @Override @@ -56,11 +56,9 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { /** * Get player as a plugin player - * - * @param uuid - * The UUID of the player to get - * @param cl - * The type of the player + * + * @param uuid The UUID of the player to get + * @param cl The type of the player * @return The requested player object */ @SuppressWarnings("unchecked") @@ -79,7 +77,7 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { return player; } catch (Exception e) { TBMCCoreAPI.SendException( - "Failed to get player with UUID " + uuid + " and class " + cl.getSimpleName() + "!", e); + "Failed to get player with UUID " + uuid + " and class " + cl.getSimpleName() + "!", e); return null; } } @@ -92,9 +90,8 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { /** * Gets the TBMCPlayer object as a specific plugin player, keeping it's data
* Make sure to use try-with-resources with this to save the data, as it may need to load the file - * - * @param cl - * The TBMCPlayer subclass + * + * @param cl The TBMCPlayer subclass */ public T asPluginPlayer(Class cl) { return getPlayer(uuid, cl); @@ -122,10 +119,9 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { player.PlayerName().set(p.getName()); Bukkit.getLogger().info("Player name saved: " + player.PlayerName().get()); } else if (!p.getName().equals(player.PlayerName().get())) { - Bukkit.getLogger().info("Renaming " + player.PlayerName().get() + " to " + p.getName()); TownyComponent.renameInTowny(player.PlayerName().get(), p.getName()); player.PlayerName().set(p.getName()); - Bukkit.getLogger().info("Renaming done."); + Bukkit.getLogger().info("Renamed to " + p.getName()); } playermap.put(p.getUniqueId() + "-" + TBMCPlayer.class.getSimpleName(), player); @@ -142,16 +138,16 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { final TBMCPlayerBase player = playermap.get(p.getUniqueId() + "-" + TBMCPlayer.class.getSimpleName()); player.save(); Bukkit.getServer().getPluginManager().callEvent(new TBMCPlayerQuitEvent(player, p)); - playermap.entrySet().removeIf(entry -> entry.getKey().startsWith(p.getUniqueId().toString())); + playermap.entrySet().removeIf(entry -> entry.getKey().startsWith(p.getUniqueId().toString())); } public static void savePlayers() { - playermap.values().forEach(p -> { + playermap.values().forEach(p -> { try { p.close(); } catch (Exception e) { TBMCCoreAPI.SendException("Error while saving player " + p.PlayerName().get() + " (" + p.getFolder() - + "/" + p.getFileName() + ")!", e); + + "/" + p.getFileName() + ")!", e); } }); } @@ -159,7 +155,7 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { /** * This method returns a TBMC player from their name. Calling this method may return an offline player which will load it, therefore it's highly recommended to use {@link #close()} to unload the * player data. Using try-with-resources may be the easiest way to achieve this. Example: - * + * *
 	 * {@code
 	 * try(TBMCPlayer player = getFromName(p))
@@ -167,9 +163,8 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase {
 	 * 	...
 	 * }
 	 * 
- * - * @param name - * The player's name + * + * @param name The player's name * @return The {@link TBMCPlayer} object for the player */ public static T getFromName(String name, Class cl) { diff --git a/ButtonCore/src/main/resources/plugin.yml b/ButtonCore/src/main/resources/plugin.yml index 81c2ccf..c277429 100755 --- a/ButtonCore/src/main/resources/plugin.yml +++ b/ButtonCore/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: ChromaCore main: buttondevteam.core.MainPlugin version: 1.0 -author: TBMCPlugins +author: NorbiPeti commands: updateplugin: description: Update a TBMC plugin @@ -21,4 +21,5 @@ depend: softdepend: - Towny - Votifier - - Multiverse-Core \ No newline at end of file + - Multiverse-Core + - Essentials \ No newline at end of file diff --git a/ButtonProcessor/src/main/java/buttondevteam/buttonproc/ConfigProcessor.java b/ButtonProcessor/src/main/java/buttondevteam/buttonproc/ConfigProcessor.java index c06169a..4225e12 100644 --- a/ButtonProcessor/src/main/java/buttondevteam/buttonproc/ConfigProcessor.java +++ b/ButtonProcessor/src/main/java/buttondevteam/buttonproc/ConfigProcessor.java @@ -1,5 +1,8 @@ package buttondevteam.buttonproc; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -10,29 +13,40 @@ import javax.lang.model.type.TypeMirror; import javax.tools.FileObject; import javax.tools.StandardLocation; import java.io.File; -import java.io.FileWriter; import java.io.IOException; public class ConfigProcessor { private final ProcessingEnvironment procEnv; - private final FileWriter sw; + private final YamlConfiguration yc = new YamlConfiguration(); + private final FileObject fo; public ConfigProcessor(ProcessingEnvironment procEnv) { + FileObject fo1; this.procEnv = procEnv; - FileWriter sw = null; try { - FileObject file = procEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "configHelp.md"); - sw = new FileWriter(new File(file.toUri())); - System.out.println(file.toUri()); + fo1 = procEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "configHelp.yml"); } catch (IOException e) { e.printStackTrace(); + fo1 = null; } - this.sw = sw; + this.fo = fo1; } public void process(Element targetcl) { if (targetcl.getModifiers().contains(Modifier.ABSTRACT)) return; - final String path = "components." + targetcl.getSimpleName(); + HasConfig hasConfig = targetcl.getAnnotation(HasConfig.class); + if (hasConfig == null) { + System.out.println("That's not our HasConfig annotation..."); + return; + } + final String path = hasConfig.global() ? "global" : "components." + targetcl.getSimpleName(); + File file = new File(fo.toUri()); + try { + if (file.exists()) + yc.load(file); + } catch (IOException | InvalidConfigurationException e) { + e.printStackTrace(); + } for (Element e : targetcl.getEnclosedElements()) { /*System.out.println("Element: "+e); System.out.println("Type: "+e.getClass()+" - "+e.getKind()); @@ -49,30 +63,18 @@ public class ConfigProcessor { String doc = procEnv.getElementUtils().getDocComment(e); if (doc == null) continue; System.out.println("DOC: " + doc); - try { - sw.append(path).append(".").append(String.valueOf(e.getSimpleName())).append(System.lineSeparator()).append(System.lineSeparator()); - sw.append(doc.trim()).append(System.lineSeparator()).append(System.lineSeparator()); - } catch (IOException e1) { - e1.printStackTrace(); - } + yc.set(path + "." + e.getSimpleName(), doc.trim()); } String javadoc = procEnv.getElementUtils().getDocComment(targetcl); + if (javadoc != null) { + System.out.println("JAVADOC"); + System.out.println(javadoc.trim()); + yc.set(path, javadoc.trim()); + } try { - if (javadoc != null) { - System.out.println("JAVADOC"); - System.out.println(javadoc.trim()); - sw.append(path).append(System.lineSeparator()).append(System.lineSeparator()); - sw.append(javadoc).append(System.lineSeparator()).append(System.lineSeparator()); - } - sw.flush(); + yc.save(file); } catch (IOException e) { e.printStackTrace(); } } - - @Override - protected void finalize() throws Throwable { - sw.close(); - super.finalize(); - } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/HasConfig.java b/ButtonProcessor/src/main/java/buttondevteam/buttonproc/HasConfig.java similarity index 82% rename from ButtonCore/src/main/java/buttondevteam/lib/architecture/HasConfig.java rename to ButtonProcessor/src/main/java/buttondevteam/buttonproc/HasConfig.java index 8e1e63a..7ffdbc1 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/HasConfig.java +++ b/ButtonProcessor/src/main/java/buttondevteam/buttonproc/HasConfig.java @@ -1,4 +1,4 @@ -package buttondevteam.lib.architecture; +package buttondevteam.buttonproc; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; @@ -10,4 +10,5 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) @Inherited public @interface HasConfig { + boolean global(); } diff --git a/CorePOM/CorePOM.iml b/CorePOM/CorePOM.iml index 0e07dc2..b42e4ee 100644 --- a/CorePOM/CorePOM.iml +++ b/CorePOM/CorePOM.iml @@ -5,7 +5,7 @@ - + diff --git a/CorePOM/pom.xml b/CorePOM/pom.xml index 024d7eb..9fa787f 100755 --- a/CorePOM/pom.xml +++ b/CorePOM/pom.xml @@ -95,10 +95,12 @@ org.apache.maven.plugins maven-compiler-plugin - 13 + 11 +