Archived
0

Some changes

This commit is contained in:
Dico Karssiens
2018-09-30 17:05:42 +01:00
parent 7f36b6f561
commit c4801757a2
41 changed files with 835 additions and 403 deletions

View File

@@ -10,6 +10,7 @@ public class ChildCommandAddress extends ModifiableCommandAddress {
final List<String> namesModifiable = new ArrayList<>(4);
List<String> names = namesModifiable;
Command command;
boolean isCommandTrailing;
public ChildCommandAddress() {
}
@@ -89,7 +90,7 @@ public class ChildCommandAddress extends ModifiableCommandAddress {
}
public void finalizeNames() {
if (names instanceof ArrayList) {
if (names == namesModifiable) {
names = Collections.unmodifiableList(namesModifiable);
}
}
@@ -103,4 +104,17 @@ public class ChildCommandAddress extends ModifiableCommandAddress {
this.parent = parent;
}
@Override
public boolean isCommandTrailing() {
return isCommandTrailing;
}
@Override
public void setCommandTrailing(boolean trailing) {
if (hasChildren()) {
throw new IllegalStateException("Address already has children, this property can't be modified");
}
isCommandTrailing = trailing;
}
}

View File

@@ -70,10 +70,11 @@ public abstract class Command {
return this;
}
/*
public Command preprocessArguments(IArgumentPreProcessor processor) {
parameterList.setArgumentPreProcessor(processor);
return this;
}
}*/
public final ParameterList getParameterList() {
return parameterList;
@@ -133,56 +134,23 @@ public abstract class Command {
// ---- EXECUTION ----
public void execute(CommandSender sender, ICommandAddress caller, ArgumentBuffer buffer) {
ExecutionContext executionContext = new ExecutionContext(sender, caller, this, buffer, false);
try {
executeWithContext(executionContext);
} catch (Throwable t) {
caller.getChatHandler().handleException(sender, executionContext, t);
}
}
public void executeWithContext(ExecutionContext context) throws CommandException {
//System.out.println("In Command.execute(sender, caller, buffer)#try{");
public void initializeAndFilterContext(ExecutionContext context) throws CommandException {
int i, n;
for (i = 0, n = contextFilters.size() - postParameterFilterCount; i < n; i++) {
contextFilters.get(i).filterContext(context);
}
context.parseParameters();
context.parse(parameterList);
for (n = contextFilters.size(); i < n; i++) {
contextFilters.get(i).filterContext(context);
if (!context.isTabComplete()) {
for (n = contextFilters.size(); i < n; i++) {
contextFilters.get(i).filterContext(context);
}
}
//System.out.println("Post-contextfilters");
String message = execute(context.getSender(), context);
context.sendMessage(EMessageType.RESULT, message);
}
public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException;
public List<String> tabComplete(CommandSender sender, ICommandAddress caller, Location location, ArgumentBuffer buffer) {
ExecutionContext executionContext = new ExecutionContext(sender, caller, this, buffer, true);
try {
return tabCompleteWithContext(executionContext, location);
} catch (CommandException ex) {
return Collections.emptyList();
}
}
public List<String> tabCompleteWithContext(ExecutionContext context, Location location) throws CommandException {
int i, n;
for (i = 0, n = contextFilters.size() - postParameterFilterCount; i < n; i++) {
contextFilters.get(i).filterContext(context);
}
context.parseParametersQuietly();
return tabComplete(context.getSender(), context, location);
}
public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
return context.getSuggestedCompletions(location);
}

View File

@@ -265,10 +265,19 @@ public final class CommandBuilder {
* @return this
* @throws IllegalStateException if the current group has no command
*/
public CommandBuilder addRequiredPermission(String permission) {
public CommandBuilder addPermission(String permission) {
return addContextFilter(IContextFilter.permission(permission));
}
/**
* Add a required permission to the command of the current group, which can be inherited
* @return this
* @throws IllegalStateException if the current group has no command
*/
public CommandBuilder addInheritablePermission(String permission) {
return addContextFilter(IContextFilter.inheritablePermission(permission));
}
/**
* Jump up a level in the address
*

View File

@@ -1,23 +0,0 @@
package io.dico.dicore.command;
/**
* This enum is intended to provide some constants for default messages.
* Can be returned by a reflective command.
* Currently, no constants have an actual message.
* Prone to removal in the future because of lack of usefullness.
*/
public enum CommandResult {
SUCCESS(null),
QUIET_ERROR(null);
private final String message;
CommandResult(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}

View File

@@ -17,27 +17,32 @@ import java.util.*;
* It is also responsible for keeping track of the parameter to complete in the case of a tab completion.
*/
public class ExecutionContext {
private CommandSender sender;
// Sender of the command
private final CommandSender sender;
// Address while parsing parameters with ContextParser
private ICommandAddress address;
// Command to execute
private Command command;
private ArgumentBuffer originalBuffer;
private ArgumentBuffer processedBuffer;
// if this flag is set, this execution is only for completion purposes.
private boolean tabComplete;
private final ArgumentBuffer buffer;
// private ArgumentBuffer processedBuffer;
// caches the buffer's cursor before parsing. This is needed to provide the original input of the player.
private int cursorStart;
// when the context starts parsing parameters, this flag is set, and any subsequent calls to #parseParameters() throw an IllegalStateException.
private boolean attemptedToParse;
//private boolean attemptedToParse;
// The parsed parameter values, mapped by parameter name.
// This also includes default values. All parameters from the parameter list are present if parsing was successful.
private Map<String, Object> parameterValueMap;
private Map<String, Object> parameterValueMap = new HashMap<>();
// this set contains the names of the parameters that were present in the command, and not given a default value.
private Set<String> parsedParameters;
private Set<String> parsedParameters = new HashSet<>();
// if this flag is set, this execution is only for completion purposes.
private boolean tabComplete;
// these fields store information required to provide completions.
// the parameter to complete is the parameter that threw an exception when it was parsing.
// the exception's message was discarded because it is a completion.
@@ -48,37 +53,11 @@ public class ExecutionContext {
// if this flag is set, any messages sent through the sendMessage methods are discarded.
private boolean muted;
public ExecutionContext(CommandSender sender, boolean tabComplete) {
public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) {
this.sender = Objects.requireNonNull(sender);
this.buffer = Objects.requireNonNull(buffer);
this.muted = tabComplete;
this.tabComplete = tabComplete;
}
/**
* Construct an execution context that is ready to parse the parameter values.
*
* @param sender the sender
* @param address the address
* @param buffer the arguments
* @param tabComplete true if this execution is a tab-completion
*/
public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) {
this(sender, tabComplete);
targetAcquired(address, command, buffer);
}
void requireAddressPresent(boolean present) {
//noinspection DoubleNegation
if ((address != null) != present) {
throw new IllegalStateException();
}
}
void targetAcquired(ICommandAddress address, Command command, ArgumentBuffer buffer) {
requireAddressPresent(false);
this.address = Objects.requireNonNull(address);
this.command = Objects.requireNonNull(command);
// If its tab completing, keep the empty element that might be at the end of the buffer
// due to a space at the end of the command.
@@ -86,65 +65,21 @@ public class ExecutionContext {
if (!tabComplete) {
buffer.dropTrailingEmptyElements();
}
this.originalBuffer = buffer;
this.processedBuffer = buffer.preprocessArguments(getParameterList().getArgumentPreProcessor());
this.cursorStart = buffer.getCursor();
}
/**
* Parse the parameters. If no exception is thrown, they were parsed successfully, and the command may continue post-parameter execution.
* Construct an execution context that is ready to parse the parameter values.
*
* @throws CommandException if an error occurs while parsing the parameters.
* @param sender the sender
* @param address the address
* @param command the command
* @param buffer the arguments
* @param tabComplete true if this execution is a tab-completion
*/
synchronized void parseParameters() throws CommandException {
requireAddressPresent(true);
if (attemptedToParse) {
throw new IllegalStateException();
}
attemptedToParse = true;
ContextParser parser = new ContextParser(this);
parameterValueMap = parser.getValueMap();
parsedParameters = parser.getParsedKeys();
parser.parse();
}
/**
* Attempts to parse parameters, without throwing an exception or sending any message.
* This method is typically used by tab completions.
* After calling this method, the context is ready to provide completions.
*/
synchronized void parseParametersQuietly() {
requireAddressPresent(true);
if (attemptedToParse) {
throw new IllegalStateException();
}
attemptedToParse = true;
boolean before = muted;
muted = true;
try {
ContextParser parser = new ContextParser(this);
parameterValueMap = parser.getValueMap();
parsedParameters = parser.getParsedKeys();
parser.parse();
parameterToComplete = parser.getCompletionTarget();
parameterToCompleteCursor = parser.getCompletionCursor();
} catch (CommandException ignored) {
} finally {
muted = before;
}
public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) {
this(sender, buffer, tabComplete);
setAddress(address);
setCommand(command);
}
/**
@@ -156,6 +91,13 @@ public class ExecutionContext {
return sender;
}
/**
* @return the buffer of arguments
*/
public ArgumentBuffer getBuffer() {
return buffer;
}
/**
* Command's address
*
@@ -165,6 +107,15 @@ public class ExecutionContext {
return address;
}
/**
* Set the address
*
* @param address the new address
*/
public void setAddress(ICommandAddress address) {
this.address = address;
}
/**
* The command
*
@@ -174,13 +125,60 @@ public class ExecutionContext {
return command;
}
/**
* Set the command
*
* @param command the new command
*/
public void setCommand(Command command) {
this.command = command;
}
/**
* @return true if this context is for a tab completion.
*/
public boolean isTabComplete() {
return tabComplete;
}
/**
* @return true if this context is muted.
*/
public boolean isMuted() {
return muted;
}
/**
* Parse parameters from the given parameter list,
* adding their values to the cache of this context.
*
* @param parameterList the parameterList
* @throws CommandException if the arguments are not valid
*/
public void parse(ParameterList parameterList) throws CommandException {
cursorStart = buffer.getCursor();
ContextParser parser = new ContextParser(this, parameterList, parameterValueMap, parsedParameters);
try {
parser.parse();
} finally {
if (tabComplete) {
parameterToComplete = parser.getCompletionTarget();
parameterToCompleteCursor = parser.getCompletionCursor();
}
}
}
/**
* The command's parameter definition.
*
* @return the parameter list
*/
@Deprecated
public ParameterList getParameterList() {
return command.getParameterList();
return null;//command.getParameterList();
}
/**
@@ -188,8 +186,9 @@ public class ExecutionContext {
*
* @return the original buffer
*/
@Deprecated
public ArgumentBuffer getOriginalBuffer() {
return originalBuffer;
return buffer;
}
/**
@@ -197,8 +196,9 @@ public class ExecutionContext {
*
* @return the argument buffer
*/
@Deprecated
public ArgumentBuffer getProcessedBuffer() {
return processedBuffer;
return buffer;
}
/**
@@ -216,7 +216,7 @@ public class ExecutionContext {
* @return original arguments.
*/
public String[] getOriginal() {
return originalBuffer.getArrayFromIndex(cursorStart);
return buffer.getArrayFromIndex(cursorStart);
}
/**
@@ -225,7 +225,7 @@ public class ExecutionContext {
* @return the path used to access this address.
*/
public String[] getRoute() {
return Arrays.copyOf(originalBuffer.toArray(), address.getDepth());
return Arrays.copyOf(buffer.toArray(), address.getDepth());
}
public Formatting getFormat(EMessageType type) {
@@ -238,9 +238,16 @@ public class ExecutionContext {
* @return the full command
*/
public String getRawInput() {
return originalBuffer.getRawInput();
return buffer.getRawInput();
}
/**
* Get the value of the parameter with the given name
*
* @param name the parameter's name
* @param <T> expected type
* @return the parsed value or the default value
*/
@SuppressWarnings("unchecked")
public <T> T get(String name) {
if (!parameterValueMap.containsKey(name)) {
@@ -254,15 +261,23 @@ public class ExecutionContext {
}
}
@SuppressWarnings("unchecked")
public <T> T get(int index) {
return get(getParameterList().getIndexedParameterName(index));
}
/**
* Get the value of the flag with the given name
*
* @param flag the flag's name, without preceding "-"
* @param <T> expected type
* @return the parsed value or the default value
*/
public <T> T getFlag(String flag) {
return get("-" + flag);
}
@SuppressWarnings("unchecked")
@Deprecated
public <T> T get(int index) {
return null;//get(getParameterList().getIndexedParameterName(index));
}
/**
* Checks if the parameter by the name was provided in the command's arguments.
*
@@ -279,8 +294,9 @@ public class ExecutionContext {
* @param index the parameter index
* @return true if it was provided
*/
@Deprecated
public boolean isProvided(int index) {
return isProvided(getParameterList().getIndexedParameterName(index));
return false;//isProvided(getParameterList().getIndexedParameterName(index));
}
/**
@@ -293,20 +309,6 @@ public class ExecutionContext {
return parameterToComplete;
}
/**
* @return true if this context is muted.
*/
public boolean isMuted() {
return muted;
}
/**
* @return true if this context is for a tab completion.
*/
public boolean isTabComplete() {
return tabComplete;
}
/**
* Get suggested completions.
*
@@ -315,19 +317,22 @@ public class ExecutionContext {
*/
public List<String> getSuggestedCompletions(Location location) {
if (parameterToComplete != null) {
return parameterToComplete.complete(this, location, processedBuffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor));
return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor));
}
ParameterList parameterList = getParameterList();
List<String> result = new ArrayList<>();
for (String name : parameterValueMap.keySet()) {
if (parameterList.getParameterByName(name).isFlag() && !parsedParameters.contains(name)) {
if (name.startsWith("-") && !parsedParameters.contains(name)) {
result.add(name);
}
}
return result;
}
/*
Chat handling
*/
public void sendMessage(String message) {
sendMessage(true, message);
}

View File

@@ -55,9 +55,10 @@ public abstract class ExtendedCommand<T extends ExtendedCommand<T>> extends Comm
return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription);
}
/*
@Override
public T preprocessArguments(IArgumentPreProcessor processor) {
return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor);
}
}*/
}

View File

@@ -2,6 +2,7 @@ package io.dico.dicore.command;
import io.dico.dicore.command.chat.IChatHandler;
import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.ParameterList;
import io.dico.dicore.command.predef.PredefinedCommand;
import org.bukkit.command.CommandSender;
@@ -131,6 +132,16 @@ public interface ICommandAddress {
* Get an unmodifiable view of the children of this address.
* Values might be duplicated for aliases.
*
* <p>
* To iterate children without duplicates, you can do something like this:
* <pre>{@code
* for (String key : address.getChildrenMainKeys()) {
* ICommandAddress child = address.getChild(key);
* // do stuff with child
* }
* }</pre>
* </p>
*
* @return the children of this address.
*/
Map<String, ? extends ICommandAddress> getChildren();
@@ -144,14 +155,19 @@ public interface ICommandAddress {
ICommandAddress getChild(String key);
/**
* Query for a child at the given key, with the given context for reference.
* Can be used to override behaviour of the tree.
* Query for a child using the given buffer, with the given context for reference.
* Can be used to override behaviour of the address tree.
* <p>
* The default implementation is as follows:
* <pre>{@code
* return buffer.hasNext() ? getChild(buffer.next()) : null;
* }</pre>
*
* @param key the key. The name or alias of a command.
* @param context context of a command being executed
* @param buffer the buffer. The name or alias of a command.
* @return the child, or null if it's not found, altered freely by the implementation
*/
ICommandAddress getChild(String key, ExecutionContext context) throws CommandException;
ICommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
/**
* Get the command dispatcher for this tree
@@ -165,6 +181,15 @@ public interface ICommandAddress {
*/
IChatHandler getChatHandler();
/**
* Returns if the command attached to this address should be treated as trailing.
* A trailing command is executed whenever the address is scanned for children.
* Its parameters are parsed and added to the context.
*
* @return true if the command attached to this address should be treated as trailing.
*/
boolean isCommandTrailing();
static ICommandAddress newChild() {
return new ChildCommandAddress();
}

View File

@@ -1,22 +0,0 @@
package io.dico.dicore.command;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.Method;
public interface ICommandReceiver {
interface Factory {
ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName);
Plugin getPlugin();
// type is CoroutineContext, but we avoid referring to Kotlin runtime here
default Object getCoroutineContext() {
return null;
}
}
}

View File

@@ -2,6 +2,8 @@ package io.dico.dicore.command;
import io.dico.dicore.command.chat.ChatHandlers;
import io.dico.dicore.command.chat.IChatHandler;
import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.ParameterList;
import io.dico.dicore.command.predef.DefaultGroupCommand;
import io.dico.dicore.command.predef.HelpCommand;
import io.dico.dicore.command.predef.PredefinedCommand;
@@ -36,7 +38,7 @@ public abstract class ModifiableCommandAddress implements ICommandAddress {
@Override
public boolean hasUserDeclaredCommand() {
Command command = getCommand();
return command != null && !(command instanceof PredefinedCommand) && !(command instanceof DefaultGroupCommand);
return command != null && !(command instanceof PredefinedCommand);
}
@Override
@@ -139,8 +141,8 @@ public abstract class ModifiableCommandAddress implements ICommandAddress {
}
@Override
public ChildCommandAddress getChild(String key, ExecutionContext context) throws CommandException {
return getChild(key);
public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
return buffer.hasNext() ? getChild(buffer.next()) : null;
}
public void addChild(ICommandAddress child) {
@@ -264,6 +266,15 @@ public abstract class ModifiableCommandAddress implements ICommandAddress {
return getRoot();
}
@Override
public boolean isCommandTrailing() {
return false;
}
public void setCommandTrailing(boolean trailing) {
throw new UnsupportedOperationException();
}
void appendDebugInformation(StringBuilder target, String linePrefix, Set<ICommandAddress> seen) {
target.append('\n').append(linePrefix);
if (!seen.add(this)) {

View File

@@ -1,5 +1,6 @@
package io.dico.dicore.command;
import java.util.List;
import java.util.Objects;
public class PermissionContextFilter implements IContextFilter {
@@ -106,4 +107,30 @@ public class PermissionContextFilter implements IContextFilter {
return failMessage;
}
/*
private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
val command = address.command ?: return result
var inherited = false
for (filter in command.contextFilters) {
when (filter) {
is PermissionContextFilter -> {
if (path.isEmpty()) result.add(filter.permission)
else if (filter.isInheritable) result.add(filter.getInheritedPermission(path))
}
is InheritingContextFilter -> {
if (filter.priority == PERMISSION && address.hasParent() && !inherited) {
inherited = true
getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result)
}
}
}
}
return result
}
*/
}

View File

@@ -85,7 +85,8 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
}
}
private static void registerMember(Map<String, org.bukkit.command.Command> map, String key, org.bukkit.command.Command value, boolean override) {
private static void registerMember(Map<String, org.bukkit.command.Command> map,
String key, org.bukkit.command.Command value, boolean override) {
if (override) {
map.put(key, value);
} else {
@@ -147,15 +148,25 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
ModifiableCommandAddress cur = this;
ChildCommandAddress child;
while (buffer.hasNext()) {
child = cur.getChild(buffer.next(), context);
int cursor = buffer.getCursor();
child = cur.getChild(context, buffer);
if (child == null
|| (context.isTabComplete() && !buffer.hasNext())
|| (child.hasCommand() && !child.getCommand().isVisibleTo(sender))
|| (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) {
buffer.rewind();
buffer.setCursor(cursor);
break;
}
cur = child;
context.setAddress(child);
if (child.hasCommand() && child.isCommandTrailing()) {
child.getCommand().initializeAndFilterContext(context);
child.getCommand().execute(context.getSender(), context);
}
}
return cur;
@@ -173,7 +184,7 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
@Override
public boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer) {
ExecutionContext context = new ExecutionContext(sender, false);
ExecutionContext context = new ExecutionContext(sender, buffer, false);
ModifiableCommandAddress targetAddress = null;
@@ -189,8 +200,15 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
}
}
context.targetAcquired(targetAddress, target, buffer);
target.executeWithContext(context);
context.setCommand(target);
if (!targetAddress.isCommandTrailing()) {
target.initializeAndFilterContext(context);
String message = target.execute(sender, context);
if (message != null && !message.isEmpty()) {
context.sendMessage(EMessageType.RESULT, message);
}
}
} catch (Throwable t) {
if (targetAddress == null) {
@@ -214,15 +232,16 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
@Override
public List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) {
ExecutionContext context = new ExecutionContext(sender, true);
ExecutionContext context = new ExecutionContext(sender, buffer, true);
try {
ICommandAddress target = getCommandTarget(context, buffer);
List<String> out;
if (target.hasCommand()) {
context.targetAcquired(target, target.getCommand(), buffer);
out = target.getCommand().tabCompleteWithContext(context, location);
context.setCommand(target.getCommand());
target.getCommand().initializeAndFilterContext(context);
out = target.getCommand().tabComplete(sender, context, location);
} else {
out = Collections.emptyList();
}

View File

@@ -7,6 +7,7 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Deprecated
public @interface PreprocessArgs {
String tokens() default "\"\"";

View File

@@ -13,7 +13,8 @@ import java.util.ListIterator;
public class DefaultPageBuilder implements IPageBuilder {
@Override
public String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen) {
public String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target,
Permissible viewer, ExecutionContext context, int pageNum, int pageLen) {
if (pageLen <= 0 || pageNum < 0) {
throw new IllegalArgumentException();
}

View File

@@ -19,7 +19,7 @@ public class HelpComponentInserter extends HelpTopicModifier {
@Override
protected List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context) {
int componentCount = components.size();
// int componentCount = components.size();
for (int i = insertions.size() - 1; i >= 0; i--) {
IInsertion insertion = insertions.get(i);

View File

@@ -1,6 +1,8 @@
package io.dico.dicore.command.parameter;
import io.dico.dicore.command.CommandException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -23,7 +25,7 @@ public class ArgumentBuffer extends AbstractList<String> implements Iterator<Str
// // drop the last element of args if it is empty
// result = args;
//} else {
result = new String[args.length + 1];
result = new String[args.length + 1];
//}
System.arraycopy(args, 0, result, 1, result.length - 1);
result[0] = Objects.requireNonNull(label);
@@ -35,16 +37,21 @@ public class ArgumentBuffer extends AbstractList<String> implements Iterator<Str
* None of the array its elements should be empty.
*
* @param array the array
* @throws NullPointerException if the array or any of its elements are null
*/
public ArgumentBuffer(String[] array) {
this.array = Objects.requireNonNull(array);
for (String elem : array) {
if (elem == null) throw new NullPointerException("ArgumentBuffer array element");
}
this.array = array;
}
public int getCursor() {
return cursor;
}
public ArgumentBuffer setCursor(int cursor) {
public @NotNull ArgumentBuffer setCursor(int cursor) {
if (cursor <= 0) {
cursor = 0;
} else if (size() <= cursor) {
@@ -60,7 +67,7 @@ public class ArgumentBuffer extends AbstractList<String> implements Iterator<Str
}
@Override
public String get(int index) {
public @NotNull String get(int index) {
return array[index];
}
@@ -91,11 +98,11 @@ public class ArgumentBuffer extends AbstractList<String> implements Iterator<Str
* @return the next value, or null
*/
@Override
public String next() {
public @Nullable String next() {
return hasNext() ? get(cursor++) : null;
}
public String requireNext(String parameterName) throws CommandException {
public @NotNull String requireNext(String parameterName) throws CommandException {
String next = next();
if (next == null) {
throw CommandException.missingArgument(parameterName);
@@ -104,7 +111,7 @@ public class ArgumentBuffer extends AbstractList<String> implements Iterator<Str
}
// useful for completion code
public String nextOrEmpty() {
public @NotNull String nextOrEmpty() {
return hasNext() ? get(cursor++) : "";
}
@@ -113,62 +120,62 @@ public class ArgumentBuffer extends AbstractList<String> implements Iterator<Str
*
* @return the previous value, or null
*/
public String previous() {
public @Nullable String previous() {
return hasPrevious() ? get(--cursor) : null;
}
public String peekNext() {
public @Nullable String peekNext() {
return hasNext() ? get(cursor) : null;
}
public String peekPrevious() {
public @Nullable String peekPrevious() {
return hasPrevious() ? get(cursor - 1) : null;
}
public ArgumentBuffer advance() {
public @NotNull ArgumentBuffer advance() {
return advance(1);
}
public ArgumentBuffer advance(int amount) {
public @NotNull ArgumentBuffer advance(int amount) {
cursor = Math.min(Math.max(0, cursor + amount), size());
return this;
}
public ArgumentBuffer rewind() {
public @NotNull ArgumentBuffer rewind() {
return rewind(1);
}
public ArgumentBuffer rewind(int amount) {
public @NotNull ArgumentBuffer rewind(int amount) {
return advance(-amount);
}
String[] getArray() {
@NotNull String[] getArray() {
return array;
}
public String[] getArrayFromCursor() {
public @NotNull String[] getArrayFromCursor() {
return getArrayFromIndex(cursor);
}
public String[] getArrayFromIndex(int index) {
public @NotNull String[] getArrayFromIndex(int index) {
return Arrays.copyOfRange(array, index, array.length);
}
public String getRawInput() {
public @NotNull String getRawInput() {
return String.join(" ", array);
}
public String[] toArray() {
public @NotNull String[] toArray() {
return array.clone();
}
@Override
public Iterator<String> iterator() {
public @NotNull Iterator<String> iterator() {
return this;
}
@Override
public ListIterator<String> listIterator() {
public @NotNull ListIterator<String> listIterator() {
return new ListIterator<String>() {
@Override
public boolean hasNext() {
@@ -243,13 +250,14 @@ public class ArgumentBuffer extends AbstractList<String> implements Iterator<Str
}
}
public ArgumentBuffer preprocessArguments(IArgumentPreProcessor preProcessor) {
String[] array = this.array;
// processor shouldn't touch any items prior to the cursor
if (array != (array = preProcessor.process(cursor, array))) {
return new ArgumentBuffer(array).setCursor(cursor);
}
return this;
/**
* Preprocess this argument buffer with the given preprocessor
*
* @param preProcessor preprocessor
* @return a new ArgumentBuffer with processed contents. Might be this buffer if nothing changed.
*/
public @NotNull ArgumentBuffer preprocessArguments(IArgumentPreProcessor preProcessor) {
return preProcessor.process(this, -1);
}
/**
@@ -273,10 +281,15 @@ public class ArgumentBuffer extends AbstractList<String> implements Iterator<Str
}
@SuppressWarnings("MethodDoesntCallSuperMethod")
public ArgumentBuffer clone() {
public @NotNull ArgumentBuffer clone() {
ArgumentBuffer result = getUnaffectingCopy();
this.unaffectingCopy = null;
return result;
}
@Override
public String toString() {
return String.format("ArgumentBuffer(size = %d, cursor = %d)", size(), getCursor());
}
}

View File

@@ -11,39 +11,44 @@ public class ArgumentMergingPreProcessor implements IArgumentPreProcessor {
}
@Override
public String[] process(int argStart, String[] args) {
if (!(0 <= argStart && argStart <= args.length)) {
throw new IndexOutOfBoundsException();
}
Parser parser = new Parser(argStart, args.clone());
return parser.doProcess();
public ArgumentBuffer process(ArgumentBuffer buffer, int count) {
Parser parser = new Parser(buffer.getArray().clone(), buffer.getCursor(), count);
String[] array = parser.doProcess();
ArgumentBuffer result = new ArgumentBuffer(array);
parser.updateBuffer(result);
return result;
}
private class Parser {
private final int argStart;
private final String[] args;
private final int start;
private final int count;
private int foundSectionCount;
private int currentIndex;
private int sectionStart;
private char closingToken;
private int sectionEnd;
private int removeCount;
Parser(int argStart, String[] args) {
this.argStart = argStart;
Parser(String[] args, int start, int count) {
this.start = start;
this.args = args;
this.count = count;
}
private void reset() {
removeCount = 0;
closingToken = 0;
foundSectionCount = 0;
currentIndex = start;
sectionStart = -1;
closingToken = 0;
sectionEnd = -1;
currentIndex = argStart;
removeCount = 0;
}
private boolean findNextSectionStart() {
if (count >= 0 && foundSectionCount >= count) return false;
while (currentIndex < args.length) {
String arg = args[currentIndex];
if (arg == null) {
@@ -127,9 +132,11 @@ public class ArgumentMergingPreProcessor implements IArgumentPreProcessor {
sectionStart = -1;
sectionEnd = -1;
++foundSectionCount;
}
public String[] doProcess() {
String[] doProcess() {
reset();
while (findNextSectionStart()) {
@@ -155,6 +162,14 @@ public class ArgumentMergingPreProcessor implements IArgumentPreProcessor {
return result;
}
void updateBuffer(ArgumentBuffer buffer) {
if (count < 0) {
buffer.setCursor(start);
} else {
buffer.setCursor(currentIndex);
}
}
}
}

View File

@@ -13,21 +13,27 @@ public class ContextParser {
private final Parameter<?, ?> m_repeatedParam;
private final List<Parameter<?, ?>> m_indexedParams;
private final int m_maxIndex;
private final int m_requiredIndex;
private final int m_maxRequiredIndex;
private Map<String, Object> m_valueMap = new HashMap<>();
private Set<String> m_parsedKeys = new HashSet<>();
private Map<String, Object> m_valueMap;
private Set<String> m_parsedKeys;
private int m_completionCursor = -1;
private Parameter<?, ?> m_completionTarget = null;
public ContextParser(ExecutionContext context) {
this.m_context = context;
this.m_buffer = context.getProcessedBuffer();
this.m_paramList = context.getParameterList();
this.m_repeatedParam = m_paramList.getRepeatedParameter();
this.m_indexedParams = m_paramList.getIndexedParameters();
this.m_maxIndex = m_indexedParams.size() - 1;
this.m_requiredIndex = m_paramList.getRequiredCount() - 1;
public ContextParser(ExecutionContext context,
ParameterList parameterList,
Map<String, Object> valueMap,
Set<String> keySet) {
m_context = context;
m_paramList = parameterList;
m_valueMap = valueMap;
m_parsedKeys = keySet;
m_buffer = context.getBuffer();
m_repeatedParam = m_paramList.getRepeatedParameter();
m_indexedParams = m_paramList.getIndexedParameters();
m_maxIndex = m_indexedParams.size() - 1;
m_maxRequiredIndex = m_paramList.getRequiredCount() - 1;
}
public ExecutionContext getContext() {
@@ -102,7 +108,7 @@ public class ContextParser {
m_curParamIndex++;
m_curParam = m_indexedParams.get(m_curParamIndex);
prepareRepeatedParameterIfSet();
requireInput = m_curParamIndex <= m_requiredIndex;
requireInput = m_curParamIndex <= m_maxRequiredIndex;
} else if (m_buffer.hasNext()) {
throw new CommandException("Too many arguments for /" + m_context.getAddress().getAddress());
@@ -146,7 +152,7 @@ public class ContextParser {
private void prepareRepeatedParameterIfSet() throws CommandException {
if (m_curParam != null && m_curParam == m_repeatedParam) {
if (m_curParam.isFlag() && m_curParamIndex < m_requiredIndex) {
if (m_curParam.isFlag() && m_curParamIndex < m_maxRequiredIndex) {
Parameter<?, ?> requiredParam = m_indexedParams.get(m_curParamIndex + 1);
reportParameterRequired(requiredParam);
}

View File

@@ -3,19 +3,26 @@ package io.dico.dicore.command.parameter;
/**
* An interface to process tokens such as quotes
*/
@Deprecated
public interface IArgumentPreProcessor {
/**
* Preprocess the arguments without modifying the array.
* Might return the same array (in which case no changes were made).
* Preprocess the arguments contained within the given ArgumentBuffer.
* If no changes are made, this might return the same buffer.
* Any arguments preceding {@code buffer.getCursor()} will not be affected.
*
* @param argStart the index within the array where the given arguments start (the part before that identifies the command)
* @param args the arguments
* <p>
* If {@code count} is non-negative, it declares a limit on the number of arguments after preprocessing.
* In that case, the buffer's cursor is set to the index of the first argument following processed arguments.
* </p>
*
* @param buffer the argument buffer
* @param count the maximum number of (processed) arguments
* @return the arguments after preprocessing
*/
String[] process(int argStart, String[] args);
ArgumentBuffer process(ArgumentBuffer buffer, int count);
IArgumentPreProcessor NONE = (argStart, args) -> args;
IArgumentPreProcessor NONE = (buffer, count) -> buffer;
/**
* Get an IArgumentPreProcessor that merges arguments between any two tokens
@@ -31,3 +38,4 @@ public interface IArgumentPreProcessor {
}
}

View File

@@ -5,10 +5,12 @@ import java.util.*;
/**
* IParameter definition for a command
*/
@SuppressWarnings("UnusedReturnValue")
public class ParameterList {
//private ParameterList parent;
private List<Parameter<?, ?>> indexedParameters;
private Map<String, Parameter<?, ?>> byName;
private IArgumentPreProcessor argumentPreProcessor = IArgumentPreProcessor.NONE;
//private IArgumentPreProcessor argumentPreProcessor = IArgumentPreProcessor.NONE;
private int requiredCount = -1;
private boolean repeatFinalParameter;
@@ -18,12 +20,22 @@ public class ParameterList {
// parameter is taken for repeating
private boolean finalParameterMayBeFlag;
/*
public ParameterList(ParameterList parent) {
this();
if (parent.repeatFinalParameter) {
throw new IllegalArgumentException("Parent may not have repeating parameters");
}
this.parent = parent;
}*/
public ParameterList() {
this.indexedParameters = new ArrayList<>();
this.byName = new LinkedHashMap<>();
this.repeatFinalParameter = false;
}
/*
public IArgumentPreProcessor getArgumentPreProcessor() {
return argumentPreProcessor;
}
@@ -31,7 +43,7 @@ public class ParameterList {
public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) {
this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor;
return this;
}
}*/
public boolean repeatFinalParameter() {
return repeatFinalParameter;

View File

@@ -20,7 +20,8 @@ public class EnumParameterType<E extends Enum> extends SimpleParameterType<E, Vo
}
}
@Override protected E parse(Parameter<E, Void> parameter, CommandSender sender, String input) throws CommandException {
@Override
protected E parse(Parameter<E, Void> parameter, CommandSender sender, String input) throws CommandException {
for (E constant : universe) {
if (constant.name().equalsIgnoreCase(input)) {
return constant;
@@ -30,7 +31,8 @@ public class EnumParameterType<E extends Enum> extends SimpleParameterType<E, Vo
throw CommandException.invalidArgument(parameter.getName(), "the enum value does not exist");
}
@Override public List<String> complete(Parameter<E, Void> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
@Override
public List<String> complete(Parameter<E, Void> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
String input = buffer.next().toUpperCase();
List<String> result = new ArrayList<>();
for (E constant : universe) {

View File

@@ -146,56 +146,4 @@ public abstract class ParameterType<TReturn, TParamInfo> {
}
public @interface Reference {
/**
* The path to the static field holding the parameter type referred.
*
* @return The path
*/
String value();
}
public static class ReferenceUtil {
private ReferenceUtil() {
}
/**
* Get the ParameterType with the associated Reference
*
* @param ref the reference
* @return the parameter type object
* @throws IllegalArgumentException if the class is found, but the field doesn't exist.
* @throws IllegalStateException if this method fails to find the object for any other reason
*/
public static Object getReference(Reference ref) {
String[] path = ref.value().split("\\.");
if (path.length < 2) {
throw new IllegalStateException();
}
String fieldName = path[path.length - 1];
String className = String.join(".", Arrays.copyOfRange(path, 0, path.length - 1));
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(ex);
}
Object result = Reflection.getStaticFieldValue(clazz, fieldName);
if (result == null) {
throw new IllegalStateException();
}
return result;
}
}
}

View File

@@ -38,9 +38,9 @@ public class DefaultGroupCommand extends PredefinedCommand<DefaultGroupCommand>
noArgumentFilter = new IContextFilter() {
@Override
public void filterContext(ExecutionContext context) throws CommandException {
if (context.getProcessedBuffer().hasNext()) {
if (context.getBuffer().hasNext()) {
throw new CommandException("No such command: /" + context.getAddress().getAddress()
+ " " + context.getProcessedBuffer().next());
+ " " + context.getBuffer().next());
}
}

View File

@@ -0,0 +1,36 @@
package io.dico.dicore.command.registration.reflect;
import io.dico.dicore.command.ExecutionContext;
import java.lang.reflect.Method;
public interface ICommandInterceptor {
/**
* Get the receiver of the command, if applicable.
* A command has a receiver if its first parameter implements {@link ICommandReceiver}
* and its instance object implements this interface.
*
* @param context the context of execution
* @param target the method of the command
* @param cmdName the name of the command
* @return the receiver
*/
default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) {
return null;
}
/**
* If applicable, get the coroutine context to use in suspend functions (Kotlin only).
* The return type is object to avoid depending on the kotlin runtime.
*
* @param context the context of execution
* @param target the method of the command
* @param cmdName the name of the command
* @return the coroutine context
*/
default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) {
return null;
}
}

View File

@@ -0,0 +1,5 @@
package io.dico.dicore.command.registration.reflect;
public interface ICommandReceiver {
}

View File

@@ -15,6 +15,8 @@ public final class ReflectiveCommand extends Command {
private final Method method;
private final Object instance;
private String[] parameterOrder;
// hasContinuation | hasContext | hasSender | hasReceiver
private final int flags;
ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException {
@@ -56,6 +58,8 @@ public final class ReflectiveCommand extends Command {
return instance;
}
public String getCmdName() { return cmdAnnotation.value(); }
void setParameterOrder(String[] parameterOrder) {
this.parameterOrder = parameterOrder;
}
@@ -86,18 +90,24 @@ public final class ReflectiveCommand extends Command {
Object[] args = new Object[parameterOrder.length + start];
int i = 0;
if ((flags & 1) != 0) {
int mask = 1;
if ((flags & mask) != 0) {
try {
args[i++] = ((ICommandReceiver.Factory) instance).getReceiver(context, method, cmdAnnotation.value());
args[i++] = ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName());
} catch (Exception ex) {
handleException(ex);
return null; // unreachable
}
}
if ((flags & 2) != 0) {
mask <<= 1;
if ((flags & mask) != 0) {
args[i++] = sender;
}
if ((flags & 4) != 0) {
mask <<= 1;
if ((flags & mask) != 0) {
args[i++] = context;
}
@@ -105,11 +115,12 @@ public final class ReflectiveCommand extends Command {
args[i] = context.get(parameterOrder[i - start]);
}
if (!isSuspendFunction()) {
return callSynchronously(args);
mask <<= 1;
if ((flags & mask) != 0) {
return callAsCoroutine(context, args);
}
return callAsCoroutine(context, args);
return callSynchronously(args);
}
private boolean isSuspendFunction() {
@@ -137,9 +148,6 @@ public final class ReflectiveCommand extends Command {
if (returned instanceof String) {
return (String) returned;
}
if (returned instanceof CommandResult) {
return ((CommandResult) returned).getMessage();
}
return null;
}
@@ -160,7 +168,7 @@ public final class ReflectiveCommand extends Command {
}
private String callAsCoroutine(ExecutionContext context, Object[] args) {
return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandReceiver.Factory) instance, context, args);
return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandInterceptor) instance, context, args);
}
}

View File

@@ -3,7 +3,6 @@ package io.dico.dicore.command.registration.reflect;
import io.dico.dicore.command.*;
import io.dico.dicore.command.annotation.*;
import io.dico.dicore.command.annotation.GroupMatchedCommands.GroupEntry;
import io.dico.dicore.command.parameter.IArgumentPreProcessor;
import io.dico.dicore.command.parameter.Parameter;
import io.dico.dicore.command.parameter.ParameterList;
import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
@@ -199,15 +198,20 @@ public class ReflectiveRegistration {
static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException {
ParameterList list = command.getParameterList();
boolean hasReceiverParameter = false;
boolean hasSenderParameter = false;
boolean hasContextParameter = false;
boolean hasContinuationParameter = false;
int start = 0;
Class<?> firstParameterType = null;
int end = parameters.length;
Class<?> senderParameterType = null;
if (parameters.length > start
&& command.getInstance() instanceof ICommandReceiver.Factory
&& ICommandReceiver.class.isAssignableFrom(firstParameterType = parameters[start].getType())) {
&& command.getInstance() instanceof ICommandInterceptor
&& ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) {
hasReceiverParameter = true;
start++;
}
@@ -217,20 +221,18 @@ public class ReflectiveRegistration {
start++;
}
boolean hasContextParameter = false;
if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) {
hasContextParameter = true;
start++;
}
if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) {
hasContinuationParameter = true;
end--;
}
String[] parameterNames = lookupParameterNames(method, parameters, start);
for (int i = start, n = parameters.length; i < n; i++) {
if (parameters[i].getType().getName().equals("kotlin.coroutines.Continuation")) {
List<String> temp = new ArrayList<>(Arrays.asList(parameterNames));
temp.remove(i - start);
parameterNames = temp.toArray(new String[0]);
continue;
}
for (int i = start, n = end; i < n; i++) {
Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]);
list.addParameter(parameter);
}
@@ -256,11 +258,12 @@ public class ReflectiveRegistration {
list.setRequiredCount(list.getIndexedParameters().size());
}
/*
PreprocessArgs preprocessArgs = method.getAnnotation(PreprocessArgs.class);
if (preprocessArgs != null) {
IArgumentPreProcessor preProcessor = IArgumentPreProcessor.mergeOnTokens(preprocessArgs.tokens(), preprocessArgs.escapeChar());
list.setArgumentPreProcessor(preProcessor);
}
}*/
Desc desc = method.getAnnotation(Desc.class);
if (desc != null) {
@@ -286,7 +289,16 @@ public class ReflectiveRegistration {
list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs());
list.setFinalParameterMayBeFlag(true);
return (hasSenderParameter ? 2 : 0) | (hasContextParameter ? 4 : 0) | (hasReceiverParameter ? 1 : 0);
int flags = 0;
if (hasContinuationParameter) flags |= 1;
flags <<= 1;
if (hasContextParameter) flags |= 1;
flags <<= 1;
if (hasSenderParameter) flags |= 1;
flags <<= 1;
if (hasReceiverParameter) flags |= 1;
return flags;
}
public static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command) throws CommandParseException {
@@ -373,7 +385,7 @@ public class ReflectiveRegistration {
@Cmd({"tp", "tpto"})
@RequirePermissions("teleport.self")
public (static) String|void|CommandResult onCommand(Player sender, Player target, @Flag("force", permission = "teleport.self.force") boolean force) {
public (static) String|void onCommand(Player sender, Player target, @Flag("force", permission = "teleport.self.force") boolean force) {
Validate.isTrue(force || !hasTpToggledOff(target), "Target has teleportation disabled. Use -force to ignore");
sender.teleport(target);
//return

View File

@@ -1,9 +1,6 @@
package io.dico.dicore.command.registration.reflect
import io.dico.dicore.command.CommandException
import io.dico.dicore.command.EMessageType
import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.ICommandReceiver
import io.dico.dicore.command.*
import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.GlobalScope
@@ -22,15 +19,16 @@ fun isSuspendFunction(method: Method): Boolean {
fun callAsCoroutine(
command: ReflectiveCommand,
factory: ICommandReceiver.Factory,
factory: ICommandInterceptor,
context: ExecutionContext,
args: Array<Any?>
): String? {
val coroutineContext = factory.getCoroutineContext(context, command.method, command.cmdName) as CoroutineContext
// UNDISPATCHED causes the handler to run until the first suspension point on the current thread,
// meaning command handlers that don't have suspension points will run completely synchronously.
// Tasks that take time to compute should suspend the coroutine and resume on another thread.
val job = GlobalScope.async(context = factory.coroutineContext as CoroutineContext, start = UNDISPATCHED) {
val job = GlobalScope.async(context = coroutineContext, start = UNDISPATCHED) {
suspendCoroutineUninterceptedOrReturn<Any?> { cont ->
command.method.invoke(command.instance, *args, cont.intercepted())
}
@@ -41,12 +39,12 @@ fun callAsCoroutine(
}
job.invokeOnCompletion {
val cc = context.address.chatHandler
val chatHandler = context.address.chatHandler
try {
val result = job.getResult()
cc.sendMessage(context.sender, EMessageType.RESULT, result)
chatHandler.sendMessage(context.sender, EMessageType.RESULT, result)
} catch (ex: Throwable) {
cc.handleException(context.sender, context, ex)
chatHandler.handleException(context.sender, context, ex)
}
}

View File

@@ -0,0 +1,73 @@
package io.dico.dicore.command.example;
import io.dico.dicore.command.CommandBuilder;
import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.Validate;
import io.dico.dicore.command.annotation.Cmd;
import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.Parameter;
import io.dico.dicore.command.parameter.type.ParameterConfig;
import io.dico.dicore.command.parameter.type.ParameterType;
import org.bukkit.command.CommandSender;
public class ParameterInfoObjectExample {
private @interface ParentPermission {
String value();
}
static class MyInfoObject {
public static final ParameterConfig<ParentPermission, MyInfoObject> config = new ParameterConfig<ParentPermission, MyInfoObject>() {
@Override
protected MyInfoObject toParameterInfo(ParentPermission annotation) {
return new MyInfoObject(annotation.value());
}
};
private String permissionParent;
MyInfoObject(String permissionParent) {
this.permissionParent = permissionParent;
}
public String getPermissionParent() {
return permissionParent;
}
}
static class MyParameterType extends ParameterType<String, MyInfoObject> {
public MyParameterType() {
super(String.class, MyInfoObject.config);
}
@Override
public String parse(Parameter<String, MyInfoObject> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
String value = buffer.next();
MyInfoObject mio = parameter.getParamInfo();
if (mio != null) {
String permission = mio.permissionParent + "." + value;
Validate.isAuthorized(sender, permission);
}
return value;
}
}
static class MyCommands {
@Cmd("test")
Object cmdTest(@ParentPermission("test.permission") String value) {
return "You have permission to use the argument '" + value + "'!";
}
}
static void main(String[] args) {
new CommandBuilder()
.addParameterType(false, new MyParameterType())
.registerCommands(new MyCommands());
}
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
gradlew vendored Normal file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,9 +1,8 @@
package io.dico.parcels2.command
import io.dico.dicore.command.CommandException
import io.dico.dicore.command.EMessageType
import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.ICommandReceiver
import io.dico.dicore.command.*
import io.dico.dicore.command.registration.reflect.ICommandInterceptor
import io.dico.dicore.command.registration.reflect.ICommandReceiver
import io.dico.parcels2.*
import io.dico.parcels2.PlayerProfile.Real
import io.dico.parcels2.PlayerProfile.Unresolved
@@ -13,13 +12,16 @@ import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
import java.lang.reflect.Method
abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandReceiver.Factory {
override fun getPlugin(): Plugin = plugin
abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandInterceptor {
override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver {
return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName)
}
override fun getCoroutineContext(context: ExecutionContext?, target: Method?, cmdName: String?): Any {
return plugin.coroutineContext
}
protected fun checkConnected(action: String) {
if (!plugin.storage.isConnected) err("Parcels cannot $action right now because of a database error")
}
@@ -57,8 +59,6 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
.format(progress * 100, elapsedTime / 1000.0)
)
}
override fun getCoroutineContext() = plugin.coroutineContext
}
fun err(message: String): Nothing = throw CommandException(message)

View File

@@ -1,6 +1,7 @@
package io.dico.parcels2.command
import io.dico.dicore.command.*
import io.dico.dicore.command.parameter.ArgumentBuffer
import io.dico.dicore.command.predef.DefaultGroupCommand
import io.dico.dicore.command.registration.reflect.ReflectiveRegistration
import io.dico.parcels2.Interactables
@@ -107,10 +108,12 @@ class SpecialCommandAddress : ChildCommandAddress() {
}
}
// h:1
@Throws(CommandException::class)
override fun getChild(key: String, context: ExecutionContext): ChildCommandAddress? {
override fun getChild(context: ExecutionContext, buffer: ArgumentBuffer): ChildCommandAddress? {
speciallyParsedIndex = null
val key = buffer.next() ?: return null
for (specialKey in speciallyTreatedKeys) {
if (key.startsWith(specialKey)) {
val result = getChild(specialKey.substring(0, specialKey.length - 1))

View File

@@ -2,7 +2,7 @@ package io.dico.parcels2.command
import io.dico.dicore.command.CommandException
import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.ICommandReceiver
import io.dico.dicore.command.registration.reflect.ICommandReceiver
import io.dico.dicore.command.Validate
import io.dico.parcels2.*
import io.dico.parcels2.Privilege.*

View File

@@ -30,7 +30,7 @@ class ParcelParameterType(val parcelProvider: ParcelProvider) : ParameterType<Pa
val regex = Regex.fromLiteral("((.+)->)?([0-9]+):([0-9]+)")
override fun parse(parameter: Parameter<Parcel, Void>, sender: CommandSender, buffer: ArgumentBuffer): Parcel {
val matchResult = regex.matchEntire(buffer.next())
val matchResult = regex.matchEntire(buffer.next()!!)
?: invalidInput(parameter, "must match (w->)?a:b (/${regex.pattern}/)")
val world = parcelProvider.getTargetWorld(matchResult.groupValues[2], sender, parameter)
@@ -66,7 +66,7 @@ class ProfileParameterType : ParameterType<PlayerProfile, Int>(PlayerProfile::cl
val allowReal = (info and REAL) != 0
val allowFake = (info and FAKE) != 0
val input = buffer.next()
val input = buffer.next()!!
return PlayerProfile.byName(input, allowReal, allowFake)
}

View File

@@ -91,7 +91,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, TargetKind) {
override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget {
var input = buffer.next()
var input = buffer.next()!!
val worldString = input.substringBefore("/", missingDelimiterValue = "")
input = input.substringAfter("/")

View File

@@ -279,7 +279,6 @@ class DefaultParcelGenerator(
val region = getRegion(parcel)
val blocks = parcelTraverser.traverseRegion(region)
val blockCount = region.blockCount.toDouble()
val world = world
val floorHeight = o.floorHeight
val airType = airType

View File

@@ -50,7 +50,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
return
}
//val newlyCreatedWorlds = mutableListOf<ParcelWorld>()
val newlyCreatedWorlds = mutableListOf<ParcelWorld>()
for ((worldName, worldOptions) in options.worlds.entries) {
var parcelWorld = _worlds[worldName]
if (parcelWorld != null) continue
@@ -70,7 +70,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
val time = DateTime.now()
plugin.storage.setWorldCreationTime(parcelWorld.id, time)
parcelWorld.creationTime = time
//newlyCreatedWorlds.add(parcelWorld)
newlyCreatedWorlds.add(parcelWorld)
} else {
GlobalScope.launch(context = Dispatchers.Unconfined) {
parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: DateTime.now()
@@ -80,10 +80,10 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
_worlds[worldName] = parcelWorld
}
loadStoredData()
loadStoredData(newlyCreatedWorlds.toSet())
}
private fun loadStoredData() {
private fun loadStoredData(newlyCreatedWorlds: Collection<ParcelWorld> = emptyList()) {
plugin.launch(Dispatchers.Default) {
val migration = plugin.options.migration
if (migration.enabled) {
@@ -105,9 +105,8 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
val channel = plugin.storage.transmitAllParcelData()
while (true) {
val (id, data) = channel.receiveOrNull() ?: break
if (data == null) continue
val parcel = getParcelById(id) ?: continue
parcel.copyData(data, callerIsDatabase = true)
data?.let { parcel.copyData(it, callerIsDatabase = true) }
}
}

View File

@@ -85,8 +85,6 @@ class ParcelListeners(
newTo = newTo.add(it, delta * 100 * if (it == Dimension.Y) 0.5 else speed)
}
event.to = Location(
toLoc.world,
newTo.x, newTo.y.clampMin(0.0).clampMax(255.0), newTo.z,

View File

@@ -32,7 +32,7 @@ fun getHikariConfig(dialectName: String,
username = dco.username
password = dco.password
connectionTimeout = 15000
leakDetectionThreshold = 30000
leakDetectionThreshold = 10000
connectionTestQuery = "SELECT 1"