Add plugin & component to commands, unregister on disable

#88
This commit is contained in:
Norbi Peti 2020-02-14 19:20:20 +01:00
parent d2ab3511c2
commit 23f3c0f133
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
9 changed files with 119 additions and 43 deletions

View file

@ -5,7 +5,6 @@ import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.Command2.Subcommand;
import buttondevteam.lib.chat.Command2MC;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import lombok.val;
@ -19,9 +18,8 @@ import java.util.Optional;
"Component command",
"Can be used to enable/disable/list components"
})
public class ComponentCommand extends ICommand2MC<MainPlugin> {
@Override
public void onRegister(Command2MC<MainPlugin> manager) {
public class ComponentCommand extends ICommand2MC {
public ComponentCommand() {
getManager().addParamConverter(Plugin.class, arg -> Bukkit.getPluginManager().getPlugin(arg), "Plugin not found!");
}
@ -31,7 +29,7 @@ public class ComponentCommand extends ICommand2MC<MainPlugin> {
})
public boolean enable(CommandSender sender, Plugin plugin, String component) {
if (plugin instanceof ButtonPlugin) {
if (!((ButtonPlugin<?>) plugin).justReload()) {
if (!((ButtonPlugin) plugin).justReload()) {
sender.sendMessage("§cCouldn't reload config, check console.");
return true;
}

View file

@ -44,7 +44,7 @@ import java.util.Optional;
import java.util.UUID;
import java.util.logging.Logger;
public class MainPlugin extends ButtonPlugin<MainPlugin> {
public class MainPlugin extends ButtonPlugin {
public static MainPlugin Instance;
public static Permission permission;
@Nullable
@ -117,8 +117,8 @@ public class MainPlugin extends ButtonPlugin<MainPlugin> {
if (Bukkit.getPluginManager().isPluginEnabled("Votifier") && economy != null)
Component.registerComponent(this, new VotifierComponent(economy));
ComponentManager.enableComponents();
getCommand2MC().registerCommand(new ComponentCommand());
getCommand2MC().registerCommand(new ChromaCommand());
registerCommand(new ComponentCommand());
registerCommand(new ChromaCommand());
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this);
TBMCCoreAPI.RegisterEventsForExceptions(getCommand2MC(), this);
ChromaGamerBase.addConverter(commandSender -> Optional.ofNullable(commandSender instanceof ConsoleCommandSender || commandSender instanceof BlockCommandSender

View file

@ -2,13 +2,13 @@ package buttondevteam.core;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.channel.ChannelComponent;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TBMCChatAPI;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
@ -42,7 +42,7 @@ public class TestPrepare {
}
}));
//noinspection unchecked
Component.registerComponent(Mockito.mock(ButtonPlugin.class), new ChannelComponent());
Component.registerComponent(Mockito.mock(JavaPlugin.class), new ChannelComponent());
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null));
}
}

View file

@ -4,6 +4,7 @@ import buttondevteam.buttonproc.HasConfig;
import buttondevteam.core.ComponentManager;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2MC;
import buttondevteam.lib.chat.ICommand2MC;
import lombok.AccessLevel;
import lombok.Getter;
import org.bukkit.configuration.InvalidConfigurationException;
@ -22,9 +23,9 @@ import java.util.Optional;
import java.util.Stack;
@HasConfig(global = true)
public abstract class ButtonPlugin<TP extends ButtonPlugin<TP>> extends JavaPlugin {
public abstract class ButtonPlugin extends JavaPlugin {
@Getter //Needs to be static as we don't know the plugin when a command is handled
private Command2MC<TP> command2MC = new Command2MC<>();
private static Command2MC command2MC = new Command2MC();
@Getter(AccessLevel.PROTECTED)
private IHaveConfig iConfig;
private CommentedConfiguration yaml;
@ -84,7 +85,7 @@ public abstract class ButtonPlugin<TP extends ButtonPlugin<TP>> extends JavaPlug
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); - TODO
getCommand2MC().unregisterCommands(this);
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e);
}
@ -147,6 +148,16 @@ public abstract class ButtonPlugin<TP extends ButtonPlugin<TP>> extends JavaPlug
}
}
/**
* Registers command and sets its plugin.
*
* @param command The command to register
*/
protected void registerCommand(ICommand2MC command) {
command.registerToPlugin(this);
getCommand2MC().registerCommand(command);
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ConfigOpts {

View file

@ -23,8 +23,8 @@ import java.util.stream.Collectors;
* Configuration is based on class name
*/
@HasConfig(global = false) //Used for obtaining javadoc
public abstract class Component<TP extends ButtonPlugin> {
private static HashMap<Class<? extends Component>, Component<? extends JavaPlugin>> components = new HashMap<>();
public abstract class Component<TP extends JavaPlugin> {
@SuppressWarnings("rawtypes") private static HashMap<Class<? extends Component>, Component<? extends JavaPlugin>> components = new HashMap<>();
@Getter
private boolean enabled = false;
@ -44,11 +44,12 @@ public abstract class Component<TP extends ButtonPlugin> {
* Registers a component checking it's dependencies and calling {@link #register(JavaPlugin)}.<br>
* Make sure to register the dependencies first.<br>
* The component will be enabled automatically, regardless of when it was registered.<br>
* <b>If not using {@link ButtonPlugin}, call {@link ComponentManager#unregComponents(ButtonPlugin)} on plugin disable.</b>
*
* @param component The component to register
* @return Whether the component is registered successfully (it may have failed to enable)
*/
public static <T extends ButtonPlugin> boolean registerComponent(T plugin, Component<T> component) {
public static <T extends JavaPlugin> boolean registerComponent(T plugin, Component<T> component) {
return registerUnregisterComponent(plugin, component, true);
}
@ -64,11 +65,11 @@ public abstract class Component<TP extends ButtonPlugin> {
return registerUnregisterComponent(plugin, component, false);
}
public static <T extends ButtonPlugin> boolean registerUnregisterComponent(T plugin, Component<T> component, boolean register) {
public static <T extends JavaPlugin> boolean registerUnregisterComponent(T plugin, Component<T> component, boolean register) {
try {
val metaAnn = component.getClass().getAnnotation(ComponentMetadata.class);
if (metaAnn != null) {
Class<? extends Component>[] dependencies = metaAnn.depends();
@SuppressWarnings("rawtypes") Class<? extends Component>[] dependencies = metaAnn.depends();
for (val dep : dependencies) { //TODO: Support dependencies at enable/disable as well
if (!components.containsKey(dep)) {
plugin.getLogger().warning("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + " as a required dependency is missing/disabled: " + dep.getSimpleName());
@ -85,7 +86,8 @@ public abstract class Component<TP extends ButtonPlugin> {
updateConfig(plugin, component);
component.register(plugin);
components.put(component.getClass(), component);
plugin.getComponentStack().push(component);
if (plugin instanceof ButtonPlugin)
((ButtonPlugin) plugin).getComponentStack().push(component);
if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled().get()) {
try { //Enable components registered after the previous ones getting enabled
setComponentEnabled(component, true);
@ -138,8 +140,7 @@ public abstract class Component<TP extends ButtonPlugin> {
//System.out.println("Done enabling "+component.getClassName());
} else {
component.disable();
//TBMCChatAPI.RemoveCommands(component); - TODO
component.getPlugin().getCommand2MC().unregisterCommand();
ButtonPlugin.getCommand2MC().unregisterCommands(component);
}
}
@ -160,6 +161,7 @@ public abstract class Component<TP extends ButtonPlugin> {
*
* @return The currently registered components
*/
@SuppressWarnings("rawtypes")
public static Map<Class<? extends Component>, Component<? extends JavaPlugin>> getComponents() {
return Collections.unmodifiableMap(components);
}
@ -200,13 +202,16 @@ public abstract class Component<TP extends ButtonPlugin> {
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}.
* Registers a command 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
* @param command Custom coded command class
*/
protected final <T extends ButtonPlugin<T>> void registerCommand(ICommand2MC<T> commandBase) {
getPlugin().getCommand2MC().registerCommand(commandBase);
protected final void registerCommand(ICommand2MC command) {
if (plugin instanceof ButtonPlugin)
command.registerToPlugin((ButtonPlugin) plugin);
command.registerToComponent(this);
ButtonPlugin.getCommand2MC().registerCommand(command);
}
/**

View file

@ -114,6 +114,8 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
private ArrayList<String> commandHelp = new ArrayList<>(); //Mainly needed by Discord
private char commandChar;
/**
* Adds a param converter that obtains a specific object from a string parameter.
* The converter may return null.
@ -260,6 +262,7 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
public abstract void registerCommand(TC command);
protected void registerCommand(TC command, @SuppressWarnings("SameParameterValue") char commandChar) {
this.commandChar = commandChar;
val path = command.getCommandPath();
int x = path.indexOf(' ');
val mainPath = commandChar + path.substring(0, x == -1 ? path.length() : x);
@ -357,8 +360,19 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
return Collections.unmodifiableSet(subcommands.keySet());
}*/
public void unregisterCommand() {
/**
* Unregisters all of the subcommands in the given command.
*
* @param command The command object
*/
public void unregisterCommand(ICommand2<TP> command) {
var path = command.getCommandPath();
for (val method : command.getClass().getMethods()) {
val ann = method.getAnnotation(Subcommand.class);
if (ann == null) continue;
val subcommand = commandChar + path + getCommandPath(method.getName(), ' ');
subcommands.remove(subcommand);
}
}
/**

View file

@ -2,6 +2,7 @@ package buttondevteam.lib.chat;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
@ -18,11 +19,17 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Optional;
import java.util.function.Function;
public class Command2MC<TP extends ButtonPlugin<TP>> extends Command2<ICommand2MC<TP>, Command2MCSender> implements Listener {
public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implements Listener {
/**
* Don't use directly, use the method in Component and ButtonPlugin to automatically unregister the command when needed.
*
* @param command The command to register
*/
@Override
public void registerCommand(ICommand2MC<TP> command) {
public void registerCommand(ICommand2MC command) {
super.registerCommand(command, '/');
var perm = "chroma.command." + command.getCommandPath().replace(' ', '.');
if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset
@ -47,11 +54,11 @@ public class Command2MC<TP extends ButtonPlugin<TP>> extends Command2<ICommand2M
}
@Override
public boolean hasPermission(Command2MCSender sender, ICommand2MC<TP> command, Method method) {
public boolean hasPermission(Command2MCSender sender, ICommand2MC command, Method method) {
return hasPermission(sender.getSender(), command, method);
}
public boolean hasPermission(CommandSender sender, ICommand2MC<TP> command, Method method) {
public boolean hasPermission(CommandSender sender, ICommand2MC command, Method method) {
if (sender instanceof ConsoleCommandSender) return true; //Always allow the console
String pg;
boolean p = true;
@ -81,7 +88,7 @@ public class Command2MC<TP extends ButtonPlugin<TP>> extends Command2<ICommand2M
* @param method The subcommand to check
* @return The permission group for the subcommand or empty string
*/
private String permGroup(ICommand2MC<TP> command, Method method) {
private String permGroup(ICommand2MC command, Method method) {
val sc = method.getAnnotation(Subcommand.class);
if (sc != null && sc.permGroup().length() > 0) {
return sc.permGroup();
@ -120,6 +127,21 @@ public class Command2MC<TP extends ButtonPlugin<TP>> extends Command2<ICommand2M
super.addParamConverter(cl, converter, "§c" + errormsg);
}
public void unregisterCommands(ButtonPlugin plugin) {
/*var cmds = subcommands.values().stream().map(sd -> sd.command).filter(cmd -> plugin.equals(cmd.getPlugin())).toArray(ICommand2MC[]::new);
for (var cmd : cmds)
unregisterCommand(cmd);*/
subcommands.values().removeIf(sd -> plugin.equals(sd.command.getPlugin()));
}
public void unregisterCommands(Component<?> component) {
/*var cmds = subcommands.values().stream().map(sd -> sd.command).filter(cmd -> component.equals(cmd.getComponent())).toArray(ICommand2MC[]::new);
for (var cmd : cmds)
unregisterCommand(cmd);*/
subcommands.values().removeIf(sd -> Optional.ofNullable(sd.command.getComponent())
.map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false));
}
@EventHandler
private void handleTabComplete(TabCompleteEvent event) {
String commandline = event.getBuffer();
@ -129,7 +151,7 @@ public class Command2MC<TP extends ButtonPlugin<TP>> extends Command2<ICommand2M
String subcommand = commandline.substring(0, i).toLowerCase();
if (subcommand.length() == 0 || subcommand.charAt(0) != '/') subcommand = '/' + subcommand; //Console
//System.out.println("Subcommand: " + subcommand);
SubcommandData<ICommand2MC<TP>> sd = subcommands.get(subcommand); //O(1)
SubcommandData<ICommand2MC> sd = subcommands.get(subcommand); //O(1)
if (sd == null) continue;
//System.out.println("ht: " + Arrays.toString(sd.helpText));
Arrays.stream(sd.helpText).skip(1).map(ht -> new HashMap.SimpleEntry<>(ht, subcommands.get(ht))).filter(e -> e.getValue() != null)

View file

@ -45,13 +45,10 @@ public abstract class ICommand2<TP extends Command2Sender> {
private final String path;
@Getter
private Command2<?, TP> manager; //TIL that if I use a raw type on a variable then none of the type args will work (including what's defined on a method, not on the type)
private final Command2<?, TP> manager; //TIL that if I use a raw type on a variable then none of the type args will work (including what's defined on a method, not on the type)
public <T extends ICommand2<TP>> ICommand2() {
public <T extends ICommand2<TP>> ICommand2(Command2<T, TP> manager) {
path = getcmdpath();
}
public <T extends ICommand2<TP>> void onRegister(Command2<T, TP> manager) {
this.manager = manager;
}

View file

@ -1,17 +1,46 @@
package buttondevteam.lib.chat;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component;
import lombok.Getter;
import javax.annotation.Nullable;
@SuppressWarnings("JavadocReference")
public abstract class ICommand2MC extends ICommand2<Command2MCSender> {
@Getter
private ButtonPlugin plugin;
@Getter
@Nullable
private Component<?> component;
public abstract class ICommand2MC<T extends ButtonPlugin<T>> extends ICommand2<Command2MCSender> {
public ICommand2MC() {
super(ButtonPlugin.getCommand2MC());
}
public void onRegister(Command2MC<T> manager) {
/**
* Called from {@link buttondevteam.lib.architecture.Component#registerCommand(ICommand2MC)} and {@link ButtonPlugin#registerCommand(ICommand2MC)}
*/
public void registerToPlugin(ButtonPlugin plugin) {
if (this.plugin == null)
this.plugin = plugin;
else
throw new IllegalStateException("The command is already assigned to a plugin!");
}
@Override
/**
* Called from {@link buttondevteam.lib.architecture.Component#registerCommand(ICommand2MC)}
*/
public void registerToComponent(Component<?> component) {
if (this.component == null)
this.component = component;
else
throw new IllegalStateException("The command is already assigned to a component!");
}
/*@Override
public <TX extends ICommand2<Command2MCSender>> void onRegister(Command2<TX, Command2MCSender> manager) {
super.onRegister(manager);
onRegister((Command2MC<T>) manager);
}
onRegister((Command2MC) manager); //If ICommand2 is inherited with the same type arg, this would fail but I don't want to add another type param to ICommand2
} //For example: class IOffender extends ICommand2<Command2MCSender>*/
}