Fixed list config, converted old cmds, removed old cmd system

#87
Fixed permission issue without perm plugin (#74)
Checking Bukkit perms if Vault says no, so PEX works with default perms (#80)
Most of the time it just means checking the perm plugin twice since the PermissibleBase is injected usually
This commit is contained in:
Norbi Peti 2020-01-31 21:55:21 +01:00
parent 1a4829e894
commit a1919c04d0
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
24 changed files with 127 additions and 654 deletions

View file

@ -24,6 +24,7 @@
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" /> <orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" /> <orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" /> <orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" /> <orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" /> <orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" /> <orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" />

View file

@ -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;
}
}
}

View file

@ -84,6 +84,15 @@ public class MainPlugin extends ButtonPlugin {
return getIConfig().getData("test", false); 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<Boolean> useVaultForCommands() {
return getIConfig().getData("useVaultForCommands", true);
}*/
@Override @Override
public void pluginEnable() { public void pluginEnable() {
// Logs "Plugin Enabled", registers commands // Logs "Plugin Enabled", registers commands

View file

@ -64,12 +64,16 @@ public class MemberComponent extends Component<MainPlugin> implements Listener {
if (permission != null && !permission.playerInGroup(event.getPlayer(), memberGroup().get()) if (permission != null && !permission.playerInGroup(event.getPlayer(), memberGroup().get())
&& (new Date(event.getPlayer().getFirstPlayed()).toInstant().plus(registeredForDays().get(), ChronoUnit.DAYS).isBefore(Instant.now()) && (new Date(event.getPlayer().getFirstPlayed()).toInstant().plus(registeredForDays().get(), ChronoUnit.DAYS).isBefore(Instant.now())
|| event.getPlayer().getStatistic(playtime.getKey()) > playtime.getValue() * playedHours().get())) { || event.getPlayer().getStatistic(playtime.getKey()) > playtime.getValue() * playedHours().get())) {
try {
if (permission.playerAddGroup(null, event.getPlayer(), memberGroup().get())) { if (permission.playerAddGroup(null, event.getPlayer(), memberGroup().get())) {
event.getPlayer().sendMessage("§bYou are a member now. YEEHAW"); event.getPlayer().sendMessage("§bYou are a member now. YEEHAW");
MainPlugin.Instance.getLogger().info("Added " + event.getPlayer().getName() + " as a member."); MainPlugin.Instance.getLogger().info("Added " + event.getPlayer().getName() + " as a member.");
} else { } 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."); 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.");
}
} }
} }

View file

@ -1,9 +1,8 @@
package buttondevteam.core.component.randomtp; package buttondevteam.core.component.randomtp;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.TBMCCommandBase;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -11,8 +10,11 @@ import org.bukkit.entity.Player;
import java.util.logging.Logger; import java.util.logging.Logger;
// @formatter:off // @formatter:off
@SuppressWarnings("FieldCanBeLocal")@CommandClass @SuppressWarnings("FieldCanBeLocal")@CommandClass(helpText = {
public class RandomTP extends TBMCCommandBase "§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 private final int radius = 70; //set how far apart the five teleport positions are
@ -53,10 +55,8 @@ public class RandomTP extends TBMCCommandBase
/*================================================================================================*/ /*================================================================================================*/
public void onEnable(Component component) public void onEnable(RandomTPComponent component)
{ {
TBMCChatAPI.AddCommand(component, this);
world = Bukkit.getWorld("World"); world = Bukkit.getWorld("World");
border = world.getWorldBorder(); border = world.getWorldBorder();
Logger logger = component.getPlugin().getLogger(); Logger logger = component.getPlugin().getLogger();
@ -68,22 +68,10 @@ public class RandomTP extends TBMCCommandBase
/*================================================================================================*/ /*================================================================================================*/
public String[] GetHelpText(String alias) @Command2.Subcommand
public boolean def(CommandSender sender, Player player)
{ {
return new String[] if (sender.isOp()) return rtp(player);
{
"§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 boolean OnCommand(CommandSender sender, String command, String[] args)
{
if (args.length == 0) return false;
if (sender.isOp()) return rtp(Bukkit.getPlayer(args[0]));
else sender.sendMessage("§7 hmm, " + sender.getName() + "... " + sender.getName() + "... nope, no operator permissions."); else sender.sendMessage("§7 hmm, " + sender.getName() + "... " + sender.getName() + "... nope, no operator permissions.");

View file

@ -13,7 +13,9 @@ import buttondevteam.lib.architecture.ComponentMetadata;
public class RandomTPComponent extends Component<MainPlugin> { public class RandomTPComponent extends Component<MainPlugin> {
@Override @Override
protected void enable() { protected void enable() {
new RandomTP().onEnable(this); //It registers it's command var rtp = new RandomTP();
registerCommand(rtp);
rtp.onEnable(this);
} }
@Override @Override

View file

@ -1,22 +1,28 @@
package buttondevteam.core.component.restart; package buttondevteam.core.component.restart;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.chat.TBMCCommandBase;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; 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 @RequiredArgsConstructor
public class PrimeRestartCommand extends TBMCCommandBase { public class PrimeRestartCommand extends ICommand2MC {
private final RestartComponent component; private final RestartComponent component;
@Override
public boolean OnCommand(CommandSender sender, String alias, String[] args) { public void def(CommandSender sender, @Command2.TextArg @Command2.OptionalArg String somethingrandom) {
loud = args.length > 0; loud = somethingrandom != null;
if (Bukkit.getOnlinePlayers().size() > 0) { if (Bukkit.getOnlinePlayers().size() > 0) {
sender.sendMessage("§bPlayers online, restart delayed."); sender.sendMessage("§bPlayers online, restart delayed.");
if (loud) if (loud)
@ -28,21 +34,10 @@ public class PrimeRestartCommand extends TBMCCommandBase {
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online. Restarting server.", component.getRestartBroadcast()); TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online. Restarting server.", component.getRestartBroadcast());
Bukkit.spigot().restart(); Bukkit.spigot().restart();
} }
return true;
} }
@Getter @Getter
private static boolean plsrestart = false; private static boolean plsrestart = false;
@Getter @Getter
private static boolean loud = false; 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" //
};
}
} }

View file

@ -20,7 +20,7 @@ public class RestartComponent extends Component<MainPlugin> implements Listener
@Override @Override
public void enable() { public void enable() {
registerCommand(new ScheduledRestartCommand(this)); registerCommand(new ScheduledRestartCommand(this));
TBMCChatAPI.AddCommand(this, new PrimeRestartCommand(this)); registerCommand(new PrimeRestartCommand(this));
registerListener(this); registerListener(this);
restartBroadcast = TBMCSystemChatEvent.BroadcastTarget.add("restartCountdown"); restartBroadcast = TBMCSystemChatEvent.BroadcastTarget.add("restartCountdown");
} }

View file

@ -3,16 +3,15 @@ package buttondevteam.core.component.updater;
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata; import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.chat.TBMCChatAPI;
/** /**
* 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) @ComponentMetadata(enabledByDefault = false)
public class PluginUpdaterComponent extends Component<MainPlugin> { //TODO: Config public class PluginUpdaterComponent extends Component<MainPlugin> { //TODO: Config
@Override @Override
public void enable() { public void enable() {
TBMCChatAPI.AddCommand(this, new UpdatePluginCommand()); registerCommand(new UpdatePluginCommand());
} }
@Override @Override

View file

@ -2,40 +2,41 @@ package buttondevteam.core.component.updater;
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCCommandBase; import buttondevteam.lib.chat.ICommand2MC;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.lang.reflect.Method;
@CommandClass(modOnly = true) @CommandClass(modOnly = true)
public class UpdatePluginCommand extends TBMCCommandBase { public class UpdatePluginCommand extends ICommand2MC {
@Override public void def(CommandSender sender, @Command2.OptionalArg String plugin, @Command2.OptionalArg String branch) {
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> { Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> {
if (args.length == 0) { if (plugin == null) {
sender.sendMessage("Downloading plugin names..."); sender.sendMessage("Downloading plugin names...");
boolean first = true; boolean first = true;
for (String plugin : PluginUpdater.GetPluginNames()) { for (String plugin2 : PluginUpdater.GetPluginNames()) {
if (first) { if (first) {
sender.sendMessage("§6---- Plugin names ----"); sender.sendMessage("§6---- Plugin names ----");
first = false; first = false;
} }
sender.sendMessage("- " + plugin); sender.sendMessage("- " + plugin2);
} }
} else { } else {
TBMCCoreAPI.UpdatePlugin(args[0], sender, args.length == 1 ? "master" : args[1]); TBMCCoreAPI.UpdatePlugin(plugin, sender, branch == null ? "master" : branch);
} }
}); });
return true;
} }
@Override @Override
public String[] GetHelpText(String alias) { public String[] getHelpText(Method method, Command2.Subcommand ann) {
return new String[]{ // return new String[]{ //
"§6---- Update plugin ----", // "§6---- Update plugin ----", //
"This command downloads the latest version of a TBMC plugin from GitHub", // "This command downloads the latest version of a custom plugin from GitHub", //
"To update a plugin: /" + alias + " <plugin>", // "To update a plugin: add its name", //
"To list the plugin names: /" + alias // "To list the plugin names: don't type a name" //
}; };
} }
} }

View file

@ -4,7 +4,6 @@ import buttondevteam.buttonproc.HasConfig;
import buttondevteam.core.ComponentManager; import buttondevteam.core.ComponentManager;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2MC; import buttondevteam.lib.chat.Command2MC;
import buttondevteam.lib.chat.TBMCChatAPI;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
@ -85,7 +84,7 @@ public abstract class ButtonPlugin extends JavaPlugin {
if (ConfigData.saveNow(getConfig())) if (ConfigData.saveNow(getConfig()))
getLogger().info("Saved configuration changes."); getLogger().info("Saved configuration changes.");
iConfig = null; //Clearing the hashmap is not enough, we need to update the section as well 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) { } catch (Exception e) {
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e); TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e);
} }

View file

@ -5,8 +5,6 @@ import buttondevteam.core.ComponentManager;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException; import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException;
import buttondevteam.lib.chat.ICommand2MC; import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.chat.TBMCCommandBase;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.val; import lombok.val;
@ -143,7 +141,7 @@ public abstract class Component<TP extends JavaPlugin> {
//System.out.println("Done enabling "+component.getClassName()); //System.out.println("Done enabling "+component.getClassName());
} else { } else {
component.disable(); component.disable();
TBMCChatAPI.RemoveCommands(component); //TBMCChatAPI.RemoveCommands(component); - TODO
} }
} }
@ -213,15 +211,6 @@ public abstract class Component<TP extends JavaPlugin> {
ButtonPlugin.getCommand2MC().registerCommand(commandBase); 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 * Registers a Listener to this component
* *

View file

@ -1,9 +1,8 @@
package buttondevteam.lib.architecture; package buttondevteam.lib.architecture;
import lombok.AccessLevel;
import lombok.Setter;
import lombok.val; import lombok.val;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -12,14 +11,25 @@ import java.util.function.Predicate;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
public class ListConfigData<T> extends ConfigData<ListConfigData.List<T>> { public class ListConfigData<T> extends ConfigData<ListConfigData.List<T>> {
@SuppressWarnings("unchecked")
ListConfigData(ConfigurationSection config, String path, List<T> def, Runnable saveAction) { ListConfigData(ConfigurationSection config, String path, List<T> def, Runnable saveAction) {
super(config, path, def, def, saveAction); super(config, path, def, new ArrayList<>(def), list -> {
var l = new List<>((ArrayList<T>) 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 def.listConfig = this; //Can't make the List class non-static or pass this in the super() constructor
} }
public static class List<T> extends ArrayList<T> { public static class List<T> extends ArrayList<T> {
@Setter(AccessLevel.PACKAGE) private ListConfigData<T> listConfig;
ListConfigData<T> listConfig;
public List(@NotNull Collection<? extends T> c) {
super(c);
}
public List() {
}
private void update() { private void update() {
listConfig.set(this); //Update the config model and start save task if needed listConfig.set(this); //Update the config model and start save task if needed

View file

@ -135,7 +135,7 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
try { try {
handleCommandAsync(sender, commandline, sd, subcommand, sync); handleCommandAsync(sender, commandline, sd, subcommand, sync);
} catch (Exception e) { } 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 return true; //We found a method

View file

@ -6,6 +6,7 @@ import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.server.TabCompleteEvent; import org.bukkit.event.server.TabCompleteEvent;
@ -16,7 +17,6 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implements Listener { public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implements Listener {
@ -26,7 +26,7 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
var perm = "chroma.command." + command.getCommandPath().replace(' ', '.'); var perm = "chroma.command." + command.getCommandPath().replace(' ', '.');
if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset
Bukkit.getPluginManager().addPermission(new Permission(perm, 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()) { for (val method : command.getClass().getMethods()) {
if (!method.isAnnotationPresent(Subcommand.class)) continue; if (!method.isAnnotationPresent(Subcommand.class)) continue;
String pg = permGroup(command, method); String pg = permGroup(command, method);
@ -34,7 +34,6 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
perm = "chroma." + pg; perm = "chroma." + pg;
if (Bukkit.getPluginManager().getPermission(perm) == null) //It may occur multiple times if (Bukkit.getPluginManager().getPermission(perm) == null) //It may occur multiple times
Bukkit.getPluginManager().addPermission(new Permission(perm, 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 PermissionDefault.OP)); //Do not allow any commands that belong to a group
} }
} }
@ -50,16 +49,18 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
boolean p = true; boolean p = true;
String[] perms = { String[] perms = {
"chroma.command." + command.getCommandPath().replace(' ', '.'), "chroma.command." + command.getCommandPath().replace(' ', '.'),
(pg = permGroup(command, method)).length() > 0 ? "chroma." + pg : null, (pg = permGroup(command, method)).length() > 0 ? "chroma." + pg : null
modOnly(command) ? "tbmc.admin" : null
}; };
for (String perm : perms) { for (String perm : perms) {
if (perm != null) { if (perm != null) {
if (p) { //Use OfflinePlayer to avoid fetching player data if (p) { //Use OfflinePlayer to avoid fetching player data
if (sender instanceof OfflinePlayer) 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 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 } else break; //If any of the permissions aren't granted then don't allow
} }
} }
@ -67,17 +68,7 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
} }
/** /**
* Returns true if this class or <u>any</u> of the superclasses are mod only. * Returns the first group found in the hierarchy starting from the command method <b>or</b> the mod group if <i>any</i></i> 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 <u>any</u> of the superclasses are mod only.
* *
* @param method The subcommand to check * @param method The subcommand to check
* @return The permission group for the subcommand or empty string * @return The permission group for the subcommand or empty string
@ -87,6 +78,8 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
if (sc != null && sc.permGroup().length() > 0) { if (sc != null && sc.permGroup().length() > 0) {
return sc.permGroup(); return sc.permGroup();
} }
if (getAnnForValue(command.getClass(), CommandClass.class, CommandClass::modOnly, false))
return Subcommand.MOD_GROUP;
return getAnnForValue(command.getClass(), CommandClass.class, CommandClass::permGroup, ""); return getAnnForValue(command.getClass(), CommandClass.class, CommandClass::permGroup, "");
} }

View file

@ -17,4 +17,9 @@ public class Command2MCSender implements Command2Sender {
public void sendMessage(String[] message) { public void sendMessage(String[] message) {
sender.sendMessage(message); sender.sendMessage(message);
} }
@Override
public String getName() {
return sender.getName();
}
} }

View file

@ -4,4 +4,6 @@ public interface Command2Sender { //We don't need the 'extras' of CommandSender
void sendMessage(String message); void sendMessage(String message);
void sendMessage(String[] message); void sendMessage(String[] message);
String getName();
} }

View file

@ -73,7 +73,7 @@ public abstract class ICommand2<TP extends Command2Sender> {
String path = getClass().getAnnotation(CommandClass.class).path(), String path = getClass().getAnnotation(CommandClass.class).path(),
prevpath = path = path.length() == 0 ? getFromClass.apply(getClass()) : path; prevpath = path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
for (Class<?> cl = getClass().getSuperclass(); cl != null 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()) { // .getSuperclass()) { //
String newpath; String newpath;
if (!cl.isAnnotationPresent(CommandClass.class) if (!cl.isAnnotationPresent(CommandClass.class)

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -1,250 +1,19 @@
package buttondevteam.lib.chat; package buttondevteam.lib.chat;
import buttondevteam.core.CommandCaller;
import buttondevteam.core.MainPlugin;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.channel.Channel.RecipientTestResult; import buttondevteam.core.component.channel.Channel.RecipientTestResult;
import buttondevteam.lib.*; import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCChatPreprocessEvent;
import buttondevteam.lib.TBMCSystemChatEvent;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; 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.Arrays;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class TBMCChatAPI { public class TBMCChatAPI {
private static final HashMap<String, TBMCCommandBase> commands = new HashMap<>();
public static HashMap<String, TBMCCommandBase> 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.<br>
* 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<String> cmds = new ArrayList<String>();
Consumer<String> addToCmds = cmd -> {
if (cmds.size() == 0)
cmds.add("§6---- Subcommands ----");
cmds.add(cmd);
};
for (Entry<String, TBMCCommandBase> 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
}
/**
* <p>
* This method adds a plugin's commands to help and sets their executor.
* </p>
* <p>
* </p>
* <b>The command classes have to have a constructor each with no parameters</b>
* <p>
* The <u>command must be registered</u> in the caller plugin's plugin.yml. Otherwise the plugin will output a messsage to console.
* </p>
* <p>
* <i>Using this method after the server is done loading will have no effect.</i>
* </p>
*
* @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<? extends TBMCCommandBase> 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<Class<? extends TBMCCommandBase>> cmds = rf.getSubTypesOf(TBMCCommandBase.class);
for (Class<? extends TBMCCommandBase> 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);
}
}
}
/**
* <p>
* This method adds a plugin's command to help and sets it's executor. They will be automatically unregistered on plugin disable.
* </p>
* <p>
* The <u>command must be registered</u> in the caller plugin's plugin.yml. Otherwise the plugin will output a messsage to console.
* </p>
* <p>
* <i>Using this method after the server is done loading will have no effect.</i>
* </p>
*
* @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<? extends TBMCCommandBase> 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);
}
}
/**
* <p>
* This method adds a plugin's command to help and sets its executor. They will be automatically unregistered on plugin disable.
* </p>
* <p>
* The <u>command must be registered</u> in the caller plugin's plugin.yml. Otherwise the plugin will output a message to console.
* </p>
* <p>
* <i>Using this method after the server is done loading will have no effect.</i>
* </p>
*
* @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);
}
}
/**
* <p>
* This method adds a plugin's command to help and sets its executor. They will be automatically unregistered on component disable.
* </p>
* <p>
* The <u>command must be registered</u> in the caller plugin's plugin.yml. Otherwise the plugin will output a message to console.
* </p>
* <p>
* <i>Using this method after the server is done loading will have no effect.</i>
* </p>
*
* @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)}.<br> * Sends a chat message to Minecraft. Make sure that the channel is registered with {@link #RegisterChatChannel(Channel)}.<br>
* This will also send the error message to the sender, if they can't send the message. * This will also send the error message to the sender, if they can't send the message.

View file

@ -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. <u><b>Note:</b></u> 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.<br>
* For example:<br>
* "u admin updateplugin" or "u" for the top level one<br>
* <u>The path must be lowercase!</u><br>
* <b>Abstract classes with no {@link CommandClass} annotations will be ignored.</b>
*
* @return The command path, <i>which is the command class name by default</i> (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<Class<?>, 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
}

View file

@ -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 {
}