CommandFramework3 #94
54
SpigotCore_Main/src/de/steamwar/command/InternalCommand.java
Normale Datei
54
SpigotCore_Main/src/de/steamwar/command/InternalCommand.java
Normale Datei
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
|
||||
class InternalCommand {
|
||||
|
||||
private SWCommand swCommand;
|
||||
private Method method;
|
||||
private Parameter[] parameters;
|
||||
|
||||
InternalCommand(SWCommand swCommand, Method method) {
|
||||
this.swCommand = swCommand;
|
||||
this.method = method;
|
||||
parameters = method.getParameters();
|
||||
}
|
||||
|
||||
boolean invoke(CommandSender commandSender, String[] args) {
|
||||
if (args.length < parameters.length - 1) return false;
|
||||
Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, parameters, args);
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
method.invoke(swCommand, objects);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new SecurityException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
53
SpigotCore_Main/src/de/steamwar/command/InternalTabComplete.java
Normale Datei
53
SpigotCore_Main/src/de/steamwar/command/InternalTabComplete.java
Normale Datei
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
|
||||
class InternalTabComplete {
|
||||
|
||||
private SWCommand swCommand;
|
||||
private Method method;
|
||||
private Parameter[] parameters;
|
||||
|
||||
InternalTabComplete(SWCommand swCommand, Method method) {
|
||||
this.swCommand = swCommand;
|
||||
this.method = method;
|
||||
parameters = method.getParameters();
|
||||
}
|
||||
|
||||
SWCommandUtils.TabComplete invoke(CommandSender commandSender, String[] args) {
|
||||
if (args.length < parameters.length - 1) return null;
|
||||
Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, parameters, args);
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
return (SWCommandUtils.TabComplete) method.invoke(swCommand, objects);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new SecurityException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
102
SpigotCore_Main/src/de/steamwar/command/SWCommand.java
Normale Datei
102
SpigotCore_Main/src/de/steamwar/command/SWCommand.java
Normale Datei
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class SWCommand {
|
||||
|
||||
private final Set<InternalCommand> commandSet = new HashSet<>();
|
||||
private final Set<InternalTabComplete> tabCompleteSet = new HashSet<>();
|
||||
private Consumer<CommandSender> helpMessage = sender -> {};
|
||||
|
||||
|
||||
Lixfel
hat
Da du glaube sowieso keinen Befehl doppelt einfügst und dann häufig drüberiterierst, wäre glaube ich eine ArrayList angebrachter. Da du glaube sowieso keinen Befehl doppelt einfügst und dann häufig drüberiterierst, wäre glaube ich eine ArrayList angebrachter.
YoyoNow
hat
Ich glaube eher eine LinkedList, weil ich nur drüber iteriere oder? Ich glaube eher eine LinkedList, weil ich nur drüber iteriere oder?
Lixfel
hat
Nein, der Vorteil einer Linkedlist ist eher nur bei häufigem Entfernen aus der Mitte gegeben. Die ArrayList ist auch beim Iterieren schneller, weil da ja einfach nur der index um eins erhöht werden muss (bessere Speicherpositionierung) Nein, der Vorteil einer Linkedlist ist eher nur bei häufigem Entfernen aus der Mitte gegeben. Die ArrayList ist auch beim Iterieren schneller, weil da ja einfach nur der index um eins erhöht werden muss (bessere Speicherpositionierung)
|
||||
protected SWCommand(String command, String... aliases) {
|
||||
SWCommandUtils.commandMap.register("steamwar", new Command(command, "", "/" + command, Arrays.asList(aliases)) {
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String alias, String[] args) {
|
||||
for (InternalCommand internalCommand : commandSet) {
|
||||
if (internalCommand.invoke(sender, args)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
|
||||
List<String> strings = new ArrayList<>();
|
||||
for (InternalTabComplete internalTabComplete : tabCompleteSet) {
|
||||
SWCommandUtils.TabComplete tabComplete = internalTabComplete.invoke(sender, args);
|
||||
if (tabComplete != null) {
|
||||
strings.addAll(tabComplete.tabCompletes);
|
||||
}
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected final void register(String methodName) {
|
||||
if (methodName.equals("onCommand")) return;
|
||||
if (methodName.equals("onTabComplete")) return;
|
||||
Method[] methods = this.getClass().getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
if (!validMethod(method)) continue;
|
||||
if (method.getReturnType() == Void.TYPE) {
|
||||
commandSet.add(new InternalCommand(this, method));
|
||||
}
|
||||
if (method.getReturnType() == SWCommandUtils.TabComplete.class) {
|
||||
tabCompleteSet.add(new InternalTabComplete(this, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validMethod(Method method) {
|
||||
Parameter[] parameters = method.getParameters();
|
||||
if (parameters.length == 0) return false;
|
||||
if (!CommandSender.class.isAssignableFrom(parameters[0].getType())) return false;
|
||||
for (int i = 1; i < parameters.length; i++) {
|
||||
Parameter parameter = parameters[i];
|
||||
Class<?> clazz = parameter.getType();
|
||||
if (parameter.isVarArgs() && i == parameters.length - 1) {
|
||||
clazz = parameter.getType().getComponentType();
|
||||
}
|
||||
if (clazz.isEnum()) continue;
|
||||
if (!SWCommandUtils.MAPPER_FUNCTIONS.containsKey(clazz)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected final void setHelpMessage(Consumer<CommandSender> helpMessage) {
|
||||
assert helpMessage != null;
|
||||
this.helpMessage = helpMessage;
|
||||
}
|
||||
|
||||
protected final <T extends CommandSender> void sendHelpMessage(T sender) {
|
||||
helpMessage.accept(sender);
|
||||
}
|
||||
|
||||
}
|
130
SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java
Normale Datei
130
SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java
Normale Datei
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
class SWCommandUtils {
|
||||
|
||||
private SWCommandUtils() {
|
||||
throw new IllegalStateException("Utility Class");
|
||||
}
|
||||
|
||||
static final Map<Class<?>, Function<String, ?>> MAPPER_FUNCTIONS = new HashMap<>();
|
||||
|
||||
static final Function<String, ?> ERROR_FUNCTION = s -> {
|
||||
throw new SecurityException(s);
|
||||
};
|
||||
|
||||
static final BiFunction<Class<Enum<?>>, String, Enum<?>> ENUM_MAPPER = (enumClass, s) -> {
|
||||
Enum<?>[] enums = enumClass.getEnumConstants();
|
||||
for (Enum<?> e : enums) {
|
||||
if (e.name().equals(s)) return e;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
static {
|
||||
MAPPER_FUNCTIONS.put(boolean.class, Boolean::parseBoolean);
|
||||
Lixfel
hat
Wird wirklich irgendwo als Parameter "Boolean" benötigt? Oder "Float"? Oder... Wird wirklich irgendwo als Parameter "Boolean" benötigt? Oder "Float"? Oder...
YoyoNow
hat
Ich finde Boolean und Float ist benötigt, da man diese Mapper von außen nicht so schön erzeugen kann und wir sollten etwas Zukunftsicher an sich sache gehen, was benötigt wird. Ich finde Boolean und Float ist benötigt, da man diese Mapper von außen nicht so schön erzeugen kann und wir sollten etwas Zukunftsicher an sich sache gehen, was benötigt wird.
|
||||
MAPPER_FUNCTIONS.put(Boolean.class, Boolean::parseBoolean);
|
||||
MAPPER_FUNCTIONS.put(float.class, Float::parseFloat);
|
||||
MAPPER_FUNCTIONS.put(Float.class, Float::parseFloat);
|
||||
MAPPER_FUNCTIONS.put(double.class, Double::parseDouble);
|
||||
MAPPER_FUNCTIONS.put(Double.class, Double::parseDouble);
|
||||
MAPPER_FUNCTIONS.put(int.class, Integer::parseInt);
|
||||
MAPPER_FUNCTIONS.put(Integer.class, Integer::parseInt);
|
||||
MAPPER_FUNCTIONS.put(String.class, s -> s);
|
||||
MAPPER_FUNCTIONS.put(StringBuilder.class, StringBuilder::new);
|
||||
MAPPER_FUNCTIONS.put(Player.class, Bukkit::getPlayer);
|
||||
MAPPER_FUNCTIONS.put(UUID.class, UUID::fromString);
|
||||
}
|
||||
|
||||
static final CommandMap commandMap;
|
||||
|
||||
static {
|
||||
try {
|
||||
final Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
|
||||
commandMapField.setAccessible(true);
|
||||
commandMap = (CommandMap) commandMapField.get(Bukkit.getServer());
|
||||
} catch (NoSuchFieldException | IllegalAccessException exception) {
|
||||
Bukkit.shutdown();
|
||||
throw new SecurityException("Oh shit. Commands cannot be register.", exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object[] generateArgumentArray(CommandSender commandSender, Parameter[] parameters, String[] args) {
|
||||
Object[] arguments = new Object[args.length + 1];
|
||||
boolean varArgs = false;
|
||||
if (parameters[parameters.length - 1].isVarArgs()) {
|
||||
varArgs = true;
|
||||
arguments = new Object[parameters.length];
|
||||
}
|
||||
arguments[0] = parameters[0].getType().cast(commandSender);
|
||||
|
||||
for (int i = 1; i < parameters.length - (varArgs ? 1 : 0); i++) {
|
||||
Class<?> clazz = parameters[i].getType();
|
||||
arguments[i] = mapper(clazz).apply(args[i - 1]);
|
||||
}
|
||||
|
||||
if (varArgs) {
|
||||
Object[] varArgument = new Object[args.length - parameters.length + 2];
|
||||
arguments[arguments.length - 1] = varArgument;
|
||||
Function<String, Object> mapper = mapper(parameters[parameters.length - 1].getType().getComponentType());
|
||||
|
||||
int index = 0;
|
||||
for (int i = parameters.length - 2; i < args.length; i++) {
|
||||
varArgument[index++] = mapper.apply(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Function<String, Object> mapper(Class<?> clazz) {
|
||||
if (clazz.isEnum()) {
|
||||
Class<Enum<?>> enumClass = (Class<Enum<?>>) clazz;
|
||||
return s -> ENUM_MAPPER.apply(enumClass, s);
|
||||
} else {
|
||||
return s -> MAPPER_FUNCTIONS.getOrDefault(clazz, ERROR_FUNCTION).apply(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TabComplete {
|
||||
final List<String> tabCompletes;
|
||||
|
||||
public TabComplete(List<String> tabCompletes) {
|
||||
this.tabCompletes = tabCompletes;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
In neuem Issue referenzieren
Einen Benutzer sperren
Das ist nicht so das, was ich fürs FightSystem gemeint habe. Ich möchte eigentlich nicht, dass sich der Command merkt, ob er jetzt enabled oder disabled ist, sondern den Command einfach Registrieren und aber auch wieder Entregistrieren können. Dann kann ich nämlich auch bestimmen, wass der Command macht, wenn er "disabled" ist, oder gar komplexere State-Machines umsetzen.
Ok ich gucke, dass ich das eingebaut bekomme, an sich muss ich ja nur unregister können. Weril registerieren tust du ja mit einer Instanz erzeugen,
Dies sollte nun möglich sein.