Started working on command system

Added a bunch of TODOs
ThorpeCore
This commit is contained in:
Norbi Peti 2019-01-27 01:10:10 +01:00
parent 2beb6662ea
commit 5b75c2fa02
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
4 changed files with 107 additions and 9 deletions

View file

@ -48,10 +48,10 @@ public class ComponentCommand extends TBMCCommandBase {
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())
sender.sendMessage("§cComponent not found!");
return oc;
}
if (!oc.isPresent()) //TODO: There may be multiple components with the same name
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) {

View file

@ -7,7 +7,7 @@ import java.util.HashMap;
import java.util.function.Function;
/**
* Members of this interface should be protected (access level)
* A config system
*/
public final class IHaveConfig {
private final HashMap<String, ConfigData<?>> datamap = new HashMap<>();
@ -15,7 +15,7 @@ public final class IHaveConfig {
private ConfigurationSection config;
/**
* May be used in testing
* May be used in testing.
*
* @param section May be null for testing
*/

View file

@ -1,12 +1,22 @@
package buttondevteam.lib.chat;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bukkit.command.CommandSender;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.function.Function;
/**
* The method name is the subcommand, use underlines (_) to add further subcommands.
* The args may be null if the conversion failed.
*/
public abstract class Command2 {
/**
* Default handler for commands, can be used to copy the args too.
@ -14,7 +24,7 @@ public abstract class Command2 {
* @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
* @return The success of the command
*/
public boolean def(CommandSender sender, String command, @TextArg String args) {
return false;
@ -28,4 +38,92 @@ public abstract class Command2 {
@Retention(RetentionPolicy.RUNTIME)
public @interface TextArg {
}
}
/**
* Methods annotated with this will be recognised as subcommands
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subcommand {
}
@RequiredArgsConstructor
private static class SubcommandData {
public final Method method;
public final Command2 command;
}
private static HashMap<String, SubcommandData> subcommands = new HashMap<>();
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();
}
/**
* Adds a param converter that obtains a specific object from a string parameter.
* The converter may return null.
*
* @param cl The class of the result object
* @param converter The converter to use
* @param <T> The type of the result
*/
public static <T> void addParamConverter(Class<T> cl, Function<String, T> converter) {
paramConverters.put(cl, converter);
}
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);
SubcommandData sd = subcommands.get(subcommand); //O(1)
if (sd == null) continue; //TODO: This will run each time someone runs any command
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);
if (cl == String.class) {
params.add(param);
continue;
}
val conv = paramConverters.get(cl);
if (conv == null)
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);
return true; //We found a method
}
return false; //Didn't handle
} //TODO: Use preprocess event and add to the help
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();
path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
return path;
}
} //TODO: Support Player instead of CommandSender

View file

@ -1,4 +1,4 @@
name: ButtonCore
name: ThorpeCore
main: buttondevteam.core.MainPlugin
version: 1.0
author: TBMCPlugins