SteamWar/SpigotCore
Archiviert
13
0

Add testing to SpigotCore_Main
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful

Add CommandRegistering
Add initial implementation for CommandPart
Dieser Commit ist enthalten in:
yoyosource 2021-12-08 22:39:56 +01:00
Ursprung 9596309dc9
Commit 13b6dcd74b
13 geänderte Dateien mit 508 neuen und 47 gelöschten Zeilen

Datei anzeigen

@ -40,6 +40,16 @@ sourceSets {
exclude '**/*.java', '**/*.kt'
}
}
test {
java {
srcDirs = ['testsrc']
}
resources {
srcDirs = ['testsrc']
exclude '**/*.java', '**/*.kt'
}
}
}
dependencies {
@ -51,6 +61,12 @@ dependencies {
testCompileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.22'
testImplementation files("${project.rootDir}/lib/Spigot-1.15.jar")
testImplementation files("${project.rootDir}/lib/WorldEdit-1.12.jar")
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.hamcrest:hamcrest:2.2'
}
processResources {

Datei anzeigen

@ -19,7 +19,7 @@
package de.steamwar.command;
public class CommandParseException extends Exception {
public class CommandParseException extends RuntimeException {
public CommandParseException() {
}

Datei anzeigen

@ -0,0 +1,119 @@
/*
* 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 lombok.AllArgsConstructor;
import org.bukkit.command.CommandSender;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
public class CommandPart {
private static final String[] EMPTY_ARRAY = new String[0];
@AllArgsConstructor
private static class CheckArgumentResult {
private final boolean success;
private final Object value;
}
private TypeMapper<?> typeMapper;
private GuardChecker guard;
private Class<?> varArgType = null;
private String optional = null;
private boolean help;
private CommandPart next = null;
CommandPart(TypeMapper<?> typeMapper, GuardChecker guard, Class<?> varArgType, String optional, boolean help) {
this.typeMapper = typeMapper;
this.guard = guard;
if (optional != null && varArgType != null) {
throw new IllegalArgumentException("A vararg part can't have an optional part!");
}
this.varArgType = varArgType;
this.optional = optional;
this.help = help;
}
void setNext(CommandPart next) {
if (varArgType != null) {
throw new IllegalArgumentException("There can't be a next part if this is a vararg part!");
}
this.next = next;
}
void generateArgumentArray(List<Object> current, CommandSender commandSender, String[] args, int startIndex) {
if (startIndex >= args.length) {
return;
}
if (varArgType != null) {
Object array = Array.newInstance(varArgType, args.length - startIndex);
for (int i = startIndex; i < args.length; i++) {
CheckArgumentResult validArgument = checkArgument(help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND, commandSender, args, i);
if (!validArgument.success) {
throw new CommandParseException();
}
Array.set(array, i - startIndex, validArgument.value);
}
current.add(array);
return;
}
CheckArgumentResult validArgument = checkArgument(help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND, commandSender, args, startIndex);
if (!validArgument.success && optional == null) {
throw new CommandParseException();
}
if (optional != null) {
current.add(typeMapper.map(commandSender, EMPTY_ARRAY, optional));
next.generateArgumentArray(current, commandSender, args, startIndex);
return;
}
current.add(validArgument.value);
next.generateArgumentArray(current, commandSender, args, startIndex + 1);
}
void generateTabComplete(List<String> current, CommandSender commandSender, String[] args, int startIndex) {
}
private CheckArgumentResult checkArgument(GuardCheckType guardCheckType, CommandSender commandSender, String[] args, int index) {
try {
Object value = typeMapper.map(commandSender, Arrays.copyOf(args, index - 1), args[index]);
if (guard != null) {
GuardResult guardResult = guard.guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, index - 1), args[index]);
switch (guardResult) {
case ALLOWED:
return new CheckArgumentResult(true, value);
case DENIED:
throw new CommandNoHelpException();
case DENIED_WITH_HELP:
default:
return new CheckArgumentResult(false, null);
}
}
return new CheckArgumentResult(true, value);
} catch (Exception e) {
return new CheckArgumentResult(false, null);
}
}
}

Datei anzeigen

@ -0,0 +1,63 @@
/*
* 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.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.command.SimpleCommandMap;
import java.lang.reflect.Field;
import java.util.Map;
class CommandRegistering {
private static final CommandMap commandMap;
private static final Map<String, Command> knownCommandMap;
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 registered.", exception);
}
try {
final Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands");
knownCommandsField.setAccessible(true);
knownCommandMap = (Map<String, Command>) knownCommandsField.get(commandMap);
} catch (NoSuchFieldException | IllegalAccessException exception) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", exception);
}
}
static void unregister(Command command) {
knownCommandMap.remove(command.getName());
command.getAliases().forEach(knownCommandMap::remove);
command.unregister(commandMap);
}
static void register(Command command) {
commandMap.register("steamwar", command);
}
}

Datei anzeigen

@ -61,13 +61,7 @@ public abstract class SWCommand {
if (!initialized) {
createMapping();
}
try {
if (!commandList.stream().anyMatch(s -> s.invoke(sender, args))) {
commandHelpList.stream().anyMatch(s -> s.invoke(sender, args));
}
} catch (CommandNoHelpException e) {
// Ignored
}
SWCommand.this.execute(sender, alias, args);
return false;
}
@ -76,21 +70,52 @@ public abstract class SWCommand {
if (!initialized) {
createMapping();
}
String string = args[args.length - 1].toLowerCase();
return commandList.stream()
.filter(s -> !s.noTabComplete)
.map(s -> s.tabComplete(sender, args))
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(s -> !s.isEmpty())
.filter(s -> s.toLowerCase().startsWith(string))
.collect(Collectors.toList());
return SWCommand.this.tabComplete(sender, alias, args);
}
};
unregister();
register();
}
// This is used for the tests!
SWCommand(boolean noRegister, String command, String... aliases) {
this.command = new Command(command, "", "/" + command, Arrays.asList(aliases)) {
@Override
public boolean execute(CommandSender sender, String alias, String[] args) {
SWCommand.this.execute(sender, alias, args);
return false;
}
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
return SWCommand.this.tabComplete(sender, alias, args);
}
};
createMapping();
}
void execute(CommandSender sender, String alias, String[] args) {
try {
if (!commandList.stream().anyMatch(s -> s.invoke(sender, args))) {
commandHelpList.stream().anyMatch(s -> s.invoke(sender, args));
}
} catch (CommandNoHelpException e) {
// Ignored
}
}
List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
String string = args[args.length - 1].toLowerCase();
return commandList.stream()
.filter(s -> !s.noTabComplete)
.map(s -> s.tabComplete(sender, args))
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(s -> !s.isEmpty())
.filter(s -> s.toLowerCase().startsWith(string))
.collect(Collectors.toList());
}
private synchronized void createMapping() {
List<Method> methods = methods();
for (Method method : methods) {
@ -219,13 +244,11 @@ public abstract class SWCommand {
}
public void unregister() {
SWCommandUtils.knownCommandMap.remove(command.getName());
command.getAliases().forEach(SWCommandUtils.knownCommandMap::remove);
command.unregister(SWCommandUtils.commandMap);
CommandRegistering.unregister(command);
}
public void register() {
SWCommandUtils.commandMap.register("steamwar", this.command);
CommandRegistering.register(command);
}
@Register(help = true)
@ -332,6 +355,12 @@ public abstract class SWCommand {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
protected @interface StaticValue {
String[] value() default {};
String[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
protected @interface OptionalValue {
String defaultValue(); // Will pe parsed against the TypeMapper specified by the parameter or annotation
}
}

Datei anzeigen

@ -91,28 +91,6 @@ public class SWCommandUtils {
MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper);
}
static final CommandMap commandMap;
static final Map<String, Command> knownCommandMap;
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 registered.", exception);
}
try {
final Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands");
knownCommandsField.setAccessible(true);
knownCommandMap = (Map<String, Command>) knownCommandsField.get(commandMap);
} catch (NoSuchFieldException | IllegalAccessException exception) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", exception);
}
}
static Object[] generateArgumentArray(CommandSender commandSender, TypeMapper<?>[] parameters, GuardChecker[] guards, String[] args, Class<?> varArgType, String[] subCommand) throws CommandParseException {
Object[] arguments = new Object[parameters.length + 1];
int index = 0;

Datei anzeigen

@ -179,10 +179,10 @@ class SubCommand {
method.invoke(swCommand, objects);
} catch (CommandNoHelpException e) {
throw e;
} catch (IllegalAccessException | RuntimeException | InvocationTargetException e) {
throw new SecurityException(e.getMessage(), e);
} catch (CommandParseException e) {
return false;
} catch (IllegalAccessException | RuntimeException | InvocationTargetException e) {
throw new SecurityException(e.getMessage(), e);
}
return true;
}

Datei anzeigen

@ -38,7 +38,6 @@ import java.util.logging.Level;
public class Core extends JavaPlugin{
private static Core instance;
private static final int version;
public static Message MESSAGE;

Datei anzeigen

Datei anzeigen

@ -0,0 +1,122 @@
/*
* 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;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import java.util.Set;
public class TestCommandSender implements CommandSender {
@Override
public void sendMessage(String s) {
}
@Override
public void sendMessage(String[] strings) {
}
@Override
public Server getServer() {
return null;
}
@Override
public String getName() {
return null;
}
@Override
public Spigot spigot() {
return null;
}
@Override
public boolean isPermissionSet(String s) {
return false;
}
@Override
public boolean isPermissionSet(Permission permission) {
return false;
}
@Override
public boolean hasPermission(String s) {
return false;
}
@Override
public boolean hasPermission(Permission permission) {
return false;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String s, boolean b) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String s, boolean b, int i) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int i) {
return null;
}
@Override
public void removeAttachment(PermissionAttachment permissionAttachment) {
}
@Override
public void recalculatePermissions() {
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return null;
}
@Override
public boolean isOp() {
return false;
}
@Override
public void setOp(boolean b) {
}
}

Datei anzeigen

@ -0,0 +1,41 @@
/*
* 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;
public class ExecutionIdentifier extends RuntimeException {
public ExecutionIdentifier() {
}
public ExecutionIdentifier(String message) {
super(message);
}
public ExecutionIdentifier(String message, Throwable cause) {
super(message, cause);
}
public ExecutionIdentifier(Throwable cause) {
super(cause);
}
public ExecutionIdentifier(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

Datei anzeigen

@ -0,0 +1,34 @@
/*
* 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;
public class SimpleCommand extends SWCommand {
public SimpleCommand() {
super(true, "simple");
}
@Register
public void execute(CommandSender sender) {
throw new ExecutionIdentifier("Simple execute without any parameters");
}
}

Datei anzeigen

@ -0,0 +1,60 @@
/*
* 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 de.steamwar.TestCommandSender;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class SimpleCommandTest {
private SimpleCommand simpleCommand;
@Before
public void setUp() throws Exception {
simpleCommand = new SimpleCommand();
}
@Test
public void testCommandParsing() {
try {
simpleCommand.execute(new TestCommandSender(), "", new String[]{});
} catch (SecurityException securityException) {
if (securityException.getCause().getCause() instanceof ExecutionIdentifier) {
ExecutionIdentifier executionIdentifier = (ExecutionIdentifier) securityException.getCause().getCause();
assertThat(executionIdentifier.getMessage(), is("Simple execute without any parameters"));
return;
}
}
assert false;
}
@Test
public void testUnknownCommandParsing() {
try {
simpleCommand.execute(new TestCommandSender(), "", new String[]{"unknown"});
} catch (SecurityException securityException) {
assert false;
}
}
}