New command system, fixes, improvements #61
8 changed files with 194 additions and 49 deletions
|
@ -2,59 +2,66 @@ package buttondevteam.core;
|
|||
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.chat.CommandClass;
|
||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
||||
import lombok.val;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@CommandClass(modOnly = true)
|
||||
public class ComponentCommand extends TBMCCommandBase {
|
||||
@Override
|
||||
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
|
||||
if (args.length < 1)
|
||||
return false;
|
||||
boolean enable = true;
|
||||
try {
|
||||
switch (args[0]) {
|
||||
case "enable":
|
||||
enable = true;
|
||||
break;
|
||||
case "disable":
|
||||
enable = false;
|
||||
break;
|
||||
case "list":
|
||||
sender.sendMessage("§6List of components:");
|
||||
Component.getComponents().values().stream().map(c -> c.getPlugin().getName() + " - " + c.getClass().getSimpleName() + " - " + (c.isEnabled() ? "en" : "dis") + "abled").forEach(sender::sendMessage);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
public class ComponentCommand extends Command2 {
|
||||
public ComponentCommand() {
|
||||
addParamConverter(Plugin.class, arg -> Bukkit.getPluginManager().getPlugin(arg));
|
||||
|
||||
}
|
||||
if (args.length < 2)
|
||||
return false;
|
||||
val oc = getComponentOrError(args[1], sender);
|
||||
|
||||
@Subcommand
|
||||
public boolean enable(CommandSender sender, Plugin plugin, String component) {
|
||||
if (plugin == null) return respond(sender, "§cPlugin not found!");
|
||||
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
|
||||
public boolean disable(CommandSender sender, Plugin plugin, String component) {
|
||||
if (plugin == null) return respond(sender, "§cPlugin not found!");
|
||||
return enable_disable(sender, plugin, component, false);
|
||||
}
|
||||
|
||||
@Subcommand
|
||||
public boolean list(CommandSender sender, 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
|
||||
.map(c -> c.getPlugin().getName() + " - " + c.getClass().getSimpleName() + " - " + (c.isEnabled() ? "en" : "dis") + "abled").forEach(sender::sendMessage);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean enable_disable(CommandSender sender, Plugin plugin, String component, boolean enable) {
|
||||
try {
|
||||
val oc = getComponentOrError(plugin, component, sender);
|
||||
if (!oc.isPresent())
|
||||
return true;
|
||||
if (enable) //Reload config so the new config values are read
|
||||
getPlugin().reloadConfig(); //All changes are saved to disk on disable
|
||||
Component.setComponentEnabled(oc.get(), enable);
|
||||
sender.sendMessage(oc.get().getClass().getSimpleName() + " " + (enable ? "en" : "dis") + "abled.");
|
||||
} catch (Exception e) {
|
||||
TBMCCoreAPI.SendException("Couldn't " + (enable ? "en" : "dis") + "able component " + args[0] + "!", e);
|
||||
TBMCCoreAPI.SendException("Couldn't " + (enable ? "en" : "dis") + "able component " + component + "!", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Optional<Component> getComponentOrError(String arg, CommandSender sender) {
|
||||
val oc = Component.getComponents().values().stream().filter(c -> c.getClass().getSimpleName().equalsIgnoreCase(arg)).findAny();
|
||||
if (!oc.isPresent()) //TODO: There may be multiple components with the same name
|
||||
private Optional<Component> getComponentOrError(Plugin plugin, String arg, CommandSender sender) {
|
||||
val oc = Component.getComponents().values().stream()
|
||||
.filter(c -> plugin.getName().equals(c.getPlugin().getName()))
|
||||
.filter(c -> c.getClass().getSimpleName().equalsIgnoreCase(arg)).findAny();
|
||||
if (!oc.isPresent())
|
||||
sender.sendMessage("§cComponent not found!"); //^ Much simpler to solve in the new command system
|
||||
return oc; //TODO: Offer overload options with clickable link (with all the plugins it found)
|
||||
} //TODO: Tabcompletion for the new command system
|
||||
|
||||
@Override
|
||||
public String[] GetHelpText(String alias) {
|
||||
public String[] GetHelpText(String alias) { //TODO
|
||||
return new String[]{
|
||||
"§6---- Component command ----",
|
||||
"Enable or disable or list components"
|
||||
|
|
|
@ -14,6 +14,7 @@ import buttondevteam.lib.architecture.ButtonPlugin;
|
|||
import buttondevteam.lib.architecture.Component;
|
||||
import buttondevteam.lib.architecture.ConfigData;
|
||||
import buttondevteam.lib.chat.Color;
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import buttondevteam.lib.player.ChromaGamerBase;
|
||||
import buttondevteam.lib.player.TBMCPlayer;
|
||||
|
@ -68,7 +69,7 @@ public class MainPlugin extends ButtonPlugin {
|
|||
Component.registerComponent(this, new MemberComponent());
|
||||
Component.registerComponent(this, new TownyComponent());
|
||||
ComponentManager.enableComponents();
|
||||
TBMCChatAPI.AddCommand(this, ComponentCommand.class);
|
||||
Command2.registerCommand(new ComponentCommand());
|
||||
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this);
|
||||
ChromaGamerBase.addConverter(commandSender -> Optional.ofNullable(commandSender instanceof ConsoleCommandSender || commandSender instanceof BlockCommandSender
|
||||
? TBMCPlayer.getPlayer(new UUID(0, 0), TBMCPlayer.class) : null)); //Console & cmdblocks
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
package buttondevteam.core;
|
||||
|
||||
import buttondevteam.lib.TBMCCommandPreprocessEvent;
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.player.TBMCPlayerBase;
|
||||
import lombok.val;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.server.ServerCommandEvent;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
@ -32,4 +40,33 @@ public class PlayerListener implements Listener {
|
|||
Bukkit.getOnlinePlayers().stream().filter(event::shouldSendTo)
|
||||
.forEach(p -> p.sendMessage(event.getChannel().DisplayName().get().substring(0, 2) + event.getMessage()));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerChatPreprocess(PlayerCommandPreprocessEvent event) {
|
||||
handlePreprocess(event.getPlayer(), event.getMessage(), event);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onSystemChatPreprocess(ServerCommandEvent event) {
|
||||
handlePreprocess(event.getSender(), "/" + event.getCommand(), event);
|
||||
if (event.isCancelled()) event.setCommand("dontrunthiscmd"); //Bugfix
|
||||
}
|
||||
|
||||
private void handlePreprocess(CommandSender sender, String message, Cancellable event) {
|
||||
if (event.isCancelled()) return;
|
||||
val ev = new TBMCCommandPreprocessEvent(sender, message);
|
||||
Bukkit.getPluginManager().callEvent(ev);
|
||||
if (ev.isCancelled())
|
||||
event.setCancelled(true); //Cancel the original event
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onTBMCPreprocess(TBMCCommandPreprocessEvent event) {
|
||||
if (event.isCancelled()) return;
|
||||
try {
|
||||
event.setCancelled(Command2.handleCommand(event.getSender(), event.getMessage()));
|
||||
} catch (Exception e) {
|
||||
TBMCCoreAPI.SendException("Command processing failed for sender '" + event.getSender() + "' and message '" + event.getMessage() + "'", e);
|
||||
}
|
||||
}
|
||||
}
|
39
ButtonCore/src/main/java/buttondevteam/lib/TBMCCommandPreprocessEvent.java
Executable file
39
ButtonCore/src/main/java/buttondevteam/lib/TBMCCommandPreprocessEvent.java
Executable file
|
@ -0,0 +1,39 @@
|
|||
package buttondevteam.lib;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Can be used to change or handle commands before they're sent.
|
||||
* <b>Called on using player, console and Discord commands.</b>
|
||||
*
|
||||
* @author NorbiPeti
|
||||
*/
|
||||
@Getter
|
||||
public class TBMCCommandPreprocessEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
private final CommandSender sender;
|
||||
@Setter
|
||||
private String message;
|
||||
@Setter
|
||||
private boolean cancelled;
|
||||
|
||||
public TBMCCommandPreprocessEvent(CommandSender sender, String message) {
|
||||
this.sender = sender;
|
||||
this.message = message; //TODO: Actually call from Discord as well
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package buttondevteam.lib.architecture;
|
|||
import buttondevteam.core.ComponentManager;
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException;
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
||||
import lombok.Getter;
|
||||
|
@ -183,6 +184,16 @@ public abstract class Component {
|
|||
*/
|
||||
protected abstract void disable();
|
||||
|
||||
/**
|
||||
* Registers a TBMCCommand to the component. Make sure to use {@link buttondevteam.lib.chat.CommandClass} and {@link buttondevteam.lib.chat.Command2.Subcommand}.
|
||||
* You don't need to register the command in plugin.yml.
|
||||
*
|
||||
* @param commandBase Custom coded command class
|
||||
*/
|
||||
protected final void registerCommand(Command2 commandBase) {
|
||||
Command2.registerCommand(commandBase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a TBMCCommand to the component. Make sure to add it to plugin.yml and use {@link buttondevteam.lib.chat.CommandClass}.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package buttondevteam.lib.chat;
|
||||
|
||||
import buttondevteam.lib.player.ChromaGamerBase;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
@ -22,14 +23,26 @@ public abstract class Command2 {
|
|||
* Default handler for commands, can be used to copy the args too.
|
||||
*
|
||||
* @param sender The sender which ran the command
|
||||
* @param command The (sub)command ran by the user
|
||||
* @param args All of the arguments passed as is
|
||||
* @return The success of the command
|
||||
*/
|
||||
public boolean def(CommandSender sender, String command, @TextArg String args) {
|
||||
@Subcommand
|
||||
public boolean def(CommandSender sender, @TextArg String args) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Return with this.
|
||||
*
|
||||
* @param sender The sender of the command
|
||||
* @param message The message to send to the sender
|
||||
* @return Always true so that the usage isn't shown
|
||||
*/
|
||||
protected boolean respond(CommandSender sender, String message) {
|
||||
sender.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: @CommandClass(helpText=...)
|
||||
* Parameters annotated with this receive all of the remaining arguments
|
||||
|
@ -57,9 +70,6 @@ public abstract class Command2 {
|
|||
private static HashMap<Class<?>, Function<String, ?>> paramConverters = new HashMap<>();
|
||||
|
||||
public Command2() {
|
||||
for (val method : getClass().getMethods())
|
||||
if (method.isAnnotationPresent(Subcommand.class))
|
||||
subcommands.put(method.getName().replace('_', ' '), new SubcommandData(method, this));
|
||||
path = getcmdpath();
|
||||
}
|
||||
|
||||
|
@ -76,16 +86,41 @@ public abstract class Command2 {
|
|||
}
|
||||
|
||||
public static boolean handleCommand(CommandSender sender, String commandline) throws Exception {
|
||||
for (int i = commandline.lastIndexOf(' '); i != -1; i = commandline.lastIndexOf(' ', i - 1)) {
|
||||
String subcommand = commandline.substring(0, i);
|
||||
for (int i = commandline.length(); i != -1; i = commandline.lastIndexOf(' ', i - 1)) {
|
||||
String subcommand = commandline.substring(0, i).toLowerCase();
|
||||
//System.out.println("Subcommand: "+subcommand);
|
||||
//System.out.println("Subcmds: "+subcommands.toString());
|
||||
SubcommandData sd = subcommands.get(subcommand); //O(1)
|
||||
if (sd == null) continue; //TODO: This will run each time someone runs any command
|
||||
//System.out.println("sd.method: "+sd.method); //TODO: Rename in Maven
|
||||
val params = new ArrayList<Object>(sd.method.getParameterCount());
|
||||
int j = 0, pj;
|
||||
for (val cl : sd.method.getParameterTypes()) {
|
||||
pj = j + 1;
|
||||
j = commandline.indexOf(' ', j + 1);
|
||||
String param = subcommand.substring(pj, j);
|
||||
int j = subcommand.length(), pj;
|
||||
Class<?>[] parameterTypes = sd.method.getParameterTypes();
|
||||
if (parameterTypes.length == 0)
|
||||
throw new Exception("No sender parameter for method '" + sd.method + "'");
|
||||
val sendertype = parameterTypes[0];
|
||||
final ChromaGamerBase cg;
|
||||
if (sendertype.isAssignableFrom(sender.getClass()))
|
||||
params.add(sender); //The command either expects a CommandSender or it is a Player, or some other expected type
|
||||
else if (ChromaGamerBase.class.isAssignableFrom(sendertype)
|
||||
&& (cg = ChromaGamerBase.getFromSender(sender)) != null
|
||||
&& cg.getClass() == sendertype) //The command expects a user of our system
|
||||
params.add(cg);
|
||||
else {
|
||||
sender.sendMessage("§cYou need to be a " + sendertype.getSimpleName() + " to use this command.");
|
||||
return true;
|
||||
}
|
||||
for (int i1 = 1; i1 < parameterTypes.length; i1++) {
|
||||
Class<?> cl = parameterTypes[i1];
|
||||
pj = j + 1; //Start index
|
||||
if (pj == commandline.length() + 1) { //No param given
|
||||
params.add(null);
|
||||
continue; //Fill the remaining params with nulls
|
||||
}
|
||||
j = commandline.indexOf(' ', j + 1); //End index
|
||||
if (j == -1) //Last parameter
|
||||
j = commandline.length();
|
||||
String param = commandline.substring(pj, j);
|
||||
if (cl == String.class) {
|
||||
params.add(param);
|
||||
continue;
|
||||
|
@ -95,11 +130,25 @@ public abstract class Command2 {
|
|||
throw new Exception("No suitable converter found for parameter type '" + cl.getCanonicalName() + "' for command '" + sd.method.toString() + "'");
|
||||
params.add(conv.apply(param));
|
||||
}
|
||||
sd.method.invoke(sd.command, params);
|
||||
//System.out.println("Our params: "+params);
|
||||
val ret = sd.method.invoke(sd.command, params.toArray()); //I FORGOT TO TURN IT INTO AN ARRAY (for a long time)
|
||||
if (ret instanceof Boolean) {
|
||||
if (!(boolean) ret)
|
||||
sender.sendMessage("Wrong usage."); //TODO: Show help text
|
||||
} else if (ret != null)
|
||||
throw new Exception("Wrong return type! Must return a boolean or void. Return value: "+ret);
|
||||
return true; //We found a method
|
||||
}
|
||||
return false; //Didn't handle
|
||||
} //TODO: Use preprocess event and add to the help
|
||||
} //TODO: Add to the help
|
||||
|
||||
public static void registerCommand(Command2 command) {
|
||||
for (val method : command.getClass().getMethods())
|
||||
if (method.isAnnotationPresent(Subcommand.class))
|
||||
subcommands.put("/" + command.path + //Add command path (class name by default)
|
||||
(method.getName().equals("def") ? "" : " " + method.getName().replace('_', ' ').toLowerCase()), //Add method name, unless it's 'def'
|
||||
new SubcommandData(method, command)); //Result of the above (def) is that it will show the help text
|
||||
}
|
||||
|
||||
private final String path;
|
||||
|
||||
|
@ -126,4 +175,4 @@ public abstract class Command2 {
|
|||
path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
|
||||
return path;
|
||||
}
|
||||
} //TODO: Support Player instead of CommandSender
|
||||
} //TODO: Test support of Player instead of CommandSender
|
||||
|
|
|
@ -160,7 +160,7 @@ public class TBMCChatAPI {
|
|||
} catch (Exception e) {
|
||||
TBMCCoreAPI.SendException("An error occured while registering command " + thecmdclass.getSimpleName(), e);
|
||||
}
|
||||
} //TODO: onCommand(CommandSender sender, String alias, int arg1, String arg2) (planned for a while)
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
|
|
@ -15,3 +15,4 @@ commands:
|
|||
description: Add or remove a member
|
||||
component:
|
||||
description: Enable or disable or list components
|
||||
dontrunthiscmd:
|
Loading…
Reference in a new issue