diff options
187 files changed, 13981 insertions, 13502 deletions
diff --git a/.editorconfig b/.editorconfig index 32f5e31..169357e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,19 +1,19 @@ -[*] -charset=utf-8 -end_of_line=lf -insert_final_newline=false -indent_style=space -indent_size=4 - -[{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}] -indent_style=space -indent_size=2 - -[{*.ddl,*.sql}] -indent_style=space -indent_size=2 - -[{*.yml,*.yaml}] -indent_style=space -indent_size=2 - +[*]
+charset=utf-8
+end_of_line=lf
+insert_final_newline=false
+indent_style=space
+indent_size=4
+
+[{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}]
+indent_style=space
+indent_size=2
+
+[{*.ddl,*.sql}]
+indent_style=space
+indent_size=2
+
+[{*.yml,*.yaml}]
+indent_style=space
+indent_size=2
+
@@ -6,4 +6,5 @@ build/ /debug/ target/ /gradle-output.txt -/*.java
\ No newline at end of file +/*.java +*.dump
\ No newline at end of file @@ -1,16 +1,14 @@ # Parcels -Plot management and world generator plugin inspired by [PlotMe](https://github.com/WorldCretornica/PlotMe-Core). +Plot management and world generator plugin inspired by [PlotMe](https://github.com/WorldCretornica/PlotMe-Core). +Newer version of discontinued [Parcels](https://github.com/Dico200/Parcels-Java). -Newer version of the discontinued [Parcels-Java](https://github.com/Dico200/Parcels-Java). - -Written in Kotlin. -This project is WIP. +Written in Kotlin. +This project is WIP. ![world screenshot](https://i.imgur.com/tpbKrQI.png) ## Build -1. Add `worldedit-bukkit-7.0.0-beta-01.jar` to `/debug/plugins` directory -2. Run `gradle releaseJar` -3. Kotlin stdlib classpath is placed in `/debug/lib` directory and artifact can be found in `/debug/plugins` directory +1. Run `gradle releaseJar` +1. Kotlin stdlib classpath is placed in `/debug/lib` directory and artifact can be found in `/debug/plugins` directory diff --git a/build.gradle.kts b/build.gradle.kts index 12c2d97..249cbff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,31 +9,32 @@ import java.net.URL val stdout = PrintWriter("gradle-output.txt") group = "io.dico" -version = "0.2" +version = "0.3" plugins { java - kotlin("jvm") version "1.3.0-rc-57" + kotlin("jvm") version "1.3.0" id("com.github.johnrengelman.plugin-shadow") version "2.0.3" } - allprojects { apply<JavaPlugin>() apply(plugin = "idea") repositories { mavenCentral() - maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") - maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots") - maven("https://dl.bintray.com/kotlin/exposed") - maven("https://dl.bintray.com/kotlin/kotlin-eap") + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots/") + maven("https://dl.bintray.com/kotlin/exposed/") + maven("https://dl.bintray.com/kotlin/kotlin-dev/") + maven("https://dl.bintray.com/kotlin/kotlin-eap/") maven("https://dl.bintray.com/kotlin/kotlinx/") + maven("http://maven.sk89q.com/repo") } dependencies { - val spigotVersion = "1.13.1-R0.1-SNAPSHOT" + val spigotVersion = "1.13.2-R0.1-SNAPSHOT" c.provided("org.bukkit:bukkit:$spigotVersion") { isTransitive = false } c.provided("org.spigotmc:spigot-api:$spigotVersion") { isTransitive = false } @@ -52,13 +53,15 @@ project(":dicore3:dicore3-core") { } } +val coroutinesCore = kotlinx("coroutines-core:0.26.1-eap13") + project(":dicore3:dicore3-command") { apply<KotlinPlatformJvmPlugin>() dependencies { c.kotlinStd(kotlin("stdlib-jdk8")) c.kotlinStd(kotlin("reflect")) - c.kotlinStd(kotlinx("coroutines-core:0.26.1-eap13")) + c.kotlinStd(coroutinesCore) compile(project(":dicore3:dicore3-core")) compile("com.thoughtworks.paranamer:paranamer:2.8") @@ -72,12 +75,13 @@ dependencies { c.kotlinStd(kotlin("stdlib-jdk8")) c.kotlinStd(kotlin("reflect")) - c.kotlinStd(kotlinx("coroutines-core:0.26.1-eap13")) - c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.7-eap13") + c.kotlinStd(coroutinesCore) + c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.12") // not on sk89q maven repo yet - compileClasspath(files("$rootDir/debug/plugins/worldedit-bukkit-7.0.0-beta-01.jar")) - compileClasspath(files("$rootDir/debug/lib/spigot-1.13.1.jar")) + //compileClasspath(files("$rootDir/debug/plugins/worldedit-bukkit-7.0.0-beta-01.jar")) + // compileClasspath(files("$rootDir/debug/lib/spigot-1.13.2.jar")) + compileClasspath("com.sk89q.worldedit:worldedit-bukkit:7.0.0-SNAPSHOT") compile("org.jetbrains.exposed:exposed:0.10.5") { isTransitive = false } compile("joda-time:joda-time:2.10") @@ -137,7 +141,7 @@ tasks { // spigot ships a later version in the root, so we must relocate ours relocate("org.yaml.snakeyaml.", "io.dico.parcels2.lib.org.yaml.snakeyaml.") - manifest.attributes["Class-Path"] = "../lib/kotlin-stdlib.jar" + manifest.attributes["Class-Path"] = "../lib/kotlin-stdlib.jar ../lib/mariadb-java-client-2.2.6.jar" dependsOn(kotlinStdlibJar) } @@ -146,11 +150,6 @@ tasks { val jarUrl = URL("https://yivesmirror.com/files/spigot/spigot-latest.jar") val serverJarFile = file("$serverDir/lib/spigot.jar") - - - doFirst { - - } } } @@ -171,5 +170,7 @@ val ConfigurationContainer.`provided`: Configuration val ConfigurationContainer.`kotlinStd`: Configuration get() = findByName("kotlinStd") ?: create("kotlinStd").let { compileClasspath.extendsFrom(it) } -fun Jar.fromFiles(files: Iterable<File>) = +fun Jar.fromFiles(files: Iterable<File>) { afterEvaluate { from(*files.map { if (it.isDirectory) it else zipTree(it) }.toTypedArray()) } +} + diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java index 9a26f61..72bc303 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java @@ -1,120 +1,120 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.predef.DefaultGroupCommand; -import io.dico.dicore.command.predef.HelpCommand; - -import java.util.*; - -public class ChildCommandAddress extends ModifiableCommandAddress { - ModifiableCommandAddress parent; - final List<String> namesModifiable = new ArrayList<>(4); - List<String> names = namesModifiable; - Command command; - boolean isCommandTrailing; - - public ChildCommandAddress() { - } - - public ChildCommandAddress(Command command) { - this.command = command; - } - - public ChildCommandAddress(Command command, String name, String... aliases) { - this(command); - addNameAndAliases(name, aliases); - } - - public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) { - ChildCommandAddress rv = new ChildCommandAddress(); - rv.setupAsPlaceholder(name, aliases); - return rv; - } - - public void setupAsPlaceholder(String name, String... aliases) { - if (!hasCommand()) { - setCommand(DefaultGroupCommand.getInstance()); - } - - addNameAndAliases(name, aliases); - HelpCommand.registerAsChild(this); - } - - @Override - public boolean isRoot() { - return false; - } - - @Override - public ModifiableCommandAddress getParent() { - return parent; - } - - @Override - public Command getCommand() { - return command; - } - - @Override - public void setCommand(Command command) { - if (hasUserDeclaredCommand()) { - throw new IllegalStateException("Command is already set at address \"" + getAddress() + "\""); - } - this.command = command; - } - - @Override - public List<String> getNames() { - return names; - } - - public void addNameAndAliases(String name, String... aliases) { - names.add(name); - names.addAll(Arrays.asList(aliases)); - } - - @Override - public String getMainKey() { - return namesModifiable.isEmpty() ? null : namesModifiable.get(0); - } - - @Override - public String getAddress() { - ICommandAddress address = this; - int depth = getDepth(); - String[] keys = new String[depth]; - for (int i = depth - 1; i >= 0; i--) { - keys[i] = address.getMainKey(); - address = address.getParent(); - } - return String.join(" ", keys); - } - - public void finalizeNames() { - if (names == namesModifiable) { - names = Collections.unmodifiableList(namesModifiable); - } - } - - Iterator<String> modifiableNamesIterator() { - return namesModifiable.iterator(); - } - - void setParent(ModifiableCommandAddress parent) { - finalizeNames(); - 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; - } - -} +package io.dico.dicore.command;
+
+import io.dico.dicore.command.predef.DefaultGroupCommand;
+import io.dico.dicore.command.predef.HelpCommand;
+
+import java.util.*;
+
+public class ChildCommandAddress extends ModifiableCommandAddress {
+ ModifiableCommandAddress parent;
+ final List<String> namesModifiable = new ArrayList<>(4);
+ List<String> names = namesModifiable;
+ Command command;
+ boolean isCommandTrailing;
+
+ public ChildCommandAddress() {
+ }
+
+ public ChildCommandAddress(Command command) {
+ this.command = command;
+ }
+
+ public ChildCommandAddress(Command command, String name, String... aliases) {
+ this(command);
+ addNameAndAliases(name, aliases);
+ }
+
+ public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) {
+ ChildCommandAddress rv = new ChildCommandAddress();
+ rv.setupAsPlaceholder(name, aliases);
+ return rv;
+ }
+
+ public void setupAsPlaceholder(String name, String... aliases) {
+ if (!hasCommand()) {
+ setCommand(DefaultGroupCommand.getInstance());
+ }
+
+ addNameAndAliases(name, aliases);
+ HelpCommand.registerAsChild(this);
+ }
+
+ @Override
+ public boolean isRoot() {
+ return false;
+ }
+
+ @Override
+ public ModifiableCommandAddress getParent() {
+ return parent;
+ }
+
+ @Override
+ public Command getCommand() {
+ return command;
+ }
+
+ @Override
+ public void setCommand(Command command) {
+ if (hasUserDeclaredCommand()) {
+ throw new IllegalStateException("Command is already set at address \"" + getAddress() + "\"");
+ }
+ this.command = command;
+ }
+
+ @Override
+ public List<String> getNames() {
+ return names;
+ }
+
+ public void addNameAndAliases(String name, String... aliases) {
+ names.add(name);
+ names.addAll(Arrays.asList(aliases));
+ }
+
+ @Override
+ public String getMainKey() {
+ return namesModifiable.isEmpty() ? null : namesModifiable.get(0);
+ }
+
+ @Override
+ public String getAddress() {
+ ICommandAddress address = this;
+ int depth = getDepth();
+ String[] keys = new String[depth];
+ for (int i = depth - 1; i >= 0; i--) {
+ keys[i] = address.getMainKey();
+ address = address.getParent();
+ }
+ return String.join(" ", keys);
+ }
+
+ public void finalizeNames() {
+ if (names == namesModifiable) {
+ names = Collections.unmodifiableList(namesModifiable);
+ }
+ }
+
+ Iterator<String> modifiableNamesIterator() {
+ return namesModifiable.iterator();
+ }
+
+ void setParent(ModifiableCommandAddress parent) {
+ finalizeNames();
+ 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;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/Command.java b/dicore3/command/src/main/java/io/dico/dicore/command/Command.java index 894e74e..97f4b78 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/Command.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/Command.java @@ -1,158 +1,158 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.IContextFilter.Priority; -import io.dico.dicore.command.parameter.ArgumentBuffer; -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.ParameterType; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -public abstract class Command { - private static final String[] EMPTY_DESCRIPTION = new String[0]; - private final ParameterList parameterList = new ParameterList(); - private final List<IContextFilter> contextFilters = new ArrayList<>(3); - private String[] description = EMPTY_DESCRIPTION; - private String shortDescription; - - public Command addParameter(Parameter<?, ?> parameter) { - parameterList.addParameter(parameter); - return this; - } - - public <TType> Command addParameter(String name, String description, ParameterType<TType, Void> type) { - return addParameter(new Parameter<>(name, description, type, null, false, null)); - } - - public <TType, TParamInfo> Command addParameter(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) { - return addParameter(new Parameter<>(name, description, type, paramInfo, false, null)); - } - - public <TType> Command addFlag(String name, String description, ParameterType<TType, Void> type) { - return addParameter(new Parameter<>('-' + name, description, type, null, true, null)); - } - - public <TType, TParamInfo> Command addFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) { - return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, null)); - } - - public <TType> Command addAuthorizedFlag(String name, String description, ParameterType<TType, Void> type, String permission) { - return addParameter(new Parameter<>('-' + name, description, type, null, true, permission)); - } - - public <TType, TParamInfo> Command addAuthorizedFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo, String permission) { - return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, permission)); - } - - public Command requiredParameters(int requiredParameters) { - parameterList.setRequiredCount(requiredParameters); - return this; - } - - public Command repeatFinalParameter() { - parameterList.setRepeatFinalParameter(true); - return this; - } - - public Command setDescription(String... description) { - this.description = Objects.requireNonNull(description); - return this; - } - - public Command setShortDescription(String shortDescription) { - this.shortDescription = shortDescription; - return this; - } - - /* - public Command preprocessArguments(IArgumentPreProcessor processor) { - parameterList.setArgumentPreProcessor(processor); - return this; - }*/ - - public final ParameterList getParameterList() { - return parameterList; - } - - public final String[] getDescription() { - return description.length == 0 ? description : description.clone(); - } - - public String getShortDescription() { - return shortDescription; - } - - /** - * ---- CONTEXT FILTERS ---- - * Filter the contexts. For example, if the sender must be a player but it's the console, - * throw a CommandException describing the problem. - */ - private transient int postParameterFilterCount = 0; - - public Command addContextFilter(IContextFilter contextFilter) { - Objects.requireNonNull(contextFilter); - if (!contextFilters.contains(contextFilter)) { - contextFilters.add(contextFilter); - contextFilters.sort(null); - - if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) { - postParameterFilterCount++; - } - } - return this; - } - - public List<IContextFilter> getContextFilters() { - return Collections.unmodifiableList(contextFilters); - } - - public Command removeContextFilter(IContextFilter contextFilter) { - boolean ret = contextFilters.remove(contextFilter); - if (ret) { - if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) { - postParameterFilterCount--; - } - } - return this; - } - - // ---- CONTROL FLOW IN COMMAND TREES ---- - - public boolean isVisibleTo(CommandSender sender) { - return true; - } - - public boolean takePrecedenceOverSubcommand(String subCommand, ArgumentBuffer buffer) { - return false; - } - - // ---- EXECUTION ---- - - 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.parse(parameterList); - - if (!context.isTabComplete()) { - for (n = contextFilters.size(); i < n; i++) { - contextFilters.get(i).filterContext(context); - } - } - } - - public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException; - - public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) { - return context.getSuggestedCompletions(location); - } - -} +package io.dico.dicore.command;
+
+import io.dico.dicore.command.IContextFilter.Priority;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+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.ParameterType;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public abstract class Command {
+ private static final String[] EMPTY_DESCRIPTION = new String[0];
+ private final ParameterList parameterList = new ParameterList();
+ private final List<IContextFilter> contextFilters = new ArrayList<>(3);
+ private String[] description = EMPTY_DESCRIPTION;
+ private String shortDescription;
+
+ public Command addParameter(Parameter<?, ?> parameter) {
+ parameterList.addParameter(parameter);
+ return this;
+ }
+
+ public <TType> Command addParameter(String name, String description, ParameterType<TType, Void> type) {
+ return addParameter(new Parameter<>(name, description, type, null, false, null));
+ }
+
+ public <TType, TParamInfo> Command addParameter(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) {
+ return addParameter(new Parameter<>(name, description, type, paramInfo, false, null));
+ }
+
+ public <TType> Command addFlag(String name, String description, ParameterType<TType, Void> type) {
+ return addParameter(new Parameter<>('-' + name, description, type, null, true, null));
+ }
+
+ public <TType, TParamInfo> Command addFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) {
+ return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, null));
+ }
+
+ public <TType> Command addAuthorizedFlag(String name, String description, ParameterType<TType, Void> type, String permission) {
+ return addParameter(new Parameter<>('-' + name, description, type, null, true, permission));
+ }
+
+ public <TType, TParamInfo> Command addAuthorizedFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo, String permission) {
+ return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, permission));
+ }
+
+ public Command requiredParameters(int requiredParameters) {
+ parameterList.setRequiredCount(requiredParameters);
+ return this;
+ }
+
+ public Command repeatFinalParameter() {
+ parameterList.setRepeatFinalParameter(true);
+ return this;
+ }
+
+ public Command setDescription(String... description) {
+ this.description = Objects.requireNonNull(description);
+ return this;
+ }
+
+ public Command setShortDescription(String shortDescription) {
+ this.shortDescription = shortDescription;
+ return this;
+ }
+
+ /*
+ public Command preprocessArguments(IArgumentPreProcessor processor) {
+ parameterList.setArgumentPreProcessor(processor);
+ return this;
+ }*/
+
+ public final ParameterList getParameterList() {
+ return parameterList;
+ }
+
+ public final String[] getDescription() {
+ return description.length == 0 ? description : description.clone();
+ }
+
+ public String getShortDescription() {
+ return shortDescription;
+ }
+
+ /**
+ * ---- CONTEXT FILTERS ----
+ * Filter the contexts. For example, if the sender must be a player but it's the console,
+ * throw a CommandException describing the problem.
+ */
+ private transient int postParameterFilterCount = 0;
+
+ public Command addContextFilter(IContextFilter contextFilter) {
+ Objects.requireNonNull(contextFilter);
+ if (!contextFilters.contains(contextFilter)) {
+ contextFilters.add(contextFilter);
+ contextFilters.sort(null);
+
+ if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) {
+ postParameterFilterCount++;
+ }
+ }
+ return this;
+ }
+
+ public List<IContextFilter> getContextFilters() {
+ return Collections.unmodifiableList(contextFilters);
+ }
+
+ public Command removeContextFilter(IContextFilter contextFilter) {
+ boolean ret = contextFilters.remove(contextFilter);
+ if (ret) {
+ if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) {
+ postParameterFilterCount--;
+ }
+ }
+ return this;
+ }
+
+ // ---- CONTROL FLOW IN COMMAND TREES ----
+
+ public boolean isVisibleTo(CommandSender sender) {
+ return true;
+ }
+
+ public boolean takePrecedenceOverSubcommand(String subCommand, ArgumentBuffer buffer) {
+ return false;
+ }
+
+ // ---- EXECUTION ----
+
+ 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.parse(parameterList);
+
+ if (!context.isTabComplete()) {
+ for (n = contextFilters.size(); i < n; i++) {
+ contextFilters.get(i).filterContext(context);
+ }
+ }
+ }
+
+ public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException;
+
+ public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
+ return context.getSuggestedCompletions(location);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java index e527f27..118dd0f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java @@ -1,432 +1,432 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.chat.IChatHandler; -import io.dico.dicore.command.parameter.type.IParameterTypeSelector; -import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector; -import io.dico.dicore.command.parameter.type.ParameterType; -import io.dico.dicore.command.predef.HelpCommand; -import io.dico.dicore.command.predef.PredefinedCommand; -import io.dico.dicore.command.predef.SyntaxCommand; -import io.dico.dicore.command.registration.reflect.ReflectiveRegistration; - -import java.util.HashSet; -import java.util.Objects; -import java.util.function.Consumer; - -/** - * Mimic of WorldEdit's CommandGraph - */ -public final class CommandBuilder { - private final RootCommandAddress root; - private ModifiableCommandAddress cur; - private IParameterTypeSelector selector; - - /** - * Instantiate a new CommandBuilder with a new command root system - * Commands registered to this command builder might interfere with - * commands registered to other commands builders or by other plugins. - */ - public CommandBuilder() { - this(new RootCommandAddress()); - } - - /** - * Instantiate a new CommandBuilder with a specified root address. - * If the root address is identical to that of another command builder, - * they will modify the same tree. - * - * @param root the root address - */ - public CommandBuilder(RootCommandAddress root) { - this.root = Objects.requireNonNull(root); - this.cur = root; - this.selector = new MapBasedParameterTypeSelector(true); - } - - /** - * Add a sub command at the current address - * The current address can be inspected using {@link #getAddress()} - * - * @param name the name of the command - * @param command the command executor - * @param aliases any aliases - * @return this - */ - public CommandBuilder addSubCommand(String name, Command command, String... aliases) { - ChildCommandAddress address = new ChildCommandAddress(command); - address.addNameAndAliases(name, aliases); - return addSubCommand(address); - } - - /** - * Add a subcommand as an address at the current address - * The result of this call is the same as - * {@code addSubCommand(address.getMainKey(), address.getCommand(), address.getNames().sublist(1).toArray(new String[0]))} - * - * @param address the address - * @return this - * @throws IllegalArgumentException if {@code address.isRoot()} - */ - public CommandBuilder addSubCommand(ICommandAddress address) { - cur.addChild(address); - return this; - } - - /** - * Search the given class for any (static) methods using command annotations - * The class gets a localized parameter type selector if it defines parameter types. - * Any commands found are registered as sub commands to the current address. - * - * @param clazz the clazz - * @return this - * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class - * @see #registerCommands(Class, Object) - */ - public CommandBuilder registerCommands(Class<?> clazz) { - return registerCommands(clazz, null); - } - - /** - * Search the given object's class for methods using command annotations. - * If the object is null, only static methods are checked. Otherwise, instance methods are also checked. - * The class gets a localized parameter type selector if it defines parameter types. - * Any commands found are registered as sub commands to the current address. - * - * @param object the object - * @return this - * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class - * @see #registerCommands(Class, Object) - */ - public CommandBuilder registerCommands(Object object) { - return registerCommands(object.getClass(), object); - } - - /** - * Search the given class for methods using command annotations. - * The class gets a localized parameter type selector if it defines parameter types. - * Any commands found are registered as sub commands to the current address. - * The instance is used to invoke non-static methods. - * - * @param clazz the class - * @param instance the instance, null if only static methods - * @return this - * @throws IllegalArgumentException if instance is not null and it's not an instance of the class - * @throws IllegalArgumentException if another exception occurs while parsing the methods of this class - */ - public CommandBuilder registerCommands(Class<?> clazz, Object instance) { - try { - ReflectiveRegistration.parseCommandGroup(cur, selector, clazz, instance); - return this; - } catch (Exception ex) { - throw new IllegalArgumentException(ex); - } - } - - /** - * register the {@link HelpCommand} as a sub command at the current address - * - * @return this - */ - public CommandBuilder registerHelpCommand() { - HelpCommand.registerAsChild(cur); - return this; - } - - /** - * register the {@link SyntaxCommand} as a sub command a the current address - * - * @return this - */ - public CommandBuilder registerSyntaxCommand() { - SyntaxCommand.registerAsChild(cur); - return this; - } - - /** - * Generate the predefined commands. - * These are presets. - * Examples include {@code help} and {@code syntax}. - * <p> - * Predefined commands can be registered through {@link PredefinedCommand#registerPredefinedCommandGenerator(String, Consumer)} - * - * @param commands the commands - * @return this - */ - public CommandBuilder generatePredefinedCommands(String... commands) { - ReflectiveRegistration.generateCommands(cur, commands); - return this; - } - - /** - * Unregister any childs present at the given keys. - * <p> - * This method can be used to remove unwanted keys, that might have been added - * outside of your control. For example, because you didn't want all commands - * registered by {@link #registerCommands(Class, Object)}, or because you didn't - * want the help command registered by {@link #group(String, String...)} - * - * @param removeAliases true if any aliases of the children present at the keys should be removed - * @param keys a varargs array containing the keys - * @return this - * @throws IllegalArgumentException if keys array is empty - */ - public CommandBuilder unregisterCommands(boolean removeAliases, String... keys) { - cur.removeChildren(removeAliases, keys); - return this; - } - - /** - * Jump to the sub-address with the given name as main key. - * If an address with the exact name as main key exists, - * that address becomes the current address. - * <p> - * Otherwise, a new addresses is registered with the name and aliases. - * New addresses registered by this command have a HelpCommand added by default. - * <p> - * After this call, any registered commands are registered as a sub command - * to the new address. To restore the previous state, a call to {@link #parent()} - * should be made. - * <p> - * If the address is the target of a command, it will provide information about its sub commands - * using the HelpCommand. - * - * @param name the main key - * @param aliases the aliases - * @return this - */ - public CommandBuilder group(String name, String... aliases) { - ChildCommandAddress address = cur.getChild(name); - if (address == null || !name.equals(address.getMainKey())) { - address = new ChildCommandAddress(); - address.setupAsPlaceholder(name, aliases); - cur.addChild(address); - } - cur = address; - return this; - } - - /** - * Similar to {@link #group(String, String[])} but this will force overwrite any present group, - * using the address passed. The address MUST be an instance of {@link ChildCommandAddress}. - * - * <p>The address must not have a parent or any keys</p> - * - * @param address the address object to use - * @param name the main key - * @param aliases any aliases - * @return this - * @throws IllegalArgumentException if any of the requirements set out above aren't met - */ - public CommandBuilder group(ICommandAddress address, String name, String... aliases) { - if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) { - throw new IllegalArgumentException(); - } - - ChildCommandAddress asChild = (ChildCommandAddress) address; - asChild.setupAsPlaceholder(name, aliases); - cur.addChild(address); - cur = asChild; - return this; - } - - /** - * Sets the description of a group created by {@link #group(String, String...)} - * Can be called subsequently to making a call to {@link #group(String, String...)} - * - * @param shortDescription a short description - * @param description the lines of a full description. - * @return this - * @throws IllegalStateException if the current group has no command - */ - public CommandBuilder setGroupDescription(String shortDescription, String... description) { - Command command = cur.getCommand(); - if (command == null) throw new IllegalStateException(); - cur.setCommand(command - .setShortDescription(shortDescription) - .setDescription(description)); - return this; - } - - /** - * Add a context filter to the command of the current group - * @return this - * @throws IllegalStateException if the current group has no command - */ - public CommandBuilder addContextFilter(IContextFilter contextFilter) { - Command command = cur.getCommand(); - if (command == null) throw new IllegalStateException(); - cur.setCommand(command - .addContextFilter(contextFilter)); - return this; - } - - /** - * Add a required permission to the command of the current group - * @return this - * @throws IllegalStateException if the current group has no command - */ - 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 - * - * @return this - * @throws IllegalStateException if the address is empty - * // has a depth of 0 // is at level 0 - */ - public CommandBuilder parent() { - if (cur.hasParent()) { - cur = cur.getParent(); - return this; - } - throw new IllegalStateException("No parent exists at this address"); - } - - /** - * Jump to the root (empty) address, - * such that a subsequent call to {@link #parent()} - * will throw a {@link IllegalStateException} - * - * @return this - */ - public CommandBuilder root() { - cur = root; - return this; - } - - /** - * Get the current address, as a space-separated string - * - * @return the current address - */ - public String getAddress() { - return cur.getAddress(); - } - - /** - * Get the depth of the current address. - * This is equivalent to {@code getAddress().split(" ").length}. - * If the address is empty, the depth is 0. - * - * @return the depth - */ - public int getDepth() { - return cur.getDepth(); - } - - /** - * Set the command at the current group. The command is set - * a level higher than it would be if this were a call to {@link #addSubCommand(String, Command, String...)} - * <p> - * If a call to {@link #setGroupDescription(String, String...)} was made at the same address before, - * the description is copied to the given executor. - * - * @param command the executor - * @return this - * @throws IllegalArgumentException if the command at the address is present and declared by the user, - * in other words, it's not a {@link PredefinedCommand} - */ - public CommandBuilder setCommand(Command command) { - Command current = cur.getCommand(); - if (current instanceof HelpCommand && current != HelpCommand.INSTANCE) { - command.setShortDescription(current.getShortDescription()); - command.setDescription(current.getDescription()); - } - - cur.setCommand(command); - return this; - } - - /** - * Configure the chat handler at this address. The chat handler - * is used for all children down the tree if they don't explicitly have - * their own chat handler configured. If this isn't configured, - * {@code ChatHandlers.defaultChat()} is used. - * - * @param chatHandler the chat handler - * @return this - */ - public CommandBuilder setChatHandler(IChatHandler chatHandler) { - cur.setChatHandler(chatHandler); - return this; - } - - /** - * Add the parameter type to this builder's selector. - * - * @param type the type - * @param <T> the return type of the parameter type - * @return this - */ - public <T> CommandBuilder addParameterType(ParameterType<T, Void> type) { - selector.addType(false, type); - return this; - } - - /** - * Add the parameter type to this builder's selector. - * - * @param infolessAlias whether to also register the type with an infoless alias. - * this increases the priority assigned to the type if no info object is present. - * @param type the type - * @param <T> the return type of the parameter type - * @param <C> the parameter config type (info object) - * @return this - */ - - public <T, C> CommandBuilder addParameterType(boolean infolessAlias, ParameterType<T, C> type) { - selector.addType(infolessAlias, type); - return this; - } - - /** - * Get the dispatcher for the root address. - * The dispatcher should be used to finally register all commands, - * after they are all declared. - * - * @return the dispatcher - */ - public ICommandDispatcher getDispatcher() { - return root; - } - - /** - * Print debugging information about the current addresses and commands in this builder - * A StackTraceElement indicating where this was called from is also included - * - * @return this - */ - public CommandBuilder printDebugInformation() { - String address = cur == root ? "<root>" : cur.getAddress(); - StackTraceElement caller = getCallsite(); - - StringBuilder message = new StringBuilder("### CommandBuilder dump ###"); - message.append("\nCalled from ").append(caller); - message.append("\nPosition: ").append(address); - cur.appendDebugInformation(message, "", new HashSet<>()); - - System.out.println(message); - return this; - } - - private static StackTraceElement getCallsite() { - // [0] Thread.currentThread() - // [1] CommandBuilder.getCallsite() - // [2] Calling method - // [3] Method calling the calling method - StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - return trace.length > 3 ? trace[3] : null; - } - -} +package io.dico.dicore.command;
+
+import io.dico.dicore.command.chat.IChatHandler;
+import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
+import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector;
+import io.dico.dicore.command.parameter.type.ParameterType;
+import io.dico.dicore.command.predef.HelpCommand;
+import io.dico.dicore.command.predef.PredefinedCommand;
+import io.dico.dicore.command.predef.SyntaxCommand;
+import io.dico.dicore.command.registration.reflect.ReflectiveRegistration;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Mimic of WorldEdit's CommandGraph
+ */
+public final class CommandBuilder {
+ private final RootCommandAddress root;
+ private ModifiableCommandAddress cur;
+ private IParameterTypeSelector selector;
+
+ /**
+ * Instantiate a new CommandBuilder with a new command root system
+ * Commands registered to this command builder might interfere with
+ * commands registered to other commands builders or by other plugins.
+ */
+ public CommandBuilder() {
+ this(new RootCommandAddress());
+ }
+
+ /**
+ * Instantiate a new CommandBuilder with a specified root address.
+ * If the root address is identical to that of another command builder,
+ * they will modify the same tree.
+ *
+ * @param root the root address
+ */
+ public CommandBuilder(RootCommandAddress root) {
+ this.root = Objects.requireNonNull(root);
+ this.cur = root;
+ this.selector = new MapBasedParameterTypeSelector(true);
+ }
+
+ /**
+ * Add a sub command at the current address
+ * The current address can be inspected using {@link #getAddress()}
+ *
+ * @param name the name of the command
+ * @param command the command executor
+ * @param aliases any aliases
+ * @return this
+ */
+ public CommandBuilder addSubCommand(String name, Command command, String... aliases) {
+ ChildCommandAddress address = new ChildCommandAddress(command);
+ address.addNameAndAliases(name, aliases);
+ return addSubCommand(address);
+ }
+
+ /**
+ * Add a subcommand as an address at the current address
+ * The result of this call is the same as
+ * {@code addSubCommand(address.getMainKey(), address.getCommand(), address.getNames().sublist(1).toArray(new String[0]))}
+ *
+ * @param address the address
+ * @return this
+ * @throws IllegalArgumentException if {@code address.isRoot()}
+ */
+ public CommandBuilder addSubCommand(ICommandAddress address) {
+ cur.addChild(address);
+ return this;
+ }
+
+ /**
+ * Search the given class for any (static) methods using command annotations
+ * The class gets a localized parameter type selector if it defines parameter types.
+ * Any commands found are registered as sub commands to the current address.
+ *
+ * @param clazz the clazz
+ * @return this
+ * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class
+ * @see #registerCommands(Class, Object)
+ */
+ public CommandBuilder registerCommands(Class<?> clazz) {
+ return registerCommands(clazz, null);
+ }
+
+ /**
+ * Search the given object's class for methods using command annotations.
+ * If the object is null, only static methods are checked. Otherwise, instance methods are also checked.
+ * The class gets a localized parameter type selector if it defines parameter types.
+ * Any commands found are registered as sub commands to the current address.
+ *
+ * @param object the object
+ * @return this
+ * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class
+ * @see #registerCommands(Class, Object)
+ */
+ public CommandBuilder registerCommands(Object object) {
+ return registerCommands(object.getClass(), object);
+ }
+
+ /**
+ * Search the given class for methods using command annotations.
+ * The class gets a localized parameter type selector if it defines parameter types.
+ * Any commands found are registered as sub commands to the current address.
+ * The instance is used to invoke non-static methods.
+ *
+ * @param clazz the class
+ * @param instance the instance, null if only static methods
+ * @return this
+ * @throws IllegalArgumentException if instance is not null and it's not an instance of the class
+ * @throws IllegalArgumentException if another exception occurs while parsing the methods of this class
+ */
+ public CommandBuilder registerCommands(Class<?> clazz, Object instance) {
+ try {
+ ReflectiveRegistration.parseCommandGroup(cur, selector, clazz, instance);
+ return this;
+ } catch (Exception ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+
+ /**
+ * register the {@link HelpCommand} as a sub command at the current address
+ *
+ * @return this
+ */
+ public CommandBuilder registerHelpCommand() {
+ HelpCommand.registerAsChild(cur);
+ return this;
+ }
+
+ /**
+ * register the {@link SyntaxCommand} as a sub command a the current address
+ *
+ * @return this
+ */
+ public CommandBuilder registerSyntaxCommand() {
+ SyntaxCommand.registerAsChild(cur);
+ return this;
+ }
+
+ /**
+ * Generate the predefined commands.
+ * These are presets.
+ * Examples include {@code help} and {@code syntax}.
+ * <p>
+ * Predefined commands can be registered through {@link PredefinedCommand#registerPredefinedCommandGenerator(String, Consumer)}
+ *
+ * @param commands the commands
+ * @return this
+ */
+ public CommandBuilder generatePredefinedCommands(String... commands) {
+ ReflectiveRegistration.generateCommands(cur, commands);
+ return this;
+ }
+
+ /**
+ * Unregister any childs present at the given keys.
+ * <p>
+ * This method can be used to remove unwanted keys, that might have been added
+ * outside of your control. For example, because you didn't want all commands
+ * registered by {@link #registerCommands(Class, Object)}, or because you didn't
+ * want the help command registered by {@link #group(String, String...)}
+ *
+ * @param removeAliases true if any aliases of the children present at the keys should be removed
+ * @param keys a varargs array containing the keys
+ * @return this
+ * @throws IllegalArgumentException if keys array is empty
+ */
+ public CommandBuilder unregisterCommands(boolean removeAliases, String... keys) {
+ cur.removeChildren(removeAliases, keys);
+ return this;
+ }
+
+ /**
+ * Jump to the sub-address with the given name as main key.
+ * If an address with the exact name as main key exists,
+ * that address becomes the current address.
+ * <p>
+ * Otherwise, a new addresses is registered with the name and aliases.
+ * New addresses registered by this command have a HelpCommand added by default.
+ * <p>
+ * After this call, any registered commands are registered as a sub command
+ * to the new address. To restore the previous state, a call to {@link #parent()}
+ * should be made.
+ * <p>
+ * If the address is the target of a command, it will provide information about its sub commands
+ * using the HelpCommand.
+ *
+ * @param name the main key
+ * @param aliases the aliases
+ * @return this
+ */
+ public CommandBuilder group(String name, String... aliases) {
+ ChildCommandAddress address = cur.getChild(name);
+ if (address == null || !name.equals(address.getMainKey())) {
+ address = new ChildCommandAddress();
+ address.setupAsPlaceholder(name, aliases);
+ cur.addChild(address);
+ }
+ cur = address;
+ return this;
+ }
+
+ /**
+ * Similar to {@link #group(String, String[])} but this will force overwrite any present group,
+ * using the address passed. The address MUST be an instance of {@link ChildCommandAddress}.
+ *
+ * <p>The address must not have a parent or any keys</p>
+ *
+ * @param address the address object to use
+ * @param name the main key
+ * @param aliases any aliases
+ * @return this
+ * @throws IllegalArgumentException if any of the requirements set out above aren't met
+ */
+ public CommandBuilder group(ICommandAddress address, String name, String... aliases) {
+ if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) {
+ throw new IllegalArgumentException();
+ }
+
+ ChildCommandAddress asChild = (ChildCommandAddress) address;
+ asChild.setupAsPlaceholder(name, aliases);
+ cur.addChild(address);
+ cur = asChild;
+ return this;
+ }
+
+ /**
+ * Sets the description of a group created by {@link #group(String, String...)}
+ * Can be called subsequently to making a call to {@link #group(String, String...)}
+ *
+ * @param shortDescription a short description
+ * @param description the lines of a full description.
+ * @return this
+ * @throws IllegalStateException if the current group has no command
+ */
+ public CommandBuilder setGroupDescription(String shortDescription, String... description) {
+ Command command = cur.getCommand();
+ if (command == null) throw new IllegalStateException();
+ cur.setCommand(command
+ .setShortDescription(shortDescription)
+ .setDescription(description));
+ return this;
+ }
+
+ /**
+ * Add a context filter to the command of the current group
+ * @return this
+ * @throws IllegalStateException if the current group has no command
+ */
+ public CommandBuilder addContextFilter(IContextFilter contextFilter) {
+ Command command = cur.getCommand();
+ if (command == null) throw new IllegalStateException();
+ cur.setCommand(command
+ .addContextFilter(contextFilter));
+ return this;
+ }
+
+ /**
+ * Add a required permission to the command of the current group
+ * @return this
+ * @throws IllegalStateException if the current group has no command
+ */
+ 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
+ *
+ * @return this
+ * @throws IllegalStateException if the address is empty
+ * // has a depth of 0 // is at level 0
+ */
+ public CommandBuilder parent() {
+ if (cur.hasParent()) {
+ cur = cur.getParent();
+ return this;
+ }
+ throw new IllegalStateException("No parent exists at this address");
+ }
+
+ /**
+ * Jump to the root (empty) address,
+ * such that a subsequent call to {@link #parent()}
+ * will throw a {@link IllegalStateException}
+ *
+ * @return this
+ */
+ public CommandBuilder root() {
+ cur = root;
+ return this;
+ }
+
+ /**
+ * Get the current address, as a space-separated string
+ *
+ * @return the current address
+ */
+ public String getAddress() {
+ return cur.getAddress();
+ }
+
+ /**
+ * Get the depth of the current address.
+ * This is equivalent to {@code getAddress().split(" ").length}.
+ * If the address is empty, the depth is 0.
+ *
+ * @return the depth
+ */
+ public int getDepth() {
+ return cur.getDepth();
+ }
+
+ /**
+ * Set the command at the current group. The command is set
+ * a level higher than it would be if this were a call to {@link #addSubCommand(String, Command, String...)}
+ * <p>
+ * If a call to {@link #setGroupDescription(String, String...)} was made at the same address before,
+ * the description is copied to the given executor.
+ *
+ * @param command the executor
+ * @return this
+ * @throws IllegalArgumentException if the command at the address is present and declared by the user,
+ * in other words, it's not a {@link PredefinedCommand}
+ */
+ public CommandBuilder setCommand(Command command) {
+ Command current = cur.getCommand();
+ if (current instanceof HelpCommand && current != HelpCommand.INSTANCE) {
+ command.setShortDescription(current.getShortDescription());
+ command.setDescription(current.getDescription());
+ }
+
+ cur.setCommand(command);
+ return this;
+ }
+
+ /**
+ * Configure the chat handler at this address. The chat handler
+ * is used for all children down the tree if they don't explicitly have
+ * their own chat handler configured. If this isn't configured,
+ * {@code ChatHandlers.defaultChat()} is used.
+ *
+ * @param chatHandler the chat handler
+ * @return this
+ */
+ public CommandBuilder setChatHandler(IChatHandler chatHandler) {
+ cur.setChatHandler(chatHandler);
+ return this;
+ }
+
+ /**
+ * Add the parameter type to this builder's selector.
+ *
+ * @param type the type
+ * @param <T> the return type of the parameter type
+ * @return this
+ */
+ public <T> CommandBuilder addParameterType(ParameterType<T, Void> type) {
+ selector.addType(false, type);
+ return this;
+ }
+
+ /**
+ * Add the parameter type to this builder's selector.
+ *
+ * @param infolessAlias whether to also register the type with an infoless alias.
+ * this increases the priority assigned to the type if no info object is present.
+ * @param type the type
+ * @param <T> the return type of the parameter type
+ * @param <C> the parameter config type (info object)
+ * @return this
+ */
+
+ public <T, C> CommandBuilder addParameterType(boolean infolessAlias, ParameterType<T, C> type) {
+ selector.addType(infolessAlias, type);
+ return this;
+ }
+
+ /**
+ * Get the dispatcher for the root address.
+ * The dispatcher should be used to finally register all commands,
+ * after they are all declared.
+ *
+ * @return the dispatcher
+ */
+ public ICommandDispatcher getDispatcher() {
+ return root;
+ }
+
+ /**
+ * Print debugging information about the current addresses and commands in this builder
+ * A StackTraceElement indicating where this was called from is also included
+ *
+ * @return this
+ */
+ public CommandBuilder printDebugInformation() {
+ String address = cur == root ? "<root>" : cur.getAddress();
+ StackTraceElement caller = getCallsite();
+
+ StringBuilder message = new StringBuilder("### CommandBuilder dump ###");
+ message.append("\nCalled from ").append(caller);
+ message.append("\nPosition: ").append(address);
+ cur.appendDebugInformation(message, "", new HashSet<>());
+
+ System.out.println(message);
+ return this;
+ }
+
+ private static StackTraceElement getCallsite() {
+ // [0] Thread.currentThread()
+ // [1] CommandBuilder.getCallsite()
+ // [2] Calling method
+ // [3] Method calling the calling method
+ StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+ return trace.length > 3 ? trace[3] : null;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java b/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java index b859952..adf66de 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java @@ -1,28 +1,28 @@ -package io.dico.dicore.command; - -public class CommandException extends Exception { - - public CommandException() { - } - - public CommandException(String message) { - super(message); - } - - public CommandException(String message, Throwable cause) { - super(message, cause); - } - - public CommandException(Throwable cause) { - super(cause); - } - - public static CommandException missingArgument(String parameterName) { - return new CommandException("Missing argument for " + parameterName); - } - - public static CommandException invalidArgument(String parameterName, String syntaxHelp) { - return new CommandException("Invalid input for " + parameterName + ", should be " + syntaxHelp); - } - -} +package io.dico.dicore.command;
+
+public class CommandException extends Exception {
+
+ public CommandException() {
+ }
+
+ public CommandException(String message) {
+ super(message);
+ }
+
+ public CommandException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CommandException(Throwable cause) {
+ super(cause);
+ }
+
+ public static CommandException missingArgument(String parameterName) {
+ return new CommandException("Missing argument for " + parameterName);
+ }
+
+ public static CommandException invalidArgument(String parameterName, String syntaxHelp) {
+ return new CommandException("Invalid input for " + parameterName + ", should be " + syntaxHelp);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java b/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java index fba8780..0b02459 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java @@ -1,19 +1,19 @@ -package io.dico.dicore.command; - -public enum EMessageType { - GOOD_NEWS, - BAD_NEWS, - NEUTRAL, - INFORMATIVE, - WARNING, - INSTRUCTION, - EXCEPTION, - RESULT, - CUSTOM, - - DESCRIPTION, - SYNTAX, - HIGHLIGHT, - SUBCOMMAND, - NUMBER, -} +package io.dico.dicore.command;
+
+public enum EMessageType {
+ GOOD_NEWS,
+ BAD_NEWS,
+ NEUTRAL,
+ INFORMATIVE,
+ WARNING,
+ INSTRUCTION,
+ EXCEPTION,
+ RESULT,
+ CUSTOM,
+
+ DESCRIPTION,
+ SYNTAX,
+ HIGHLIGHT,
+ SUBCOMMAND,
+ NUMBER,
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java b/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java index 83b0151..980477a 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java @@ -1,12 +1,12 @@ -package io.dico.dicore.command; - -/** - * Override policies for registering to the command map - */ -public enum EOverridePolicy { - OVERRIDE_ALL, - MAIN_KEY_ONLY, - MAIN_AND_FALLBACK, - FALLBACK_ONLY, - OVERRIDE_NONE -} +package io.dico.dicore.command;
+
+/**
+ * Override policies for registering to the command map
+ */
+public enum EOverridePolicy {
+ OVERRIDE_ALL,
+ MAIN_KEY_ONLY,
+ MAIN_AND_FALLBACK,
+ FALLBACK_ONLY,
+ OVERRIDE_NONE
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java index a329f40..af42650 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java @@ -1,385 +1,385 @@ -package io.dico.dicore.command; - -import io.dico.dicore.Formatting; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.ContextParser; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.ParameterList; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.*; - -/** - * The context of execution. - * <p> - * This class is responsible for the control flow of parameter parsing, as well as caching and providing the parsed parameter values. - * It is also responsible for keeping track of the parameter to complete in the case of a tab completion. - */ -public class ExecutionContext { - // Sender of the command - private final CommandSender sender; - // Address while parsing parameters with ContextParser - private ICommandAddress address; - // Command to execute - private Command command; - // 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; - - - // 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 = 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 = new HashSet<>(); - - - // 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. - private Parameter<?, ?> parameterToComplete; - // this is the cursor that the ArgumentBuffer is reset to when suggested completions are requested. - private int parameterToCompleteCursor = -1; - - // if this flag is set, any messages sent through the sendMessage methods are discarded. - private boolean muted; - - public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) { - this.sender = Objects.requireNonNull(sender); - this.buffer = Objects.requireNonNull(buffer); - this.muted = tabComplete; - this.tabComplete = tabComplete; - - // 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. - // This allows the parser to correctly identify the parameter to be completed in this case. - if (!tabComplete) { - buffer.dropTrailingEmptyElements(); - } - } - - /** - * Construct an execution context that is ready to parse the parameter values. - * - * @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 - */ - public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) { - this(sender, buffer, tabComplete); - setAddress(address); - setCommand(command); - } - - /** - * Sender of the command - * - * @return the sender of the command - */ - public CommandSender getSender() { - return sender; - } - - /** - * @return the buffer of arguments - */ - public ArgumentBuffer getBuffer() { - return buffer; - } - - /** - * Command's address - * - * @return the command's address - */ - public ICommandAddress getAddress() { - return address; - } - - /** - * Set the address - * - * @param address the new address - */ - public void setAddress(ICommandAddress address) { - this.address = address; - } - - /** - * The command - * - * @return the command - */ - public Command getCommand() { - 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 null;//command.getParameterList(); - } - - /** - * Get the buffer as it was before preprocessing the arguments. - * - * @return the original buffer - */ - @Deprecated - public ArgumentBuffer getOriginalBuffer() { - return buffer; - } - - /** - * The arguments - * - * @return the argument buffer - */ - @Deprecated - public ArgumentBuffer getProcessedBuffer() { - return buffer; - } - - /** - * The cursor start, in other words, the buffer's cursor before parameters were parsed. - * - * @return the cursor start - */ - public int getCursorStart() { - return cursorStart; - } - - /** - * The original arguments. - * - * @return original arguments. - */ - public String[] getOriginal() { - return buffer.getArrayFromIndex(cursorStart); - } - - /** - * The path used to access this address. - * - * @return the path used to access this address. - */ - public String[] getRoute() { - return Arrays.copyOf(buffer.toArray(), address.getDepth()); - } - - public Formatting getFormat(EMessageType type) { - return address.getChatHandler().getChatFormatForType(type); - } - - /** - * The full command as cached by the buffer. Might be incomplete depending on how it was dispatched. - * - * @return the full command - */ - public String 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)) { - throw new IllegalArgumentException(); - } - - try { - return (T) parameterValueMap.get(name); - } catch (ClassCastException ex) { - throw new IllegalArgumentException("Invalid type parameter requested for parameter " + name, ex); - } - } - - /** - * 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. - * - * @param name the parameter name - * @return true if it was provided - */ - public boolean isProvided(String name) { - return parsedParameters.contains(name); - } - - /** - * Checks if the parameter by the index was provided in the command's arguments. - * - * @param index the parameter index - * @return true if it was provided - */ - @Deprecated - public boolean isProvided(int index) { - return false;//isProvided(getParameterList().getIndexedParameterName(index)); - } - - /** - * The parameter to complete. - * This parameter is requested suggestions - * - * @return the parameter to complete. - */ - public Parameter<?, ?> getParameterToComplete() { - return parameterToComplete; - } - - /** - * Get suggested completions. - * - * @param location The location as passed to {link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}, or null if requested in another way. - * @return completions. - */ - public List<String> getSuggestedCompletions(Location location) { - if (parameterToComplete != null) { - return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor)); - } - - List<String> result = new ArrayList<>(); - for (String name : parameterValueMap.keySet()) { - if (name.startsWith("-") && !parsedParameters.contains(name)) { - result.add(name); - } - } - return result; - } - - /* - Chat handling - */ - - public void sendMessage(String message) { - sendMessage(true, message); - } - - public void sendMessage(EMessageType messageType, String message) { - sendMessage(messageType, true, message); - } - - public void sendMessage(boolean translateColours, String message) { - sendMessage(EMessageType.NEUTRAL, translateColours, message); - } - - public void sendMessage(EMessageType messageType, boolean translateColours, String message) { - if (!muted) { - if (translateColours) { - message = Formatting.translateChars('&', message); - } - address.getChatHandler().sendMessage(this, messageType, message); - } - } - - public void sendMessage(String messageFormat, Object... args) { - sendMessage(true, messageFormat, args); - } - - public void sendMessage(EMessageType messageType, String messageFormat, Object... args) { - sendMessage(messageType, true, messageFormat, args); - } - - public void sendMessage(boolean translateColours, String messageFormat, Object... args) { - sendMessage(EMessageType.NEUTRAL, translateColours, messageFormat, args); - } - - public void sendMessage(EMessageType messageType, boolean translateColours, String messageFormat, Object... args) { - sendMessage(messageType, translateColours, String.format(messageFormat, args)); - } - - public void sendHelpMessage(int page) { - if (!muted) { - address.getChatHandler().sendHelpMessage(sender, this, address, page); - } - } - - public void sendSyntaxMessage() { - if (!muted) { - address.getChatHandler().sendSyntaxMessage(sender, this, address); - } - } - -} +package io.dico.dicore.command;
+
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.ContextParser;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.ParameterList;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.*;
+
+/**
+ * The context of execution.
+ * <p>
+ * This class is responsible for the control flow of parameter parsing, as well as caching and providing the parsed parameter values.
+ * It is also responsible for keeping track of the parameter to complete in the case of a tab completion.
+ */
+public class ExecutionContext {
+ // Sender of the command
+ private final CommandSender sender;
+ // Address while parsing parameters with ContextParser
+ private ICommandAddress address;
+ // Command to execute
+ private Command command;
+ // 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;
+
+
+ // 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 = 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 = new HashSet<>();
+
+
+ // 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.
+ private Parameter<?, ?> parameterToComplete;
+ // this is the cursor that the ArgumentBuffer is reset to when suggested completions are requested.
+ private int parameterToCompleteCursor = -1;
+
+ // if this flag is set, any messages sent through the sendMessage methods are discarded.
+ private boolean muted;
+
+ public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) {
+ this.sender = Objects.requireNonNull(sender);
+ this.buffer = Objects.requireNonNull(buffer);
+ this.muted = tabComplete;
+ this.tabComplete = tabComplete;
+
+ // 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.
+ // This allows the parser to correctly identify the parameter to be completed in this case.
+ if (!tabComplete) {
+ buffer.dropTrailingEmptyElements();
+ }
+ }
+
+ /**
+ * Construct an execution context that is ready to parse the parameter values.
+ *
+ * @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
+ */
+ public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) {
+ this(sender, buffer, tabComplete);
+ setAddress(address);
+ setCommand(command);
+ }
+
+ /**
+ * Sender of the command
+ *
+ * @return the sender of the command
+ */
+ public CommandSender getSender() {
+ return sender;
+ }
+
+ /**
+ * @return the buffer of arguments
+ */
+ public ArgumentBuffer getBuffer() {
+ return buffer;
+ }
+
+ /**
+ * Command's address
+ *
+ * @return the command's address
+ */
+ public ICommandAddress getAddress() {
+ return address;
+ }
+
+ /**
+ * Set the address
+ *
+ * @param address the new address
+ */
+ public void setAddress(ICommandAddress address) {
+ this.address = address;
+ }
+
+ /**
+ * The command
+ *
+ * @return the command
+ */
+ public Command getCommand() {
+ 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 null;//command.getParameterList();
+ }
+
+ /**
+ * Get the buffer as it was before preprocessing the arguments.
+ *
+ * @return the original buffer
+ */
+ @Deprecated
+ public ArgumentBuffer getOriginalBuffer() {
+ return buffer;
+ }
+
+ /**
+ * The arguments
+ *
+ * @return the argument buffer
+ */
+ @Deprecated
+ public ArgumentBuffer getProcessedBuffer() {
+ return buffer;
+ }
+
+ /**
+ * The cursor start, in other words, the buffer's cursor before parameters were parsed.
+ *
+ * @return the cursor start
+ */
+ public int getCursorStart() {
+ return cursorStart;
+ }
+
+ /**
+ * The original arguments.
+ *
+ * @return original arguments.
+ */
+ public String[] getOriginal() {
+ return buffer.getArrayFromIndex(cursorStart);
+ }
+
+ /**
+ * The path used to access this address.
+ *
+ * @return the path used to access this address.
+ */
+ public String[] getRoute() {
+ return Arrays.copyOf(buffer.toArray(), address.getDepth());
+ }
+
+ public Formatting getFormat(EMessageType type) {
+ return address.getChatHandler().getChatFormatForType(type);
+ }
+
+ /**
+ * The full command as cached by the buffer. Might be incomplete depending on how it was dispatched.
+ *
+ * @return the full command
+ */
+ public String 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)) {
+ throw new IllegalArgumentException();
+ }
+
+ try {
+ return (T) parameterValueMap.get(name);
+ } catch (ClassCastException ex) {
+ throw new IllegalArgumentException("Invalid type parameter requested for parameter " + name, ex);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @param name the parameter name
+ * @return true if it was provided
+ */
+ public boolean isProvided(String name) {
+ return parsedParameters.contains(name);
+ }
+
+ /**
+ * Checks if the parameter by the index was provided in the command's arguments.
+ *
+ * @param index the parameter index
+ * @return true if it was provided
+ */
+ @Deprecated
+ public boolean isProvided(int index) {
+ return false;//isProvided(getParameterList().getIndexedParameterName(index));
+ }
+
+ /**
+ * The parameter to complete.
+ * This parameter is requested suggestions
+ *
+ * @return the parameter to complete.
+ */
+ public Parameter<?, ?> getParameterToComplete() {
+ return parameterToComplete;
+ }
+
+ /**
+ * Get suggested completions.
+ *
+ * @param location The location as passed to {link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}, or null if requested in another way.
+ * @return completions.
+ */
+ public List<String> getSuggestedCompletions(Location location) {
+ if (parameterToComplete != null) {
+ return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor));
+ }
+
+ List<String> result = new ArrayList<>();
+ for (String name : parameterValueMap.keySet()) {
+ if (name.startsWith("-") && !parsedParameters.contains(name)) {
+ result.add(name);
+ }
+ }
+ return result;
+ }
+
+ /*
+ Chat handling
+ */
+
+ public void sendMessage(String message) {
+ sendMessage(true, message);
+ }
+
+ public void sendMessage(EMessageType messageType, String message) {
+ sendMessage(messageType, true, message);
+ }
+
+ public void sendMessage(boolean translateColours, String message) {
+ sendMessage(EMessageType.NEUTRAL, translateColours, message);
+ }
+
+ public void sendMessage(EMessageType messageType, boolean translateColours, String message) {
+ if (!muted) {
+ if (translateColours) {
+ message = Formatting.translateChars('&', message);
+ }
+ address.getChatHandler().sendMessage(this, messageType, message);
+ }
+ }
+
+ public void sendMessage(String messageFormat, Object... args) {
+ sendMessage(true, messageFormat, args);
+ }
+
+ public void sendMessage(EMessageType messageType, String messageFormat, Object... args) {
+ sendMessage(messageType, true, messageFormat, args);
+ }
+
+ public void sendMessage(boolean translateColours, String messageFormat, Object... args) {
+ sendMessage(EMessageType.NEUTRAL, translateColours, messageFormat, args);
+ }
+
+ public void sendMessage(EMessageType messageType, boolean translateColours, String messageFormat, Object... args) {
+ sendMessage(messageType, translateColours, String.format(messageFormat, args));
+ }
+
+ public void sendHelpMessage(int page) {
+ if (!muted) {
+ address.getChatHandler().sendHelpMessage(sender, this, address, page);
+ }
+ }
+
+ public void sendSyntaxMessage() {
+ if (!muted) {
+ address.getChatHandler().sendSyntaxMessage(sender, this, address);
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java index 47c2aca..eb93b30 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java @@ -1,64 +1,64 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.parameter.IArgumentPreProcessor; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.type.ParameterType; - -@SuppressWarnings("unchecked") -public abstract class ExtendedCommand<T extends ExtendedCommand<T>> extends Command { - protected boolean modifiable; - - public ExtendedCommand() { - this(true); - } - - public ExtendedCommand(boolean modifiable) { - this.modifiable = modifiable; - } - - protected T newModifiableInstance() { - return (T) this; - } - - @Override - public T addParameter(Parameter<?, ?> parameter) { - return modifiable ? (T) super.addParameter(parameter) : newModifiableInstance().addParameter(parameter); - } - - @Override - public T addContextFilter(IContextFilter contextFilter) { - return modifiable ? (T) super.addContextFilter(contextFilter) : newModifiableInstance().addContextFilter(contextFilter); - } - - @Override - public T removeContextFilter(IContextFilter contextFilter) { - return modifiable ? (T) super.removeContextFilter(contextFilter) : newModifiableInstance().removeContextFilter(contextFilter); - } - - @Override - public T requiredParameters(int requiredParameters) { - return modifiable ? (T) super.requiredParameters(requiredParameters) : newModifiableInstance().requiredParameters(requiredParameters); - } - - @Override - public T repeatFinalParameter() { - return modifiable ? (T) super.repeatFinalParameter() : newModifiableInstance().repeatFinalParameter(); - } - - @Override - public T setDescription(String... description) { - return modifiable ? (T) super.setDescription(description) : newModifiableInstance().setDescription(description); - } - - @Override - public T setShortDescription(String shortDescription) { - return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription); - } - - /* - @Override - public T preprocessArguments(IArgumentPreProcessor processor) { - return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor); - }*/ - -} +package io.dico.dicore.command;
+
+import io.dico.dicore.command.parameter.IArgumentPreProcessor;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.type.ParameterType;
+
+@SuppressWarnings("unchecked")
+public abstract class ExtendedCommand<T extends ExtendedCommand<T>> extends Command {
+ protected boolean modifiable;
+
+ public ExtendedCommand() {
+ this(true);
+ }
+
+ public ExtendedCommand(boolean modifiable) {
+ this.modifiable = modifiable;
+ }
+
+ protected T newModifiableInstance() {
+ return (T) this;
+ }
+
+ @Override
+ public T addParameter(Parameter<?, ?> parameter) {
+ return modifiable ? (T) super.addParameter(parameter) : newModifiableInstance().addParameter(parameter);
+ }
+
+ @Override
+ public T addContextFilter(IContextFilter contextFilter) {
+ return modifiable ? (T) super.addContextFilter(contextFilter) : newModifiableInstance().addContextFilter(contextFilter);
+ }
+
+ @Override
+ public T removeContextFilter(IContextFilter contextFilter) {
+ return modifiable ? (T) super.removeContextFilter(contextFilter) : newModifiableInstance().removeContextFilter(contextFilter);
+ }
+
+ @Override
+ public T requiredParameters(int requiredParameters) {
+ return modifiable ? (T) super.requiredParameters(requiredParameters) : newModifiableInstance().requiredParameters(requiredParameters);
+ }
+
+ @Override
+ public T repeatFinalParameter() {
+ return modifiable ? (T) super.repeatFinalParameter() : newModifiableInstance().repeatFinalParameter();
+ }
+
+ @Override
+ public T setDescription(String... description) {
+ return modifiable ? (T) super.setDescription(description) : newModifiableInstance().setDescription(description);
+ }
+
+ @Override
+ public T setShortDescription(String shortDescription) {
+ return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription);
+ }
+
+ /*
+ @Override
+ public T preprocessArguments(IArgumentPreProcessor processor) {
+ return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor);
+ }*/
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java index 158b1f0..f900139 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java @@ -1,205 +1,205 @@ -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; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Interface for an address of a command. - * <p> - * The address holds what the name and aliases of a command are. - * The address also (optionally) holds a reference to a {@link Command} - * <p> - * One instance of {@link Command} can be held by multiple addresses, - * because the address decides what the command's name and aliases are. - * <p> - * The address holds children by key in a map. This map's keys include aliases for its children. - * This creates a tree of addresses. If a command is dispatches, the tree is traversed untill a command is found - * and no children deeper down match the command (there are exceptions to the later as defined by - * {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} - * and {@link Command#isVisibleTo(CommandSender)} - */ -public interface ICommandAddress { - - /** - * @return true if this address has a parent. - */ - boolean hasParent(); - - /** - * Get the parent of this address - * - * @return the parent of this address, or null if none exists. - */ - ICommandAddress getParent(); - - /** - * @return true if this address has a command. - */ - boolean hasCommand(); - - /** - * @return true if this address has a command that is not an instance of {@link PredefinedCommand} - */ - boolean hasUserDeclaredCommand(); - - /** - * @return Get the command of this address, or null if none exists. - */ - Command getCommand(); - - /** - * @return true if this address is an instance of {@link RootCommandAddress} - */ - boolean isRoot(); - - /** - * @return the root address of the tree which this address resides in. - */ - ICommandAddress getRoot(); - - /** - * A list of the names of this address, at the current level. - * The first entry is the main key, the subsequent ones are aliases. - * <p> - * Untill an address is assigned a parent, this list is mutable. - * <p> - * If {@link #isRoot()}, this returns an immutable, empty list. - * - * @return the list of names. - */ - List<String> getNames(); - - /** - * A list of the aliases of this address. That is, {@link #getNames()} - * without the first entry. - * - * @return a list of aliases - */ - List<String> getAliases(); - - /** - * @return The first element of {@link #getNames()} - */ - String getMainKey(); - - /** - * Get the address of this command. - * That is, the main keys of all commands leading up to this address, and this address itself, separated by a space. - * In other words, the command without the / that is required to target the command at this address. - * - * @return the address of this command. - */ - String getAddress(); - - /** - * Get the amount of addresses that separate this address from the root of the tree, + 1. - * The root of the tree has a depth of 0. Each subsequent child has its depth incremented by 1. - * - * @return The depth of this address - */ - int getDepth(); - - /** - * @return true if the depth of this address is larger than the argument. - */ - boolean isDepthLargerThan(int depth); - - /** - * @return true if this address has any children. - */ - boolean hasChildren(); - - /** - * @return total number of children, not considering any aliases - */ - int getNumberOfRealChildren(); - - /** - * Get an unmodifiable view of all main keys of the children of this address. - * - * @return the main keys - */ - Collection<String> getChildrenMainKeys(); - - /** - * 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(); - - /** - * Query for a child at the given key. - * - * @param key the key. The name or alias of a command. - * @return the child, or null if it's not found - */ - ICommandAddress getChild(String key); - - /** - * 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 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(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; - - /** - * Get the command dispatcher for this tree - * - * @return the command dispatcher - */ - ICommandDispatcher getDispatcherForTree(); - - /** - * @return The desired chathandler for use by commands at this address and any sub-addresses, if they define no explicit chat handler. - */ - 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(); - } - - static ICommandAddress newChild(Command command) { - return new ChildCommandAddress(command); - } - - static ICommandAddress newRoot() { - return new RootCommandAddress(); - } - -} +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;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface for an address of a command.
+ * <p>
+ * The address holds what the name and aliases of a command are.
+ * The address also (optionally) holds a reference to a {@link Command}
+ * <p>
+ * One instance of {@link Command} can be held by multiple addresses,
+ * because the address decides what the command's name and aliases are.
+ * <p>
+ * The address holds children by key in a map. This map's keys include aliases for its children.
+ * This creates a tree of addresses. If a command is dispatches, the tree is traversed untill a command is found
+ * and no children deeper down match the command (there are exceptions to the later as defined by
+ * {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
+ * and {@link Command#isVisibleTo(CommandSender)}
+ */
+public interface ICommandAddress {
+
+ /**
+ * @return true if this address has a parent.
+ */
+ boolean hasParent();
+
+ /**
+ * Get the parent of this address
+ *
+ * @return the parent of this address, or null if none exists.
+ */
+ ICommandAddress getParent();
+
+ /**
+ * @return true if this address has a command.
+ */
+ boolean hasCommand();
+
+ /**
+ * @return true if this address has a command that is not an instance of {@link PredefinedCommand}
+ */
+ boolean hasUserDeclaredCommand();
+
+ /**
+ * @return Get the command of this address, or null if none exists.
+ */
+ Command getCommand();
+
+ /**
+ * @return true if this address is an instance of {@link RootCommandAddress}
+ */
+ boolean isRoot();
+
+ /**
+ * @return the root address of the tree which this address resides in.
+ */
+ ICommandAddress getRoot();
+
+ /**
+ * A list of the names of this address, at the current level.
+ * The first entry is the main key, the subsequent ones are aliases.
+ * <p>
+ * Untill an address is assigned a parent, this list is mutable.
+ * <p>
+ * If {@link #isRoot()}, this returns an immutable, empty list.
+ *
+ * @return the list of names.
+ */
+ List<String> getNames();
+
+ /**
+ * A list of the aliases of this address. That is, {@link #getNames()}
+ * without the first entry.
+ *
+ * @return a list of aliases
+ */
+ List<String> getAliases();
+
+ /**
+ * @return The first element of {@link #getNames()}
+ */
+ String getMainKey();
+
+ /**
+ * Get the address of this command.
+ * That is, the main keys of all commands leading up to this address, and this address itself, separated by a space.
+ * In other words, the command without the / that is required to target the command at this address.
+ *
+ * @return the address of this command.
+ */
+ String getAddress();
+
+ /**
+ * Get the amount of addresses that separate this address from the root of the tree, + 1.
+ * The root of the tree has a depth of 0. Each subsequent child has its depth incremented by 1.
+ *
+ * @return The depth of this address
+ */
+ int getDepth();
+
+ /**
+ * @return true if the depth of this address is larger than the argument.
+ */
+ boolean isDepthLargerThan(int depth);
+
+ /**
+ * @return true if this address has any children.
+ */
+ boolean hasChildren();
+
+ /**
+ * @return total number of children, not considering any aliases
+ */
+ int getNumberOfRealChildren();
+
+ /**
+ * Get an unmodifiable view of all main keys of the children of this address.
+ *
+ * @return the main keys
+ */
+ Collection<String> getChildrenMainKeys();
+
+ /**
+ * 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();
+
+ /**
+ * Query for a child at the given key.
+ *
+ * @param key the key. The name or alias of a command.
+ * @return the child, or null if it's not found
+ */
+ ICommandAddress getChild(String key);
+
+ /**
+ * 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 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(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
+
+ /**
+ * Get the command dispatcher for this tree
+ *
+ * @return the command dispatcher
+ */
+ ICommandDispatcher getDispatcherForTree();
+
+ /**
+ * @return The desired chathandler for use by commands at this address and any sub-addresses, if they define no explicit chat handler.
+ */
+ 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();
+ }
+
+ static ICommandAddress newChild(Command command) {
+ return new ChildCommandAddress(command);
+ }
+
+ static ICommandAddress newRoot() {
+ return new RootCommandAddress();
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java index 055171d..4f3ffc6 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java @@ -1,146 +1,146 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.registration.CommandMap; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.List; -import java.util.Map; - -public interface ICommandDispatcher { - - /** - * Get a potentially indirect child of the root of this dispatcher - * - * @param buffer the argument buffer with the subsequent keys to traverse. Any keys beyond the first that isn't found are ignored. - * @return the child, or this same instance of no child is found. - */ - ICommandAddress getDeepChild(ArgumentBuffer buffer); - - /** - * Similar to {@link #getDeepChild(ArgumentBuffer)}, - * but this method incorporates checks on the command of traversed children: - * {@link Command#isVisibleTo(CommandSender)} - * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} - * <p> - * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command. - * - * @param sender the sender of the command - * @param buffer the command itself as a buffer. - * @return the address that is the target of the command. - */ - @Deprecated - ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer); - - /** - * Similar to {@link #getDeepChild(ArgumentBuffer)}, - * but this method incorporates checks on the command of traversed children: - * {@link Command#isVisibleTo(CommandSender)} - * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} - * <p> - * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command. - * - * @param context the context of the command. The context must not have its address set. - * @param buffer the command itself as a buffer. - * @return the address that is the target of the command. - */ - ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; - - /** - * dispatch the command - * - * @param sender the sender - * @param command the command - * @return true if a command has executed - */ - boolean dispatchCommand(CommandSender sender, String[] command); - - /** - * dispatch the command - * - * @param sender the sender - * @param usedLabel the label (word after the /) - * @param args the arguments - * @return true if a command has executed - */ - boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args); - - /** - * dispatch the command - * - * @param sender the sender - * @param buffer the command - * @return true if a command has executed - */ - boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer); - - /** - * suggest tab completions - * - * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * args must be sanitized such that it contains no empty elements, particularly at the last index. - * @return tab completions - */ - List<String> getTabCompletions(CommandSender sender, Location location, String[] args); - - /** - * suggest tab completions - * - * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * @param usedLabel the label as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * @return tab completions - */ - List<String> getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args); - - /** - * suggest tab completions - * - * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} - * @param buffer the arguments as a buffer - * @return tab completions - */ - List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer); - - /** - * Register this dispatcher's commands to the command map - * - * @throws UnsupportedOperationException if this dispatcher is not the root of the tree - */ - default void registerToCommandMap() { - registerToCommandMap(null, CommandMap.getCommandMap(), EOverridePolicy.OVERRIDE_ALL); - } - - /** - * Register this dispatcher's commands to the command map - * - * @param fallbackPrefix the fallback prefix to use, null if none - * @param overridePolicy the override policy - * @throws UnsupportedOperationException if this dispatcher is not the root of the tree - */ - default void registerToCommandMap(String fallbackPrefix, EOverridePolicy overridePolicy) { - registerToCommandMap(fallbackPrefix, CommandMap.getCommandMap(), overridePolicy); - } - - /** - * Register this dispatcher's commands to the command map - * - * @param fallbackPrefix the fallback prefix to use, null if none - * @param map the command map - * @param overridePolicy the override policy - * @throws UnsupportedOperationException if this dispatcher is not the root of the tree - */ - void registerToCommandMap(String fallbackPrefix, Map<String, org.bukkit.command.Command> map, EOverridePolicy overridePolicy); - - default void unregisterFromCommandMap() { - unregisterFromCommandMap(CommandMap.getCommandMap()); - } - - void unregisterFromCommandMap(Map<String, org.bukkit.command.Command> map); - -} +package io.dico.dicore.command;
+
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.registration.CommandMap;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ICommandDispatcher {
+
+ /**
+ * Get a potentially indirect child of the root of this dispatcher
+ *
+ * @param buffer the argument buffer with the subsequent keys to traverse. Any keys beyond the first that isn't found are ignored.
+ * @return the child, or this same instance of no child is found.
+ */
+ ICommandAddress getDeepChild(ArgumentBuffer buffer);
+
+ /**
+ * Similar to {@link #getDeepChild(ArgumentBuffer)},
+ * but this method incorporates checks on the command of traversed children:
+ * {@link Command#isVisibleTo(CommandSender)}
+ * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
+ * <p>
+ * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command.
+ *
+ * @param sender the sender of the command
+ * @param buffer the command itself as a buffer.
+ * @return the address that is the target of the command.
+ */
+ @Deprecated
+ ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer);
+
+ /**
+ * Similar to {@link #getDeepChild(ArgumentBuffer)},
+ * but this method incorporates checks on the command of traversed children:
+ * {@link Command#isVisibleTo(CommandSender)}
+ * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
+ * <p>
+ * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command.
+ *
+ * @param context the context of the command. The context must not have its address set.
+ * @param buffer the command itself as a buffer.
+ * @return the address that is the target of the command.
+ */
+ ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
+
+ /**
+ * dispatch the command
+ *
+ * @param sender the sender
+ * @param command the command
+ * @return true if a command has executed
+ */
+ boolean dispatchCommand(CommandSender sender, String[] command);
+
+ /**
+ * dispatch the command
+ *
+ * @param sender the sender
+ * @param usedLabel the label (word after the /)
+ * @param args the arguments
+ * @return true if a command has executed
+ */
+ boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args);
+
+ /**
+ * dispatch the command
+ *
+ * @param sender the sender
+ * @param buffer the command
+ * @return true if a command has executed
+ */
+ boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer);
+
+ /**
+ * suggest tab completions
+ *
+ * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * args must be sanitized such that it contains no empty elements, particularly at the last index.
+ * @return tab completions
+ */
+ List<String> getTabCompletions(CommandSender sender, Location location, String[] args);
+
+ /**
+ * suggest tab completions
+ *
+ * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param usedLabel the label as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @return tab completions
+ */
+ List<String> getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args);
+
+ /**
+ * suggest tab completions
+ *
+ * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param buffer the arguments as a buffer
+ * @return tab completions
+ */
+ List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer);
+
+ /**
+ * Register this dispatcher's commands to the command map
+ *
+ * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
+ */
+ default void registerToCommandMap() {
+ registerToCommandMap(null, CommandMap.getCommandMap(), EOverridePolicy.OVERRIDE_ALL);
+ }
+
+ /**
+ * Register this dispatcher's commands to the command map
+ *
+ * @param fallbackPrefix the fallback prefix to use, null if none
+ * @param overridePolicy the override policy
+ * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
+ */
+ default void registerToCommandMap(String fallbackPrefix, EOverridePolicy overridePolicy) {
+ registerToCommandMap(fallbackPrefix, CommandMap.getCommandMap(), overridePolicy);
+ }
+
+ /**
+ * Register this dispatcher's commands to the command map
+ *
+ * @param fallbackPrefix the fallback prefix to use, null if none
+ * @param map the command map
+ * @param overridePolicy the override policy
+ * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
+ */
+ void registerToCommandMap(String fallbackPrefix, Map<String, org.bukkit.command.Command> map, EOverridePolicy overridePolicy);
+
+ default void unregisterFromCommandMap() {
+ unregisterFromCommandMap(CommandMap.getCommandMap());
+ }
+
+ void unregisterFromCommandMap(Map<String, org.bukkit.command.Command> map);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java index 5c05e27..bf34873 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java @@ -1,201 +1,201 @@ -package io.dico.dicore.command; - -import io.dico.dicore.exceptions.checkedfunctions.CheckedConsumer; -import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -public interface IContextFilter extends Comparable<IContextFilter> { - - /** - * Filter the given context by this filter's criteria. - * If the context does not match the criteria, an exception is thrown describing the problem. - * - * @param context the context to match - * @throws CommandException if it doesn't match - */ - void filterContext(ExecutionContext context) throws CommandException; - - /** - * Filter an execution context for a direct or indirect sub command of the command that registered this filter. - * - * @param subContext the context for the execution - * @param path the path traversed from the command that registered this filter to the executed command - */ - default void filterSubContext(ExecutionContext subContext, String... path) throws CommandException { - filterContext(subContext); - } - - /** - * Get the priority of this context filter. - * The priorities determine the order in which a command's context filters are executed. - * - * @return the priority - */ - Priority getPriority(); - - default boolean allowsContext(ExecutionContext context) { - try { - filterContext(context); - return true; - } catch (CommandException ex) { - return false; - } - } - - /** - * Used to sort filters in execution order. That is, filters are ordered by {@link #getPriority()} - * - * @param o compared filter - * @return comparison value - */ - @Override - default int compareTo(@NotNull IContextFilter o) { - return getPriority().compareTo(o.getPriority()); - } - - /* - default boolean isInheritable() { - return false; - } - - default IContextFilter inherit(String... components) { - if (!isInheritable()) { - throw new IllegalStateException("This IContextFilter cannot be inherited"); - } - - return this; - }*/ - - /** - * IContextFilter priorities. Executes from top to bottom. - */ - enum Priority { - /** - * This priority should have checks on the sender type. - * Any filters on this priority are tested before permissions are. - * This is the highest priority. - */ - VERY_EARLY, // sender type check - - /** - * This priority is specific to permissions. - */ - PERMISSION, - - /** - * Early priority. Post permissions, pre parameter-parsing. - */ - EARLY, - - /** - * Normal priority. Post permissions, pre parameter-parsing. - */ - NORMAL, - - /** - * Late priority. Post permissions, pre parameter-parsing. - */ - LATE, - - /** - * Very late priority. Post permissions, pre parameter-parsing. - */ - VERY_LATE, - - /** - * Post parameters priority. Post permissions, post parameter-parsing. - * This is the lowest priority. - */ - POST_PARAMETERS; - - private IContextFilter inheritor; - - /** - * Get the context filter that inherits context filters from the parent of the same priority. - * If this filter is also present at the parent, it will do the same for the parent's parent, and so on. - * - * @return the inheritor - */ - public IContextFilter getInheritor() { - if (inheritor == null) { - inheritor = InheritingContextFilter.inheritingPriority(this); - } - return inheritor; - } - - } - - /** - * Ensures that only {@link org.bukkit.entity.Player} type senders can execute the command. - */ - IContextFilter PLAYER_ONLY = filterSender(Priority.VERY_EARLY, Validate::isPlayer); - - /** - * Ensures that only {@link org.bukkit.command.ConsoleCommandSender} type senders can execute the command. - */ - IContextFilter CONSOLE_ONLY = filterSender(Priority.VERY_EARLY, Validate::isConsole); - - /** - * This filter is not working as intended. - * <p> - * There is supposed to be a permission filter that takes a base, and appends the command's address to the base, and checks that permission. - */ - IContextFilter INHERIT_PERMISSIONS = Priority.PERMISSION.getInheritor(); - - static IContextFilter fromCheckedRunnable(Priority priority, CheckedRunnable<? extends CommandException> runnable) { - return new IContextFilter() { - @Override - public void filterContext(ExecutionContext context) throws CommandException { - runnable.checkedRun(); - } - - @Override - public Priority getPriority() { - return priority; - } - }; - } - - static IContextFilter filterSender(Priority priority, CheckedConsumer<? super CommandSender, ? extends CommandException> consumer) { - return new IContextFilter() { - @Override - public void filterContext(ExecutionContext context) throws CommandException { - consumer.checkedAccept(context.getSender()); - } - - @Override - public Priority getPriority() { - return priority; - } - }; - } - - static IContextFilter permission(String permission) { - return new PermissionContextFilter(permission); - } - - static IContextFilter permission(String permission, String failMessage) { - return new PermissionContextFilter(permission, failMessage); - } - - static IContextFilter inheritablePermission(String permission) { - return new PermissionContextFilter(permission, true); - } - - /** - * Produce an inheritable permission context filter. - * A permission component is an element in {@code permission.split("\\.")} - * - * @param permission The permission that is required for the command that this is directly assigned to - * @param componentInsertionIndex the index where any sub-components are inserted. -1 for "at the end". - * @param failMessage the message to send if the permission is not met - * @return the context filter - * @throws IllegalArgumentException if componentInsertionIndex is out of range - */ - static IContextFilter inheritablePermission(String permission, int componentInsertionIndex, String failMessage) { - return new PermissionContextFilter(permission, componentInsertionIndex, failMessage); - } - -} - +package io.dico.dicore.command;
+
+import io.dico.dicore.exceptions.checkedfunctions.CheckedConsumer;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+
+public interface IContextFilter extends Comparable<IContextFilter> {
+
+ /**
+ * Filter the given context by this filter's criteria.
+ * If the context does not match the criteria, an exception is thrown describing the problem.
+ *
+ * @param context the context to match
+ * @throws CommandException if it doesn't match
+ */
+ void filterContext(ExecutionContext context) throws CommandException;
+
+ /**
+ * Filter an execution context for a direct or indirect sub command of the command that registered this filter.
+ *
+ * @param subContext the context for the execution
+ * @param path the path traversed from the command that registered this filter to the executed command
+ */
+ default void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
+ filterContext(subContext);
+ }
+
+ /**
+ * Get the priority of this context filter.
+ * The priorities determine the order in which a command's context filters are executed.
+ *
+ * @return the priority
+ */
+ Priority getPriority();
+
+ default boolean allowsContext(ExecutionContext context) {
+ try {
+ filterContext(context);
+ return true;
+ } catch (CommandException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Used to sort filters in execution order. That is, filters are ordered by {@link #getPriority()}
+ *
+ * @param o compared filter
+ * @return comparison value
+ */
+ @Override
+ default int compareTo(@NotNull IContextFilter o) {
+ return getPriority().compareTo(o.getPriority());
+ }
+
+ /*
+ default boolean isInheritable() {
+ return false;
+ }
+
+ default IContextFilter inherit(String... components) {
+ if (!isInheritable()) {
+ throw new IllegalStateException("This IContextFilter cannot be inherited");
+ }
+
+ return this;
+ }*/
+
+ /**
+ * IContextFilter priorities. Executes from top to bottom.
+ */
+ enum Priority {
+ /**
+ * This priority should have checks on the sender type.
+ * Any filters on this priority are tested before permissions are.
+ * This is the highest priority.
+ */
+ VERY_EARLY, // sender type check
+
+ /**
+ * This priority is specific to permissions.
+ */
+ PERMISSION,
+
+ /**
+ * Early priority. Post permissions, pre parameter-parsing.
+ */
+ EARLY,
+
+ /**
+ * Normal priority. Post permissions, pre parameter-parsing.
+ */
+ NORMAL,
+
+ /**
+ * Late priority. Post permissions, pre parameter-parsing.
+ */
+ LATE,
+
+ /**
+ * Very late priority. Post permissions, pre parameter-parsing.
+ */
+ VERY_LATE,
+
+ /**
+ * Post parameters priority. Post permissions, post parameter-parsing.
+ * This is the lowest priority.
+ */
+ POST_PARAMETERS;
+
+ private IContextFilter inheritor;
+
+ /**
+ * Get the context filter that inherits context filters from the parent of the same priority.
+ * If this filter is also present at the parent, it will do the same for the parent's parent, and so on.
+ *
+ * @return the inheritor
+ */
+ public IContextFilter getInheritor() {
+ if (inheritor == null) {
+ inheritor = InheritingContextFilter.inheritingPriority(this);
+ }
+ return inheritor;
+ }
+
+ }
+
+ /**
+ * Ensures that only {@link org.bukkit.entity.Player} type senders can execute the command.
+ */
+ IContextFilter PLAYER_ONLY = filterSender(Priority.VERY_EARLY, Validate::isPlayer);
+
+ /**
+ * Ensures that only {@link org.bukkit.command.ConsoleCommandSender} type senders can execute the command.
+ */
+ IContextFilter CONSOLE_ONLY = filterSender(Priority.VERY_EARLY, Validate::isConsole);
+
+ /**
+ * This filter is not working as intended.
+ * <p>
+ * There is supposed to be a permission filter that takes a base, and appends the command's address to the base, and checks that permission.
+ */
+ IContextFilter INHERIT_PERMISSIONS = Priority.PERMISSION.getInheritor();
+
+ static IContextFilter fromCheckedRunnable(Priority priority, CheckedRunnable<? extends CommandException> runnable) {
+ return new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ runnable.checkedRun();
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+ static IContextFilter filterSender(Priority priority, CheckedConsumer<? super CommandSender, ? extends CommandException> consumer) {
+ return new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ consumer.checkedAccept(context.getSender());
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+ static IContextFilter permission(String permission) {
+ return new PermissionContextFilter(permission);
+ }
+
+ static IContextFilter permission(String permission, String failMessage) {
+ return new PermissionContextFilter(permission, failMessage);
+ }
+
+ static IContextFilter inheritablePermission(String permission) {
+ return new PermissionContextFilter(permission, true);
+ }
+
+ /**
+ * Produce an inheritable permission context filter.
+ * A permission component is an element in {@code permission.split("\\.")}
+ *
+ * @param permission The permission that is required for the command that this is directly assigned to
+ * @param componentInsertionIndex the index where any sub-components are inserted. -1 for "at the end".
+ * @param failMessage the message to send if the permission is not met
+ * @return the context filter
+ * @throws IllegalArgumentException if componentInsertionIndex is out of range
+ */
+ static IContextFilter inheritablePermission(String permission, int componentInsertionIndex, String failMessage) {
+ return new PermissionContextFilter(permission, componentInsertionIndex, failMessage);
+ }
+
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java index 0b4875b..58cd7a6 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java @@ -1,64 +1,64 @@ -package io.dico.dicore.command; - -import java.util.List; - -public abstract class InheritingContextFilter implements IContextFilter { - private static final String[] emptyStringArray = new String[0]; - - private static String[] addParent(String[] path, String parent) { - String[] out = new String[path.length + 1]; - System.arraycopy(path, 0, out, 1, path.length); - out[0] = parent; - return out; - } - - protected abstract boolean isInherited(IContextFilter filter); - - @Override - public void filterContext(ExecutionContext context) throws CommandException { - ICommandAddress address = context.getAddress(); - - String[] traversedPath = emptyStringArray; - do { - traversedPath = addParent(traversedPath, address.getMainKey()); - address = address.getParent(); - - if (address != null && address.hasCommand()) { - boolean doBreak = true; - - Command command = address.getCommand(); - List<IContextFilter> contextFilterList = command.getContextFilters(); - for (IContextFilter filter : contextFilterList) { - if (isInherited(filter)) { - if (filter == this) { - // do the same for next parent - // this method is necessary to keep traversedPath information - doBreak = false; - } else { - filter.filterSubContext(context, traversedPath); - } - } - } - - if (doBreak) { - break; - } - } - } while (address != null); - } - - static InheritingContextFilter inheritingPriority(Priority priority) { - return new InheritingContextFilter() { - @Override - protected boolean isInherited(IContextFilter filter) { - return filter.getPriority() == priority; - } - - @Override - public Priority getPriority() { - return priority; - } - }; - } - -} +package io.dico.dicore.command;
+
+import java.util.List;
+
+public abstract class InheritingContextFilter implements IContextFilter {
+ private static final String[] emptyStringArray = new String[0];
+
+ private static String[] addParent(String[] path, String parent) {
+ String[] out = new String[path.length + 1];
+ System.arraycopy(path, 0, out, 1, path.length);
+ out[0] = parent;
+ return out;
+ }
+
+ protected abstract boolean isInherited(IContextFilter filter);
+
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ ICommandAddress address = context.getAddress();
+
+ String[] traversedPath = emptyStringArray;
+ do {
+ traversedPath = addParent(traversedPath, address.getMainKey());
+ address = address.getParent();
+
+ if (address != null && address.hasCommand()) {
+ boolean doBreak = true;
+
+ Command command = address.getCommand();
+ List<IContextFilter> contextFilterList = command.getContextFilters();
+ for (IContextFilter filter : contextFilterList) {
+ if (isInherited(filter)) {
+ if (filter == this) {
+ // do the same for next parent
+ // this method is necessary to keep traversedPath information
+ doBreak = false;
+ } else {
+ filter.filterSubContext(context, traversedPath);
+ }
+ }
+ }
+
+ if (doBreak) {
+ break;
+ }
+ }
+ } while (address != null);
+ }
+
+ static InheritingContextFilter inheritingPriority(Priority priority) {
+ return new InheritingContextFilter() {
+ @Override
+ protected boolean isInherited(IContextFilter filter) {
+ return filter.getPriority() == priority;
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java index 71b5ca4..5f55122 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java @@ -1,35 +1,35 @@ -package io.dico.dicore.command; - -import io.dico.dicore.exceptions.checkedfunctions.CheckedBiFunction; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.List; -import java.util.Objects; -import java.util.function.BiFunction; - -public class LambdaCommand extends ExtendedCommand<LambdaCommand> { - private CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor; - private BiFunction<CommandSender, ExecutionContext, List<String>> completer; - - public LambdaCommand executor(CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor) { - this.executor = Objects.requireNonNull(executor); - return this; - } - - public LambdaCommand completer(BiFunction<CommandSender, ExecutionContext, List<String>> completer) { - this.completer = Objects.requireNonNull(completer); - return this; - } - - @Override - public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - return executor.checkedApply(sender, context); - } - - @Override - public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) { - return completer == null ? super.tabComplete(sender, context, location) : completer.apply(sender, context); - } - -} +package io.dico.dicore.command;
+
+import io.dico.dicore.exceptions.checkedfunctions.CheckedBiFunction;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+public class LambdaCommand extends ExtendedCommand<LambdaCommand> {
+ private CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor;
+ private BiFunction<CommandSender, ExecutionContext, List<String>> completer;
+
+ public LambdaCommand executor(CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor) {
+ this.executor = Objects.requireNonNull(executor);
+ return this;
+ }
+
+ public LambdaCommand completer(BiFunction<CommandSender, ExecutionContext, List<String>> completer) {
+ this.completer = Objects.requireNonNull(completer);
+ return this;
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ return executor.checkedApply(sender, context);
+ }
+
+ @Override
+ public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
+ return completer == null ? super.tabComplete(sender, context, location) : completer.apply(sender, context);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java index 0cfd755..70e3e03 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java @@ -1,312 +1,312 @@ -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; - -import java.util.*; - -public abstract class ModifiableCommandAddress implements ICommandAddress { - Map<String, ChildCommandAddress> children; - Collection<String> childrenMainKeys = Collections.emptyList(); - - // the chat handler as configured by the programmer - IChatHandler chatHandler; - // cache for the algorithm that finds the first chat handler going up the tree - transient IChatHandler cachedChatHandlerFallback; - - ModifiableCommandAddress helpChild; - - public ModifiableCommandAddress() { - this.children = new LinkedHashMap<>(4); - } - - @Override - public boolean hasParent() { - return getParent() != null; - } - - @Override - public boolean hasCommand() { - return getCommand() != null; - } - - @Override - public boolean hasUserDeclaredCommand() { - Command command = getCommand(); - return command != null && !(command instanceof PredefinedCommand); - } - - @Override - public Command getCommand() { - return null; - } - - @Override - public boolean isRoot() { - return false; - } - - @Override - public List<String> getNames() { - return null; - } - - @Override - public List<String> getAliases() { - List<String> names = getNames(); - if (names == null) { - return null; - } - if (names.isEmpty()) { - return Collections.emptyList(); - } - return names.subList(1, names.size()); - } - - @Override - public String getMainKey() { - return null; - } - - public void setCommand(Command command) { - throw new UnsupportedOperationException(); - } - - @Override - public abstract ModifiableCommandAddress getParent(); - - @Override - public RootCommandAddress getRoot() { - ModifiableCommandAddress out = this; - while (out.hasParent()) { - out = out.getParent(); - } - return out.isRoot() ? (RootCommandAddress) out : null; - } - - @Override - public int getDepth() { - int depth = 0; - ICommandAddress address = this; - while (address.hasParent()) { - address = address.getParent(); - depth++; - } - return depth; - } - - @Override - public boolean isDepthLargerThan(int value) { - int depth = 0; - ICommandAddress address = this; - do { - if (depth > value) { - return true; - } - - address = address.getParent(); - depth++; - } while (address != null); - return false; - } - - @Override - public boolean hasChildren() { - return !children.isEmpty(); - } - - @Override - public int getNumberOfRealChildren() { - return childrenMainKeys.size(); - } - - @Override - public Collection<String> getChildrenMainKeys() { - return Collections.unmodifiableCollection(childrenMainKeys); - } - - @Override - public Map<String, ? extends ModifiableCommandAddress> getChildren() { - return Collections.unmodifiableMap(children); - } - - @Override - public ChildCommandAddress getChild(String key) { - return children.get(key); - } - - @Override - public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - return buffer.hasNext() ? getChild(buffer.next()) : null; - } - - public void addChild(ICommandAddress child) { - if (!(child instanceof ChildCommandAddress)) { - throw new IllegalArgumentException("Argument must be a ChildCommandAddress"); - } - - ChildCommandAddress mChild = (ChildCommandAddress) child; - if (mChild.parent != null) { - throw new IllegalArgumentException("Argument already has a parent"); - } - - if (mChild.names.isEmpty()) { - throw new IllegalArgumentException("Argument must have names"); - } - - Iterator<String> names = mChild.modifiableNamesIterator(); - String mainKey = names.next(); - - if (!childrenMainKeys.contains(mainKey)) { - if (!(childrenMainKeys instanceof ArrayList)) { - childrenMainKeys = new ArrayList<>(); - } - childrenMainKeys.add(mainKey); - } - - children.put(mainKey, mChild); - - while (names.hasNext()) { - String name = names.next(); - if (children.putIfAbsent(name, mChild) != null) { - names.remove(); - } - } - - mChild.setParent(this); - - if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) { - helpChild = mChild; - } - } - - public void removeChildren(boolean removeAliases, String... keys) { - if (keys.length == 0) { - throw new IllegalArgumentException("keys is empty"); - } - - for (String key : keys) { - ChildCommandAddress keyTarget = getChild(key); - if (keyTarget == null) { - continue; - } - - if (removeAliases) { - Iterator<String> iterator = keyTarget.namesModifiable.iterator(); - boolean first = true; - while (iterator.hasNext()) { - String alias = iterator.next(); - ChildCommandAddress aliasTarget = getChild(key); - if (aliasTarget == keyTarget) { - if (first) { - childrenMainKeys.remove(alias); - } - children.remove(alias); - } - iterator.remove(); - first = false; - } - - } else { - if (key.equals(keyTarget.getMainKey())) { - childrenMainKeys.remove(key); - } - - children.remove(key); - keyTarget.namesModifiable.remove(key); - } - } - } - - public boolean hasHelpCommand() { - return helpChild != null; - } - - public ModifiableCommandAddress getHelpCommand() { - return helpChild; - } - - @Override - public IChatHandler getChatHandler() { - if (cachedChatHandlerFallback == null) { - if (chatHandler != null) { - cachedChatHandlerFallback = chatHandler; - } else if (!hasParent()) { - cachedChatHandlerFallback = ChatHandlers.defaultChat(); - } else { - cachedChatHandlerFallback = getParent().getChatHandler(); - } - } - return cachedChatHandlerFallback; - } - - public void setChatHandler(IChatHandler chatHandler) { - this.chatHandler = chatHandler; - resetChatHandlerCache(new HashSet<>()); - } - - void resetChatHandlerCache(Set<ModifiableCommandAddress> dejaVu) { - if (dejaVu.add(this)) { - cachedChatHandlerFallback = chatHandler; - for (ChildCommandAddress address : children.values()) { - if (address.chatHandler == null) { - address.resetChatHandlerCache(dejaVu); - } - } - } - } - - @Override - public ICommandDispatcher getDispatcherForTree() { - 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)) { - target.append("<duplicate of address '").append(getAddress()).append("'>"); - return; - } - - if (this instanceof ChildCommandAddress) { - List<String> namesModifiable = ((ChildCommandAddress) this).namesModifiable; - if (namesModifiable.isEmpty()) { - target.append("<no key>"); - } else { - Iterator<String> keys = namesModifiable.iterator(); - target.append(keys.next()).append(' '); - if (keys.hasNext()) { - target.append('(').append(keys.next()); - while (keys.hasNext()) { - target.append(" ,").append(keys.next()); - } - target.append(") "); - } - } - } else { - target.append("<root> "); - } - - String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : "<no command>"; - target.append(commandClass); - - for (ChildCommandAddress child : new HashSet<>(children.values())) { - child.appendDebugInformation(target, linePrefix + " ", seen); - } - } - -} +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;
+
+import java.util.*;
+
+public abstract class ModifiableCommandAddress implements ICommandAddress {
+ Map<String, ChildCommandAddress> children;
+ Collection<String> childrenMainKeys = Collections.emptyList();
+
+ // the chat handler as configured by the programmer
+ IChatHandler chatHandler;
+ // cache for the algorithm that finds the first chat handler going up the tree
+ transient IChatHandler cachedChatHandlerFallback;
+
+ ModifiableCommandAddress helpChild;
+
+ public ModifiableCommandAddress() {
+ this.children = new LinkedHashMap<>(4);
+ }
+
+ @Override
+ public boolean hasParent() {
+ return getParent() != null;
+ }
+
+ @Override
+ public boolean hasCommand() {
+ return getCommand() != null;
+ }
+
+ @Override
+ public boolean hasUserDeclaredCommand() {
+ Command command = getCommand();
+ return command != null && !(command instanceof PredefinedCommand);
+ }
+
+ @Override
+ public Command getCommand() {
+ return null;
+ }
+
+ @Override
+ public boolean isRoot() {
+ return false;
+ }
+
+ @Override
+ public List<String> getNames() {
+ return null;
+ }
+
+ @Override
+ public List<String> getAliases() {
+ List<String> names = getNames();
+ if (names == null) {
+ return null;
+ }
+ if (names.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return names.subList(1, names.size());
+ }
+
+ @Override
+ public String getMainKey() {
+ return null;
+ }
+
+ public void setCommand(Command command) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public abstract ModifiableCommandAddress getParent();
+
+ @Override
+ public RootCommandAddress getRoot() {
+ ModifiableCommandAddress out = this;
+ while (out.hasParent()) {
+ out = out.getParent();
+ }
+ return out.isRoot() ? (RootCommandAddress) out : null;
+ }
+
+ @Override
+ public int getDepth() {
+ int depth = 0;
+ ICommandAddress address = this;
+ while (address.hasParent()) {
+ address = address.getParent();
+ depth++;
+ }
+ return depth;
+ }
+
+ @Override
+ public boolean isDepthLargerThan(int value) {
+ int depth = 0;
+ ICommandAddress address = this;
+ do {
+ if (depth > value) {
+ return true;
+ }
+
+ address = address.getParent();
+ depth++;
+ } while (address != null);
+ return false;
+ }
+
+ @Override
+ public boolean hasChildren() {
+ return !children.isEmpty();
+ }
+
+ @Override
+ public int getNumberOfRealChildren() {
+ return childrenMainKeys.size();
+ }
+
+ @Override
+ public Collection<String> getChildrenMainKeys() {
+ return Collections.unmodifiableCollection(childrenMainKeys);
+ }
+
+ @Override
+ public Map<String, ? extends ModifiableCommandAddress> getChildren() {
+ return Collections.unmodifiableMap(children);
+ }
+
+ @Override
+ public ChildCommandAddress getChild(String key) {
+ return children.get(key);
+ }
+
+ @Override
+ public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ return buffer.hasNext() ? getChild(buffer.next()) : null;
+ }
+
+ public void addChild(ICommandAddress child) {
+ if (!(child instanceof ChildCommandAddress)) {
+ throw new IllegalArgumentException("Argument must be a ChildCommandAddress");
+ }
+
+ ChildCommandAddress mChild = (ChildCommandAddress) child;
+ if (mChild.parent != null) {
+ throw new IllegalArgumentException("Argument already has a parent");
+ }
+
+ if (mChild.names.isEmpty()) {
+ throw new IllegalArgumentException("Argument must have names");
+ }
+
+ Iterator<String> names = mChild.modifiableNamesIterator();
+ String mainKey = names.next();
+
+ if (!childrenMainKeys.contains(mainKey)) {
+ if (!(childrenMainKeys instanceof ArrayList)) {
+ childrenMainKeys = new ArrayList<>();
+ }
+ childrenMainKeys.add(mainKey);
+ }
+
+ children.put(mainKey, mChild);
+
+ while (names.hasNext()) {
+ String name = names.next();
+ if (children.putIfAbsent(name, mChild) != null) {
+ names.remove();
+ }
+ }
+
+ mChild.setParent(this);
+
+ if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) {
+ helpChild = mChild;
+ }
+ }
+
+ public void removeChildren(boolean removeAliases, String... keys) {
+ if (keys.length == 0) {
+ throw new IllegalArgumentException("keys is empty");
+ }
+
+ for (String key : keys) {
+ ChildCommandAddress keyTarget = getChild(key);
+ if (keyTarget == null) {
+ continue;
+ }
+
+ if (removeAliases) {
+ Iterator<String> iterator = keyTarget.namesModifiable.iterator();
+ boolean first = true;
+ while (iterator.hasNext()) {
+ String alias = iterator.next();
+ ChildCommandAddress aliasTarget = getChild(key);
+ if (aliasTarget == keyTarget) {
+ if (first) {
+ childrenMainKeys.remove(alias);
+ }
+ children.remove(alias);
+ }
+ iterator.remove();
+ first = false;
+ }
+
+ } else {
+ if (key.equals(keyTarget.getMainKey())) {
+ childrenMainKeys.remove(key);
+ }
+
+ children.remove(key);
+ keyTarget.namesModifiable.remove(key);
+ }
+ }
+ }
+
+ public boolean hasHelpCommand() {
+ return helpChild != null;
+ }
+
+ public ModifiableCommandAddress getHelpCommand() {
+ return helpChild;
+ }
+
+ @Override
+ public IChatHandler getChatHandler() {
+ if (cachedChatHandlerFallback == null) {
+ if (chatHandler != null) {
+ cachedChatHandlerFallback = chatHandler;
+ } else if (!hasParent()) {
+ cachedChatHandlerFallback = ChatHandlers.defaultChat();
+ } else {
+ cachedChatHandlerFallback = getParent().getChatHandler();
+ }
+ }
+ return cachedChatHandlerFallback;
+ }
+
+ public void setChatHandler(IChatHandler chatHandler) {
+ this.chatHandler = chatHandler;
+ resetChatHandlerCache(new HashSet<>());
+ }
+
+ void resetChatHandlerCache(Set<ModifiableCommandAddress> dejaVu) {
+ if (dejaVu.add(this)) {
+ cachedChatHandlerFallback = chatHandler;
+ for (ChildCommandAddress address : children.values()) {
+ if (address.chatHandler == null) {
+ address.resetChatHandlerCache(dejaVu);
+ }
+ }
+ }
+ }
+
+ @Override
+ public ICommandDispatcher getDispatcherForTree() {
+ 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)) {
+ target.append("<duplicate of address '").append(getAddress()).append("'>");
+ return;
+ }
+
+ if (this instanceof ChildCommandAddress) {
+ List<String> namesModifiable = ((ChildCommandAddress) this).namesModifiable;
+ if (namesModifiable.isEmpty()) {
+ target.append("<no key>");
+ } else {
+ Iterator<String> keys = namesModifiable.iterator();
+ target.append(keys.next()).append(' ');
+ if (keys.hasNext()) {
+ target.append('(').append(keys.next());
+ while (keys.hasNext()) {
+ target.append(" ,").append(keys.next());
+ }
+ target.append(") ");
+ }
+ }
+ } else {
+ target.append("<root> ");
+ }
+
+ String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : "<no command>";
+ target.append(commandClass);
+
+ for (ChildCommandAddress child : new HashSet<>(children.values())) {
+ child.appendDebugInformation(target, linePrefix + " ", seen);
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java index 75b2035..c996356 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java @@ -1,136 +1,136 @@ -package io.dico.dicore.command; - -import java.util.List; -import java.util.Objects; - -public class PermissionContextFilter implements IContextFilter { - private String permission; - private String[] permissionComponents; - private int componentInsertionIndex; - private String failMessage; - - public PermissionContextFilter(String permission) { - this.permission = Objects.requireNonNull(permission); - } - - public PermissionContextFilter(String permission, String failMessage) { - this(permission); - this.failMessage = failMessage; - } - - public PermissionContextFilter(String permission, boolean inheritable) { - this(permission, null, inheritable); - } - - public PermissionContextFilter(String permission, String failMessage, boolean inheritable) { - this(permission, failMessage); - if (inheritable) { - setupInheritability(-1); - } - } - - public PermissionContextFilter(String permission, int componentInsertionIndex, String failMessage) { - this(permission, failMessage); - setupInheritability(componentInsertionIndex); - } - - private void setupInheritability(int componentInsertionIndex) { - this.permissionComponents = permission.split("\\."); - this.componentInsertionIndex = componentInsertionIndex < 0 ? permissionComponents.length : componentInsertionIndex; - if (componentInsertionIndex > permissionComponents.length) throw new IllegalArgumentException(); - } - - private void doFilter(ExecutionContext context, String permission) throws CommandException { - if (failMessage != null) { - Validate.isAuthorized(context.getSender(), permission, failMessage); - } else { - Validate.isAuthorized(context.getSender(), permission); - } - } - - @Override - public void filterContext(ExecutionContext context) throws CommandException { - doFilter(context, permission); - } - - public String getInheritedPermission(String[] components) { - int insertedAmount = components.length; - String[] currentComponents = permissionComponents; - int currentAmount = currentComponents.length; - String[] targetArray = new String[currentAmount + insertedAmount]; - - int insertionIndex; - //int newInsertionIndex; - if (componentInsertionIndex == -1) { - insertionIndex = currentAmount; - //newInsertionIndex = -1; - } else { - insertionIndex = componentInsertionIndex; - //newInsertionIndex = insertionIndex + insertedAmount; - } - - // copy the current components up to insertionIndex - System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex); - // copy the new components into the array at insertionIndex - System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount); - // copy the current components from insertionIndex + inserted amount - System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex); - - return String.join(".", targetArray); - } - - @Override - public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException { - if (isInheritable()) { - doFilter(subContext, getInheritedPermission(path)); - } - } - - @Override - public Priority getPriority() { - return Priority.PERMISSION; - } - - public boolean isInheritable() { - return permissionComponents != null; - } - - public String getPermission() { - return permission; - } - - public int getComponentInsertionIndex() { - return componentInsertionIndex; - } - - public String getFailMessage() { - 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 - } - */ - -} +package io.dico.dicore.command;
+
+import java.util.List;
+import java.util.Objects;
+
+public class PermissionContextFilter implements IContextFilter {
+ private String permission;
+ private String[] permissionComponents;
+ private int componentInsertionIndex;
+ private String failMessage;
+
+ public PermissionContextFilter(String permission) {
+ this.permission = Objects.requireNonNull(permission);
+ }
+
+ public PermissionContextFilter(String permission, String failMessage) {
+ this(permission);
+ this.failMessage = failMessage;
+ }
+
+ public PermissionContextFilter(String permission, boolean inheritable) {
+ this(permission, null, inheritable);
+ }
+
+ public PermissionContextFilter(String permission, String failMessage, boolean inheritable) {
+ this(permission, failMessage);
+ if (inheritable) {
+ setupInheritability(-1);
+ }
+ }
+
+ public PermissionContextFilter(String permission, int componentInsertionIndex, String failMessage) {
+ this(permission, failMessage);
+ setupInheritability(componentInsertionIndex);
+ }
+
+ private void setupInheritability(int componentInsertionIndex) {
+ this.permissionComponents = permission.split("\\.");
+ this.componentInsertionIndex = componentInsertionIndex < 0 ? permissionComponents.length : componentInsertionIndex;
+ if (componentInsertionIndex > permissionComponents.length) throw new IllegalArgumentException();
+ }
+
+ private void doFilter(ExecutionContext context, String permission) throws CommandException {
+ if (failMessage != null) {
+ Validate.isAuthorized(context.getSender(), permission, failMessage);
+ } else {
+ Validate.isAuthorized(context.getSender(), permission);
+ }
+ }
+
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ doFilter(context, permission);
+ }
+
+ public String getInheritedPermission(String[] components) {
+ int insertedAmount = components.length;
+ String[] currentComponents = permissionComponents;
+ int currentAmount = currentComponents.length;
+ String[] targetArray = new String[currentAmount + insertedAmount];
+
+ int insertionIndex;
+ //int newInsertionIndex;
+ if (componentInsertionIndex == -1) {
+ insertionIndex = currentAmount;
+ //newInsertionIndex = -1;
+ } else {
+ insertionIndex = componentInsertionIndex;
+ //newInsertionIndex = insertionIndex + insertedAmount;
+ }
+
+ // copy the current components up to insertionIndex
+ System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex);
+ // copy the new components into the array at insertionIndex
+ System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount);
+ // copy the current components from insertionIndex + inserted amount
+ System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex);
+
+ return String.join(".", targetArray);
+ }
+
+ @Override
+ public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
+ if (isInheritable()) {
+ doFilter(subContext, getInheritedPermission(path));
+ }
+ }
+
+ @Override
+ public Priority getPriority() {
+ return Priority.PERMISSION;
+ }
+
+ public boolean isInheritable() {
+ return permissionComponents != null;
+ }
+
+ public String getPermission() {
+ return permission;
+ }
+
+ public int getComponentInsertionIndex() {
+ return componentInsertionIndex;
+ }
+
+ public String getFailMessage() {
+ 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
+ }
+ */
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java index 44f0540..a17cfb0 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java @@ -76,7 +76,7 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom } - private static void debugChildren(ModifiableCommandAddress address) { + public static void debugChildren(ModifiableCommandAddress address) { Collection<String> keys = address.getChildrenMainKeys(); for (String key : keys) { ChildCommandAddress child = address.getChild(key); @@ -233,18 +233,19 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom @Override public List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) { ExecutionContext context = new ExecutionContext(sender, buffer, true); + long start = System.currentTimeMillis(); try { ICommandAddress target = getCommandTarget(context, buffer); - List<String> out; - if (target.hasCommand()) { + List<String> out = Collections.emptyList(); + /*if (target.hasCommand()) { context.setCommand(target.getCommand()); target.getCommand().initializeAndFilterContext(context); out = target.getCommand().tabComplete(sender, context, location); } else { out = Collections.emptyList(); - } + }*/ int cursor = buffer.getCursor(); String input; @@ -269,6 +270,11 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom } catch (CommandException ex) { return Collections.emptyList(); + } finally { + long duration = System.currentTimeMillis() - start; + if (duration > 2) { + System.out.println(String.format("Complete took %.3f seconds", duration / 1000.0)); + } } } diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java b/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java index 596ad08..3b1f7d4 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java @@ -1,52 +1,52 @@ -package io.dico.dicore.command; - -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; - -import java.util.Optional; - -public class Validate { - - private Validate() { - - } - - //@Contract("false, _ -> fail") - public static void isTrue(boolean expression, String failMessage) throws CommandException { - if (!expression) { - throw new CommandException(failMessage); - } - } - - //@Contract("null, _ -> fail") - public static void notNull(Object obj, String failMessage) throws CommandException { - Validate.isTrue(obj != null, failMessage); - } - - public static void isAuthorized(CommandSender sender, String permission, String failMessage) throws CommandException { - Validate.isTrue(sender.hasPermission(permission), failMessage); - } - - public static void isAuthorized(CommandSender sender, String permission) throws CommandException { - Validate.isAuthorized(sender, permission, "You do not have permission to use that command"); - } - - //@Contract("null -> fail") - public static void isPlayer(CommandSender sender) throws CommandException { - isTrue(sender instanceof Player, "That command can only be used by players"); - } - - //@Contract("null -> fail") - public static void isConsole(CommandSender sender) throws CommandException { - isTrue(sender instanceof ConsoleCommandSender, "That command can only be used by the console"); - } - - public static <T> T returnIfPresent(Optional<T> maybe, String failMessage) throws CommandException { - if (!maybe.isPresent()) { - throw new CommandException(failMessage); - } - return maybe.get(); - } - -} +package io.dico.dicore.command;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.entity.Player;
+
+import java.util.Optional;
+
+public class Validate {
+
+ private Validate() {
+
+ }
+
+ //@Contract("false, _ -> fail")
+ public static void isTrue(boolean expression, String failMessage) throws CommandException {
+ if (!expression) {
+ throw new CommandException(failMessage);
+ }
+ }
+
+ //@Contract("null, _ -> fail")
+ public static void notNull(Object obj, String failMessage) throws CommandException {
+ Validate.isTrue(obj != null, failMessage);
+ }
+
+ public static void isAuthorized(CommandSender sender, String permission, String failMessage) throws CommandException {
+ Validate.isTrue(sender.hasPermission(permission), failMessage);
+ }
+
+ public static void isAuthorized(CommandSender sender, String permission) throws CommandException {
+ Validate.isAuthorized(sender, permission, "You do not have permission to use that command");
+ }
+
+ //@Contract("null -> fail")
+ public static void isPlayer(CommandSender sender) throws CommandException {
+ isTrue(sender instanceof Player, "That command can only be used by players");
+ }
+
+ //@Contract("null -> fail")
+ public static void isConsole(CommandSender sender) throws CommandException {
+ isTrue(sender instanceof ConsoleCommandSender, "That command can only be used by the console");
+ }
+
+ public static <T> T returnIfPresent(Optional<T> maybe, String failMessage) throws CommandException {
+ if (!maybe.isPresent()) {
+ throw new CommandException(failMessage);
+ }
+ return maybe.get();
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java index 467ba4b..b0277c6 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java @@ -1,52 +1,52 @@ -package io.dico.dicore.command.annotation; - -import io.dico.dicore.command.parameter.type.ParameterConfig; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface BigRange { - Class<?> MEMORY_CLASS = Memory.class; - ParameterConfig<BigRange, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(BigRange.class); - Memory DEFAULT = new Memory("MIN", "MAX", "0"); - - String min() default "MIN"; - - String max() default "MAX"; - - String defaultValue() default "0"; - - class Memory { - private final String min; - private final String max; - private final String defaultValue; - - public Memory(BigRange range) { - this(range.min(), range.max(), range.defaultValue()); - } - - public Memory(String min, String max, String defaultValue) { - this.min = min; - this.max = max; - this.defaultValue = defaultValue; - } - - public String min() { - return min; - } - - public String max() { - return max; - } - - public String defaultValue() { - return defaultValue; - } - - } - -} +package io.dico.dicore.command.annotation;
+
+import io.dico.dicore.command.parameter.type.ParameterConfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface BigRange {
+ Class<?> MEMORY_CLASS = Memory.class;
+ ParameterConfig<BigRange, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(BigRange.class);
+ Memory DEFAULT = new Memory("MIN", "MAX", "0");
+
+ String min() default "MIN";
+
+ String max() default "MAX";
+
+ String defaultValue() default "0";
+
+ class Memory {
+ private final String min;
+ private final String max;
+ private final String defaultValue;
+
+ public Memory(BigRange range) {
+ this(range.min(), range.max(), range.defaultValue());
+ }
+
+ public Memory(String min, String max, String defaultValue) {
+ this.min = min;
+ this.max = max;
+ this.defaultValue = defaultValue;
+ }
+
+ public String min() {
+ return min;
+ }
+
+ public String max() {
+ return max;
+ }
+
+ public String defaultValue() {
+ return defaultValue;
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java index 109490a..dfad0bb 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java @@ -1,16 +1,16 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Cmd { - - String value(); - - String[] aliases() default {}; - -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Cmd {
+
+ String value();
+
+ String[] aliases() default {};
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java index ea51e44..3eb2d53 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java @@ -1,27 +1,27 @@ -package io.dico.dicore.command.annotation; - -import io.dico.dicore.command.parameter.type.IParameterTypeSelector; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to mark methods that register a parameter type to the localized selector for use in reflective commands. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface CmdParamType { - - /** - * If this flag is set, the type is registered without its annotation type. - * As a result, the {@link IParameterTypeSelector} is more likely to select it (faster). - * This is irrelevant if there is no annotation type or param config. - * - * @return true if this parameter type should be registered without its annotation type too - */ - boolean infolessAlias() default false; - -} - +package io.dico.dicore.command.annotation;
+
+import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to mark methods that register a parameter type to the localized selector for use in reflective commands.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface CmdParamType {
+
+ /**
+ * If this flag is set, the type is registered without its annotation type.
+ * As a result, the {@link IParameterTypeSelector} is more likely to select it (faster).
+ * This is irrelevant if there is no annotation type or param config.
+ *
+ * @return true if this parameter type should be registered without its annotation type too
+ */
+ boolean infolessAlias() default false;
+
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java index 868884c..7983997 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java @@ -1,35 +1,35 @@ -package io.dico.dicore.command.annotation; - -public class CommandAnnotationUtils { - - /** - * Get the short description from a {@link Desc} annotation. - * If {@link Desc#shortVersion()} is given, returns that. - * Otherwise, returns the first element of {@link Desc#value()} - * If neither is available, returns null. - * - * @param desc the annotation - * @return the short description - */ - public static String getShortDescription(Desc desc) { - String descString; - if (desc == null) { - descString = null; - } else if (!desc.shortVersion().isEmpty()) { - descString = desc.shortVersion(); - } else if (desc.value().length > 0) { - descString = desc.value()[0]; - if (desc.value().length > 1) { - //System.out.println("[Command Warning] Multiline descriptions not supported here. Keep it short for: " + targetIdentifier); - } - if (descString != null && descString.isEmpty()) { - descString = null; - } - } else { - descString = null; - } - - return descString; - } - -} +package io.dico.dicore.command.annotation;
+
+public class CommandAnnotationUtils {
+
+ /**
+ * Get the short description from a {@link Desc} annotation.
+ * If {@link Desc#shortVersion()} is given, returns that.
+ * Otherwise, returns the first element of {@link Desc#value()}
+ * If neither is available, returns null.
+ *
+ * @param desc the annotation
+ * @return the short description
+ */
+ public static String getShortDescription(Desc desc) {
+ String descString;
+ if (desc == null) {
+ descString = null;
+ } else if (!desc.shortVersion().isEmpty()) {
+ descString = desc.shortVersion();
+ } else if (desc.value().length > 0) {
+ descString = desc.value()[0];
+ if (desc.value().length > 1) {
+ //System.out.println("[Command Warning] Multiline descriptions not supported here. Keep it short for: " + targetIdentifier);
+ }
+ if (descString != null && descString.isEmpty()) {
+ descString = null;
+ }
+ } else {
+ descString = null;
+ }
+
+ return descString;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java index 0011fb8..ab3e555 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java @@ -1,27 +1,27 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -public @interface Desc { - - /** - * Multiline description if {@link #shortVersion} is set. - * Otherwise, this should be an array with one element (aka, you don't have to add array brackets). - * - * @return the multiline description. - * @see CommandAnnotationUtils#getShortDescription(Desc) - */ - String[] value(); - - /** - * Short description, use if {@link #value} is multi-line. - * To get a short description from a {@link Desc}, you should use {@link CommandAnnotationUtils#getShortDescription(Desc)} - * - * @return short description - * @see CommandAnnotationUtils#getShortDescription(Desc) - */ - String shortVersion() default ""; - -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Desc {
+
+ /**
+ * Multiline description if {@link #shortVersion} is set.
+ * Otherwise, this should be an array with one element (aka, you don't have to add array brackets).
+ *
+ * @return the multiline description.
+ * @see CommandAnnotationUtils#getShortDescription(Desc)
+ */
+ String[] value();
+
+ /**
+ * Short description, use if {@link #value} is multi-line.
+ * To get a short description from a {@link Desc}, you should use {@link CommandAnnotationUtils#getShortDescription(Desc)}
+ *
+ * @return short description
+ * @see CommandAnnotationUtils#getShortDescription(Desc)
+ */
+ String shortVersion() default "";
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java index 31a47dd..edc50ef 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java @@ -1,16 +1,16 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Flag { - - String value() default ""; - - String permission() default ""; - -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Flag {
+
+ String value() default "";
+
+ String permission() default "";
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java index 9b7164d..eeff351 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java @@ -1,14 +1,14 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface GenerateCommands { - - String[] value() default {"help"}; - -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface GenerateCommands {
+
+ String[] value() default {"help"};
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java index 53e3e9e..de89e00 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java @@ -1,68 +1,68 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to define sub-groups of the group registered reflectively from all methods in a class. - * <p> - * Commands are selected for grouping by matching their method's names to a regular expression. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface GroupMatchedCommands { - - @Retention(RetentionPolicy.RUNTIME) - @interface GroupEntry { - - /** - * Regular expression to match method names for this group - * Must be non-empty - * - * @return the regular expression - */ - String regex(); - - /** - * The name or main key of the sub-group or address - * Must be non-empty - * - * @return the group name - */ - String group(); - - /** - * The aliases for the sub-group - * - * @return the group aliases - */ - String[] groupAliases() default {}; - - /** - * Generated (predefined) commands for the sub-group - */ - String[] generatedCommands() default {}; - - /** - * @see Desc - */ - String[] description() default {}; - - /** - * @see Desc - */ - String shortDescription() default ""; - } - - /** - * The defined groups. - * If a method name matches the regex of multiple groups, - * groups are prioritized by the order in which they appear in this array. - * - * @return the defined groups - */ - GroupEntry[] value(); - -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to define sub-groups of the group registered reflectively from all methods in a class.
+ * <p>
+ * Commands are selected for grouping by matching their method's names to a regular expression.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface GroupMatchedCommands {
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface GroupEntry {
+
+ /**
+ * Regular expression to match method names for this group
+ * Must be non-empty
+ *
+ * @return the regular expression
+ */
+ String regex();
+
+ /**
+ * The name or main key of the sub-group or address
+ * Must be non-empty
+ *
+ * @return the group name
+ */
+ String group();
+
+ /**
+ * The aliases for the sub-group
+ *
+ * @return the group aliases
+ */
+ String[] groupAliases() default {};
+
+ /**
+ * Generated (predefined) commands for the sub-group
+ */
+ String[] generatedCommands() default {};
+
+ /**
+ * @see Desc
+ */
+ String[] description() default {};
+
+ /**
+ * @see Desc
+ */
+ String shortDescription() default "";
+ }
+
+ /**
+ * The defined groups.
+ * If a method name matches the regex of multiple groups,
+ * groups are prioritized by the order in which they appear in this array.
+ *
+ * @return the defined groups
+ */
+ GroupEntry[] value();
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java index fa42e6b..d84358b 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java @@ -1,14 +1,14 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface NamedArg { - - String value(); - -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface NamedArg {
+
+ String value();
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java index 57f53bd..32b0723 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java @@ -1,17 +1,17 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Deprecated -public @interface PreprocessArgs { - - String tokens() default "\"\""; - - char escapeChar() default '\\'; - -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Deprecated
+public @interface PreprocessArgs {
+
+ String tokens() default "\"\"";
+
+ char escapeChar() default '\\';
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java index 3fd4160..98aec22 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java @@ -1,67 +1,67 @@ -package io.dico.dicore.command.annotation; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.Validate; -import io.dico.dicore.command.parameter.type.ParameterConfig; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Range { - Class<?> MEMORY_CLASS = Memory.class; - ParameterConfig<Range, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(Range.class); - Memory DEFAULT = new Memory(-Double.MAX_VALUE, Double.MAX_VALUE, 0); - - double min() default -Double.MAX_VALUE; - - double max() default Double.MAX_VALUE; - - double defaultValue() default 0; - - class Memory { - private final double min; - private final double max; - private final double defaultValue; - - public Memory(Range range) { - this(range.min(), range.max(), range.defaultValue()); - } - - public Memory(double min, double max, double defaultValue) { - this.min = min; - this.max = max; - this.defaultValue = defaultValue; - } - - public double min() { - return min; - } - - public double max() { - return max; - } - - public double defaultValue() { - return defaultValue; - } - - public void validate(Number x, String failMessage) throws CommandException { - Validate.isTrue(valid(x), failMessage); - } - - public boolean valid(Number x) { - double d = x.doubleValue(); - return min <= d && d <= max; - } - - public boolean isDefault() { - return this == DEFAULT || (min == DEFAULT.min && max == DEFAULT.max && defaultValue == DEFAULT.defaultValue); - } - - } - -} +package io.dico.dicore.command.annotation;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.Validate;
+import io.dico.dicore.command.parameter.type.ParameterConfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Range {
+ Class<?> MEMORY_CLASS = Memory.class;
+ ParameterConfig<Range, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(Range.class);
+ Memory DEFAULT = new Memory(-Double.MAX_VALUE, Double.MAX_VALUE, 0);
+
+ double min() default -Double.MAX_VALUE;
+
+ double max() default Double.MAX_VALUE;
+
+ double defaultValue() default 0;
+
+ class Memory {
+ private final double min;
+ private final double max;
+ private final double defaultValue;
+
+ public Memory(Range range) {
+ this(range.min(), range.max(), range.defaultValue());
+ }
+
+ public Memory(double min, double max, double defaultValue) {
+ this.min = min;
+ this.max = max;
+ this.defaultValue = defaultValue;
+ }
+
+ public double min() {
+ return min;
+ }
+
+ public double max() {
+ return max;
+ }
+
+ public double defaultValue() {
+ return defaultValue;
+ }
+
+ public void validate(Number x, String failMessage) throws CommandException {
+ Validate.isTrue(valid(x), failMessage);
+ }
+
+ public boolean valid(Number x) {
+ double d = x.doubleValue();
+ return min <= d && d <= max;
+ }
+
+ public boolean isDefault() {
+ return this == DEFAULT || (min == DEFAULT.min && max == DEFAULT.max && defaultValue == DEFAULT.defaultValue);
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java index 362f05c..3510ca8 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java @@ -1,11 +1,11 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface RequireConsole { -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequireConsole {
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java index 02f5548..68d5706 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java @@ -1,14 +1,14 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface RequireParameters { - - int value(); - -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequireParameters {
+
+ int value();
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java index d2ba782..13d7167 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java @@ -1,33 +1,33 @@ -package io.dico.dicore.command.annotation; - -import io.dico.dicore.command.IContextFilter; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * If this annotation is not present, inheriting permissions is default. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface RequirePermissions { - - /** - * Any permissions that must be present on the sender - * - * @return an array of permission nodes - */ - String[] value(); - - /** - * Whether permissions should (also) be inherited from the parent. - * This uses {@link IContextFilter#INHERIT_PERMISSIONS} - * This is true by default. - * - * @return true if permissions should be inherited. - */ - boolean inherit() default true; - -} +package io.dico.dicore.command.annotation;
+
+import io.dico.dicore.command.IContextFilter;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * If this annotation is not present, inheriting permissions is default.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequirePermissions {
+
+ /**
+ * Any permissions that must be present on the sender
+ *
+ * @return an array of permission nodes
+ */
+ String[] value();
+
+ /**
+ * Whether permissions should (also) be inherited from the parent.
+ * This uses {@link IContextFilter#INHERIT_PERMISSIONS}
+ * This is true by default.
+ *
+ * @return true if permissions should be inherited.
+ */
+ boolean inherit() default true;
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java index 2165e05..3f447f9 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java @@ -1,11 +1,11 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface RequirePlayer { -} +package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequirePlayer {
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java index c0d23ee..ac2a76d 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java @@ -1,94 +1,94 @@ -package io.dico.dicore.command.chat; - -import io.dico.dicore.Formatting; -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.HelpPages; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -public class AbstractChatHandler implements IChatHandler { - private @NotNull HelpPages helpPages; - - public AbstractChatHandler(@NotNull HelpPages helpPages) { - this.helpPages = helpPages; - } - - public AbstractChatHandler() { - this(HelpPages.newDefaultHelpPages()); - } - - @NotNull - public HelpPages getHelpPages() { - return helpPages; - } - - public void setHelpPages(@NotNull HelpPages helpPages) { - this.helpPages = helpPages; - } - - @Override - public Formatting getChatFormatForType(EMessageType type) { - switch (type) { - case EXCEPTION: - case BAD_NEWS: - return Formatting.RED; - case INSTRUCTION: - case NEUTRAL: - return Formatting.GRAY; - case CUSTOM: - return Formatting.WHITE; - case INFORMATIVE: - return Formatting.AQUA; - case RESULT: - default: - case GOOD_NEWS: - return Formatting.GREEN; - case WARNING: - return Formatting.YELLOW; - - case DESCRIPTION: - return Formatting.GREEN; - case SYNTAX: - return Formatting.AQUA; - case HIGHLIGHT: - return Formatting.RED; - case SUBCOMMAND: - return Formatting.GRAY; - case NUMBER: - return Formatting.YELLOW; - } - } - - @Override - public String getMessagePrefixForType(EMessageType type) { - return ""; - } - - protected String createMessage(EMessageType type, String message) { - if (message == null || message.isEmpty()) return null; - return getMessagePrefixForType(type) + getChatFormatForType(type) + message; - } - - @Override - public String createMessage(ExecutionContext context, EMessageType type, String message) { - return createMessage(type, message); - } - - @Override - public String createMessage(CommandSender sender, EMessageType type, String message) { - return createMessage(type, message); - } - - @Override - public String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) { - return helpPages.getHelpPage(sender, context, address, page); - } - - @Override - public String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) { - return helpPages.getSyntax(sender, context, address); - } - -} +package io.dico.dicore.command.chat;
+
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.HelpPages;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+
+public class AbstractChatHandler implements IChatHandler {
+ private @NotNull HelpPages helpPages;
+
+ public AbstractChatHandler(@NotNull HelpPages helpPages) {
+ this.helpPages = helpPages;
+ }
+
+ public AbstractChatHandler() {
+ this(HelpPages.newDefaultHelpPages());
+ }
+
+ @NotNull
+ public HelpPages getHelpPages() {
+ return helpPages;
+ }
+
+ public void setHelpPages(@NotNull HelpPages helpPages) {
+ this.helpPages = helpPages;
+ }
+
+ @Override
+ public Formatting getChatFormatForType(EMessageType type) {
+ switch (type) {
+ case EXCEPTION:
+ case BAD_NEWS:
+ return Formatting.RED;
+ case INSTRUCTION:
+ case NEUTRAL:
+ return Formatting.GRAY;
+ case CUSTOM:
+ return Formatting.WHITE;
+ case INFORMATIVE:
+ return Formatting.AQUA;
+ case RESULT:
+ default:
+ case GOOD_NEWS:
+ return Formatting.GREEN;
+ case WARNING:
+ return Formatting.YELLOW;
+
+ case DESCRIPTION:
+ return Formatting.GREEN;
+ case SYNTAX:
+ return Formatting.AQUA;
+ case HIGHLIGHT:
+ return Formatting.RED;
+ case SUBCOMMAND:
+ return Formatting.GRAY;
+ case NUMBER:
+ return Formatting.YELLOW;
+ }
+ }
+
+ @Override
+ public String getMessagePrefixForType(EMessageType type) {
+ return "";
+ }
+
+ protected String createMessage(EMessageType type, String message) {
+ if (message == null || message.isEmpty()) return null;
+ return getMessagePrefixForType(type) + getChatFormatForType(type) + message;
+ }
+
+ @Override
+ public String createMessage(ExecutionContext context, EMessageType type, String message) {
+ return createMessage(type, message);
+ }
+
+ @Override
+ public String createMessage(CommandSender sender, EMessageType type, String message) {
+ return createMessage(type, message);
+ }
+
+ @Override
+ public String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) {
+ return helpPages.getHelpPage(sender, context, address, page);
+ }
+
+ @Override
+ public String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) {
+ return helpPages.getSyntax(sender, context, address);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java index 232d5cf..cebdb45 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java @@ -1,20 +1,20 @@ -package io.dico.dicore.command.chat; - -/** - * Static factory methods for {@link IChatHandler} - */ -public class ChatHandlers { - private static final IChatHandler defaultChat; - - private ChatHandlers() { - - } - - public static IChatHandler defaultChat() { - return defaultChat; - } - - static { - defaultChat = new AbstractChatHandler(); - } -} +package io.dico.dicore.command.chat;
+
+/**
+ * Static factory methods for {@link IChatHandler}
+ */
+public class ChatHandlers {
+ private static final IChatHandler defaultChat;
+
+ private ChatHandlers() {
+
+ }
+
+ public static IChatHandler defaultChat() {
+ return defaultChat;
+ }
+
+ static {
+ defaultChat = new AbstractChatHandler();
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java index 98283ef..0dd6002 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java @@ -1,60 +1,60 @@ -package io.dico.dicore.command.chat; - -import io.dico.dicore.Formatting; -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.command.CommandSender; - -//TODO add methods to send JSON messages -public interface IChatHandler { - - default void sendMessage(ExecutionContext context, EMessageType type, String message) { - message = createMessage(context, type, message); - if (message != null) { - context.getSender().sendMessage(message); - } - } - - default void sendMessage(CommandSender sender, EMessageType type, String message) { - message = createMessage(sender, type, message); - if (message != null) { - sender.sendMessage(message); - } - } - - default void handleCommandException(CommandSender sender, ExecutionContext context, CommandException exception) { - sendMessage(context, EMessageType.EXCEPTION, exception.getMessage()); - } - - default void handleException(CommandSender sender, ExecutionContext context, Throwable exception) { - if (exception instanceof CommandException) { - handleCommandException(sender, context, (CommandException) exception); - } else { - sendMessage(sender, EMessageType.EXCEPTION, "An internal error occurred whilst executing this command"); - exception.printStackTrace(); - } - } - - default void sendHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) { - sender.sendMessage(createHelpMessage(sender, context, address, page)); - } - - default void sendSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) { - sender.sendMessage(createSyntaxMessage(sender, context, address)); - } - - Formatting getChatFormatForType(EMessageType type); - - String getMessagePrefixForType(EMessageType type); - - String createMessage(ExecutionContext context, EMessageType type, String message); - - String createMessage(CommandSender sender, EMessageType type, String message); - - String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page); - - String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address); - -} +package io.dico.dicore.command.chat;
+
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.command.CommandSender;
+
+//TODO add methods to send JSON messages
+public interface IChatHandler {
+
+ default void sendMessage(ExecutionContext context, EMessageType type, String message) {
+ message = createMessage(context, type, message);
+ if (message != null) {
+ context.getSender().sendMessage(message);
+ }
+ }
+
+ default void sendMessage(CommandSender sender, EMessageType type, String message) {
+ message = createMessage(sender, type, message);
+ if (message != null) {
+ sender.sendMessage(message);
+ }
+ }
+
+ default void handleCommandException(CommandSender sender, ExecutionContext context, CommandException exception) {
+ sendMessage(context, EMessageType.EXCEPTION, exception.getMessage());
+ }
+
+ default void handleException(CommandSender sender, ExecutionContext context, Throwable exception) {
+ if (exception instanceof CommandException) {
+ handleCommandException(sender, context, (CommandException) exception);
+ } else {
+ sendMessage(sender, EMessageType.EXCEPTION, "An internal error occurred whilst executing this command");
+ exception.printStackTrace();
+ }
+ }
+
+ default void sendHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) {
+ sender.sendMessage(createHelpMessage(sender, context, address, page));
+ }
+
+ default void sendSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) {
+ sender.sendMessage(createSyntaxMessage(sender, context, address));
+ }
+
+ Formatting getChatFormatForType(EMessageType type);
+
+ String getMessagePrefixForType(EMessageType type);
+
+ String createMessage(ExecutionContext context, EMessageType type, String message);
+
+ String createMessage(CommandSender sender, EMessageType type, String message);
+
+ String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page);
+
+ String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java index 4382d93..616e85a 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java @@ -1,92 +1,92 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.defaults.*; -import org.bukkit.permissions.Permissible; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class HelpPages { - private @NotNull IPageBuilder pageBuilder; - private @NotNull IPageLayout pageLayout; - private int pageLength; - private @NotNull List<IHelpTopic> helpTopics; - private @NotNull IHelpTopic syntaxTopic; - - public HelpPages(@NotNull IPageBuilder pageBuilder, @NotNull IPageLayout pageLayout, int pageLength, @NotNull IHelpTopic syntaxTopic, @NotNull List<IHelpTopic> helpTopics) { - this.pageBuilder = pageBuilder; - this.pageLayout = pageLayout; - this.pageLength = pageLength; - this.syntaxTopic = syntaxTopic; - this.helpTopics = helpTopics; - } - - public HelpPages(IPageBuilder pageBuilder, IPageLayout pageLayout, int pageLength, IHelpTopic syntaxTopic, IHelpTopic... helpTopics) { - this(pageBuilder, pageLayout, pageLength, syntaxTopic, new ArrayList<>(Arrays.asList(helpTopics))); - } - - @SuppressWarnings("RedundantArrayCreation") - public static HelpPages newDefaultHelpPages() { - IHelpTopic syntaxTopic = new SyntaxHelpTopic(); - return new HelpPages(new DefaultPageBuilder(), new DefaultPageLayout(), 12, - syntaxTopic, new IHelpTopic[]{new DescriptionHelpTopic(), syntaxTopic, new SubcommandsHelpTopic()}); - } - - public @NotNull IPageBuilder getPageBuilder() { - return pageBuilder; - } - - public void setPageBuilder(@NotNull IPageBuilder pageBuilder) { - this.pageBuilder = pageBuilder; - } - - public @NotNull IPageLayout getPageLayout() { - return pageLayout; - } - - public void setPageLayout(@NotNull IPageLayout pageLayout) { - this.pageLayout = pageLayout; - } - - public int getPageLength() { - return pageLength; - } - - public void setPageLength(int pageLength) { - this.pageLength = pageLength; - } - - public @NotNull IHelpTopic getSyntaxTopic() { - return syntaxTopic; - } - - public void setSyntaxTopic(@NotNull IHelpTopic syntaxTopic) { - this.syntaxTopic = syntaxTopic; - } - - @NotNull - public List<IHelpTopic> getHelpTopics() { - return helpTopics; - } - - public void setHelpTopics(@NotNull List<IHelpTopic> helpTopics) { - this.helpTopics = helpTopics; - } - - public @NotNull String getHelpPage(Permissible viewer, ExecutionContext context, ICommandAddress address, int page) { - return pageBuilder.getPage(helpTopics, pageLayout, address, viewer, context, page, pageLength); - } - - public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) { - List<IHelpComponent> components = syntaxTopic.getComponents(address, viewer, context, false); - if (components.isEmpty()) { - return getHelpPage(viewer, context, address, 1); - } - return DefaultPageBuilder.combine(components); - } - -} +package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.defaults.*;
+import org.bukkit.permissions.Permissible;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class HelpPages {
+ private @NotNull IPageBuilder pageBuilder;
+ private @NotNull IPageLayout pageLayout;
+ private int pageLength;
+ private @NotNull List<IHelpTopic> helpTopics;
+ private @NotNull IHelpTopic syntaxTopic;
+
+ public HelpPages(@NotNull IPageBuilder pageBuilder, @NotNull IPageLayout pageLayout, int pageLength, @NotNull IHelpTopic syntaxTopic, @NotNull List<IHelpTopic> helpTopics) {
+ this.pageBuilder = pageBuilder;
+ this.pageLayout = pageLayout;
+ this.pageLength = pageLength;
+ this.syntaxTopic = syntaxTopic;
+ this.helpTopics = helpTopics;
+ }
+
+ public HelpPages(IPageBuilder pageBuilder, IPageLayout pageLayout, int pageLength, IHelpTopic syntaxTopic, IHelpTopic... helpTopics) {
+ this(pageBuilder, pageLayout, pageLength, syntaxTopic, new ArrayList<>(Arrays.asList(helpTopics)));
+ }
+
+ @SuppressWarnings("RedundantArrayCreation")
+ public static HelpPages newDefaultHelpPages() {
+ IHelpTopic syntaxTopic = new SyntaxHelpTopic();
+ return new HelpPages(new DefaultPageBuilder(), new DefaultPageLayout(), 12,
+ syntaxTopic, new IHelpTopic[]{new DescriptionHelpTopic(), syntaxTopic, new SubcommandsHelpTopic()});
+ }
+
+ public @NotNull IPageBuilder getPageBuilder() {
+ return pageBuilder;
+ }
+
+ public void setPageBuilder(@NotNull IPageBuilder pageBuilder) {
+ this.pageBuilder = pageBuilder;
+ }
+
+ public @NotNull IPageLayout getPageLayout() {
+ return pageLayout;
+ }
+
+ public void setPageLayout(@NotNull IPageLayout pageLayout) {
+ this.pageLayout = pageLayout;
+ }
+
+ public int getPageLength() {
+ return pageLength;
+ }
+
+ public void setPageLength(int pageLength) {
+ this.pageLength = pageLength;
+ }
+
+ public @NotNull IHelpTopic getSyntaxTopic() {
+ return syntaxTopic;
+ }
+
+ public void setSyntaxTopic(@NotNull IHelpTopic syntaxTopic) {
+ this.syntaxTopic = syntaxTopic;
+ }
+
+ @NotNull
+ public List<IHelpTopic> getHelpTopics() {
+ return helpTopics;
+ }
+
+ public void setHelpTopics(@NotNull List<IHelpTopic> helpTopics) {
+ this.helpTopics = helpTopics;
+ }
+
+ public @NotNull String getHelpPage(Permissible viewer, ExecutionContext context, ICommandAddress address, int page) {
+ return pageBuilder.getPage(helpTopics, pageLayout, address, viewer, context, page, pageLength);
+ }
+
+ public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) {
+ List<IHelpComponent> components = syntaxTopic.getComponents(address, viewer, context, false);
+ if (components.isEmpty()) {
+ return getHelpPage(viewer, context, address, 1);
+ }
+ return DefaultPageBuilder.combine(components);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java index 7aeb304..b295a35 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java @@ -1,24 +1,24 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.permissions.Permissible; - -import java.util.List; -import java.util.Objects; - -public abstract class HelpTopicModifier implements IHelpTopic { - private final IHelpTopic delegate; - - public HelpTopicModifier(IHelpTopic delegate) { - this.delegate = Objects.requireNonNull(delegate); - } - - @Override - public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context); - } - - protected abstract List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context); - -} +package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+import java.util.Objects;
+
+public abstract class HelpTopicModifier implements IHelpTopic {
+ private final IHelpTopic delegate;
+
+ public HelpTopicModifier(IHelpTopic delegate) {
+ this.delegate = Objects.requireNonNull(delegate);
+ }
+
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context);
+ }
+
+ protected abstract List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java index e1867b5..446c7d7 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java @@ -1,9 +1,9 @@ -package io.dico.dicore.command.chat.help; - -public interface IHelpComponent { - - int lineCount(); - - void appendTo(StringBuilder sb); - -} +package io.dico.dicore.command.chat.help;
+
+public interface IHelpComponent {
+
+ int lineCount();
+
+ void appendTo(StringBuilder sb);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java index 0b3fba2..0f1b5c9 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java @@ -1,23 +1,23 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public interface IHelpTopic { - - /** - * Get the components of this help topic - * - * @param target The address of the command to provide help about - * @param viewer The permissible that the page will be shown to (null -> choose a default set). - * @param context Context of the command execution - * @param isForPage A boolean indicating if the components are to be used in a page (for help) - * @return a mutable list of components to include in the help pages - */ - List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage); - - -} +package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public interface IHelpTopic {
+
+ /**
+ * Get the components of this help topic
+ *
+ * @param target The address of the command to provide help about
+ * @param viewer The permissible that the page will be shown to (null -> choose a default set).
+ * @param context Context of the command execution
+ * @param isForPage A boolean indicating if the components are to be used in a page (for help)
+ * @return a mutable list of components to include in the help pages
+ */
+ List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage);
+
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java index 1ae3561..2789975 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java @@ -1,7 +1,7 @@ -package io.dico.dicore.command.chat.help; - -public interface IPageBorder extends IHelpComponent { - - void setPageCount(int pageCount); - -} +package io.dico.dicore.command.chat.help;
+
+public interface IPageBorder extends IHelpComponent {
+
+ void setPageCount(int pageCount);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java index 86d9450..0584ade 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java @@ -1,13 +1,13 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public interface IPageBuilder { - - String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen); - -} +package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public interface IPageBuilder {
+
+ String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java index 3223e32..32c41d7 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java @@ -1,20 +1,20 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.permissions.Permissible; - -public interface IPageLayout { - - /** - * Get the page borders for a help page - * - * @param target the address that help is displayed for - * @param viewer the viewer of the help page, or null if irrelevant - * @param context the context of the execution - * @param pageNum the page number as displayed in the help page (so it's 1-bound and not 0-bound) - * @return the page borders. - */ - PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum); - -} +package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.permissions.Permissible;
+
+public interface IPageLayout {
+
+ /**
+ * Get the page borders for a help page
+ *
+ * @param target the address that help is displayed for
+ * @param viewer the viewer of the help page, or null if irrelevant
+ * @param context the context of the execution
+ * @param pageNum the page number as displayed in the help page (so it's 1-bound and not 0-bound)
+ * @return the page borders.
+ */
+ PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java index 43c0514..cd105b1 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java @@ -1,76 +1,76 @@ -package io.dico.dicore.command.chat.help; - -import java.util.Arrays; - -public class PageBorders { - private final IPageBorder header, footer; - - public PageBorders(IPageBorder header, IPageBorder footer) { - this.header = header; - this.footer = footer; - } - - public IPageBorder getHeader() { - return header; - } - - public IPageBorder getFooter() { - return footer; - } - - public static IPageBorder simpleBorder(String... lines) { - return new SimplePageBorder(lines); - } - - public static IPageBorder disappearingBorder(int pageNum, String... lines) { - return disappearingBorder(pageNum, 0, lines); - } - - public static IPageBorder disappearingBorder(int pageNum, int keptLines, String... lines) { - return new DisappearingPageBorder(pageNum, keptLines, lines); - } - - static class SimplePageBorder extends SimpleHelpComponent implements IPageBorder { - private final String replacedSequence; - - public SimplePageBorder(String replacedSequence, String... lines) { - super(lines); - this.replacedSequence = replacedSequence; - } - - public SimplePageBorder(String... lines) { - super(lines); - this.replacedSequence = "%pageCount%"; - } - - @Override - public void setPageCount(int pageCount) { - String[] lines = this.lines; - for (int i = 0; i < lines.length; i++) { - lines[i] = lines[i].replace(replacedSequence, Integer.toString(pageCount)); - } - } - - } - - static class DisappearingPageBorder extends SimpleHelpComponent implements IPageBorder { - private final int pageNum; - private final int keptLines; - - public DisappearingPageBorder(int pageNum, int keptLines, String... lines) { - super(lines); - this.pageNum = pageNum; - this.keptLines = keptLines; - } - - @Override - public void setPageCount(int pageCount) { - if (pageCount == pageNum) { - String[] lines = this.lines; - this.lines = Arrays.copyOfRange(lines, Math.max(0, lines.length - keptLines), lines.length); - } - } - - } - -} +package io.dico.dicore.command.chat.help;
+
+import java.util.Arrays;
+
+public class PageBorders {
+ private final IPageBorder header, footer;
+
+ public PageBorders(IPageBorder header, IPageBorder footer) {
+ this.header = header;
+ this.footer = footer;
+ }
+
+ public IPageBorder getHeader() {
+ return header;
+ }
+
+ public IPageBorder getFooter() {
+ return footer;
+ }
+
+ public static IPageBorder simpleBorder(String... lines) {
+ return new SimplePageBorder(lines);
+ }
+
+ public static IPageBorder disappearingBorder(int pageNum, String... lines) {
+ return disappearingBorder(pageNum, 0, lines);
+ }
+
+ public static IPageBorder disappearingBorder(int pageNum, int keptLines, String... lines) {
+ return new DisappearingPageBorder(pageNum, keptLines, lines);
+ }
+
+ static class SimplePageBorder extends SimpleHelpComponent implements IPageBorder {
+ private final String replacedSequence;
+
+ public SimplePageBorder(String replacedSequence, String... lines) {
+ super(lines);
+ this.replacedSequence = replacedSequence;
+ }
+
+ public SimplePageBorder(String... lines) {
+ super(lines);
+ this.replacedSequence = "%pageCount%";
+ }
+
+ @Override
+ public void setPageCount(int pageCount) {
+ String[] lines = this.lines;
+ for (int i = 0; i < lines.length; i++) {
+ lines[i] = lines[i].replace(replacedSequence, Integer.toString(pageCount));
+ }
+ }
+
+ }
+
+ static class DisappearingPageBorder extends SimpleHelpComponent implements IPageBorder {
+ private final int pageNum;
+ private final int keptLines;
+
+ public DisappearingPageBorder(int pageNum, int keptLines, String... lines) {
+ super(lines);
+ this.pageNum = pageNum;
+ this.keptLines = keptLines;
+ }
+
+ @Override
+ public void setPageCount(int pageCount) {
+ if (pageCount == pageNum) {
+ String[] lines = this.lines;
+ this.lines = Arrays.copyOfRange(lines, Math.max(0, lines.length - keptLines), lines.length);
+ }
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java index 22707fd..1b74d0f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java @@ -1,27 +1,27 @@ -package io.dico.dicore.command.chat.help; - -public class SimpleHelpComponent implements IHelpComponent { - String[] lines; - - public SimpleHelpComponent(String... lines) { - this.lines = lines; - } - - @Override - public int lineCount() { - return lines.length; - } - - @Override - public void appendTo(StringBuilder sb) { - String[] lines = this.lines; - int len = lines.length; - if (0 < len) { - sb.append(lines[0]); - } - for (int i = 1; i < len; i++) { - sb.append('\n').append(lines[i]); - } - } - -} +package io.dico.dicore.command.chat.help;
+
+public class SimpleHelpComponent implements IHelpComponent {
+ String[] lines;
+
+ public SimpleHelpComponent(String... lines) {
+ this.lines = lines;
+ }
+
+ @Override
+ public int lineCount() {
+ return lines.length;
+ }
+
+ @Override
+ public void appendTo(StringBuilder sb) {
+ String[] lines = this.lines;
+ int len = lines.length;
+ if (0 < len) {
+ sb.append(lines[0]);
+ }
+ for (int i = 1; i < len; i++) {
+ sb.append('\n').append(lines[i]);
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java index a584e7e..e06730f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java @@ -1,115 +1,115 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.*; -import org.bukkit.permissions.Permissible; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -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) { - if (pageLen <= 0 || pageNum < 0) { - throw new IllegalArgumentException(); - } - - List<IHelpComponent> components = new LinkedList<>(); - for (IHelpTopic topic : helpTopics) { - components.addAll(topic.getComponents(target, viewer, context, true)); - } - - PageBorders pageBorders = null; - int componentStartIdx = -1; - int componentEndIdx = -1; - int totalPageCount = 0; - int curPageLines = 0; - - ListIterator<IHelpComponent> iterator = components.listIterator(); - - while (iterator.hasNext()) { - if (curPageLines == 0) { - - if (pageBorders != null) { - iterator.add(pageBorders.getFooter()); - } - - if (pageNum == totalPageCount) { - componentStartIdx = iterator.nextIndex(); - } else if (pageNum + 1 == totalPageCount) { - componentEndIdx = iterator.nextIndex(); - } - - pageBorders = pageLayout.getPageBorders(target, viewer, context, totalPageCount + 1); - - if (pageBorders != null) { - iterator.add(pageBorders.getHeader()); - iterator.previous(); - - curPageLines += pageBorders.getFooter().lineCount(); - } - - totalPageCount++; - } - - IHelpComponent component = iterator.next(); - int lineCount = component.lineCount(); - curPageLines += lineCount; - - if (curPageLines >= pageLen) { - curPageLines = 0; - } - } - - if (componentStartIdx == -1) { - // page does not exist - return ""; - } - - if (componentEndIdx == -1) { - componentEndIdx = components.size(); - } - - StringBuilder sb = new StringBuilder(); - iterator = components.listIterator(componentStartIdx); - int count = componentEndIdx - componentStartIdx; - boolean first = true; - - while (count-- > 0) { - IHelpComponent component = iterator.next(); - if (component instanceof IPageBorder) { - ((IPageBorder) component).setPageCount(totalPageCount); - } - if (first) { - first = false; - } else { - sb.append('\n'); - } - component.appendTo(sb); - - } - - return sb.toString(); - } - - public static String combine(List<IHelpComponent> components) { - StringBuilder rv = new StringBuilder(); - - Iterator<IHelpComponent> iterator = components.iterator(); - if (iterator.hasNext()) { - iterator.next().appendTo(rv); - } - while (iterator.hasNext()) { - rv.append('\n'); - iterator.next().appendTo(rv); - } - - return rv.toString(); - } - -} +package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.*;
+import org.bukkit.permissions.Permissible;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+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) {
+ if (pageLen <= 0 || pageNum < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ List<IHelpComponent> components = new LinkedList<>();
+ for (IHelpTopic topic : helpTopics) {
+ components.addAll(topic.getComponents(target, viewer, context, true));
+ }
+
+ PageBorders pageBorders = null;
+ int componentStartIdx = -1;
+ int componentEndIdx = -1;
+ int totalPageCount = 0;
+ int curPageLines = 0;
+
+ ListIterator<IHelpComponent> iterator = components.listIterator();
+
+ while (iterator.hasNext()) {
+ if (curPageLines == 0) {
+
+ if (pageBorders != null) {
+ iterator.add(pageBorders.getFooter());
+ }
+
+ if (pageNum == totalPageCount) {
+ componentStartIdx = iterator.nextIndex();
+ } else if (pageNum + 1 == totalPageCount) {
+ componentEndIdx = iterator.nextIndex();
+ }
+
+ pageBorders = pageLayout.getPageBorders(target, viewer, context, totalPageCount + 1);
+
+ if (pageBorders != null) {
+ iterator.add(pageBorders.getHeader());
+ iterator.previous();
+
+ curPageLines += pageBorders.getFooter().lineCount();
+ }
+
+ totalPageCount++;
+ }
+
+ IHelpComponent component = iterator.next();
+ int lineCount = component.lineCount();
+ curPageLines += lineCount;
+
+ if (curPageLines >= pageLen) {
+ curPageLines = 0;
+ }
+ }
+
+ if (componentStartIdx == -1) {
+ // page does not exist
+ return "";
+ }
+
+ if (componentEndIdx == -1) {
+ componentEndIdx = components.size();
+ }
+
+ StringBuilder sb = new StringBuilder();
+ iterator = components.listIterator(componentStartIdx);
+ int count = componentEndIdx - componentStartIdx;
+ boolean first = true;
+
+ while (count-- > 0) {
+ IHelpComponent component = iterator.next();
+ if (component instanceof IPageBorder) {
+ ((IPageBorder) component).setPageCount(totalPageCount);
+ }
+ if (first) {
+ first = false;
+ } else {
+ sb.append('\n');
+ }
+ component.appendTo(sb);
+
+ }
+
+ return sb.toString();
+ }
+
+ public static String combine(List<IHelpComponent> components) {
+ StringBuilder rv = new StringBuilder();
+
+ Iterator<IHelpComponent> iterator = components.iterator();
+ if (iterator.hasNext()) {
+ iterator.next().appendTo(rv);
+ }
+ while (iterator.hasNext()) {
+ rv.append('\n');
+ iterator.next().appendTo(rv);
+ }
+
+ return rv.toString();
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java index 8d3d004..d950b09 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java @@ -1,40 +1,40 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.ModifiableCommandAddress; -import io.dico.dicore.Formatting; -import io.dico.dicore.command.chat.IChatHandler; -import io.dico.dicore.command.chat.help.IPageBorder; -import io.dico.dicore.command.chat.help.IPageLayout; -import io.dico.dicore.command.chat.help.PageBorders; -import org.bukkit.permissions.Permissible; - -public class DefaultPageLayout implements IPageLayout { - - @Override - public PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum) { - IChatHandler c = context.getAddress().getChatHandler(); - String prefix = c.getMessagePrefixForType(EMessageType.INFORMATIVE); - Formatting informative = c.getChatFormatForType(EMessageType.INFORMATIVE); - Formatting number = c.getChatFormatForType(EMessageType.NEUTRAL); - - String nextPageCommand; - ICommandAddress executor = context.getAddress(); - if (((ModifiableCommandAddress) executor).hasHelpCommand()) { - nextPageCommand = ((ModifiableCommandAddress) executor).getHelpCommand().getAddress() + ' ' + (pageNum + 1); - } else { - nextPageCommand = executor.getAddress() + ' ' + (pageNum + 1); - } - - String header = prefix + informative + "Help page " + number + pageNum + informative + - '/' + number + "%pageCount%" + informative + " for /" + target.getAddress(); - String footer = informative + "Type /" + nextPageCommand + " for the next page"; - - IPageBorder headerBorder = PageBorders.simpleBorder("", header); - IPageBorder footerBorder = PageBorders.disappearingBorder(pageNum, footer); - return new PageBorders(headerBorder, footerBorder); - } - -} +package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.ModifiableCommandAddress;
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.chat.IChatHandler;
+import io.dico.dicore.command.chat.help.IPageBorder;
+import io.dico.dicore.command.chat.help.IPageLayout;
+import io.dico.dicore.command.chat.help.PageBorders;
+import org.bukkit.permissions.Permissible;
+
+public class DefaultPageLayout implements IPageLayout {
+
+ @Override
+ public PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum) {
+ IChatHandler c = context.getAddress().getChatHandler();
+ String prefix = c.getMessagePrefixForType(EMessageType.INFORMATIVE);
+ Formatting informative = c.getChatFormatForType(EMessageType.INFORMATIVE);
+ Formatting number = c.getChatFormatForType(EMessageType.NEUTRAL);
+
+ String nextPageCommand;
+ ICommandAddress executor = context.getAddress();
+ if (((ModifiableCommandAddress) executor).hasHelpCommand()) {
+ nextPageCommand = ((ModifiableCommandAddress) executor).getHelpCommand().getAddress() + ' ' + (pageNum + 1);
+ } else {
+ nextPageCommand = executor.getAddress() + ' ' + (pageNum + 1);
+ }
+
+ String header = prefix + informative + "Help page " + number + pageNum + informative +
+ '/' + number + "%pageCount%" + informative + " for /" + target.getAddress();
+ String footer = informative + "Type /" + nextPageCommand + " for the next page";
+
+ IPageBorder headerBorder = PageBorders.simpleBorder("", header);
+ IPageBorder footerBorder = PageBorders.disappearingBorder(pageNum, footer);
+ return new PageBorders(headerBorder, footerBorder);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java index ae88ea2..0899db4 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java @@ -1,45 +1,45 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.Command; -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.Formatting; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import io.dico.dicore.command.chat.help.SimpleHelpComponent; -import org.bukkit.permissions.Permissible; - -import java.util.ArrayList; -import java.util.List; - -public class DescriptionHelpTopic implements IHelpTopic { - - @Override - public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - List<IHelpComponent> out = new ArrayList<>(); - Formatting format = context.getFormat(EMessageType.DESCRIPTION); - - if (!target.hasCommand()) { - return out; - } - Command command = target.getCommand(); - String[] description = command.getDescription(); - if (description.length == 0) { - String shortDescription = command.getShortDescription(); - if (shortDescription == null) { - return out; - } - - description = new String[]{shortDescription}; - } - - for (int i = 0; i < description.length; i++) { - description[i] = format + description[i]; - } - - out.add(new SimpleHelpComponent(description)); - return out; - } - -} +package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.Command;
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import io.dico.dicore.command.chat.help.SimpleHelpComponent;
+import org.bukkit.permissions.Permissible;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DescriptionHelpTopic implements IHelpTopic {
+
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ List<IHelpComponent> out = new ArrayList<>();
+ Formatting format = context.getFormat(EMessageType.DESCRIPTION);
+
+ if (!target.hasCommand()) {
+ return out;
+ }
+ Command command = target.getCommand();
+ String[] description = command.getDescription();
+ if (description.length == 0) {
+ String shortDescription = command.getShortDescription();
+ if (shortDescription == null) {
+ return out;
+ }
+
+ description = new String[]{shortDescription};
+ }
+
+ for (int i = 0; i < description.length; i++) {
+ description[i] = format + description[i];
+ }
+
+ out.add(new SimpleHelpComponent(description));
+ return out;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java index 0e680ae..b302769 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java @@ -1,59 +1,59 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.Formatting; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import io.dico.dicore.command.chat.help.SimpleHelpComponent; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.Permissible; - -import java.util.*; - -public class SubcommandsHelpTopic implements IHelpTopic { - - @Override - public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - Collection<String> mainKeys = target.getChildrenMainKeys(); - if (mainKeys.isEmpty()) { - return Collections.emptyList(); - } - - List<IHelpComponent> result = new ArrayList<>(); - - mainKeys = new ArrayList<>(target.getChildrenMainKeys()); - ((ArrayList<String>) mainKeys).sort(null); - - CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender(); - for (String key : mainKeys) { - ICommandAddress child = target.getChild(key); - if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) { - result.add(getComponent(child, viewer, context)); - } - } - - return result; - } - - public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) { - Formatting subcommand = colorOf(context, EMessageType.SUBCOMMAND); - Formatting highlight = colorOf(context, EMessageType.HIGHLIGHT); - - String address = subcommand + "/" + child.getParent().getAddress() + ' ' + highlight + child.getMainKey(); - - String description = child.hasCommand() ? child.getCommand().getShortDescription() : null; - if (description != null) { - Formatting descriptionFormat = colorOf(context, EMessageType.DESCRIPTION); - return new SimpleHelpComponent(address, descriptionFormat + description); - } - - return new SimpleHelpComponent(address); - } - - private static Formatting colorOf(ExecutionContext context, EMessageType type) { - return context.getAddress().getChatHandler().getChatFormatForType(type); - } - -} +package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import io.dico.dicore.command.chat.help.SimpleHelpComponent;
+import org.bukkit.command.CommandSender;
+import org.bukkit.permissions.Permissible;
+
+import java.util.*;
+
+public class SubcommandsHelpTopic implements IHelpTopic {
+
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ Collection<String> mainKeys = target.getChildrenMainKeys();
+ if (mainKeys.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List<IHelpComponent> result = new ArrayList<>();
+
+ mainKeys = new ArrayList<>(target.getChildrenMainKeys());
+ ((ArrayList<String>) mainKeys).sort(null);
+
+ CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender();
+ for (String key : mainKeys) {
+ ICommandAddress child = target.getChild(key);
+ if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) {
+ result.add(getComponent(child, viewer, context));
+ }
+ }
+
+ return result;
+ }
+
+ public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) {
+ Formatting subcommand = colorOf(context, EMessageType.SUBCOMMAND);
+ Formatting highlight = colorOf(context, EMessageType.HIGHLIGHT);
+
+ String address = subcommand + "/" + child.getParent().getAddress() + ' ' + highlight + child.getMainKey();
+
+ String description = child.hasCommand() ? child.getCommand().getShortDescription() : null;
+ if (description != null) {
+ Formatting descriptionFormat = colorOf(context, EMessageType.DESCRIPTION);
+ return new SimpleHelpComponent(address, descriptionFormat + description);
+ }
+
+ return new SimpleHelpComponent(address);
+ }
+
+ private static Formatting colorOf(ExecutionContext context, EMessageType type) {
+ return context.getAddress().getChatHandler().getChatFormatForType(type);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java index d0e3ebe..f0f820f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java @@ -1,92 +1,92 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.Command; -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.Formatting; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import io.dico.dicore.command.chat.help.SimpleHelpComponent; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.ParameterList; -import org.bukkit.permissions.Permissible; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class SyntaxHelpTopic implements IHelpTopic { - - @Override - public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - if (!target.hasCommand()) { - return Collections.emptyList(); - } - - if (target.hasChildren()) { - if (!isForPage) { - // HelpPages will send help instead of syntax, which might in turn include syntax as well. - return Collections.emptyList(); - } - - if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) { - // no point adding syntax at all - return Collections.emptyList(); - } - } - - StringBuilder line = new StringBuilder(); - if (isForPage) - line.append(context.getFormat(EMessageType.SYNTAX)) - .append("Syntax: "); - - line.append('/') - .append(context.getFormat(EMessageType.INSTRUCTION)) - .append(target.getAddress()) - .append(' '); - - addShortSyntax(line, target, context); - - return Collections.singletonList(new SimpleHelpComponent(line.toString())); - } - - private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) { - if (address.hasCommand()) { - Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX); - Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT); - builder.append(syntaxColor); - - Command command = address.getCommand(); - ParameterList list = command.getParameterList(); - Parameter<?, ?> repeated = list.getRepeatedParameter(); - - int requiredCount = list.getRequiredCount(); - List<Parameter<?, ?>> indexedParameters = list.getIndexedParameters(); - for (int i = 0, n = indexedParameters.size(); i < n; i++) { - builder.append(i < requiredCount ? " <" : " ["); - Parameter<?, ?> param = indexedParameters.get(i); - builder.append(param.getName()); - if (param == repeated) { - builder.append(highlight).append("...").append(syntaxColor); - } - builder.append(i < requiredCount ? '>' : ']'); - } - - Map<String, Parameter<?, ?>> parametersByName = list.getParametersByName(); - for (Parameter<?, ?> param : parametersByName.values()) { - if (param.isFlag()) { - builder.append(" [").append(param.getName()); - if (param.expectsInput()) { - builder.append(" <").append(param.getName()).append(">"); - } - builder.append(']'); - } - } - - } else { - builder.append(' '); - } - } - -} +package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.Command;
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import io.dico.dicore.command.chat.help.SimpleHelpComponent;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.ParameterList;
+import org.bukkit.permissions.Permissible;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class SyntaxHelpTopic implements IHelpTopic {
+
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ if (!target.hasCommand()) {
+ return Collections.emptyList();
+ }
+
+ if (target.hasChildren()) {
+ if (!isForPage) {
+ // HelpPages will send help instead of syntax, which might in turn include syntax as well.
+ return Collections.emptyList();
+ }
+
+ if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) {
+ // no point adding syntax at all
+ return Collections.emptyList();
+ }
+ }
+
+ StringBuilder line = new StringBuilder();
+ if (isForPage)
+ line.append(context.getFormat(EMessageType.SYNTAX))
+ .append("Syntax: ");
+
+ line.append('/')
+ .append(context.getFormat(EMessageType.INSTRUCTION))
+ .append(target.getAddress())
+ .append(' ');
+
+ addShortSyntax(line, target, context);
+
+ return Collections.singletonList(new SimpleHelpComponent(line.toString()));
+ }
+
+ private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) {
+ if (address.hasCommand()) {
+ Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX);
+ Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT);
+ builder.append(syntaxColor);
+
+ Command command = address.getCommand();
+ ParameterList list = command.getParameterList();
+ Parameter<?, ?> repeated = list.getRepeatedParameter();
+
+ int requiredCount = list.getRequiredCount();
+ List<Parameter<?, ?>> indexedParameters = list.getIndexedParameters();
+ for (int i = 0, n = indexedParameters.size(); i < n; i++) {
+ builder.append(i < requiredCount ? " <" : " [");
+ Parameter<?, ?> param = indexedParameters.get(i);
+ builder.append(param.getName());
+ if (param == repeated) {
+ builder.append(highlight).append("...").append(syntaxColor);
+ }
+ builder.append(i < requiredCount ? '>' : ']');
+ }
+
+ Map<String, Parameter<?, ?>> parametersByName = list.getParametersByName();
+ for (Parameter<?, ?> param : parametersByName.values()) {
+ if (param.isFlag()) {
+ builder.append(" [").append(param.getName());
+ if (param.expectsInput()) {
+ builder.append(" <").append(param.getName()).append(">");
+ }
+ builder.append(']');
+ }
+ }
+
+ } else {
+ builder.append(' ');
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java index 4f0026d..eb3605d 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java @@ -1,29 +1,29 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.IHelpComponent; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public enum EInsertionStage implements IInsertionFunction { - START { - @Override - public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) { - return 0; - } - }, - CENTER { - @Override - public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) { - return current.size() / 2; - } - }, - END { - @Override - public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) { - return current.size(); - } - } -} +package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public enum EInsertionStage implements IInsertionFunction {
+ START {
+ @Override
+ public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ return 0;
+ }
+ },
+ CENTER {
+ @Override
+ public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ return current.size() / 2;
+ }
+ },
+ END {
+ @Override
+ public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ return current.size();
+ }
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java index f153165..75b52d0 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java @@ -1,43 +1,43 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.HelpTopicModifier; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import org.bukkit.permissions.Permissible; - -import java.util.ArrayList; -import java.util.List; - -public class HelpComponentInserter extends HelpTopicModifier { - private List<IInsertion> insertions = new ArrayList<>(); - - public HelpComponentInserter(IHelpTopic delegate) { - super(delegate); - } - - @Override - protected List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context) { - // int componentCount = components.size(); - - for (int i = insertions.size() - 1; i >= 0; i--) { - IInsertion insertion = insertions.get(i); - int idx = insertion.insertionIndex(components, target, viewer, context); - List<IHelpComponent> inserted = insertion.getComponents(target, viewer, context, true); - components.addAll(idx, inserted); - } - - return components; - } - - public HelpComponentInserter insert(IInsertionFunction insertionFunction, IHelpTopic helpTopic) { - return insert(Insertions.combine(helpTopic, insertionFunction)); - } - - public HelpComponentInserter insert(IInsertion insertion) { - insertions.add(insertion); - return this; - } - -} +package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.HelpTopicModifier;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import org.bukkit.permissions.Permissible;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HelpComponentInserter extends HelpTopicModifier {
+ private List<IInsertion> insertions = new ArrayList<>();
+
+ public HelpComponentInserter(IHelpTopic delegate) {
+ super(delegate);
+ }
+
+ @Override
+ protected List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ // int componentCount = components.size();
+
+ for (int i = insertions.size() - 1; i >= 0; i--) {
+ IInsertion insertion = insertions.get(i);
+ int idx = insertion.insertionIndex(components, target, viewer, context);
+ List<IHelpComponent> inserted = insertion.getComponents(target, viewer, context, true);
+ components.addAll(idx, inserted);
+ }
+
+ return components;
+ }
+
+ public HelpComponentInserter insert(IInsertionFunction insertionFunction, IHelpTopic helpTopic) {
+ return insert(Insertions.combine(helpTopic, insertionFunction));
+ }
+
+ public HelpComponentInserter insert(IInsertion insertion) {
+ insertions.add(insertion);
+ return this;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java index 757cb91..92309d3 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java @@ -1,7 +1,7 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.chat.help.IHelpTopic; - -interface IInsertion extends IHelpTopic, IInsertionFunction { - -} +package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.chat.help.IHelpTopic;
+
+interface IInsertion extends IHelpTopic, IInsertionFunction {
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java index e99c246..356d002 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java @@ -1,14 +1,14 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.IHelpComponent; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public interface IInsertionFunction { - - int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context); - -} +package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public interface IInsertionFunction {
+
+ int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java index 39b2784..220f05b 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java @@ -1,31 +1,31 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public class Insertions { - - private Insertions() { - - } - - public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) { - return new IInsertion() { - @Override - public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - return topic.getComponents(target, viewer, context, true); - } - - @Override - public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) { - return function.insertionIndex(current, target, viewer, context); - } - }; - } - -} +package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public class Insertions {
+
+ private Insertions() {
+
+ }
+
+ public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) {
+ return new IInsertion() {
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ return topic.getComponents(target, viewer, context, true);
+ }
+
+ @Override
+ public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ return function.insertionIndex(current, target, viewer, context);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java index aa69730..5646814 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java @@ -1,295 +1,295 @@ -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.*; - -/** - * Buffer for the arguments. - * Easy to traverse for the parser. - */ -public class ArgumentBuffer extends AbstractList<String> implements Iterator<String>, RandomAccess { - private String[] array; - private int cursor = 0; // index of the next return value - private transient ArgumentBuffer unaffectingCopy = null; // see #getUnaffectingCopy() - - public ArgumentBuffer(String label, String[] args) { - this(combine(label, args)); - } - - private static String[] combine(String label, String[] args) { - String[] result; - //if (args.length > 0 && "".equals(args[args.length - 1])) { - // // drop the last element of args if it is empty - // result = args; - //} else { - result = new String[args.length + 1]; - //} - System.arraycopy(args, 0, result, 1, result.length - 1); - result[0] = Objects.requireNonNull(label); - return result; - } - - /** - * Constructs a new ArgumentBuffer using the given array, without copying it first. - * 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) { - for (String elem : array) { - if (elem == null) throw new NullPointerException("ArgumentBuffer array element"); - } - this.array = array; - - } - - public int getCursor() { - return cursor; - } - - public @NotNull ArgumentBuffer setCursor(int cursor) { - if (cursor <= 0) { - cursor = 0; - } else if (size() <= cursor) { - cursor = size(); - } - this.cursor = cursor; - return this; - } - - @Override - public int size() { - return array.length; - } - - @Override - public @NotNull String get(int index) { - return array[index]; - } - - public int nextIndex() { - return cursor; - } - - public int previousIndex() { - return cursor - 1; - } - - public int remainingElements() { - return size() - nextIndex() - 1; - } - - @Override - public boolean hasNext() { - return nextIndex() < size(); - } - - public boolean hasPrevious() { - return 0 <= previousIndex(); - } - - /** - * Unlike conventional ListIterator implementations, this returns null if there is no next element - * - * @return the next value, or null - */ - @Override - public @Nullable String next() { - return hasNext() ? get(cursor++) : null; - } - - public @NotNull String requireNext(String parameterName) throws CommandException { - String next = next(); - if (next == null) { - throw CommandException.missingArgument(parameterName); - } - return next; - } - - // useful for completion code - public @NotNull String nextOrEmpty() { - return hasNext() ? get(cursor++) : ""; - } - - /** - * Unlike conventional ListIterator implementations, this returns null if there is no previous element - * - * @return the previous value, or null - */ - public @Nullable String previous() { - return hasPrevious() ? get(--cursor) : null; - } - - public @Nullable String peekNext() { - return hasNext() ? get(cursor) : null; - } - - public @Nullable String peekPrevious() { - return hasPrevious() ? get(cursor - 1) : null; - } - - public @NotNull ArgumentBuffer advance() { - return advance(1); - } - - public @NotNull ArgumentBuffer advance(int amount) { - cursor = Math.min(Math.max(0, cursor + amount), size()); - return this; - } - - public @NotNull ArgumentBuffer rewind() { - return rewind(1); - } - - public @NotNull ArgumentBuffer rewind(int amount) { - return advance(-amount); - } - - @NotNull String[] getArray() { - return array; - } - - public @NotNull String[] getArrayFromCursor() { - return getArrayFromIndex(cursor); - } - - public @NotNull String[] getArrayFromIndex(int index) { - return Arrays.copyOfRange(array, index, array.length); - } - - public @NotNull String getRawInput() { - return String.join(" ", array); - } - - public @NotNull String[] toArray() { - return array.clone(); - } - - @Override - public @NotNull Iterator<String> iterator() { - return this; - } - - @Override - public @NotNull ListIterator<String> listIterator() { - return new ListIterator<String>() { - @Override - public boolean hasNext() { - return ArgumentBuffer.this.hasNext(); - } - - @Override - public String next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return ArgumentBuffer.this.next(); - } - - @Override - public boolean hasPrevious() { - return ArgumentBuffer.this.hasPrevious(); - } - - @Override - public String previous() { - if (!hasPrevious()) { - throw new NoSuchElementException(); - } - return ArgumentBuffer.this.previous(); - } - - @Override - public int nextIndex() { - return ArgumentBuffer.this.nextIndex(); - } - - @Override - public int previousIndex() { - return ArgumentBuffer.this.previousIndex(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - @Override - public void set(String s) { - throw new UnsupportedOperationException(); - } - - @Override - public void add(String s) { - throw new UnsupportedOperationException(); - } - }; - } - - public void dropTrailingEmptyElements() { - int removeCount = 0; - String[] array = this.array; - for (int i = array.length - 1; i >= 0; i--) { - if ("".equals(array[i])) { - removeCount++; - } - } - - if (removeCount > 0) { - String[] newArray = new String[array.length - removeCount]; - System.arraycopy(array, 0, newArray, 0, newArray.length); - this.array = newArray; - - if (cursor > newArray.length) { - cursor = newArray.length; - } - } - } - - /** - * 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); - } - - /** - * Allows a piece of code to traverse this buffer without modifying its cursor. - * After this method has been called for the first time on this instance, if this method - * or the {@link #clone()} method are called, the operation carried out on the prior result has finished. - * As such, the same instance might be returned again. - * - * @return A view of this buffer that doesn't affect this buffer's cursor. - */ - public ArgumentBuffer getUnaffectingCopy() { - // the copy doesn't alter the cursor of this ArgumentBuffer when moved, but traverses the same array reference. - // there is only ever one copy of an ArgumentBuffer, the cursor of which is updated on every call to this method. - - ArgumentBuffer unaffectingCopy = this.unaffectingCopy; - if (unaffectingCopy == null) { - this.unaffectingCopy = unaffectingCopy = new ArgumentBuffer(array); - } - unaffectingCopy.cursor = this.cursor; - return unaffectingCopy; - } - - @SuppressWarnings("MethodDoesntCallSuperMethod") - 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()); - } - -} +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.*;
+
+/**
+ * Buffer for the arguments.
+ * Easy to traverse for the parser.
+ */
+public class ArgumentBuffer extends AbstractList<String> implements Iterator<String>, RandomAccess {
+ private String[] array;
+ private int cursor = 0; // index of the next return value
+ private transient ArgumentBuffer unaffectingCopy = null; // see #getUnaffectingCopy()
+
+ public ArgumentBuffer(String label, String[] args) {
+ this(combine(label, args));
+ }
+
+ private static String[] combine(String label, String[] args) {
+ String[] result;
+ //if (args.length > 0 && "".equals(args[args.length - 1])) {
+ // // drop the last element of args if it is empty
+ // result = args;
+ //} else {
+ result = new String[args.length + 1];
+ //}
+ System.arraycopy(args, 0, result, 1, result.length - 1);
+ result[0] = Objects.requireNonNull(label);
+ return result;
+ }
+
+ /**
+ * Constructs a new ArgumentBuffer using the given array, without copying it first.
+ * 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) {
+ for (String elem : array) {
+ if (elem == null) throw new NullPointerException("ArgumentBuffer array element");
+ }
+ this.array = array;
+
+ }
+
+ public int getCursor() {
+ return cursor;
+ }
+
+ public @NotNull ArgumentBuffer setCursor(int cursor) {
+ if (cursor <= 0) {
+ cursor = 0;
+ } else if (size() <= cursor) {
+ cursor = size();
+ }
+ this.cursor = cursor;
+ return this;
+ }
+
+ @Override
+ public int size() {
+ return array.length;
+ }
+
+ @Override
+ public @NotNull String get(int index) {
+ return array[index];
+ }
+
+ public int nextIndex() {
+ return cursor;
+ }
+
+ public int previousIndex() {
+ return cursor - 1;
+ }
+
+ public int remainingElements() {
+ return size() - nextIndex() - 1;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return nextIndex() < size();
+ }
+
+ public boolean hasPrevious() {
+ return 0 <= previousIndex();
+ }
+
+ /**
+ * Unlike conventional ListIterator implementations, this returns null if there is no next element
+ *
+ * @return the next value, or null
+ */
+ @Override
+ public @Nullable String next() {
+ return hasNext() ? get(cursor++) : null;
+ }
+
+ public @NotNull String requireNext(String parameterName) throws CommandException {
+ String next = next();
+ if (next == null) {
+ throw CommandException.missingArgument(parameterName);
+ }
+ return next;
+ }
+
+ // useful for completion code
+ public @NotNull String nextOrEmpty() {
+ return hasNext() ? get(cursor++) : "";
+ }
+
+ /**
+ * Unlike conventional ListIterator implementations, this returns null if there is no previous element
+ *
+ * @return the previous value, or null
+ */
+ public @Nullable String previous() {
+ return hasPrevious() ? get(--cursor) : null;
+ }
+
+ public @Nullable String peekNext() {
+ return hasNext() ? get(cursor) : null;
+ }
+
+ public @Nullable String peekPrevious() {
+ return hasPrevious() ? get(cursor - 1) : null;
+ }
+
+ public @NotNull ArgumentBuffer advance() {
+ return advance(1);
+ }
+
+ public @NotNull ArgumentBuffer advance(int amount) {
+ cursor = Math.min(Math.max(0, cursor + amount), size());
+ return this;
+ }
+
+ public @NotNull ArgumentBuffer rewind() {
+ return rewind(1);
+ }
+
+ public @NotNull ArgumentBuffer rewind(int amount) {
+ return advance(-amount);
+ }
+
+ @NotNull String[] getArray() {
+ return array;
+ }
+
+ public @NotNull String[] getArrayFromCursor() {
+ return getArrayFromIndex(cursor);
+ }
+
+ public @NotNull String[] getArrayFromIndex(int index) {
+ return Arrays.copyOfRange(array, index, array.length);
+ }
+
+ public @NotNull String getRawInput() {
+ return String.join(" ", array);
+ }
+
+ public @NotNull String[] toArray() {
+ return array.clone();
+ }
+
+ @Override
+ public @NotNull Iterator<String> iterator() {
+ return this;
+ }
+
+ @Override
+ public @NotNull ListIterator<String> listIterator() {
+ return new ListIterator<String>() {
+ @Override
+ public boolean hasNext() {
+ return ArgumentBuffer.this.hasNext();
+ }
+
+ @Override
+ public String next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return ArgumentBuffer.this.next();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return ArgumentBuffer.this.hasPrevious();
+ }
+
+ @Override
+ public String previous() {
+ if (!hasPrevious()) {
+ throw new NoSuchElementException();
+ }
+ return ArgumentBuffer.this.previous();
+ }
+
+ @Override
+ public int nextIndex() {
+ return ArgumentBuffer.this.nextIndex();
+ }
+
+ @Override
+ public int previousIndex() {
+ return ArgumentBuffer.this.previousIndex();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void add(String s) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public void dropTrailingEmptyElements() {
+ int removeCount = 0;
+ String[] array = this.array;
+ for (int i = array.length - 1; i >= 0; i--) {
+ if ("".equals(array[i])) {
+ removeCount++;
+ }
+ }
+
+ if (removeCount > 0) {
+ String[] newArray = new String[array.length - removeCount];
+ System.arraycopy(array, 0, newArray, 0, newArray.length);
+ this.array = newArray;
+
+ if (cursor > newArray.length) {
+ cursor = newArray.length;
+ }
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Allows a piece of code to traverse this buffer without modifying its cursor.
+ * After this method has been called for the first time on this instance, if this method
+ * or the {@link #clone()} method are called, the operation carried out on the prior result has finished.
+ * As such, the same instance might be returned again.
+ *
+ * @return A view of this buffer that doesn't affect this buffer's cursor.
+ */
+ public ArgumentBuffer getUnaffectingCopy() {
+ // the copy doesn't alter the cursor of this ArgumentBuffer when moved, but traverses the same array reference.
+ // there is only ever one copy of an ArgumentBuffer, the cursor of which is updated on every call to this method.
+
+ ArgumentBuffer unaffectingCopy = this.unaffectingCopy;
+ if (unaffectingCopy == null) {
+ this.unaffectingCopy = unaffectingCopy = new ArgumentBuffer(array);
+ }
+ unaffectingCopy.cursor = this.cursor;
+ return unaffectingCopy;
+ }
+
+ @SuppressWarnings("MethodDoesntCallSuperMethod")
+ 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());
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java index ce818b7..a9ccd20 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java @@ -1,177 +1,177 @@ -package io.dico.dicore.command.parameter; - -public class ArgumentMergingPreProcessor implements IArgumentPreProcessor { - private final String tokens; - private final char escapeChar; - - public ArgumentMergingPreProcessor(String tokens, char escapeChar) { - if ((tokens.length() & 1) != 0 || tokens.isEmpty()) throw new IllegalArgumentException(); - this.tokens = tokens; - this.escapeChar = escapeChar; - } - - @Override - 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 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(String[] args, int start, int count) { - this.start = start; - this.args = args; - this.count = count; - } - - private void reset() { - foundSectionCount = 0; - currentIndex = start; - sectionStart = -1; - closingToken = 0; - sectionEnd = -1; - removeCount = 0; - } - - private boolean findNextSectionStart() { - if (count >= 0 && foundSectionCount >= count) return false; - - while (currentIndex < args.length) { - String arg = args[currentIndex]; - if (arg == null) { - throw new IllegalArgumentException(); - } - - if (arg.isEmpty()) { - ++currentIndex; - continue; - } - - int openingTokenIndex = tokens.indexOf(arg.charAt(0)); - if (openingTokenIndex == -1 || (openingTokenIndex & 1) != 0) { - ++currentIndex; - continue; - } - - // found - closingToken = tokens.charAt(openingTokenIndex | 1); - sectionStart = currentIndex; - return true; - } - - return false; - } - - private boolean findNextSectionEnd() { - while (currentIndex < args.length) { - String arg = args[currentIndex]; - if (arg == null) { - throw new IllegalArgumentException(); - } - - if (arg.isEmpty() - || arg.charAt(arg.length() - 1) != closingToken - || (sectionStart == currentIndex && arg.length() == 1)) { - ++currentIndex; - continue; - } - - if (escapeChar != 0 - && arg.length() > 1 - && arg.charAt(arg.length() - 2) == escapeChar) { - // escaped - ++currentIndex; - continue; - } - - // found - closingToken = 0; - sectionEnd = currentIndex; - ++currentIndex; - return true; - } - - return false; - } - - private void processFoundSection() { - if (sectionStart == sectionEnd) { - String arg = args[sectionStart]; - args[sectionStart] = arg.substring(1, arg.length() - 1); - return; - } - - removeCount += sectionEnd - sectionStart; - - StringBuilder sb = new StringBuilder(); - sb.append(args[sectionStart].substring(1)); - - for (int i = sectionStart + 1; i < sectionEnd; i++) { - sb.append(' '); - sb.append(args[i]); - args[i] = null; - } - sb.append(' '); - sb.append(args[sectionEnd].substring(0, args[sectionEnd].length() - 1)); - args[sectionEnd] = null; - - args[sectionStart] = sb.toString(); - - sectionStart = -1; - sectionEnd = -1; - - ++foundSectionCount; - } - - String[] doProcess() { - reset(); - - while (findNextSectionStart()) { - if (findNextSectionEnd()) { - processFoundSection(); - } else { - currentIndex = sectionStart + 1; - } - } - - if (removeCount == 0) { - return args; - } - - String[] result = new String[args.length - removeCount]; - int i = 0; - for (String arg : args) { - if (arg != null) { - result[i++] = arg; - } - } - - return result; - } - - void updateBuffer(ArgumentBuffer buffer) { - if (count < 0) { - buffer.setCursor(start); - } else { - buffer.setCursor(currentIndex); - } - } - - } - -} - - +package io.dico.dicore.command.parameter;
+
+public class ArgumentMergingPreProcessor implements IArgumentPreProcessor {
+ private final String tokens;
+ private final char escapeChar;
+
+ public ArgumentMergingPreProcessor(String tokens, char escapeChar) {
+ if ((tokens.length() & 1) != 0 || tokens.isEmpty()) throw new IllegalArgumentException();
+ this.tokens = tokens;
+ this.escapeChar = escapeChar;
+ }
+
+ @Override
+ 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 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(String[] args, int start, int count) {
+ this.start = start;
+ this.args = args;
+ this.count = count;
+ }
+
+ private void reset() {
+ foundSectionCount = 0;
+ currentIndex = start;
+ sectionStart = -1;
+ closingToken = 0;
+ sectionEnd = -1;
+ removeCount = 0;
+ }
+
+ private boolean findNextSectionStart() {
+ if (count >= 0 && foundSectionCount >= count) return false;
+
+ while (currentIndex < args.length) {
+ String arg = args[currentIndex];
+ if (arg == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (arg.isEmpty()) {
+ ++currentIndex;
+ continue;
+ }
+
+ int openingTokenIndex = tokens.indexOf(arg.charAt(0));
+ if (openingTokenIndex == -1 || (openingTokenIndex & 1) != 0) {
+ ++currentIndex;
+ continue;
+ }
+
+ // found
+ closingToken = tokens.charAt(openingTokenIndex | 1);
+ sectionStart = currentIndex;
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean findNextSectionEnd() {
+ while (currentIndex < args.length) {
+ String arg = args[currentIndex];
+ if (arg == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (arg.isEmpty()
+ || arg.charAt(arg.length() - 1) != closingToken
+ || (sectionStart == currentIndex && arg.length() == 1)) {
+ ++currentIndex;
+ continue;
+ }
+
+ if (escapeChar != 0
+ && arg.length() > 1
+ && arg.charAt(arg.length() - 2) == escapeChar) {
+ // escaped
+ ++currentIndex;
+ continue;
+ }
+
+ // found
+ closingToken = 0;
+ sectionEnd = currentIndex;
+ ++currentIndex;
+ return true;
+ }
+
+ return false;
+ }
+
+ private void processFoundSection() {
+ if (sectionStart == sectionEnd) {
+ String arg = args[sectionStart];
+ args[sectionStart] = arg.substring(1, arg.length() - 1);
+ return;
+ }
+
+ removeCount += sectionEnd - sectionStart;
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(args[sectionStart].substring(1));
+
+ for (int i = sectionStart + 1; i < sectionEnd; i++) {
+ sb.append(' ');
+ sb.append(args[i]);
+ args[i] = null;
+ }
+ sb.append(' ');
+ sb.append(args[sectionEnd].substring(0, args[sectionEnd].length() - 1));
+ args[sectionEnd] = null;
+
+ args[sectionStart] = sb.toString();
+
+ sectionStart = -1;
+ sectionEnd = -1;
+
+ ++foundSectionCount;
+ }
+
+ String[] doProcess() {
+ reset();
+
+ while (findNextSectionStart()) {
+ if (findNextSectionEnd()) {
+ processFoundSection();
+ } else {
+ currentIndex = sectionStart + 1;
+ }
+ }
+
+ if (removeCount == 0) {
+ return args;
+ }
+
+ String[] result = new String[args.length - removeCount];
+ int i = 0;
+ for (String arg : args) {
+ if (arg != null) {
+ result[i++] = arg;
+ }
+ }
+
+ return result;
+ }
+
+ void updateBuffer(ArgumentBuffer buffer) {
+ if (count < 0) {
+ buffer.setCursor(start);
+ } else {
+ buffer.setCursor(currentIndex);
+ }
+ }
+
+ }
+
+}
+
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java index a5afce5..7418c4a 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java @@ -89,6 +89,8 @@ public class ContextParser { m_curRepeatingList = null; assignDefaultValuesToUncomputedParams(); arrayifyRepeatedParamValue(); + + m_done = true; } } diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java index 0b8198e..5495cce 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java @@ -1,41 +1,41 @@ -package io.dico.dicore.command.parameter; - -/** - * An interface to process tokens such as quotes - */ -@Deprecated -public interface IArgumentPreProcessor { - - /** - * 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. - * - * <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 - */ - ArgumentBuffer process(ArgumentBuffer buffer, int count); - - IArgumentPreProcessor NONE = (buffer, count) -> buffer; - - /** - * Get an IArgumentPreProcessor that merges arguments between any two tokens - * - * @param tokens The tokens that the merged arguments should be enclosed by, in subsequent pairs. - * Example: []{}"" - * This would mean the following would be merged: [ hello this is a merged argument] - * @param escapeChar the char that can be used to escape the given tokens - * @return The IArgumentPreProcessor - */ - static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) { - return new ArgumentMergingPreProcessor(tokens, escapeChar); - } - -} - +package io.dico.dicore.command.parameter;
+
+/**
+ * An interface to process tokens such as quotes
+ */
+@Deprecated
+public interface IArgumentPreProcessor {
+
+ /**
+ * 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.
+ *
+ * <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
+ */
+ ArgumentBuffer process(ArgumentBuffer buffer, int count);
+
+ IArgumentPreProcessor NONE = (buffer, count) -> buffer;
+
+ /**
+ * Get an IArgumentPreProcessor that merges arguments between any two tokens
+ *
+ * @param tokens The tokens that the merged arguments should be enclosed by, in subsequent pairs.
+ * Example: []{}""
+ * This would mean the following would be merged: [ hello this is a merged argument]
+ * @param escapeChar the char that can be used to escape the given tokens
+ * @return The IArgumentPreProcessor
+ */
+ static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) {
+ return new ArgumentMergingPreProcessor(tokens, escapeChar);
+ }
+
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java index 613d057..f35628c 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java @@ -1,148 +1,148 @@ -package io.dico.dicore.command.parameter; - -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 int requiredCount = -1; - private boolean repeatFinalParameter; - - // if the final parameter is repeated and the command is implemented through reflection, - // the repeated parameter is simply the last parameter of the method, rather than the last - // indexed parameter. This might be a flag. As such, this field exists to ensure the correct - // 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; - } - - public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) { - this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor; - return this; - }*/ - - public boolean repeatFinalParameter() { - return repeatFinalParameter; - } - - public ParameterList setRepeatFinalParameter(boolean repeatFinalParameter) { - this.repeatFinalParameter = repeatFinalParameter; - return this; - } - - public boolean finalParameterMayBeFlag() { - return finalParameterMayBeFlag; - } - - public ParameterList setFinalParameterMayBeFlag(boolean finalParameterMayBeFlag) { - this.finalParameterMayBeFlag = finalParameterMayBeFlag; - return this; - } - - public int getRequiredCount() { - return requiredCount == -1 ? indexedParameters.size() : requiredCount; - } - - public ParameterList setRequiredCount(int requiredCount) { - this.requiredCount = requiredCount; - return this; - } - - public boolean hasAnyParameters() { - return !byName.isEmpty(); - } - - public int getIndexedParameterCount() { - return indexedParameters.size(); - } - - public List<Parameter<?, ?>> getIndexedParameters() { - return Collections.unmodifiableList(indexedParameters); - } - - public Parameter<?, ?> getParameterByName(String name) { - return byName.get(name); - } - - public String getIndexedParameterName(int index) { - return indexedParameters.get(index).getName(); - } - - public Map<String, Parameter<?, ?>> getParametersByName() { - return Collections.unmodifiableMap(byName); - } - - /** - * Add the given parameter to the end of this parameter list - * Can be a flag - * - * @param parameter the parameter - * @return this - */ - public ParameterList addParameter(Parameter<?, ?> parameter) { - return addParameter(-1, parameter); - } - - /** - * Add the given parameter to this parameter list - * If the parameter is a flag, the index is ignored - * - * @param index parameter index number, -1 if end - * @param parameter the parameter - * @return this - * @throws NullPointerException if parameter is null - */ - public ParameterList addParameter(int index, Parameter<?, ?> parameter) { - //System.out.println("Added parameter " + parameter.getName() + ", flag: " + parameter.isFlag()); - byName.put(parameter.getName(), parameter); - if (!parameter.isFlag()) { - indexedParameters.add(index == -1 ? indexedParameters.size() : index, parameter); - } - return this; - } - - public Parameter<?, ?> getRepeatedParameter() { - if (!repeatFinalParameter) { - return null; - } - if (finalParameterMayBeFlag) { - Iterator<Parameter<?, ?>> iterator = byName.values().iterator(); - Parameter<?, ?> result = null; - while (iterator.hasNext()) { - result = iterator.next(); - } - return result; - } - - if (indexedParameters.isEmpty()) { - return null; - } - - return indexedParameters.get(indexedParameters.size() - 1); - } - -} +package io.dico.dicore.command.parameter;
+
+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 int requiredCount = -1;
+ private boolean repeatFinalParameter;
+
+ // if the final parameter is repeated and the command is implemented through reflection,
+ // the repeated parameter is simply the last parameter of the method, rather than the last
+ // indexed parameter. This might be a flag. As such, this field exists to ensure the correct
+ // 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;
+ }
+
+ public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) {
+ this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor;
+ return this;
+ }*/
+
+ public boolean repeatFinalParameter() {
+ return repeatFinalParameter;
+ }
+
+ public ParameterList setRepeatFinalParameter(boolean repeatFinalParameter) {
+ this.repeatFinalParameter = repeatFinalParameter;
+ return this;
+ }
+
+ public boolean finalParameterMayBeFlag() {
+ return finalParameterMayBeFlag;
+ }
+
+ public ParameterList setFinalParameterMayBeFlag(boolean finalParameterMayBeFlag) {
+ this.finalParameterMayBeFlag = finalParameterMayBeFlag;
+ return this;
+ }
+
+ public int getRequiredCount() {
+ return requiredCount == -1 ? indexedParameters.size() : requiredCount;
+ }
+
+ public ParameterList setRequiredCount(int requiredCount) {
+ this.requiredCount = requiredCount;
+ return this;
+ }
+
+ public boolean hasAnyParameters() {
+ return !byName.isEmpty();
+ }
+
+ public int getIndexedParameterCount() {
+ return indexedParameters.size();
+ }
+
+ public List<Parameter<?, ?>> getIndexedParameters() {
+ return Collections.unmodifiableList(indexedParameters);
+ }
+
+ public Parameter<?, ?> getParameterByName(String name) {
+ return byName.get(name);
+ }
+
+ public String getIndexedParameterName(int index) {
+ return indexedParameters.get(index).getName();
+ }
+
+ public Map<String, Parameter<?, ?>> getParametersByName() {
+ return Collections.unmodifiableMap(byName);
+ }
+
+ /**
+ * Add the given parameter to the end of this parameter list
+ * Can be a flag
+ *
+ * @param parameter the parameter
+ * @return this
+ */
+ public ParameterList addParameter(Parameter<?, ?> parameter) {
+ return addParameter(-1, parameter);
+ }
+
+ /**
+ * Add the given parameter to this parameter list
+ * If the parameter is a flag, the index is ignored
+ *
+ * @param index parameter index number, -1 if end
+ * @param parameter the parameter
+ * @return this
+ * @throws NullPointerException if parameter is null
+ */
+ public ParameterList addParameter(int index, Parameter<?, ?> parameter) {
+ //System.out.println("Added parameter " + parameter.getName() + ", flag: " + parameter.isFlag());
+ byName.put(parameter.getName(), parameter);
+ if (!parameter.isFlag()) {
+ indexedParameters.add(index == -1 ? indexedParameters.size() : index, parameter);
+ }
+ return this;
+ }
+
+ public Parameter<?, ?> getRepeatedParameter() {
+ if (!repeatFinalParameter) {
+ return null;
+ }
+ if (finalParameterMayBeFlag) {
+ Iterator<Parameter<?, ?>> iterator = byName.values().iterator();
+ Parameter<?, ?> result = null;
+ while (iterator.hasNext()) {
+ result = iterator.next();
+ }
+ return result;
+ }
+
+ if (indexedParameters.isEmpty()) {
+ return null;
+ }
+
+ return indexedParameters.get(indexedParameters.size() - 1);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java index c23e09b..063d381 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java @@ -1,46 +1,46 @@ -package io.dico.dicore.command.parameter.type; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.ArrayList; -import java.util.List; - -public class EnumParameterType<E extends Enum> extends SimpleParameterType<E, Void> { - private final E[] universe; - - public EnumParameterType(Class<E> returnType) { - super(returnType); - universe = returnType.getEnumConstants(); - if (universe == null) { - throw new IllegalArgumentException("returnType must be an enum"); - } - } - - @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; - } - } - - 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) { - String input = buffer.next().toUpperCase(); - List<String> result = new ArrayList<>(); - for (E constant : universe) { - if (constant.name().toUpperCase().startsWith(input.toUpperCase())) { - result.add(constant.name().toLowerCase()); - } - } - return result; - } - -} +package io.dico.dicore.command.parameter.type;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EnumParameterType<E extends Enum> extends SimpleParameterType<E, Void> {
+ private final E[] universe;
+
+ public EnumParameterType(Class<E> returnType) {
+ super(returnType);
+ universe = returnType.getEnumConstants();
+ if (universe == null) {
+ throw new IllegalArgumentException("returnType must be an enum");
+ }
+ }
+
+ @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;
+ }
+ }
+
+ 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) {
+ String input = buffer.next().toUpperCase();
+ List<String> result = new ArrayList<>();
+ for (E constant : universe) {
+ if (constant.name().toUpperCase().startsWith(input.toUpperCase())) {
+ result.add(constant.name().toLowerCase());
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java index 780ea0d..381b15c 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java @@ -1,44 +1,44 @@ -package io.dico.dicore.command.parameter.type; - -import java.lang.annotation.Annotation; - -/** - * An interface for an object that stores parameter types by {@link ParameterKey} and finds appropriate types for {@link ParameterKey parameterKeys} - */ -public interface IParameterTypeSelector { - - <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key); - - //<TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(ParameterKey key); - - <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key); - - - default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType) { - return selectExact(returnType, null); - } - - default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType, Class<? extends Annotation> annotationClass) { - return selectExact(new ParameterKey(returnType, annotationClass)); - } - - /* - default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType) { - return selectExactOrSubclass(returnType, null); - } - - default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType, Class<? extends Annotation> annotationClass) { - return selectExactOrSubclass(new ParameterKey(returnType, annotationClass)); - } - */ - default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType) { - return selectAny(returnType, null); - } - - default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType, Class<? extends Annotation> annotationClass) { - return selectAny(new ParameterKey(returnType, annotationClass)); - } - - void addType(boolean infolessAlias, ParameterType<?, ?> type); - -} +package io.dico.dicore.command.parameter.type;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * An interface for an object that stores parameter types by {@link ParameterKey} and finds appropriate types for {@link ParameterKey parameterKeys}
+ */
+public interface IParameterTypeSelector {
+
+ <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key);
+
+ //<TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(ParameterKey key);
+
+ <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key);
+
+
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType) {
+ return selectExact(returnType, null);
+ }
+
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType, Class<? extends Annotation> annotationClass) {
+ return selectExact(new ParameterKey(returnType, annotationClass));
+ }
+
+ /*
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType) {
+ return selectExactOrSubclass(returnType, null);
+ }
+
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType, Class<? extends Annotation> annotationClass) {
+ return selectExactOrSubclass(new ParameterKey(returnType, annotationClass));
+ }
+ */
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType) {
+ return selectAny(returnType, null);
+ }
+
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType, Class<? extends Annotation> annotationClass) {
+ return selectAny(new ParameterKey(returnType, annotationClass));
+ }
+
+ void addType(boolean infolessAlias, ParameterType<?, ?> type);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java index d407f87..ef86eab 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java @@ -1,114 +1,114 @@ -package io.dico.dicore.command.parameter.type; - -import java.lang.annotation.Annotation; -import java.util.HashMap; -import java.util.Map; - -/** - * Map based implementation of {@link IParameterTypeSelector} - */ -public class MapBasedParameterTypeSelector implements IParameterTypeSelector { - static final MapBasedParameterTypeSelector defaultSelector = new MapBasedParameterTypeSelector(false); - private final Map<ParameterKey, ParameterType<?, ?>> parameterTypeMap; - private final boolean useDefault; - - public MapBasedParameterTypeSelector(boolean useDefault) { - this.parameterTypeMap = new HashMap<>(); - this.useDefault = useDefault; - } - - @Override - public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key) { - ParameterType<?, ?> out = parameterTypeMap.get(key); - if (useDefault && out == null) { - out = defaultSelector.selectExact(key); - } - if (out == null && key.getReturnType().isEnum()) { - //noinspection unchecked - out = new EnumParameterType(key.getReturnType()); - addType(false, out); - } - return cast(out); - } - - @Override - public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key) { - ParameterType<TReturn, TParamInfo> exact = selectExact(key); - if (exact != null) { - return exact; - } - - if (key.getAnnotationClass() != null) { - exact = selectExact(new ParameterKey(key.getReturnType())); - if (exact != null) { - return exact; - } - } - - Class<?> returnType = key.getReturnType(); - Class<? extends Annotation> annotationClass = key.getAnnotationClass(); - - ParameterType<?, ?> out = selectByReturnType(parameterTypeMap, returnType, annotationClass, false); - if (out == null && useDefault) { - out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, false); - } - if (out == null) { - out = selectByReturnType(parameterTypeMap, returnType, annotationClass, true); - } - if (out == null && useDefault) { - out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, true); - } - return cast(out); - } - - private static ParameterType<?, ?> selectByReturnType(Map<ParameterKey, ParameterType<?, ?>> map, Class<?> returnType, - Class<? extends Annotation> annotationClass, boolean allowSubclass) { - ParameterType<?, ?> out = null; - if (allowSubclass) { - for (ParameterType<?, ?> type : map.values()) { - if (returnType.isAssignableFrom(type.getReturnType())) { - if (annotationClass == type.getAnnotationClass()) { - out = type; - break; - } - if (out == null) { - out = type; - } - } - } - } else { - for (ParameterType<?, ?> type : map.values()) { - if (returnType == type.getReturnType()) { - if (annotationClass == type.getAnnotationClass()) { - out = type; - break; - } - if (out == null) { - out = type; - } - } - } - } - return out; - } - - private static <T> T cast(Object o) { - //noinspection unchecked - return (T) o; - } - - @Override - public void addType(boolean infolessAlias, ParameterType<?, ?> type) { - parameterTypeMap.put(type.getTypeKey(), type); - - if (infolessAlias) { - parameterTypeMap.putIfAbsent(type.getInfolessTypeKey(), type); - } - } - - static { - // registers default parameter types - ParameterTypes.clinit(); - } - -} +package io.dico.dicore.command.parameter.type;
+
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Map based implementation of {@link IParameterTypeSelector}
+ */
+public class MapBasedParameterTypeSelector implements IParameterTypeSelector {
+ static final MapBasedParameterTypeSelector defaultSelector = new MapBasedParameterTypeSelector(false);
+ private final Map<ParameterKey, ParameterType<?, ?>> parameterTypeMap;
+ private final boolean useDefault;
+
+ public MapBasedParameterTypeSelector(boolean useDefault) {
+ this.parameterTypeMap = new HashMap<>();
+ this.useDefault = useDefault;
+ }
+
+ @Override
+ public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key) {
+ ParameterType<?, ?> out = parameterTypeMap.get(key);
+ if (useDefault && out == null) {
+ out = defaultSelector.selectExact(key);
+ }
+ if (out == null && key.getReturnType().isEnum()) {
+ //noinspection unchecked
+ out = new EnumParameterType(key.getReturnType());
+ addType(false, out);
+ }
+ return cast(out);
+ }
+
+ @Override
+ public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key) {
+ ParameterType<TReturn, TParamInfo> exact = selectExact(key);
+ if (exact != null) {
+ return exact;
+ }
+
+ if (key.getAnnotationClass() != null) {
+ exact = selectExact(new ParameterKey(key.getReturnType()));
+ if (exact != null) {
+ return exact;
+ }
+ }
+
+ Class<?> returnType = key.getReturnType();
+ Class<? extends Annotation> annotationClass = key.getAnnotationClass();
+
+ ParameterType<?, ?> out = selectByReturnType(parameterTypeMap, returnType, annotationClass, false);
+ if (out == null && useDefault) {
+ out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, false);
+ }
+ if (out == null) {
+ out = selectByReturnType(parameterTypeMap, returnType, annotationClass, true);
+ }
+ if (out == null && useDefault) {
+ out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, true);
+ }
+ return cast(out);
+ }
+
+ private static ParameterType<?, ?> selectByReturnType(Map<ParameterKey, ParameterType<?, ?>> map, Class<?> returnType,
+ Class<? extends Annotation> annotationClass, boolean allowSubclass) {
+ ParameterType<?, ?> out = null;
+ if (allowSubclass) {
+ for (ParameterType<?, ?> type : map.values()) {
+ if (returnType.isAssignableFrom(type.getReturnType())) {
+ if (annotationClass == type.getAnnotationClass()) {
+ out = type;
+ break;
+ }
+ if (out == null) {
+ out = type;
+ }
+ }
+ }
+ } else {
+ for (ParameterType<?, ?> type : map.values()) {
+ if (returnType == type.getReturnType()) {
+ if (annotationClass == type.getAnnotationClass()) {
+ out = type;
+ break;
+ }
+ if (out == null) {
+ out = type;
+ }
+ }
+ }
+ }
+ return out;
+ }
+
+ private static <T> T cast(Object o) {
+ //noinspection unchecked
+ return (T) o;
+ }
+
+ @Override
+ public void addType(boolean infolessAlias, ParameterType<?, ?> type) {
+ parameterTypeMap.put(type.getTypeKey(), type);
+
+ if (infolessAlias) {
+ parameterTypeMap.putIfAbsent(type.getInfolessTypeKey(), type);
+ }
+ }
+
+ static {
+ // registers default parameter types
+ ParameterTypes.clinit();
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java index dbd7590..d33932b 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java @@ -1,80 +1,80 @@ -package io.dico.dicore.command.parameter.type; - -import io.dico.dicore.Reflection; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; - -/** - * This class serves the purpose of having annotated parameter configurations (such as ranges for number parameters). - * Such configurations must be possible to obtain without using annotations, and as such, there should be a class conveying the information - * that is separate from the annotation itself. This class acts as a bridge from the annotation to said class conveying the information. - * - * @param <TAnnotation> the annotation type for parameters - * @param <TParamInfo> the object type that holds the information required in memory - */ -public abstract class ParameterConfig<TAnnotation extends Annotation, TParamInfo> implements Comparable<ParameterConfig<?, ?>> { - private final Class<TAnnotation> annotationClass; - // protected final TParamInfo defaultValue; - - public ParameterConfig(Class<TAnnotation> annotationClass/*, TParamInfo defaultValue*/) { - this.annotationClass = annotationClass; - //this.defaultValue = defaultValue; - } - - public final Class<TAnnotation> getAnnotationClass() { - return annotationClass; - } - /* - public TParamInfo getDefaultValue() { - return defaultValue; - }*/ - - protected abstract TParamInfo toParameterInfo(TAnnotation annotation); - - public TParamInfo getParameterInfo(Annotation annotation) { - //noinspection unchecked - return toParameterInfo((TAnnotation) annotation); - } - - public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo> - includeMemoryClass(Class<TAnnotation> annotationClass, Class<TParamInfo> memoryClass) { - Constructor<TParamInfo> constructor; - //TParamInfo defaultValue; - try { - constructor = memoryClass.getConstructor(annotationClass); - //defaultValue = Reflection.getStaticFieldValue(annotationClass, "DEFAULT"); - } catch (NoSuchMethodException | IllegalArgumentException ex) { - throw new IllegalArgumentException(ex); - } - /* - if (defaultValue == null) try { - defaultValue = memoryClass.newInstance(); - } catch (IllegalAccessException | InstantiationException ex) { - throw new IllegalArgumentException("Failed to get a default value for the param info", ex); - }*/ - - return new ParameterConfig<TAnnotation, TParamInfo>(annotationClass/*, defaultValue*/) { - - @Override - public TParamInfo toParameterInfo(TAnnotation annotation) { - try { - return constructor.newInstance(annotation); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - }; - } - - public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo> getMemoryClassFromField(Class<TAnnotation> annotationClass) { - return ParameterConfig.includeMemoryClass(annotationClass, Reflection.getStaticFieldValue(annotationClass, "MEMORY_CLASS")); - } - - @Override - public int compareTo(ParameterConfig<?, ?> o) { - return 0; - } - -} - +package io.dico.dicore.command.parameter.type;
+
+import io.dico.dicore.Reflection;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+
+/**
+ * This class serves the purpose of having annotated parameter configurations (such as ranges for number parameters).
+ * Such configurations must be possible to obtain without using annotations, and as such, there should be a class conveying the information
+ * that is separate from the annotation itself. This class acts as a bridge from the annotation to said class conveying the information.
+ *
+ * @param <TAnnotation> the annotation type for parameters
+ * @param <TParamInfo> the object type that holds the information required in memory
+ */
+public abstract class ParameterConfig<TAnnotation extends Annotation, TParamInfo> implements Comparable<ParameterConfig<?, ?>> {
+ private final Class<TAnnotation> annotationClass;
+ // protected final TParamInfo defaultValue;
+
+ public ParameterConfig(Class<TAnnotation> annotationClass/*, TParamInfo defaultValue*/) {
+ this.annotationClass = annotationClass;
+ //this.defaultValue = defaultValue;
+ }
+
+ public final Class<TAnnotation> getAnnotationClass() {
+ return annotationClass;
+ }
+ /*
+ public TParamInfo getDefaultValue() {
+ return defaultValue;
+ }*/
+
+ protected abstract TParamInfo toParameterInfo(TAnnotation annotation);
+
+ public TParamInfo getParameterInfo(Annotation annotation) {
+ //noinspection unchecked
+ return toParameterInfo((TAnnotation) annotation);
+ }
+
+ public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo>
+ includeMemoryClass(Class<TAnnotation> annotationClass, Class<TParamInfo> memoryClass) {
+ Constructor<TParamInfo> constructor;
+ //TParamInfo defaultValue;
+ try {
+ constructor = memoryClass.getConstructor(annotationClass);
+ //defaultValue = Reflection.getStaticFieldValue(annotationClass, "DEFAULT");
+ } catch (NoSuchMethodException | IllegalArgumentException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ /*
+ if (defaultValue == null) try {
+ defaultValue = memoryClass.newInstance();
+ } catch (IllegalAccessException | InstantiationException ex) {
+ throw new IllegalArgumentException("Failed to get a default value for the param info", ex);
+ }*/
+
+ return new ParameterConfig<TAnnotation, TParamInfo>(annotationClass/*, defaultValue*/) {
+
+ @Override
+ public TParamInfo toParameterInfo(TAnnotation annotation) {
+ try {
+ return constructor.newInstance(annotation);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ };
+ }
+
+ public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo> getMemoryClassFromField(Class<TAnnotation> annotationClass) {
+ return ParameterConfig.includeMemoryClass(annotationClass, Reflection.getStaticFieldValue(annotationClass, "MEMORY_CLASS"));
+ }
+
+ @Override
+ public int compareTo(ParameterConfig<?, ?> o) {
+ return 0;
+ }
+
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java index e1a62fa..e9cca7f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java @@ -1,149 +1,149 @@ -package io.dico.dicore.command.parameter.type; - -import io.dico.dicore.Reflection; -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.annotation.Range; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * A parameter type. - * Takes care of parsing, default values as well as completions. - * - * @param <TReturn> type of the parameter - * @param <TParamInfo> the info object type for the parameter (Example: {@link Range.Memory} - */ -public abstract class ParameterType<TReturn, TParamInfo> { - private final Class<TReturn> returnType; - private final ParameterConfig<?, TParamInfo> parameterConfig; - protected final ParameterType<TReturn, TParamInfo> otherType; // flag or non-flag, depending on current - - public ParameterType(Class<TReturn> returnType) { - this(returnType, null); - } - - public ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) { - this.returnType = Objects.requireNonNull(returnType); - this.parameterConfig = paramConfig; - - ParameterType<TReturn, TParamInfo> otherType = flagTypeParameter(); - this.otherType = otherType == null ? this : otherType; - } - - protected ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> parameterConfig, ParameterType<TReturn, TParamInfo> otherType) { - this.returnType = returnType; - this.parameterConfig = parameterConfig; - this.otherType = otherType; - } - - public int getExpectedAmountOfConsumedArguments() { - return 1; - } - - public boolean canBeFlag() { - return this == otherType; - } - - public boolean isFlagExplicitly() { - return this instanceof FlagParameterType; - } - - /** - * @return The return type - */ - public final Class<TReturn> getReturnType() { - return returnType; - } - - public final Class<?> getAnnotationClass() { - return parameterConfig == null ? null : parameterConfig.getAnnotationClass(); - } - - public final ParameterConfig<?, TParamInfo> getParameterConfig() { - return parameterConfig; - } - - public ParameterKey getTypeKey() { - return new ParameterKey(returnType, parameterConfig != null ? parameterConfig.getAnnotationClass() : null); - } - - public ParameterKey getInfolessTypeKey() { - return new ParameterKey(returnType, null); - } - - protected FlagParameterType<TReturn, TParamInfo> flagTypeParameter() { - return null; - } - - public ParameterType<TReturn, TParamInfo> asFlagParameter() { - return canBeFlag() ? this : otherType; - } - - public ParameterType<TReturn, TParamInfo> asNormalParameter() { - return isFlagExplicitly() ? otherType : this; - } - - public abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException; - - public TReturn parseForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - return parse(parameter, context.getSender(), buffer); - } - - public TReturn getDefaultValue(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { - return null; - } - - public TReturn getDefaultValueForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - return getDefaultValue(parameter, context.getSender(), buffer); - } - - public List<String> complete(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) { - return Collections.emptyList(); - } - - public List<String> completeForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, Location location, ArgumentBuffer buffer) { - return complete(parameter, context.getSender(), location, buffer); - } - - protected static abstract class FlagParameterType<TResult, TParamInfo> extends ParameterType<TResult, TParamInfo> { - - protected FlagParameterType(ParameterType<TResult, TParamInfo> otherType) { - super(otherType.returnType, otherType.parameterConfig, otherType); - } - - @Override - public int getExpectedAmountOfConsumedArguments() { - return otherType.getExpectedAmountOfConsumedArguments(); - } - - @Override - public boolean canBeFlag() { - return true; - } - - @Override - protected final FlagParameterType<TResult, TParamInfo> flagTypeParameter() { - return this; - } - - @Override - public ParameterType<TResult, TParamInfo> asFlagParameter() { - return this; - } - - @Override - public ParameterType<TResult, TParamInfo> asNormalParameter() { - return otherType; - } - - } - -} +package io.dico.dicore.command.parameter.type;
+
+import io.dico.dicore.Reflection;
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.annotation.Range;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A parameter type.
+ * Takes care of parsing, default values as well as completions.
+ *
+ * @param <TReturn> type of the parameter
+ * @param <TParamInfo> the info object type for the parameter (Example: {@link Range.Memory}
+ */
+public abstract class ParameterType<TReturn, TParamInfo> {
+ private final Class<TReturn> returnType;
+ private final ParameterConfig<?, TParamInfo> parameterConfig;
+ protected final ParameterType<TReturn, TParamInfo> otherType; // flag or non-flag, depending on current
+
+ public ParameterType(Class<TReturn> returnType) {
+ this(returnType, null);
+ }
+
+ public ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) {
+ this.returnType = Objects.requireNonNull(returnType);
+ this.parameterConfig = paramConfig;
+
+ ParameterType<TReturn, TParamInfo> otherType = flagTypeParameter();
+ this.otherType = otherType == null ? this : otherType;
+ }
+
+ protected ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> parameterConfig, ParameterType<TReturn, TParamInfo> otherType) {
+ this.returnType = returnType;
+ this.parameterConfig = parameterConfig;
+ this.otherType = otherType;
+ }
+
+ public int getExpectedAmountOfConsumedArguments() {
+ return 1;
+ }
+
+ public boolean canBeFlag() {
+ return this == otherType;
+ }
+
+ public boolean isFlagExplicitly() {
+ return this instanceof FlagParameterType;
+ }
+
+ /**
+ * @return The return type
+ */
+ public final Class<TReturn> getReturnType() {
+ return returnType;
+ }
+
+ public final Class<?> getAnnotationClass() {
+ return parameterConfig == null ? null : parameterConfig.getAnnotationClass();
+ }
+
+ public final ParameterConfig<?, TParamInfo> getParameterConfig() {
+ return parameterConfig;
+ }
+
+ public ParameterKey getTypeKey() {
+ return new ParameterKey(returnType, parameterConfig != null ? parameterConfig.getAnnotationClass() : null);
+ }
+
+ public ParameterKey getInfolessTypeKey() {
+ return new ParameterKey(returnType, null);
+ }
+
+ protected FlagParameterType<TReturn, TParamInfo> flagTypeParameter() {
+ return null;
+ }
+
+ public ParameterType<TReturn, TParamInfo> asFlagParameter() {
+ return canBeFlag() ? this : otherType;
+ }
+
+ public ParameterType<TReturn, TParamInfo> asNormalParameter() {
+ return isFlagExplicitly() ? otherType : this;
+ }
+
+ public abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException;
+
+ public TReturn parseForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ return parse(parameter, context.getSender(), buffer);
+ }
+
+ public TReturn getDefaultValue(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
+ return null;
+ }
+
+ public TReturn getDefaultValueForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ return getDefaultValue(parameter, context.getSender(), buffer);
+ }
+
+ public List<String> complete(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
+ return Collections.emptyList();
+ }
+
+ public List<String> completeForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, Location location, ArgumentBuffer buffer) {
+ return complete(parameter, context.getSender(), location, buffer);
+ }
+
+ protected static abstract class FlagParameterType<TResult, TParamInfo> extends ParameterType<TResult, TParamInfo> {
+
+ protected FlagParameterType(ParameterType<TResult, TParamInfo> otherType) {
+ super(otherType.returnType, otherType.parameterConfig, otherType);
+ }
+
+ @Override
+ public int getExpectedAmountOfConsumedArguments() {
+ return otherType.getExpectedAmountOfConsumedArguments();
+ }
+
+ @Override
+ public boolean canBeFlag() {
+ return true;
+ }
+
+ @Override
+ protected final FlagParameterType<TResult, TParamInfo> flagTypeParameter() {
+ return this;
+ }
+
+ @Override
+ public ParameterType<TResult, TParamInfo> asFlagParameter() {
+ return this;
+ }
+
+ @Override
+ public ParameterType<TResult, TParamInfo> asNormalParameter() {
+ return otherType;
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java index ecf19ce..667b2c7 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java @@ -1,31 +1,31 @@ -package io.dico.dicore.command.parameter.type; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import org.bukkit.command.CommandSender; - -/** - * An abstraction for parameter types that only parse a single argument - * - * @param <TReturn> the parameter type - * @param <TParamInfo> parameter info object type - */ -public abstract class SimpleParameterType<TReturn, TParamInfo> extends ParameterType<TReturn, TParamInfo> { - - public SimpleParameterType(Class<TReturn> returnType) { - super(returnType); - } - - public SimpleParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) { - super(returnType, paramConfig); - } - - protected abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, String input) throws CommandException; - - @Override - public TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { - return parse(parameter, sender, buffer.requireNext(parameter.getName())); - } - -} +package io.dico.dicore.command.parameter.type;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import org.bukkit.command.CommandSender;
+
+/**
+ * An abstraction for parameter types that only parse a single argument
+ *
+ * @param <TReturn> the parameter type
+ * @param <TParamInfo> parameter info object type
+ */
+public abstract class SimpleParameterType<TReturn, TParamInfo> extends ParameterType<TReturn, TParamInfo> {
+
+ public SimpleParameterType(Class<TReturn> returnType) {
+ super(returnType);
+ }
+
+ public SimpleParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) {
+ super(returnType, paramConfig);
+ }
+
+ protected abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, String input) throws CommandException;
+
+ @Override
+ public TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
+ return parse(parameter, sender, buffer.requireNext(parameter.getName()));
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java index e664cef..6828fcc 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java @@ -1,56 +1,56 @@ -package io.dico.dicore.command.predef; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.IContextFilter; -import org.bukkit.command.CommandSender; - -public class DefaultGroupCommand extends PredefinedCommand<DefaultGroupCommand> { - private static final DefaultGroupCommand instance; - private static final IContextFilter noArgumentFilter; - - public static DefaultGroupCommand getInstance() { - return instance; - } - - private DefaultGroupCommand(boolean modifiable) { - addContextFilter(IContextFilter.INHERIT_PERMISSIONS); - addContextFilter(noArgumentFilter); - this.modifiable = modifiable; - } - - public DefaultGroupCommand() { - this(true); - } - - @Override - protected DefaultGroupCommand newModifiableInstance() { - return new DefaultGroupCommand(true); - } - - @Override - public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - context.getAddress().getChatHandler().sendHelpMessage(sender, context, context.getAddress(), 1); - return null; - } - - static { - noArgumentFilter = new IContextFilter() { - @Override - public void filterContext(ExecutionContext context) throws CommandException { - if (context.getBuffer().hasNext()) { - throw new CommandException("No such command: /" + context.getAddress().getAddress() - + " " + context.getBuffer().next()); - } - } - - @Override - public Priority getPriority() { - return Priority.EARLY; - } - }; - - instance = new DefaultGroupCommand(false); - } - -} +package io.dico.dicore.command.predef;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.IContextFilter;
+import org.bukkit.command.CommandSender;
+
+public class DefaultGroupCommand extends PredefinedCommand<DefaultGroupCommand> {
+ private static final DefaultGroupCommand instance;
+ private static final IContextFilter noArgumentFilter;
+
+ public static DefaultGroupCommand getInstance() {
+ return instance;
+ }
+
+ private DefaultGroupCommand(boolean modifiable) {
+ addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
+ addContextFilter(noArgumentFilter);
+ this.modifiable = modifiable;
+ }
+
+ public DefaultGroupCommand() {
+ this(true);
+ }
+
+ @Override
+ protected DefaultGroupCommand newModifiableInstance() {
+ return new DefaultGroupCommand(true);
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ context.getAddress().getChatHandler().sendHelpMessage(sender, context, context.getAddress(), 1);
+ return null;
+ }
+
+ static {
+ noArgumentFilter = new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ if (context.getBuffer().hasNext()) {
+ throw new CommandException("No such command: /" + context.getAddress().getAddress()
+ + " " + context.getBuffer().next());
+ }
+ }
+
+ @Override
+ public Priority getPriority() {
+ return Priority.EARLY;
+ }
+ };
+
+ instance = new DefaultGroupCommand(false);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java index def0db1..985e055 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java @@ -1,76 +1,76 @@ -package io.dico.dicore.command.predef; - -import io.dico.dicore.command.*; -import io.dico.dicore.command.annotation.Range; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.type.NumberParameterType; -import org.bukkit.command.CommandSender; - -/** - * The help command - */ -public class HelpCommand extends PredefinedCommand<HelpCommand> { - private static final Parameter<Integer, Range.Memory> pageParameter; - public static final HelpCommand INSTANCE; - - private HelpCommand(boolean modifiable) { - super(modifiable); - getParameterList().addParameter(pageParameter); - getParameterList().setRequiredCount(0); - setDescription("Shows this help page"); - } - - @Override - protected HelpCommand newModifiableInstance() { - return new HelpCommand(true); - } - - @Override - public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - ICommandAddress target = context.getAddress(); - if (context.getAddress().getCommand() == this) { - target = target.getParent(); - } - - context.getAddress().getChatHandler().sendHelpMessage(sender, context, target, context.<Integer>get("page") - 1); - return null; - } - - public static void registerAsChild(ICommandAddress address) { - registerAsChild(address, "help"); - } - - public static void registerAsChild(ICommandAddress address, String main, String... aliases) { - ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases)); - } - - static { - pageParameter = new Parameter<>("page", "the page number", - new NumberParameterType<Integer>(Integer.TYPE) { - @Override - protected Integer parse(String input) throws NumberFormatException { - return Integer.parseInt(input); - } - - @Override - protected Integer select(Number number) { - return number.intValue(); - } - - @Override - public Integer parseForContext(Parameter<Integer, Range.Memory> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - if (context.getAddress().getCommand() == null || context.getAddress().getCommand().getClass() != HelpCommand.class) { - // An address was executed with its help command as target - buffer.next(); - return 1; - } - return parse(parameter, context.getSender(), buffer); - } - }, - new Range.Memory(1, Integer.MAX_VALUE, 1)); - - INSTANCE = new HelpCommand(false); - } - -} +package io.dico.dicore.command.predef;
+
+import io.dico.dicore.command.*;
+import io.dico.dicore.command.annotation.Range;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.type.NumberParameterType;
+import org.bukkit.command.CommandSender;
+
+/**
+ * The help command
+ */
+public class HelpCommand extends PredefinedCommand<HelpCommand> {
+ private static final Parameter<Integer, Range.Memory> pageParameter;
+ public static final HelpCommand INSTANCE;
+
+ private HelpCommand(boolean modifiable) {
+ super(modifiable);
+ getParameterList().addParameter(pageParameter);
+ getParameterList().setRequiredCount(0);
+ setDescription("Shows this help page");
+ }
+
+ @Override
+ protected HelpCommand newModifiableInstance() {
+ return new HelpCommand(true);
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ ICommandAddress target = context.getAddress();
+ if (context.getAddress().getCommand() == this) {
+ target = target.getParent();
+ }
+
+ context.getAddress().getChatHandler().sendHelpMessage(sender, context, target, context.<Integer>get("page") - 1);
+ return null;
+ }
+
+ public static void registerAsChild(ICommandAddress address) {
+ registerAsChild(address, "help");
+ }
+
+ public static void registerAsChild(ICommandAddress address, String main, String... aliases) {
+ ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases));
+ }
+
+ static {
+ pageParameter = new Parameter<>("page", "the page number",
+ new NumberParameterType<Integer>(Integer.TYPE) {
+ @Override
+ protected Integer parse(String input) throws NumberFormatException {
+ return Integer.parseInt(input);
+ }
+
+ @Override
+ protected Integer select(Number number) {
+ return number.intValue();
+ }
+
+ @Override
+ public Integer parseForContext(Parameter<Integer, Range.Memory> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ if (context.getAddress().getCommand() == null || context.getAddress().getCommand().getClass() != HelpCommand.class) {
+ // An address was executed with its help command as target
+ buffer.next();
+ return 1;
+ }
+ return parse(parameter, context.getSender(), buffer);
+ }
+ },
+ new Range.Memory(1, Integer.MAX_VALUE, 1));
+
+ INSTANCE = new HelpCommand(false);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java index 4340356..b7c5587 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java @@ -1,50 +1,50 @@ -package io.dico.dicore.command.predef; - -import io.dico.dicore.command.CommandBuilder; -import io.dico.dicore.command.ExtendedCommand; -import io.dico.dicore.command.ICommandAddress; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; - -/** - * Marker class for commands that are generated. These commands can be replaced using methods in {@link CommandBuilder} - */ -public abstract class PredefinedCommand<T extends PredefinedCommand<T>> extends ExtendedCommand<T> { - static final Map<String, Consumer<ICommandAddress>> predefinedCommandGenerators = new HashMap<>(); - - /** - * Get a predefined command - * - * @param name the name - * @return the subscriber - */ - public static Consumer<ICommandAddress> getPredefinedCommandGenerator(String name) { - return predefinedCommandGenerators.get(name); - } - - /** - * Register a predefined command - * - * @param name the name - * @param consumer the generator which adds the child to the address - * @return true if and only if the subscriber was registered (false if the name exists) - */ - public static boolean registerPredefinedCommandGenerator(String name, Consumer<ICommandAddress> consumer) { - return predefinedCommandGenerators.putIfAbsent(name, consumer) == null; - } - - static { - registerPredefinedCommandGenerator("help", HelpCommand::registerAsChild); - //noinspection StaticInitializerReferencesSubClass - registerPredefinedCommandGenerator("syntax", SyntaxCommand::registerAsChild); - } - - public PredefinedCommand() { - } - - public PredefinedCommand(boolean modifiable) { - super(modifiable); - } -} +package io.dico.dicore.command.predef;
+
+import io.dico.dicore.command.CommandBuilder;
+import io.dico.dicore.command.ExtendedCommand;
+import io.dico.dicore.command.ICommandAddress;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Marker class for commands that are generated. These commands can be replaced using methods in {@link CommandBuilder}
+ */
+public abstract class PredefinedCommand<T extends PredefinedCommand<T>> extends ExtendedCommand<T> {
+ static final Map<String, Consumer<ICommandAddress>> predefinedCommandGenerators = new HashMap<>();
+
+ /**
+ * Get a predefined command
+ *
+ * @param name the name
+ * @return the subscriber
+ */
+ public static Consumer<ICommandAddress> getPredefinedCommandGenerator(String name) {
+ return predefinedCommandGenerators.get(name);
+ }
+
+ /**
+ * Register a predefined command
+ *
+ * @param name the name
+ * @param consumer the generator which adds the child to the address
+ * @return true if and only if the subscriber was registered (false if the name exists)
+ */
+ public static boolean registerPredefinedCommandGenerator(String name, Consumer<ICommandAddress> consumer) {
+ return predefinedCommandGenerators.putIfAbsent(name, consumer) == null;
+ }
+
+ static {
+ registerPredefinedCommandGenerator("help", HelpCommand::registerAsChild);
+ //noinspection StaticInitializerReferencesSubClass
+ registerPredefinedCommandGenerator("syntax", SyntaxCommand::registerAsChild);
+ }
+
+ public PredefinedCommand() {
+ }
+
+ public PredefinedCommand(boolean modifiable) {
+ super(modifiable);
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java index 7ae8638..8532584 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java @@ -1,36 +1,36 @@ -package io.dico.dicore.command.predef; - -import io.dico.dicore.command.*; -import org.bukkit.command.CommandSender; - -/** - * The syntax command - */ -public class SyntaxCommand extends PredefinedCommand<SyntaxCommand> { - public static final SyntaxCommand INSTANCE = new SyntaxCommand(false); - - private SyntaxCommand(boolean modifiable) { - super(modifiable); - setDescription("Describes how to use the command"); - } - - @Override - protected SyntaxCommand newModifiableInstance() { - return new SyntaxCommand(true); - } - - @Override - public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - context.getAddress().getChatHandler().sendSyntaxMessage(sender, context, context.getAddress().getParent()); - return null; - } - - public static void registerAsChild(ICommandAddress address) { - registerAsChild(address, "syntax"); - } - - public static void registerAsChild(ICommandAddress address, String main, String... aliases) { - ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases)); - } - -} +package io.dico.dicore.command.predef;
+
+import io.dico.dicore.command.*;
+import org.bukkit.command.CommandSender;
+
+/**
+ * The syntax command
+ */
+public class SyntaxCommand extends PredefinedCommand<SyntaxCommand> {
+ public static final SyntaxCommand INSTANCE = new SyntaxCommand(false);
+
+ private SyntaxCommand(boolean modifiable) {
+ super(modifiable);
+ setDescription("Describes how to use the command");
+ }
+
+ @Override
+ protected SyntaxCommand newModifiableInstance() {
+ return new SyntaxCommand(true);
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ context.getAddress().getChatHandler().sendSyntaxMessage(sender, context, context.getAddress().getParent());
+ return null;
+ }
+
+ public static void registerAsChild(ICommandAddress address) {
+ registerAsChild(address, "syntax");
+ }
+
+ public static void registerAsChild(ICommandAddress address, String main, String... aliases) {
+ ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases));
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java index b5346d0..40de6ae 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java @@ -1,122 +1,122 @@ -package io.dico.dicore.command.registration; - -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.ICommandDispatcher; -import org.bukkit.Location; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.List; - -/** - * This class extends the bukkit's command class. - * Instances are injected into the command map. - */ -public class BukkitCommand extends Command { - private ICommandDispatcher dispatcher; - private ICommandAddress origin; - - public BukkitCommand(ICommandAddress address) { - super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size())); - this.dispatcher = address.getDispatcherForTree(); - this.origin = address; - - setTimingsIfNecessary(this); - } - - private static ICommandAddress validateTree(ICommandAddress tree) { - if (!tree.hasParent()) { - throw new IllegalArgumentException(); - } - if (tree.getNames().isEmpty()) { - throw new IllegalArgumentException(); - } - return tree; - } - - public ICommandAddress getOrigin() { - return origin; - } - - @Override - public boolean execute(CommandSender sender, String label, String[] args) { - if (!dispatcher.dispatchCommand(sender, label, args)) { - //System.out.println("failed to dispatch command"); - // target command not found, send a message in the future TODO - } - return true; - } - - @Override - public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { - return this.tabComplete(sender, alias, args, null); - } - - //@Override - public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { - return dispatcher.getTabCompletions(sender, alias, location, args); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - BukkitCommand that = (BukkitCommand) o; - - return getName().equals(that.getName()) && dispatcher == that.dispatcher; - } - - @Override - public int hashCode() { - return dispatcher.hashCode() | getName().hashCode(); - } - - private static void setTimingsIfNecessary(Command object) { - // with paper spigot, the timings are not set by super constructor but by CommandMap.register(), which is not invoked for this system - // I use reflection so that the project does not require paper spigot to build - try { - // public field - Field field = Command.class.getDeclaredField("timings"); - if (field.get(object) != null) return; - Class<?> clazz = Class.forName("co.aikar.timings.TimingsManager"); - // public method - Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class); - Object timings = method.invoke(null, "", object); - field.set(object, timings); - } catch (Throwable ignored) { - } - } - - /* - public static void registerToMap(ICommandAddress tree, Map<String, Command> map) { - BukkitCommand command = new BukkitCommand(tree); - Iterator<String> iterator = tree.getNames().iterator(); - map.put(iterator.next(), command); - while (iterator.hasNext()) { - map.putIfAbsent(iterator.next(), command); - } - } - - public static void unregisterFromMap(ICommandAddress tree, Map<String, Command> map) { - map.values().remove(new BukkitCommand(tree)); - } - - public static void registerChildrenToMap(ICommandAddress tree, Map<String, Command> map) { - for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) { - ICommandAddress child = entry.getValue(); - registerToMap(child, map); - } - } - - public static void unregisterChildenFromMap(ICommandAddress tree, Map<String, Command> map) { - for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) { - ICommandAddress child = entry.getValue(); - unregisterFromMap(child, map); - } - } - */ - -} +package io.dico.dicore.command.registration;
+
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.ICommandDispatcher;
+import org.bukkit.Location;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * This class extends the bukkit's command class.
+ * Instances are injected into the command map.
+ */
+public class BukkitCommand extends Command {
+ private ICommandDispatcher dispatcher;
+ private ICommandAddress origin;
+
+ public BukkitCommand(ICommandAddress address) {
+ super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size()));
+ this.dispatcher = address.getDispatcherForTree();
+ this.origin = address;
+
+ setTimingsIfNecessary(this);
+ }
+
+ private static ICommandAddress validateTree(ICommandAddress tree) {
+ if (!tree.hasParent()) {
+ throw new IllegalArgumentException();
+ }
+ if (tree.getNames().isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ return tree;
+ }
+
+ public ICommandAddress getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String label, String[] args) {
+ if (!dispatcher.dispatchCommand(sender, label, args)) {
+ //System.out.println("failed to dispatch command");
+ // target command not found, send a message in the future TODO
+ }
+ return true;
+ }
+
+ @Override
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
+ return this.tabComplete(sender, alias, args, null);
+ }
+
+ //@Override
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
+ return dispatcher.getTabCompletions(sender, alias, location, args);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BukkitCommand that = (BukkitCommand) o;
+
+ return getName().equals(that.getName()) && dispatcher == that.dispatcher;
+ }
+
+ @Override
+ public int hashCode() {
+ return dispatcher.hashCode() | getName().hashCode();
+ }
+
+ private static void setTimingsIfNecessary(Command object) {
+ // with paper spigot, the timings are not set by super constructor but by CommandMap.register(), which is not invoked for this system
+ // I use reflection so that the project does not require paper spigot to build
+ try {
+ // public field
+ Field field = Command.class.getDeclaredField("timings");
+ if (field.get(object) != null) return;
+ Class<?> clazz = Class.forName("co.aikar.timings.TimingsManager");
+ // public method
+ Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class);
+ Object timings = method.invoke(null, "", object);
+ field.set(object, timings);
+ } catch (Throwable ignored) {
+ }
+ }
+
+ /*
+ public static void registerToMap(ICommandAddress tree, Map<String, Command> map) {
+ BukkitCommand command = new BukkitCommand(tree);
+ Iterator<String> iterator = tree.getNames().iterator();
+ map.put(iterator.next(), command);
+ while (iterator.hasNext()) {
+ map.putIfAbsent(iterator.next(), command);
+ }
+ }
+
+ public static void unregisterFromMap(ICommandAddress tree, Map<String, Command> map) {
+ map.values().remove(new BukkitCommand(tree));
+ }
+
+ public static void registerChildrenToMap(ICommandAddress tree, Map<String, Command> map) {
+ for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
+ ICommandAddress child = entry.getValue();
+ registerToMap(child, map);
+ }
+ }
+
+ public static void unregisterChildenFromMap(ICommandAddress tree, Map<String, Command> map) {
+ for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
+ ICommandAddress child = entry.getValue();
+ unregisterFromMap(child, map);
+ }
+ }
+ */
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java index 780ec66..2008b29 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java @@ -1,59 +1,59 @@ -package io.dico.dicore.command.registration; - -import io.dico.dicore.Reflection; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.SimpleCommandMap; -import org.bukkit.plugin.SimplePluginManager; - -import java.util.*; - -/** - * Provides access to bukkit's {@code Map<String, org.bukkit.command.Command>} command map. - */ -@SuppressWarnings("ConstantConditions") -public class CommandMap { - private static final Map<String, Command> commandMap = findCommandMap(); - - private CommandMap() { - - } - - public static Map<String, Command> getCommandMap() { - return Objects.requireNonNull(commandMap); - } - - public static boolean isAvailable() { - return commandMap != null; - } - - public static Command get(String key) { - return commandMap.get(key); - } - - public static void put(String key, Command command) { - commandMap.put(key, command); - } - - public static Collection<String> replace(Command command, Command replacement) { - List<String> result = new ArrayList<>(); - for (Map.Entry<String, Command> entry : commandMap.entrySet()) { - if (entry.getValue() == command) { - entry.setValue(replacement); - result.add(entry.getKey()); - } - } - return result; - } - - private static Map<String, Command> findCommandMap() { - try { - return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands", - Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager())); - } catch (Exception ex) { - ex.printStackTrace(); - return null; - } - } - -} +package io.dico.dicore.command.registration;
+
+import io.dico.dicore.Reflection;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.SimpleCommandMap;
+import org.bukkit.plugin.SimplePluginManager;
+
+import java.util.*;
+
+/**
+ * Provides access to bukkit's {@code Map<String, org.bukkit.command.Command>} command map.
+ */
+@SuppressWarnings("ConstantConditions")
+public class CommandMap {
+ private static final Map<String, Command> commandMap = findCommandMap();
+
+ private CommandMap() {
+
+ }
+
+ public static Map<String, Command> getCommandMap() {
+ return Objects.requireNonNull(commandMap);
+ }
+
+ public static boolean isAvailable() {
+ return commandMap != null;
+ }
+
+ public static Command get(String key) {
+ return commandMap.get(key);
+ }
+
+ public static void put(String key, Command command) {
+ commandMap.put(key, command);
+ }
+
+ public static Collection<String> replace(Command command, Command replacement) {
+ List<String> result = new ArrayList<>();
+ for (Map.Entry<String, Command> entry : commandMap.entrySet()) {
+ if (entry.getValue() == command) {
+ entry.setValue(replacement);
+ result.add(entry.getKey());
+ }
+ }
+ return result;
+ }
+
+ private static Map<String, Command> findCommandMap() {
+ try {
+ return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands",
+ Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager()));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java index 478c70c..0b7ce3c 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java @@ -1,27 +1,27 @@ -package io.dico.dicore.command.registration.reflect; - -/** - * Thrown if an error occurs while 'parsing' a reflection command method - * Other errors can be thrown too in there that may not be directly relevant to a parsing error. - */ -public class CommandParseException extends Exception { - - public CommandParseException() { - } - - public CommandParseException(String message) { - super(message); - } - - public CommandParseException(String message, Throwable cause) { - super(message, cause); - } - - public CommandParseException(Throwable cause) { - super(cause); - } - - public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} +package io.dico.dicore.command.registration.reflect;
+
+/**
+ * Thrown if an error occurs while 'parsing' a reflection command method
+ * Other errors can be thrown too in there that may not be directly relevant to a parsing error.
+ */
+public class CommandParseException extends Exception {
+
+ public CommandParseException() {
+ }
+
+ public CommandParseException(String message) {
+ super(message);
+ }
+
+ public CommandParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CommandParseException(Throwable cause) {
+ super(cause);
+ }
+
+ public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java index 67b65e4..1e56b8a 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java @@ -1,36 +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; - } - -} +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;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java index bdcb568..d0681b1 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java @@ -1,5 +1,5 @@ -package io.dico.dicore.command.registration.reflect; - -public interface ICommandReceiver { - -} +package io.dico.dicore.command.registration.reflect;
+
+public interface ICommandReceiver {
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCallFlags.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCallFlags.java new file mode 100644 index 0000000..fa198c2 --- /dev/null +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCallFlags.java @@ -0,0 +1,186 @@ +package io.dico.dicore.command.registration.reflect; + +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier; + +/** + * Call flags store which extra parameters the target function expects on top of command parameters. + * All 4 possible extra parameters are listed below. + * <p> + * Extra parameters are ordered by the bit that represents them in the call flags. + * They can either be leading or trailing the command's parameters. + */ +public class ReflectiveCallFlags { + + /** + * Receiver ({@code this} in some kotlin functions - always first parameter) + * + * @see ICommandInterceptor#getReceiver(io.dico.dicore.command.ExecutionContext, java.lang.reflect.Method, String) + */ + public static final int RECEIVER_BIT = 1 << 0; + + /** + * CommandSender + * + * @see org.bukkit.command.CommandSender + */ + public static final int SENDER_BIT = 1 << 1; + + /** + * ExecutionContext + * + * @see io.dico.dicore.command.ExecutionContext + */ + public static final int CONTEXT_BIT = 1 << 2; + + /** + * Continuation (trailing parameters of kotlin suspended functions) + * + * @see kotlin.coroutines.Continuation + */ + public static final int CONTINUATION_BIT = 1 << 3; + + /** + * Mask of extra parameters that trail the command's parameters, instead of leading. + */ + public static final int TRAILING_MASK = CONTINUATION_BIT; + + /** + * Check if the call arg is trailing the command's parameters. + * + * @param bit the bit used for the call flag + * @return true if the call arg is trailing the command's parameters + */ + public static boolean isTrailingCallArg(int bit) { + return (bit & TRAILING_MASK) != 0; + } + + /** + * Number of call arguments leading the command parameters. + * + * @param flags the call flags + * @return the number of call arguments leading the command parameters + */ + public static int getLeadingCallArgNum(int flags) { + return Integer.bitCount(flags & ~TRAILING_MASK); + } + + /** + * Number of call arguments trailing the command parameters. + * + * @param flags the call flags + * @return the number of call arguments trailing the command parameters + */ + public static int getTrailingCallArgNum(int flags) { + return Integer.bitCount(flags & TRAILING_MASK); + } + + /** + * Check if the flags contain the call arg. + * + * @param flags the call flags + * @param bit the bit used for the call flag + * @return true if the flags contain the call arg + */ + public static boolean hasCallArg(int flags, int bit) { + return (flags & bit) != 0; + } + + /** + * Get the index used for the call arg when calling the reflective function + * + * @param flags the call flags + * @param bit the bit used for the call flag + * @param cmdParameterNum the number of parameters of the command + * @return the index used for the call arg + */ + public static int getCallArgIndex(int flags, int bit, int cmdParameterNum) { + if ((bit & TRAILING_MASK) == 0) { + // Leading. + + int preceding = precedingMaskFrom(bit); + int mask = flags & precedingMaskFrom(bit) & ~TRAILING_MASK; + + // Count the number of present call args that are leading and precede the given bit + return Integer.bitCount(flags & precedingMaskFrom(bit) & ~TRAILING_MASK); + } else { + // Trailing. + + // Count the number of present call args that are leading + // plus the number of present call args that are trailing and precede the given bit + // plus the command's parameters + + return Integer.bitCount(flags & ~TRAILING_MASK) + + Integer.bitCount(flags & precedingMaskFrom(bit) & TRAILING_MASK) + + cmdParameterNum; + } + } + + /** + * Get the mask for all bits trailing the given fromBit + * + * <p> + * For example, if the bit is 00010000 + * This function returns 00001111 + * <p> + * + * @param fromBit number with the bit set there the ones should stop. + * @return the mask for all bits trailing the given fromBit + */ + private static int precedingMaskFrom(int fromBit) { + int trailingZeros = Integer.numberOfTrailingZeros(fromBit); + if (trailingZeros == 0) return 0; + return -1 >>> -trailingZeros; + } + + /** + * Get the object array used to call the function. + * + * @param callFlags the call flags + * @param context the context + * @param parameterOrder the order of parameters in the function + * @param receiverFunction the function that will create the receiver for this call, if applicable + * @return the call args + */ + public static Object[] getCallArgs( + int callFlags, + ExecutionContext context, + String[] parameterOrder, + CheckedSupplier<Object, CommandException> receiverFunction + ) throws CommandException { + int leadingParameterNum = getLeadingCallArgNum(callFlags); + int cmdParameterNum = parameterOrder.length; + int trailingParameterNum = getTrailingCallArgNum(callFlags); + + Object[] result = new Object[leadingParameterNum + cmdParameterNum + trailingParameterNum]; + + if (hasCallArg(callFlags, RECEIVER_BIT)) { + int index = getCallArgIndex(callFlags, RECEIVER_BIT, cmdParameterNum); + result[index] = receiverFunction.get(); + } + + if (hasCallArg(callFlags, SENDER_BIT)) { + int index = getCallArgIndex(callFlags, SENDER_BIT, cmdParameterNum); + result[index] = context.getSender(); + } + + if (hasCallArg(callFlags, CONTEXT_BIT)) { + int index = getCallArgIndex(callFlags, CONTEXT_BIT, cmdParameterNum); + result[index] = context; + } + + if (hasCallArg(callFlags, CONTINUATION_BIT)) { + int index = getCallArgIndex(callFlags, CONTINUATION_BIT, cmdParameterNum); + result[index] = null; // filled in later. + } + + for (int i = 0; i < parameterOrder.length; i++) { + String parameterName = parameterOrder[i]; + result[leadingParameterNum + i] = context.get(parameterName); + } + + return result; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java index 2d7c333..f4ddc70 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java @@ -4,6 +4,8 @@ import io.dico.dicore.command.*; import io.dico.dicore.command.annotation.Cmd; import io.dico.dicore.command.annotation.GenerateCommands; import io.dico.dicore.command.parameter.type.IParameterTypeSelector; +import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier; +import kotlin.coroutines.CoroutineContext; import org.bukkit.command.CommandSender; import java.lang.reflect.InvocationTargetException; @@ -11,14 +13,11 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; public final class ReflectiveCommand extends Command { - private static final int continuationMask = 1 << 3; private final Cmd cmdAnnotation; private final Method method; private final Object instance; private String[] parameterOrder; - - // hasContinuation | hasContext | hasSender | hasReceiver - private final int flags; + private final int callFlags; ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException { if (!method.isAnnotationPresent(Cmd.class)) { @@ -48,7 +47,7 @@ public final class ReflectiveCommand extends Command { this.method = method; this.instance = instance; - this.flags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters); + this.callFlags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters); } public Method getMethod() { @@ -59,12 +58,22 @@ public final class ReflectiveCommand extends Command { return instance; } - public String getCmdName() { return cmdAnnotation.value(); } + public String getCmdName() { + return cmdAnnotation.value(); + } + + public int getCallFlags() { + return callFlags; + } void setParameterOrder(String[] parameterOrder) { this.parameterOrder = parameterOrder; } + public int getParameterNum() { + return parameterOrder.length; + } + ICommandAddress getAddress() { ChildCommandAddress result = new ChildCommandAddress(); result.setCommand(this); @@ -86,54 +95,24 @@ public final class ReflectiveCommand extends Command { @Override public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - String[] parameterOrder = this.parameterOrder; - int extraArgumentCount = Integer.bitCount(flags); - int parameterStartIndex = Integer.bitCount(flags & ~continuationMask); - - Object[] args = new Object[parameterOrder.length + extraArgumentCount]; - int i = 0; - - int mask = 1; - if ((flags & mask) != 0) { - // Has receiver + CheckedSupplier<Object, CommandException> receiverFunction = () -> { try { - args[i++] = ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName()); + return ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName()); } catch (Exception ex) { handleException(ex); return null; // unreachable } - } + }; - mask <<= 1; - if ((flags & mask) != 0) { - // Has sender - args[i++] = sender; - } - - mask <<= 1; - if ((flags & mask) != 0) { - // Has context - args[i++] = context; - } - - mask <<= 1; - if ((flags & mask) != 0) { - // Has continuation - - extraArgumentCount--; - } - - for (int n = args.length; i < n; i++) { - args[i] = context.get(parameterOrder[i - extraArgumentCount]); - } + Object[] callArgs = ReflectiveCallFlags.getCallArgs(callFlags, context, parameterOrder, receiverFunction); - if ((flags & mask) != 0) { - // Since it has continuation, call as coroutine - return callAsCoroutine(context, args); + if (ReflectiveCallFlags.hasCallArg(callFlags, ReflectiveCallFlags.CONTINUATION_BIT)) { + // If it has a continuation, call as coroutine + return callAsCoroutine(context, callArgs); } - return callSynchronously(args); + return callSynchronously(callArgs); } private boolean isSuspendFunction() { @@ -180,8 +159,11 @@ public final class ReflectiveCommand extends Command { throw new CommandException("An internal error occurred while executing this command.", ex); } - private String callAsCoroutine(ExecutionContext context, Object[] args) { - return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandInterceptor) instance, context, args); + private String callAsCoroutine(ExecutionContext executionContext, Object[] args) throws CommandException { + ICommandInterceptor factory = (ICommandInterceptor) instance; + CoroutineContext coroutineContext = (CoroutineContext) factory.getCoroutineContext(executionContext, method, getCmdName()); + int continuationIndex = ReflectiveCallFlags.getCallArgIndex(callFlags, ReflectiveCallFlags.CONTINUATION_BIT, parameterOrder.length); + return KotlinReflectiveRegistrationKt.callCommandAsCoroutine(executionContext, coroutineContext, continuationIndex, method, instance, args); } } diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java index 6b1965d..ddd5420 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java @@ -22,6 +22,8 @@ import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import static io.dico.dicore.command.registration.reflect.ReflectiveCallFlags.*; + /** * Takes care of turning a reflection {@link Method} into a command and more. */ @@ -196,47 +198,43 @@ public class ReflectiveRegistration { return new ReflectiveCommand(selector, method, instance).getAddress(); } - static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException { + static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] callParameters) throws CommandParseException { ParameterList list = command.getParameterList(); - boolean hasReceiverParameter = false; - boolean hasSenderParameter = false; - boolean hasContextParameter = false; - boolean hasContinuationParameter = false; - - int start = 0; - int end = parameters.length; - Class<?> senderParameterType = null; + int flags = 0; + int start = 0; + int end = callParameters.length; - if (parameters.length > start + if (callParameters.length > start && command.getInstance() instanceof ICommandInterceptor - && ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) { - hasReceiverParameter = true; - start++; + && ICommandReceiver.class.isAssignableFrom(callParameters[start].getType())) { + flags |= RECEIVER_BIT; + ++start; } - if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) { - hasSenderParameter = true; - start++; + if (callParameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = callParameters[start].getType())) { + flags |= SENDER_BIT; + ++start; } - if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) { - hasContextParameter = true; - start++; + if (callParameters.length > start && callParameters[start].getType() == ExecutionContext.class) { + flags |= CONTEXT_BIT; + ++start; } - if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) { - hasContinuationParameter = true; - end--; + if (callParameters.length > start && callParameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) { + flags |= CONTINUATION_BIT; + --end; } - String[] parameterNames = lookupParameterNames(method, parameters, start); + String[] parameterNames = lookupParameterNames(method, callParameters, start); for (int i = start, n = end; i < n; i++) { - Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]); + Parameter<?, ?> parameter = parseParameter(selector, method, callParameters[i], parameterNames[i - start]); list.addParameter(parameter); } - command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames); + + command.setParameterOrder(hasCallArg(flags, CONTINUATION_BIT) ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames); RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class); if (cmdPermissions != null) { @@ -277,6 +275,7 @@ public class ReflectiveRegistration { command.setDescription(); } + boolean hasSenderParameter = hasCallArg(flags, SENDER_BIT); if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) { command.addContextFilter(IContextFilter.PLAYER_ONLY); } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) { @@ -287,17 +286,9 @@ public class ReflectiveRegistration { command.addContextFilter(IContextFilter.CONSOLE_ONLY); } - list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs()); + list.setRepeatFinalParameter(callParameters.length > start && callParameters[callParameters.length - 1].isVarArgs()); list.setFinalParameterMayBeFlag(true); - 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; } diff --git a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt index 2ef6e39..273cadc 100644 --- a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt +++ b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt @@ -17,20 +17,23 @@ fun isSuspendFunction(method: Method): Boolean { return func.isSuspend } -fun callAsCoroutine( - command: ReflectiveCommand, - factory: ICommandInterceptor, - context: ExecutionContext, +@Throws(CommandException::class) +fun callCommandAsCoroutine( + executionContext: ExecutionContext, + coroutineContext: CoroutineContext, + continuationIndex: Int, + method: Method, + instance: Any?, 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 = coroutineContext, start = UNDISPATCHED) { suspendCoroutineUninterceptedOrReturn<Any?> { cont -> - command.method.invoke(command.instance, *args, cont.intercepted()) + args[continuationIndex] = cont.intercepted() + method.invoke(instance, *args) } } @@ -39,12 +42,12 @@ fun callAsCoroutine( } job.invokeOnCompletion { - val chatHandler = context.address.chatHandler + val chatHandler = executionContext.address.chatHandler try { val result = job.getResult() - chatHandler.sendMessage(context.sender, EMessageType.RESULT, result) + chatHandler.sendMessage(executionContext.sender, EMessageType.RESULT, result) } catch (ex: Throwable) { - chatHandler.handleException(context.sender, context, ex) + chatHandler.handleException(executionContext.sender, executionContext, ex) } } diff --git a/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java b/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java index 1cd4e23..c31b96f 100644 --- a/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java +++ b/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java @@ -1,73 +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()); - } - -} +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());
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java b/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java index e4facb1..ae25123 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java +++ b/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java @@ -1,182 +1,182 @@ -package io.dico.dicore; - -public interface BitModifier { - - long mask(); - - long get(long x); - - long set(long x, long value); - - default int lowerBound() { - return Long.numberOfTrailingZeros(mask()); - } - - default int upperBound() { - return 64 - Long.numberOfLeadingZeros(mask()); - } - - default int bitCount() { - return Long.bitCount(mask()); - } - - default boolean getBoolean(long x) { - return get(x) == 1; - } - - default long setBoolean(long x, boolean value) { - return set(x, value ? 1 : 0); - } - - default int getInt(long x) { - return (int) (get(x) & 0xFFFFFFFFL); - } - - default long setInt(long x, int value) { - return set(x, value & 0xFFFFFFFFL); - } - - default short getShort(long x) { - return (short) (get(x) & 0xFFFFL); - } - - default long setShort(long x, int value) { - return set(x, value & 0xFFFFL); - } - - default byte getByte(long x) { - return (byte) (get(x) & 0xFFL); - } - - default long setByte(long x, int value) { - return set(x, value & 0xFFL); - } - - final class OfSingle implements BitModifier { - private final long mask; - - public OfSingle(int bit) { - if (bit < 0 || bit >= 64) { - throw new IndexOutOfBoundsException(); - } - this.mask = 1L << bit; - } - - @Override - public int bitCount() { - return 1; - } - - @Override - public long mask() { - return mask; - } - - public boolean getBoolean(long x) { - return (x & mask) != 0; - } - - public long setBoolean(long x, boolean value) { - return value ? (x | mask) : (x & ~mask); - } - - @Override - public long get(long x) { - return getBoolean(x) ? 1 : 0; - } - - @Override - public long set(long x, long value) { - if (value < 0 || value > 1) { - throw new IllegalArgumentException(); - } - return setBoolean(x, value == 1); - } - } - - final class OfMultiple implements BitModifier { - private final int lowerBound; - private final int bitCount; - private final long mask; - - public OfMultiple(int lowerBound, int bitCount) { - int upperBound = lowerBound + bitCount; - if (lowerBound < 0 || lowerBound >= 64 || upperBound < 1 || upperBound > 64 || upperBound < lowerBound) { - throw new IndexOutOfBoundsException(); - } - this.lowerBound = lowerBound; - this.bitCount = bitCount; - this.mask = (Long.MIN_VALUE >> (bitCount - 1)) >>> (64 - bitCount - lowerBound); - } - - @Override - public int lowerBound() { - return lowerBound; - } - - @Override - public int bitCount() { - return bitCount; - } - - @Override - public int upperBound() { - return lowerBound + bitCount; - } - - @Override - public long mask() { - return mask; - } - - @Override - public long get(long x) { - return (x & mask) >>> lowerBound; - } - - @Override - public long set(long x, long value) { - return (x & ~mask) | ((value << lowerBound) & mask); - } - - } - - class Builder { - int currentIndex; - - public int getCurrentIndex() { - return currentIndex; - } - - public int getRemaining() { - return 64 - currentIndex; - } - - public OfSingle single() { - checkAvailable(1); - return new OfSingle(currentIndex++); - } - - public BitModifier size(int size) { - if (size == 1) { - return single(); - } - checkAvailable(size); - BitModifier result = new OfMultiple(currentIndex, size); - currentIndex += size; - return result; - } - - public BitModifier remaining() { - return size(getRemaining()); - } - - private void checkAvailable(int size) { - if (size <= 0 || currentIndex + size > 64) { - throw new IllegalStateException("Exceeding bit count of a long"); - } - } - - } - -} +package io.dico.dicore;
+
+public interface BitModifier {
+
+ long mask();
+
+ long get(long x);
+
+ long set(long x, long value);
+
+ default int lowerBound() {
+ return Long.numberOfTrailingZeros(mask());
+ }
+
+ default int upperBound() {
+ return 64 - Long.numberOfLeadingZeros(mask());
+ }
+
+ default int bitCount() {
+ return Long.bitCount(mask());
+ }
+
+ default boolean getBoolean(long x) {
+ return get(x) == 1;
+ }
+
+ default long setBoolean(long x, boolean value) {
+ return set(x, value ? 1 : 0);
+ }
+
+ default int getInt(long x) {
+ return (int) (get(x) & 0xFFFFFFFFL);
+ }
+
+ default long setInt(long x, int value) {
+ return set(x, value & 0xFFFFFFFFL);
+ }
+
+ default short getShort(long x) {
+ return (short) (get(x) & 0xFFFFL);
+ }
+
+ default long setShort(long x, int value) {
+ return set(x, value & 0xFFFFL);
+ }
+
+ default byte getByte(long x) {
+ return (byte) (get(x) & 0xFFL);
+ }
+
+ default long setByte(long x, int value) {
+ return set(x, value & 0xFFL);
+ }
+
+ final class OfSingle implements BitModifier {
+ private final long mask;
+
+ public OfSingle(int bit) {
+ if (bit < 0 || bit >= 64) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.mask = 1L << bit;
+ }
+
+ @Override
+ public int bitCount() {
+ return 1;
+ }
+
+ @Override
+ public long mask() {
+ return mask;
+ }
+
+ public boolean getBoolean(long x) {
+ return (x & mask) != 0;
+ }
+
+ public long setBoolean(long x, boolean value) {
+ return value ? (x | mask) : (x & ~mask);
+ }
+
+ @Override
+ public long get(long x) {
+ return getBoolean(x) ? 1 : 0;
+ }
+
+ @Override
+ public long set(long x, long value) {
+ if (value < 0 || value > 1) {
+ throw new IllegalArgumentException();
+ }
+ return setBoolean(x, value == 1);
+ }
+ }
+
+ final class OfMultiple implements BitModifier {
+ private final int lowerBound;
+ private final int bitCount;
+ private final long mask;
+
+ public OfMultiple(int lowerBound, int bitCount) {
+ int upperBound = lowerBound + bitCount;
+ if (lowerBound < 0 || lowerBound >= 64 || upperBound < 1 || upperBound > 64 || upperBound < lowerBound) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.lowerBound = lowerBound;
+ this.bitCount = bitCount;
+ this.mask = (Long.MIN_VALUE >> (bitCount - 1)) >>> (64 - bitCount - lowerBound);
+ }
+
+ @Override
+ public int lowerBound() {
+ return lowerBound;
+ }
+
+ @Override
+ public int bitCount() {
+ return bitCount;
+ }
+
+ @Override
+ public int upperBound() {
+ return lowerBound + bitCount;
+ }
+
+ @Override
+ public long mask() {
+ return mask;
+ }
+
+ @Override
+ public long get(long x) {
+ return (x & mask) >>> lowerBound;
+ }
+
+ @Override
+ public long set(long x, long value) {
+ return (x & ~mask) | ((value << lowerBound) & mask);
+ }
+
+ }
+
+ class Builder {
+ int currentIndex;
+
+ public int getCurrentIndex() {
+ return currentIndex;
+ }
+
+ public int getRemaining() {
+ return 64 - currentIndex;
+ }
+
+ public OfSingle single() {
+ checkAvailable(1);
+ return new OfSingle(currentIndex++);
+ }
+
+ public BitModifier size(int size) {
+ if (size == 1) {
+ return single();
+ }
+ checkAvailable(size);
+ BitModifier result = new OfMultiple(currentIndex, size);
+ currentIndex += size;
+ return result;
+ }
+
+ public BitModifier remaining() {
+ return size(getRemaining());
+ }
+
+ private void checkAvailable(int size) {
+ if (size <= 0 || currentIndex + size > 64) {
+ throw new IllegalStateException("Exceeding bit count of a long");
+ }
+ }
+
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/Formatting.java b/dicore3/core/src/main/java/io/dico/dicore/Formatting.java index 9fdfe0e..40afb9b 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Formatting.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Formatting.java @@ -1,295 +1,295 @@ -/* - * Copyright (c) 2017 ProjectOreville - * - * All rights reserved. - * - * Author(s): - * Dico Karssiens - */ - -package io.dico.dicore; - -import org.jetbrains.annotations.NotNull; - -public final class Formatting implements CharSequence { - public static final char FORMAT_CHAR = '\u00a7'; - private static final String CACHED_CHARS = "0123456789abcdefklmnor"; - private static final Formatting[] singleCharInstances = new Formatting[CACHED_CHARS.length()]; - - @NotNull - public static final Formatting - BLACK = from('0'), - DARK_BLUE = from('1'), - DARL_GREEN = from('2'), - CYAN = from('3'), - DARK_RED = from('4'), - PURPLE = from('5'), - ORANGE = from('6'), - GRAY = from('7'), - DARK_GRAY = from('8'), - BLUE = from('9'), - GREEN = from('a'), - AQUA = from('b'), - RED = from('c'), - PINK = from('d'), - YELLOW = from('e'), - WHITE = from('f'), - BOLD = from('l'), - STRIKETHROUGH = from('m'), - UNDERLINE = from('n'), - ITALIC = from('o'), - MAGIC = from('k'), - RESET = from('r'), - EMPTY = from('\0'); - - public static String stripAll(String value) { - return stripAll(FORMAT_CHAR, value); - } - - public static String stripAll(char alternateChar, String value) { - int index = value.indexOf(alternateChar); - int max; - if (index == -1 || index == (max = value.length() - 1)) { - return value; - } - - StringBuilder result = new StringBuilder(); - int from = 0; - do { - if (isRecognizedChar(value.charAt(index + 1))) { - result.append(value, from, index); - from = index + 2; - } else { - result.append(value, from, from = index + 2); - } - - index = value.indexOf(alternateChar, index + 1); - } while (index != -1 && index != max && from <= max); - - if (from <= max) { - result.append(value, from, value.length()); - } - return result.toString(); - } - - public static String stripFirst(String value) { - return stripFirst(FORMAT_CHAR, value); - } - - public static String stripFirst(char alternateChar, String value) { - int index = value.indexOf(alternateChar); - int max; - if (index == -1 || index == (max = value.length() - 1)) { - return value; - } - - StringBuilder result = new StringBuilder(value.length()); - int from = 0; - if (isRecognizedChar(value.charAt(index + 1))) { - result.append(value, from, index); - from = index + 2; - } else { - result.append(value, from, from = index + 2); - } - - if (from < max) { - result.append(value, from, value.length()); - } - return result.toString(); - } - - public static Formatting from(char c) { - if (isRecognizedChar(c)) { - c = Character.toLowerCase(c); - int index = CACHED_CHARS.indexOf(c); - if (index == -1) return EMPTY; - - Formatting res = singleCharInstances[index]; - if (res == null) { - singleCharInstances[index] = res = new Formatting(c); - } - return res; - } - return EMPTY; - } - - public static Formatting from(String chars) { - return chars.length() == 1 ? from(chars.charAt(0)) : getFormats(chars, '\0'); - } - - public static Formatting getFormats(String input) { - return getFormats(input, FORMAT_CHAR); - } - - public static Formatting getFormats(String input, char formatChar) { - return getFormats(input, 0, input.length(), formatChar); - } - - public static Formatting getFormats(String input, int start, int end, char formatChar) { - if ((start < 0) || (start > end) || (end > input.length())) { - throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", input.length() " + input.length()); - } - - boolean needsFormatChar = formatChar != '\0'; - char[] formats = new char[6]; - // just make sure it's not the same as formatChar - char previous = (char) (formatChar + 1); - - for (int i = start; i < end; i++) { - char c = input.charAt(i); - - if (previous == formatChar || !needsFormatChar) { - if (isColourChar(c) || isResetChar(c)) { - formats = new char[6]; - formats[0] = Character.toLowerCase(c); - } else if (isFormatChar(c)) { - char format = Character.toLowerCase(c); - for (int j = 0; j < 6; j++) { - if (formats[j] == '\0') { - formats[j] = format; - break; - } else if (formats[j] == format) { - break; - } - } - } - } - - previous = c; - } - - return formats[1] == '\0' ? from(formats[0]) : new Formatting(formats); - } - - public static String translate(String input) { - return translateChars('&', input); - } - - public static String translateChars(char alternateChar, String input) { - return translateFormat(alternateChar, FORMAT_CHAR, input); - } - - public static String revert(String input) { - return revertChars('&', input); - } - - public static String revertChars(char alternateChar, String input) { - return translateFormat(FORMAT_CHAR, alternateChar, input); - } - - public static String translateFormat(char fromChar, char toChar, String input) { - if (input == null) { - return null; - } - int n = input.length(); - if (n < 2) { - return input; - } - char[] result = null; - char previous = input.charAt(0); - for (int i = 1; i < n; i++) { - char c = input.charAt(i); - if (previous == fromChar && isRecognizedChar(c)) { - if (result == null) { - result = input.toCharArray(); - } - result[i - 1] = toChar; - } - previous = c; - } - return result == null ? input : String.valueOf(result); - } - - public static void translate(StringBuilder input) { - translateChars('&', input); - } - - public static void translateChars(char alternateChar, StringBuilder input) { - translateFormat(alternateChar, FORMAT_CHAR, input); - } - - public static void revert(StringBuilder input) { - revertChars('&', input); - } - - public static void revertChars(char alternateChar, StringBuilder input) { - translateFormat(FORMAT_CHAR, alternateChar, input); - } - - public static void translateFormat(char fromChar, char toChar, StringBuilder input) { - if (input == null) { - return; - } - int n = input.length(); - if (n < 2) { - return; - } - char previous = input.charAt(0); - for (int i = 1; i < n; i++) { - char c = input.charAt(i); - if (previous == fromChar && isRecognizedChar(c)) { - input.setCharAt(i -1, toChar); - } - previous = c; - } - } - - private static boolean isRecognizedChar(char c) { - return isColourChar(c) || isFormatChar(c) || isResetChar(c); - } - - private static boolean isColourChar(char c) { - return "0123456789abcdefABCDEF".indexOf(c) >= 0; - } - - private static boolean isResetChar(char c) { - return c == 'r' || c == 'R'; - } - - private static boolean isFormatChar(char c) { - return "klmnoKLMNO".indexOf(c) >= 0; - } - - private final String format; - - private Formatting(char[] formats) { - StringBuilder format = new StringBuilder(12); - for (char c : formats) { - if (c != '\0') { - format.append(FORMAT_CHAR).append(c); - } else { - break; - } - } - this.format = format.toString(); - } - - private Formatting(char c) { - this.format = (c != '\0') ? String.valueOf(new char[]{FORMAT_CHAR, c}) : ""; - } - - @Override - public int length() { - return format.length(); - } - - @Override - public char charAt(int index) { - return format.charAt(index); - } - - @Override - public String subSequence(int start, int end) { - return format.substring(start, end); - } - - @Override - public String toString() { - return format; - } - - public String toString(char formatChar) { - return format.replace(FORMAT_CHAR, formatChar); - } - +/*
+ * Copyright (c) 2017 ProjectOreville
+ *
+ * All rights reserved.
+ *
+ * Author(s):
+ * Dico Karssiens
+ */
+
+package io.dico.dicore;
+
+import org.jetbrains.annotations.NotNull;
+
+public final class Formatting implements CharSequence {
+ public static final char FORMAT_CHAR = '\u00a7';
+ private static final String CACHED_CHARS = "0123456789abcdefklmnor";
+ private static final Formatting[] singleCharInstances = new Formatting[CACHED_CHARS.length()];
+
+ @NotNull
+ public static final Formatting
+ BLACK = from('0'),
+ DARK_BLUE = from('1'),
+ DARL_GREEN = from('2'),
+ CYAN = from('3'),
+ DARK_RED = from('4'),
+ PURPLE = from('5'),
+ ORANGE = from('6'),
+ GRAY = from('7'),
+ DARK_GRAY = from('8'),
+ BLUE = from('9'),
+ GREEN = from('a'),
+ AQUA = from('b'),
+ RED = from('c'),
+ PINK = from('d'),
+ YELLOW = from('e'),
+ WHITE = from('f'),
+ BOLD = from('l'),
+ STRIKETHROUGH = from('m'),
+ UNDERLINE = from('n'),
+ ITALIC = from('o'),
+ MAGIC = from('k'),
+ RESET = from('r'),
+ EMPTY = from('\0');
+
+ public static String stripAll(String value) {
+ return stripAll(FORMAT_CHAR, value);
+ }
+
+ public static String stripAll(char alternateChar, String value) {
+ int index = value.indexOf(alternateChar);
+ int max;
+ if (index == -1 || index == (max = value.length() - 1)) {
+ return value;
+ }
+
+ StringBuilder result = new StringBuilder();
+ int from = 0;
+ do {
+ if (isRecognizedChar(value.charAt(index + 1))) {
+ result.append(value, from, index);
+ from = index + 2;
+ } else {
+ result.append(value, from, from = index + 2);
+ }
+
+ index = value.indexOf(alternateChar, index + 1);
+ } while (index != -1 && index != max && from <= max);
+
+ if (from <= max) {
+ result.append(value, from, value.length());
+ }
+ return result.toString();
+ }
+
+ public static String stripFirst(String value) {
+ return stripFirst(FORMAT_CHAR, value);
+ }
+
+ public static String stripFirst(char alternateChar, String value) {
+ int index = value.indexOf(alternateChar);
+ int max;
+ if (index == -1 || index == (max = value.length() - 1)) {
+ return value;
+ }
+
+ StringBuilder result = new StringBuilder(value.length());
+ int from = 0;
+ if (isRecognizedChar(value.charAt(index + 1))) {
+ result.append(value, from, index);
+ from = index + 2;
+ } else {
+ result.append(value, from, from = index + 2);
+ }
+
+ if (from < max) {
+ result.append(value, from, value.length());
+ }
+ return result.toString();
+ }
+
+ public static Formatting from(char c) {
+ if (isRecognizedChar(c)) {
+ c = Character.toLowerCase(c);
+ int index = CACHED_CHARS.indexOf(c);
+ if (index == -1) return EMPTY;
+
+ Formatting res = singleCharInstances[index];
+ if (res == null) {
+ singleCharInstances[index] = res = new Formatting(c);
+ }
+ return res;
+ }
+ return EMPTY;
+ }
+
+ public static Formatting from(String chars) {
+ return chars.length() == 1 ? from(chars.charAt(0)) : getFormats(chars, '\0');
+ }
+
+ public static Formatting getFormats(String input) {
+ return getFormats(input, FORMAT_CHAR);
+ }
+
+ public static Formatting getFormats(String input, char formatChar) {
+ return getFormats(input, 0, input.length(), formatChar);
+ }
+
+ public static Formatting getFormats(String input, int start, int end, char formatChar) {
+ if ((start < 0) || (start > end) || (end > input.length())) {
+ throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", input.length() " + input.length());
+ }
+
+ boolean needsFormatChar = formatChar != '\0';
+ char[] formats = new char[6];
+ // just make sure it's not the same as formatChar
+ char previous = (char) (formatChar + 1);
+
+ for (int i = start; i < end; i++) {
+ char c = input.charAt(i);
+
+ if (previous == formatChar || !needsFormatChar) {
+ if (isColourChar(c) || isResetChar(c)) {
+ formats = new char[6];
+ formats[0] = Character.toLowerCase(c);
+ } else if (isFormatChar(c)) {
+ char format = Character.toLowerCase(c);
+ for (int j = 0; j < 6; j++) {
+ if (formats[j] == '\0') {
+ formats[j] = format;
+ break;
+ } else if (formats[j] == format) {
+ break;
+ }
+ }
+ }
+ }
+
+ previous = c;
+ }
+
+ return formats[1] == '\0' ? from(formats[0]) : new Formatting(formats);
+ }
+
+ public static String translate(String input) {
+ return translateChars('&', input);
+ }
+
+ public static String translateChars(char alternateChar, String input) {
+ return translateFormat(alternateChar, FORMAT_CHAR, input);
+ }
+
+ public static String revert(String input) {
+ return revertChars('&', input);
+ }
+
+ public static String revertChars(char alternateChar, String input) {
+ return translateFormat(FORMAT_CHAR, alternateChar, input);
+ }
+
+ public static String translateFormat(char fromChar, char toChar, String input) {
+ if (input == null) {
+ return null;
+ }
+ int n = input.length();
+ if (n < 2) {
+ return input;
+ }
+ char[] result = null;
+ char previous = input.charAt(0);
+ for (int i = 1; i < n; i++) {
+ char c = input.charAt(i);
+ if (previous == fromChar && isRecognizedChar(c)) {
+ if (result == null) {
+ result = input.toCharArray();
+ }
+ result[i - 1] = toChar;
+ }
+ previous = c;
+ }
+ return result == null ? input : String.valueOf(result);
+ }
+
+ public static void translate(StringBuilder input) {
+ translateChars('&', input);
+ }
+
+ public static void translateChars(char alternateChar, StringBuilder input) {
+ translateFormat(alternateChar, FORMAT_CHAR, input);
+ }
+
+ public static void revert(StringBuilder input) {
+ revertChars('&', input);
+ }
+
+ public static void revertChars(char alternateChar, StringBuilder input) {
+ translateFormat(FORMAT_CHAR, alternateChar, input);
+ }
+
+ public static void translateFormat(char fromChar, char toChar, StringBuilder input) {
+ if (input == null) {
+ return;
+ }
+ int n = input.length();
+ if (n < 2) {
+ return;
+ }
+ char previous = input.charAt(0);
+ for (int i = 1; i < n; i++) {
+ char c = input.charAt(i);
+ if (previous == fromChar && isRecognizedChar(c)) {
+ input.setCharAt(i -1, toChar);
+ }
+ previous = c;
+ }
+ }
+
+ private static boolean isRecognizedChar(char c) {
+ return isColourChar(c) || isFormatChar(c) || isResetChar(c);
+ }
+
+ private static boolean isColourChar(char c) {
+ return "0123456789abcdefABCDEF".indexOf(c) >= 0;
+ }
+
+ private static boolean isResetChar(char c) {
+ return c == 'r' || c == 'R';
+ }
+
+ private static boolean isFormatChar(char c) {
+ return "klmnoKLMNO".indexOf(c) >= 0;
+ }
+
+ private final String format;
+
+ private Formatting(char[] formats) {
+ StringBuilder format = new StringBuilder(12);
+ for (char c : formats) {
+ if (c != '\0') {
+ format.append(FORMAT_CHAR).append(c);
+ } else {
+ break;
+ }
+ }
+ this.format = format.toString();
+ }
+
+ private Formatting(char c) {
+ this.format = (c != '\0') ? String.valueOf(new char[]{FORMAT_CHAR, c}) : "";
+ }
+
+ @Override
+ public int length() {
+ return format.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ return format.charAt(index);
+ }
+
+ @Override
+ public String subSequence(int start, int end) {
+ return format.substring(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return format;
+ }
+
+ public String toString(char formatChar) {
+ return format.replace(FORMAT_CHAR, formatChar);
+ }
+
}
\ No newline at end of file diff --git a/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java b/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java index b59979a..f92da6b 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java +++ b/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java @@ -1,173 +1,173 @@ -package io.dico.dicore; - -/** - * A chainable object - * <p> - * It is not possible to declare another upper bound for type parameter Subtype. - * However, it is required that it also extends the type parameter Element. - * - * @param <Subtype> the interface that is chainable - * @param <Element> the element of the chain, this is a supertype of the subtype - */ -@SuppressWarnings("unchecked") -public interface InterfaceChain<Element, Subtype extends InterfaceChain<Element, Subtype>> { - - /** - * returns the empty InterfaceChain instance. - * - * @return the empty InterfaceChain instance - */ - Subtype getEmptyInstance(); - - /** - * returns a InterfaceChain with the last added element detached. - * <p> - * if this InterfaceChain is the empty instance, the empty instance is returned. - * - * @return a InterfaceChain with the last added element detached - * @implNote for the purpose of lambdas, the default implementation also returns the empty instance. - */ - default Subtype withoutLastNode() { - return getEmptyInstance(); - } - - /** - * returns the element that was inserted as the last element - * <p> - * For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)} - * would return the given element. - * - * @return the element that was inserted as the last element - * @implNote for the purpose of lambdas, the default implementation returns this object, - * which is required to implement the Element type parameter. - */ - default Element getDelegateOfLastNode() { - //noinspection unchecked - return (Element) this; - } - - /** - * @return The number of elements chained from this InterfaceChain. - * @implNote for the purpose of lambdas, the default implementation returns 1. - */ - default int getElementCount() { - return 1; - } - - /** - * Get a new InterfaceChain that includes the given Element. - * <p> - * The default implementation of the Subtype should look like this: - * <pre> {@code - * if (element == null) { - * return this; - * } - * - * int count = getElementCount() + 1; - * return new Subtype() { - * \@Override - * public void exampleElementMethod() { - * try { - * Subtype.this.exampleElementMethod(); - * } finally { - * element.exampleElementMethod(); - * } - * } - * - * \@Override - * public Subtype withoutLastNode() { - * return Subtype.this; - * } - * - * \@Override - * public Element getDelegateOfLastNode() { - * return element; - * } - * - * \@Override - * public int getElementCount() { - * return count; - * } - * }; - * } - * </pre> - * - * @param element A new element to insert at the end of this InterfaceChain. - * @return a new InterfaceChain that includes the given Element. - */ - Subtype withElement(Element element); - - /** - * Append each of the elements to this InterfaceChain - * - * @param elements the elements to append - * @return a new InterfaceChain with the elements appended - */ - default Subtype withElements(Element... elements) { - Subtype result = (Subtype) this; - for (Element element : elements) { - result = result.withElement(element); - } - return result; - } - - /* - Example Subtypes implementation - - public class Subtypes { - - private Subtypes() { - - } - - private static final Subtype empty = new Subtype() { - @Override - public void exampleElementMethod() { - - } - - @Override - public Subtype withElement(Element other) { - return Subtypes.singleton(other); - } - - @Override - public int getElementCount() { - return 0; - } - - @Override - public Element getDelegateOfLastNode() { - return null; - } - }; - - public static Subtype empty() { - return empty; - } - - public static Subtype singleton(Element element) { - if (element instanceof Subtype) { - return (Subtype) element; - } - if (element == null) { - return empty(); - } - return new Subtype() { - @Override - public void exampleElementMethod() { - element.exampleElementMethod(); - } - - @Override - public Element getDelegateOfLastNode() { - return element; - } - }; - } - - } - - */ - -} +package io.dico.dicore;
+
+/**
+ * A chainable object
+ * <p>
+ * It is not possible to declare another upper bound for type parameter Subtype.
+ * However, it is required that it also extends the type parameter Element.
+ *
+ * @param <Subtype> the interface that is chainable
+ * @param <Element> the element of the chain, this is a supertype of the subtype
+ */
+@SuppressWarnings("unchecked")
+public interface InterfaceChain<Element, Subtype extends InterfaceChain<Element, Subtype>> {
+
+ /**
+ * returns the empty InterfaceChain instance.
+ *
+ * @return the empty InterfaceChain instance
+ */
+ Subtype getEmptyInstance();
+
+ /**
+ * returns a InterfaceChain with the last added element detached.
+ * <p>
+ * if this InterfaceChain is the empty instance, the empty instance is returned.
+ *
+ * @return a InterfaceChain with the last added element detached
+ * @implNote for the purpose of lambdas, the default implementation also returns the empty instance.
+ */
+ default Subtype withoutLastNode() {
+ return getEmptyInstance();
+ }
+
+ /**
+ * returns the element that was inserted as the last element
+ * <p>
+ * For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)}
+ * would return the given element.
+ *
+ * @return the element that was inserted as the last element
+ * @implNote for the purpose of lambdas, the default implementation returns this object,
+ * which is required to implement the Element type parameter.
+ */
+ default Element getDelegateOfLastNode() {
+ //noinspection unchecked
+ return (Element) this;
+ }
+
+ /**
+ * @return The number of elements chained from this InterfaceChain.
+ * @implNote for the purpose of lambdas, the default implementation returns 1.
+ */
+ default int getElementCount() {
+ return 1;
+ }
+
+ /**
+ * Get a new InterfaceChain that includes the given Element.
+ * <p>
+ * The default implementation of the Subtype should look like this:
+ * <pre> {@code
+ * if (element == null) {
+ * return this;
+ * }
+ *
+ * int count = getElementCount() + 1;
+ * return new Subtype() {
+ * \@Override
+ * public void exampleElementMethod() {
+ * try {
+ * Subtype.this.exampleElementMethod();
+ * } finally {
+ * element.exampleElementMethod();
+ * }
+ * }
+ *
+ * \@Override
+ * public Subtype withoutLastNode() {
+ * return Subtype.this;
+ * }
+ *
+ * \@Override
+ * public Element getDelegateOfLastNode() {
+ * return element;
+ * }
+ *
+ * \@Override
+ * public int getElementCount() {
+ * return count;
+ * }
+ * };
+ * }
+ * </pre>
+ *
+ * @param element A new element to insert at the end of this InterfaceChain.
+ * @return a new InterfaceChain that includes the given Element.
+ */
+ Subtype withElement(Element element);
+
+ /**
+ * Append each of the elements to this InterfaceChain
+ *
+ * @param elements the elements to append
+ * @return a new InterfaceChain with the elements appended
+ */
+ default Subtype withElements(Element... elements) {
+ Subtype result = (Subtype) this;
+ for (Element element : elements) {
+ result = result.withElement(element);
+ }
+ return result;
+ }
+
+ /*
+ Example Subtypes implementation
+
+ public class Subtypes {
+
+ private Subtypes() {
+
+ }
+
+ private static final Subtype empty = new Subtype() {
+ @Override
+ public void exampleElementMethod() {
+
+ }
+
+ @Override
+ public Subtype withElement(Element other) {
+ return Subtypes.singleton(other);
+ }
+
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ public Element getDelegateOfLastNode() {
+ return null;
+ }
+ };
+
+ public static Subtype empty() {
+ return empty;
+ }
+
+ public static Subtype singleton(Element element) {
+ if (element instanceof Subtype) {
+ return (Subtype) element;
+ }
+ if (element == null) {
+ return empty();
+ }
+ return new Subtype() {
+ @Override
+ public void exampleElementMethod() {
+ element.exampleElementMethod();
+ }
+
+ @Override
+ public Element getDelegateOfLastNode() {
+ return element;
+ }
+ };
+ }
+
+ }
+
+ */
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java b/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java index 5c19848..3be2179 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java +++ b/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java @@ -1,120 +1,120 @@ -package io.dico.dicore; - -import org.bukkit.Material; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -public class InventoryEventUtil { - - private InventoryEventUtil() { - - } - - public static ItemStack getNewItem(InventoryClickEvent event) { - Inventory clicked = event.getInventory(); - switch (event.getAction()) { - case SWAP_WITH_CURSOR: - case PLACE_ALL: - return event.getCursor(); - case PICKUP_ALL: - case HOTBAR_MOVE_AND_READD: - case MOVE_TO_OTHER_INVENTORY: - case DROP_ALL_SLOT: - case COLLECT_TO_CURSOR: - return null; - case PICKUP_HALF: - case PICKUP_SOME: - ItemStack item = clicked.getItem(event.getSlot()).clone(); - item.setAmount(item.getAmount() / 2); - return item; - case PICKUP_ONE: - case DROP_ONE_SLOT: - item = clicked.getItem(event.getSlot()).clone(); - item.setAmount(Math.max(0, item.getAmount() - 1)); - return item; - case PLACE_ONE: - item = event.getView().getCursor().clone(); - item.setAmount(1); - return item; - case PLACE_SOME: - item = event.getView().getCursor().clone(); - item.setAmount(item.getAmount() / 2); - return item; - case HOTBAR_SWAP: - return event.getView().getBottomInventory().getItem(event.getHotbarButton()); - default: - return clicked.getItem(event.getSlot()); - } - } - - - public static Map<Integer, ItemStack> deduceChangesIfItemAdded(Inventory inventory, ItemStack added, boolean computeNewItem) { - int addedAmount = added.getAmount(); - Map<Integer, ItemStack> rv = Collections.emptyMap(); - - for (int n = inventory.getSize(), i = 0; i < n; i++) { - if (addedAmount <= 0) break; - - ItemStack current = inventory.getItem(i); - if (current == null || current.getType() == Material.AIR || current.isSimilar(added)) { - int count = current == null ? 0 : current.getAmount(); - int max = (current == null ? added : current).getType().getMaxStackSize(); - if (count < max) { - int diff = max - count; - if (diff > addedAmount) { - diff = addedAmount; - } - addedAmount -= diff; - - if (rv.isEmpty()) rv = new LinkedHashMap<>(); - - if (computeNewItem) { - current = (current == null ? added : current).clone(); - current.setAmount(count + diff); - rv.put(i, current); - } else { - rv.put(i, null); - } - } - } - } - - return rv; - } - - - public static ItemStack getNewHeldItemIfPickedUp(PlayerInventory inventory, ItemStack added) { - int heldItemSlot = inventory.getHeldItemSlot(); - ItemStack heldItem = inventory.getItem(heldItemSlot); - - if (SpigotUtil.isItemPresent(heldItem) && !added.isSimilar(heldItem)) { - return null; - } - - int amt = added.getAmount(); - for (int i = 0; i < heldItemSlot; i++) { - ItemStack item = inventory.getItem(i); - if (!SpigotUtil.isItemPresent(item)) { - return null; - } - if (item.isSimilar(item)) { - amt -= Math.max(0, item.getMaxStackSize() - item.getAmount()); - if (amt <= 0) { - return null; - } - } - } - - added = added.clone(); - added.setAmount(amt + Math.max(0, heldItem == null ? 0 : heldItem.getAmount())); - return added; - } - - -} +package io.dico.dicore;
+
+import org.bukkit.Material;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class InventoryEventUtil {
+
+ private InventoryEventUtil() {
+
+ }
+
+ public static ItemStack getNewItem(InventoryClickEvent event) {
+ Inventory clicked = event.getInventory();
+ switch (event.getAction()) {
+ case SWAP_WITH_CURSOR:
+ case PLACE_ALL:
+ return event.getCursor();
+ case PICKUP_ALL:
+ case HOTBAR_MOVE_AND_READD:
+ case MOVE_TO_OTHER_INVENTORY:
+ case DROP_ALL_SLOT:
+ case COLLECT_TO_CURSOR:
+ return null;
+ case PICKUP_HALF:
+ case PICKUP_SOME:
+ ItemStack item = clicked.getItem(event.getSlot()).clone();
+ item.setAmount(item.getAmount() / 2);
+ return item;
+ case PICKUP_ONE:
+ case DROP_ONE_SLOT:
+ item = clicked.getItem(event.getSlot()).clone();
+ item.setAmount(Math.max(0, item.getAmount() - 1));
+ return item;
+ case PLACE_ONE:
+ item = event.getView().getCursor().clone();
+ item.setAmount(1);
+ return item;
+ case PLACE_SOME:
+ item = event.getView().getCursor().clone();
+ item.setAmount(item.getAmount() / 2);
+ return item;
+ case HOTBAR_SWAP:
+ return event.getView().getBottomInventory().getItem(event.getHotbarButton());
+ default:
+ return clicked.getItem(event.getSlot());
+ }
+ }
+
+
+ public static Map<Integer, ItemStack> deduceChangesIfItemAdded(Inventory inventory, ItemStack added, boolean computeNewItem) {
+ int addedAmount = added.getAmount();
+ Map<Integer, ItemStack> rv = Collections.emptyMap();
+
+ for (int n = inventory.getSize(), i = 0; i < n; i++) {
+ if (addedAmount <= 0) break;
+
+ ItemStack current = inventory.getItem(i);
+ if (current == null || current.getType() == Material.AIR || current.isSimilar(added)) {
+ int count = current == null ? 0 : current.getAmount();
+ int max = (current == null ? added : current).getType().getMaxStackSize();
+ if (count < max) {
+ int diff = max - count;
+ if (diff > addedAmount) {
+ diff = addedAmount;
+ }
+ addedAmount -= diff;
+
+ if (rv.isEmpty()) rv = new LinkedHashMap<>();
+
+ if (computeNewItem) {
+ current = (current == null ? added : current).clone();
+ current.setAmount(count + diff);
+ rv.put(i, current);
+ } else {
+ rv.put(i, null);
+ }
+ }
+ }
+ }
+
+ return rv;
+ }
+
+
+ public static ItemStack getNewHeldItemIfPickedUp(PlayerInventory inventory, ItemStack added) {
+ int heldItemSlot = inventory.getHeldItemSlot();
+ ItemStack heldItem = inventory.getItem(heldItemSlot);
+
+ if (SpigotUtil.isItemPresent(heldItem) && !added.isSimilar(heldItem)) {
+ return null;
+ }
+
+ int amt = added.getAmount();
+ for (int i = 0; i < heldItemSlot; i++) {
+ ItemStack item = inventory.getItem(i);
+ if (!SpigotUtil.isItemPresent(item)) {
+ return null;
+ }
+ if (item.isSimilar(item)) {
+ amt -= Math.max(0, item.getMaxStackSize() - item.getAmount());
+ if (amt <= 0) {
+ return null;
+ }
+ }
+ }
+
+ added = added.clone();
+ added.setAmount(amt + Math.max(0, heldItem == null ? 0 : heldItem.getAmount()));
+ return added;
+ }
+
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/Logging.java b/dicore3/core/src/main/java/io/dico/dicore/Logging.java index fa2a676..50c4b52 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Logging.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Logging.java @@ -1,116 +1,116 @@ -package io.dico.dicore; - -import java.util.logging.Logger; - -public interface Logging { - - void info(Object o); - - void warn(Object o); - - void error(Object o); - - void debug(Object o); - - void setDebugging(boolean debugging); - - boolean isDebugging(); - - class RootLogging implements Logging { - private final String prefix; - private final Logger root; - private boolean debugging; - - public RootLogging(String prefix, Logger root, boolean debugging) { - this.root = root; - this.prefix = prefix; - this.debugging = debugging; - } - - @Override - public void info(Object o) { - root.info(prefix(o)); - } - - @Override - public void warn(Object o) { - root.warning(prefix(o)); - } - - @Override - public void error(Object o) { - root.severe(prefix(o)); - } - - @Override - public void debug(Object o) { - if (debugging) { - root.info(String.format("[DEBUG] %s", prefix(o))); - } - } - - @Override - public boolean isDebugging() { - return debugging; - } - - @Override - public void setDebugging(boolean debugging) { - this.debugging = debugging; - } - - private String prefix(Object o) { - return String.format("[%s] %s", prefix, String.valueOf(o)); - } - } - - class SubLogging implements Logging { - protected String prefix; - private final Logging superLogger; - private boolean debugging; - - public SubLogging(String prefix, Logging superLogger, boolean debugging) { - this.superLogger = superLogger; - this.prefix = prefix; - this.debugging = debugging; - } - - @Override - public void info(Object o) { - superLogger.info(prefix(o)); - } - - @Override - public void warn(Object o) { - superLogger.warn(prefix(o)); - } - - @Override - public void error(Object o) { - superLogger.error(prefix(o)); - } - - @Override - public void debug(Object o) { - if (debugging) { - superLogger.info(String.format("[DEBUG] %s", prefix(o))); - } - } - - @Override - public boolean isDebugging() { - return debugging; - } - - @Override - public void setDebugging(boolean debugging) { - this.debugging = debugging; - } - - private String prefix(Object o) { - return String.format("[%s] %s", prefix, String.valueOf(o)); - } - - } - -} +package io.dico.dicore;
+
+import java.util.logging.Logger;
+
+public interface Logging {
+
+ void info(Object o);
+
+ void warn(Object o);
+
+ void error(Object o);
+
+ void debug(Object o);
+
+ void setDebugging(boolean debugging);
+
+ boolean isDebugging();
+
+ class RootLogging implements Logging {
+ private final String prefix;
+ private final Logger root;
+ private boolean debugging;
+
+ public RootLogging(String prefix, Logger root, boolean debugging) {
+ this.root = root;
+ this.prefix = prefix;
+ this.debugging = debugging;
+ }
+
+ @Override
+ public void info(Object o) {
+ root.info(prefix(o));
+ }
+
+ @Override
+ public void warn(Object o) {
+ root.warning(prefix(o));
+ }
+
+ @Override
+ public void error(Object o) {
+ root.severe(prefix(o));
+ }
+
+ @Override
+ public void debug(Object o) {
+ if (debugging) {
+ root.info(String.format("[DEBUG] %s", prefix(o)));
+ }
+ }
+
+ @Override
+ public boolean isDebugging() {
+ return debugging;
+ }
+
+ @Override
+ public void setDebugging(boolean debugging) {
+ this.debugging = debugging;
+ }
+
+ private String prefix(Object o) {
+ return String.format("[%s] %s", prefix, String.valueOf(o));
+ }
+ }
+
+ class SubLogging implements Logging {
+ protected String prefix;
+ private final Logging superLogger;
+ private boolean debugging;
+
+ public SubLogging(String prefix, Logging superLogger, boolean debugging) {
+ this.superLogger = superLogger;
+ this.prefix = prefix;
+ this.debugging = debugging;
+ }
+
+ @Override
+ public void info(Object o) {
+ superLogger.info(prefix(o));
+ }
+
+ @Override
+ public void warn(Object o) {
+ superLogger.warn(prefix(o));
+ }
+
+ @Override
+ public void error(Object o) {
+ superLogger.error(prefix(o));
+ }
+
+ @Override
+ public void debug(Object o) {
+ if (debugging) {
+ superLogger.info(String.format("[DEBUG] %s", prefix(o)));
+ }
+ }
+
+ @Override
+ public boolean isDebugging() {
+ return debugging;
+ }
+
+ @Override
+ public void setDebugging(boolean debugging) {
+ this.debugging = debugging;
+ }
+
+ private String prefix(Object o) {
+ return String.format("[%s] %s", prefix, String.valueOf(o));
+ }
+
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/Reflection.java b/dicore3/core/src/main/java/io/dico/dicore/Reflection.java index b7b1ed4..2452d75 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Reflection.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Reflection.java @@ -1,780 +1,780 @@ -package io.dico.dicore; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Objects; -import java.util.function.Consumer; - -/** - * Reflective utilities - */ -@SuppressWarnings("unchecked") -public class Reflection { - private static final ExceptionHandler exceptionHandler; - private static final Field fieldModifiersField = restrictedSearchField(Field.class, "modifiers"); - private static Consumer<String> errorTarget; - - private Reflection() { - - } - - static { - exceptionHandler = new ExceptionHandler() { - @Override - public void handle(Throwable ex) { - handleGenericException(ex); - } - - @Override - public Object handleGenericException(Throwable ex, Object... args) { - String action = args.length == 0 || !(args[0] instanceof String) ? "executing a reflective operation" : (String) args[0]; - ExceptionHandler.log(errorTarget, action, ex); - return null; - } - }; - - // don't use method reference here: the current reference in System.out would be cached. - setErrorTarget(msg -> System.out.println(msg)); - } - - /** - * Sets the output where ReflectiveOperationException's and similar are sent. - * This defaults to {@link System#out}. - * - * @param target The new output - * @throws NullPointerException if target is null - */ - public static void setErrorTarget(Consumer<String> target) { - errorTarget = Objects.requireNonNull(target); - } - - /** - * This search modifier tells the implementation that it should subsequently search superclasses for the field/method. - * Using this modifier means a call to {@link #deepSearchField(Class, String)} will be used instead of {@link #restrictedSearchField(Class, String)} - * and a call to {@link #deepSearchMethod(Class, String, Class[])} will be used instead of {@link #restrictedSearchMethod(Class, String, Class[])} - */ - public static final int DEEP_SEARCH = 0x1; - - /** - * This search modifier applies only to fields, and tells the implementation that a final modifier might be present on a found field, and that it should be removed. - */ - public static final int REMOVE_FINAL = 0x2; - - /** - * This search modifier applies only to methods, and tells the implementation that it should completely ignore parameter types and return the first method with a matching name - * The implementation uses {@link Class#getDeclaredMethods()} instead of {@link Class#getDeclaredMethod(String, Class[])} if this modifier is set. - */ - public static final int IGNORE_PARAMS = 0x2; - - /* - ### FIELD METHODS ### - */ - - /** - * Search a field of any accessibility within the class or any of its superclasses. - * The first field with the given name that is found will be returned. - * <p> - * If a field is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. - * <p> - * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. - * - * @param clazz The lowest class in the ladder to start searching from - * @param fieldName The name of the field - * //@param fieldType the type of the field, or null if it can be any. - * @return The field - * @throws NullPointerException if clazz is null or fieldName is null - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - */ - public static Field deepSearchField(Class<?> clazz, String fieldName/*, Class<?> fieldType*/) { - Class<?> currentClass = clazz; - Field result; - do { - // throws NPE if class or fieldName is null - result = internalSearchField(clazz, fieldName); - if (result != null) { - return result; - } - currentClass = currentClass.getSuperclass(); - } while (currentClass != null); - - throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + " and superclasses: " + fieldName); - } - - /** - * Search a field of any accessibility within the class, but not its superclasses. - * <p> - * If a field is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. - * <p> - * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. - * - * @param clazz The only class to search for the field - * @param fieldName The name of the field - * @return The field - * @throws NullPointerException if clazz or fieldName is null - * @throws IllegalArgumentException if the field does not exist - */ - public static Field restrictedSearchField(Class<?> clazz, String fieldName) { - Field result = internalSearchField(clazz, fieldName); - if (result == null) { - throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + ": " + fieldName); - } - return result; - } - - /** - * Searches for a field using the given search method. - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search in/from - * @param fieldName Name of the field - * @return The field - * @throws NullPointerException if clazz or fieldName is null - * @throws IllegalArgumentException if the field is not found - */ - public static Field searchField(int modifiers, Class<?> clazz, String fieldName) { - Field result; - if ((modifiers & DEEP_SEARCH) != 0) { - result = deepSearchField(clazz, fieldName); - } else { - result = restrictedSearchField(clazz, fieldName); - } - if ((modifiers & REMOVE_FINAL) != 0) { - removeFinalModifier(result); - } - return result; - } - - /** - * @return The same as {@link #restrictedSearchField(Class, String)}, but returns null instead of throwing IllegalArgumentException - * @see #restrictedSearchField(Class, String) - */ - private static Field internalSearchField(Class<?> clazz, String fieldName) { - Field result; - try { - // throws NullPointerException if either clazz or fieldName are null. - result = clazz.getDeclaredField(fieldName); - } catch (NoSuchFieldException | SecurityException ex) { - return null; - } - - if (!result.isAccessible()) try { - result.setAccessible(true); - } catch (SecurityException ignored) { - - } - - return result; - } - - /** - * Attempts to remove existing final modifier of the given field - * This method should always return true. - * - * @param field The field whose final modifier to remove - * @return true if the field most definitely has no final modifier after this call - * @throws NullPointerException if field is null - */ - public static boolean removeFinalModifier(Field field) { - Objects.requireNonNull(field); - try { - int modifiers = (int) fieldModifiersField.get(field); - if (modifiers != (modifiers &= ~Modifier.FINAL)) { - fieldModifiersField.set(field, modifiers); - } - return true; - } catch (Exception ex) { - return false; - } - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param <T> The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #getFieldValue(Class, String, Object) - */ - public static <T> T getFieldValue(Object instance, String fieldName) { - return getFieldValue(deepSearchField(instance.getClass(), fieldName), instance); - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param <T> The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #getFieldValue(Field, Object) - */ - public static <T> T getFieldValue(Class<?> clazz, String fieldName, Object instance) { - return getFieldValue(restrictedSearchField(clazz, fieldName), instance); - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #searchField(int, Class, String)} is used. - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param <T> The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #getFieldValue(Field, Object) - */ - public static <T> T getFieldValue(int modifiers, Class<?> clazz, String fieldName, Object instance) { - return getFieldValue(searchField(modifiers, clazz, fieldName), instance); - } - - /** - * Gets field value of the given field and the given instance - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field the field - * @param instance The instance whose field value to get - * @param <T> The expected/known field type - * @return The field value - */ - public static <T> T getFieldValue(Field field, Object instance) { - return exceptionHandler.supplySafe(() -> (T) field.get(instance)); - } - - /** - * Gets static field value of the field named fieldName - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param <T> The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #getStaticFieldValue(Field) - */ - public static <T> T getStaticFieldValue(Class<?> clazz, String fieldName) { - return getStaticFieldValue(restrictedSearchField(clazz, fieldName)); - } - - /** - * Gets static field value of the field named fieldName - * To find the field, {@link #searchField(int, Class, String)} is used. - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param <T> The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #getStaticFieldValue(Field) - */ - public static <T> T getStaticFieldValue(int modifiers, Class<?> clazz, String fieldName) { - return getStaticFieldValue(searchField(modifiers, clazz, fieldName)); - } - - /** - * Gets static field value - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * <p> - * Equivalent to the call {@code getFieldValue(field, (Object) null)} - * - * @param field the field - * @param <T> The expected/known field type - * @return The field value - * @see #getFieldValue(Field, Object) - */ - public static <T> T getStaticFieldValue(Field field) { - return getFieldValue(field, (Object) null); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param instance The instance whose field value to set - * @param fieldName the name of the field - * @param newValue the new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #setFieldValue(Class, String, Object, Object) - */ - public static void setFieldValue(Object instance, String fieldName, Object newValue) { - setFieldValue(deepSearchField(instance.getClass(), fieldName), instance, newValue); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param instance The field owner - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #setFieldValue(Field, Object, Object) - */ - public static void setFieldValue(Class<?> clazz, String fieldName, Object instance, Object newValue) { - setFieldValue(restrictedSearchField(clazz, fieldName), instance, newValue); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #searchField(int, Class, String)} is used. - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param instance The instance whose field value to set - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #setFieldValue(Field, Object, Object) - */ - public static void setFieldValue(int modifiers, Class<?> clazz, String fieldName, Object instance, Object newValue) { - setFieldValue(searchField(modifiers, clazz, fieldName), instance, newValue); - } - - /** - * Sets a field value - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field The field - * @param instance The field owner - * @param newValue The new field value - */ - public static void setFieldValue(Field field, Object instance, Object newValue) { - exceptionHandler.runSafe(() -> field.set(instance, newValue)); - } - - /** - * Sets static field value of the field name fieldName - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #setStaticFieldValue(Field, Object) - */ - public static void setStaticFieldValue(Class<?> clazz, String fieldName, Object newValue) { - setStaticFieldValue(restrictedSearchField(clazz, fieldName), newValue); - } - - /** - * Sets static field value of the field named fieldName - * To find the field, {@link #searchField(int, Class, String)} is used. - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #setStaticFieldValue(Field, Object) - */ - public static void setStaticFieldValue(int modifiers, Class<?> clazz, String fieldName, Object newValue) { - setStaticFieldValue(searchField(modifiers, clazz, fieldName), newValue); - } - - /** - * Sets a static field value - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field The field - * @param newValue The new field value - */ - public static void setStaticFieldValue(Field field, Object newValue) { - setFieldValue(field, (Object) null, newValue); - } - - /* - ### METHOD METHODS ### - */ - - /** - * Search a method of any accessibility within the class or any of its superclasses. - * The first method with the given name that is found will be returned. - * <p> - * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - * <p> - * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param clazz The lowest class in the ladder to start searching from - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz is null or methodName is null - * @throws IllegalArgumentException if the method doesn't exist - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - public static Method deepSearchMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) { - return deepSearchMethod(0, clazz, methodName, parameterTypes); - } - - /** - * Search a method of any accessibility within the class or any of its superclasses. - * The first method with the given name that is found will be returned. - * <p> - * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - * <p> - * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} - * @param clazz The lowest class in the ladder to start searching from - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz is null or methodName is null - * @throws IllegalArgumentException if the method doesn't exist - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - public static Method deepSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) { - Class<?> currentClass = clazz; - Method result; - do { - // throws NPE if class or methodName is null - result = internalSearchMethod(modifiers, currentClass, methodName, parameterTypes); - if (result != null) { - return result; - } - currentClass = currentClass.getSuperclass(); - } while (currentClass != null); - - throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + " and superclasses: " + methodName); - } - - /** - * Search a method of any accessibility within the class, but not its superclasses. - * <p> - * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - * <p> - * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param clazz The only class to search for the method - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method does not exist - */ - public static Method restrictedSearchMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) { - return restrictedSearchMethod(0, clazz, methodName, parameterTypes); - } - - /** - * Search a method of any accessibility within the class, but not its superclasses. - * <p> - * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - * <p> - * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} - * @param clazz The only class to search for the method - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method does not exist - */ - public static Method restrictedSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) { - Method result = internalSearchMethod(modifiers, clazz, methodName, parameterTypes); - if (result == null) { - throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + ": " + methodName); - } - return result; - } - - /** - * Searches for a method using the given search method. - * <p> - * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - * <p> - * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - */ - public static Method searchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) { - if ((modifiers & DEEP_SEARCH) != 0) { - return deepSearchMethod(modifiers, clazz, methodName, parameterTypes); - } else { - return restrictedSearchMethod(modifiers, clazz, methodName, parameterTypes); - } - } - - /** - * @return The same as {@link #restrictedSearchMethod(Class, String, Class[]) }, but returns null instead of throwing IllegalArgumentException - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - private static Method internalSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) { - Method result = null; - - if ((modifiers & IGNORE_PARAMS) != 0) { - - // throws NullPointerException if either clazz or methodName are null. - methodName = methodName.intern(); - for (Method method : clazz.getDeclaredMethods()) { - // all method names are interned. Identity comparison is much faster. - if (method.getName() == methodName) { - result = method; - break; - } - } - - if (result == null) { - return null; - } - - } else { - - try { - // throws NullPointerException if either clazz or methodName are null. - result = clazz.getDeclaredMethod(methodName, parameterTypes); - } catch (NoSuchMethodException | SecurityException ex) { - return null; - } - - } - - if (!result.isAccessible()) try { - result.setAccessible(true); - } catch (SecurityException ignored) { - - } - - return result; - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * modifiers {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}, and the class {@link Object#getClass() instance.getClass()} - * <p> - * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param <T> The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if instance or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static <T> T invokeMethod(Object instance, String methodName, Object... args) { - return invokeMethod(searchMethod(DEEP_SEARCH | IGNORE_PARAMS, instance.getClass(), methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * as well as the modifier {@link #IGNORE_PARAMS} - * <p> - * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param <T> The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static <T> T invokeMethod(Class<?> clazz, String methodName, Object instance, Object... args) { - return invokeMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - * For this search, the result of calling {@link Object#getClass() instance.getClass()} is used. - * <p> - * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param <T> The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if instance or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static <T> T invokeMethod(int modifiers, Object instance, String methodName, Object... args) { - return invokeMethod(searchMethod(modifiers, instance.getClass(), methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - * <p> - * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param <T> The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static <T> T invokeMethod(int modifiers, Class<?> clazz, String methodName, Object instance, Object... args) { - return invokeMethod(searchMethod(modifiers, clazz, methodName), instance, args); - } - - /** - * Invokes the method with the given instance and arguments - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param method The method to invoke - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param <T> The expected/known method return type - * @return The result of calling the method - */ - public static <T> T invokeMethod(Method method, Object instance, Object... args) { - return exceptionHandler.supplySafe(() -> (T) method.invoke(instance, args)); - } - - /** - * Invokes the static method named methodName with the given arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * as well as the modifier {@link #IGNORE_PARAMS} - * <p> - * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param args The arguments to use in the method call - * @param <T> The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeStaticMethod(Method, Object...) - */ - public static <T> T invokeStaticMethod(Class<?> clazz, String methodName, Object... args) { - return invokeStaticMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), args); - } - - /** - * Invokes the static method named methodName with the given arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - * <p> - * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param args The arguments to use in the method call - * @param <T> The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeStaticMethod(Method, Object...) - */ - public static <T> T invokeStaticMethod(int modifiers, Class<?> clazz, String methodName, Object... args) { - return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args); - } - - /** - * Invokes the static method with the given arguments - * <p> - * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param method The method to invoke - * @param args The arguments to use in the method call - * @param <T> The expected/known method return type - * @return The result of calling the method - * @see #invokeMethod(Method, Object, Object...) - */ - public static <T> T invokeStaticMethod(Method method, Object... args) { - return invokeMethod(method, (Object) null, args); - } - -} +package io.dico.dicore;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Reflective utilities
+ */
+@SuppressWarnings("unchecked")
+public class Reflection {
+ private static final ExceptionHandler exceptionHandler;
+ private static final Field fieldModifiersField = restrictedSearchField(Field.class, "modifiers");
+ private static Consumer<String> errorTarget;
+
+ private Reflection() {
+
+ }
+
+ static {
+ exceptionHandler = new ExceptionHandler() {
+ @Override
+ public void handle(Throwable ex) {
+ handleGenericException(ex);
+ }
+
+ @Override
+ public Object handleGenericException(Throwable ex, Object... args) {
+ String action = args.length == 0 || !(args[0] instanceof String) ? "executing a reflective operation" : (String) args[0];
+ ExceptionHandler.log(errorTarget, action, ex);
+ return null;
+ }
+ };
+
+ // don't use method reference here: the current reference in System.out would be cached.
+ setErrorTarget(msg -> System.out.println(msg));
+ }
+
+ /**
+ * Sets the output where ReflectiveOperationException's and similar are sent.
+ * This defaults to {@link System#out}.
+ *
+ * @param target The new output
+ * @throws NullPointerException if target is null
+ */
+ public static void setErrorTarget(Consumer<String> target) {
+ errorTarget = Objects.requireNonNull(target);
+ }
+
+ /**
+ * This search modifier tells the implementation that it should subsequently search superclasses for the field/method.
+ * Using this modifier means a call to {@link #deepSearchField(Class, String)} will be used instead of {@link #restrictedSearchField(Class, String)}
+ * and a call to {@link #deepSearchMethod(Class, String, Class[])} will be used instead of {@link #restrictedSearchMethod(Class, String, Class[])}
+ */
+ public static final int DEEP_SEARCH = 0x1;
+
+ /**
+ * This search modifier applies only to fields, and tells the implementation that a final modifier might be present on a found field, and that it should be removed.
+ */
+ public static final int REMOVE_FINAL = 0x2;
+
+ /**
+ * This search modifier applies only to methods, and tells the implementation that it should completely ignore parameter types and return the first method with a matching name
+ * The implementation uses {@link Class#getDeclaredMethods()} instead of {@link Class#getDeclaredMethod(String, Class[])} if this modifier is set.
+ */
+ public static final int IGNORE_PARAMS = 0x2;
+
+ /*
+ ### FIELD METHODS ###
+ */
+
+ /**
+ * Search a field of any accessibility within the class or any of its superclasses.
+ * The first field with the given name that is found will be returned.
+ * <p>
+ * If a field is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that.
+ *
+ * @param clazz The lowest class in the ladder to start searching from
+ * @param fieldName The name of the field
+ * //@param fieldType the type of the field, or null if it can be any.
+ * @return The field
+ * @throws NullPointerException if clazz is null or fieldName is null
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ */
+ public static Field deepSearchField(Class<?> clazz, String fieldName/*, Class<?> fieldType*/) {
+ Class<?> currentClass = clazz;
+ Field result;
+ do {
+ // throws NPE if class or fieldName is null
+ result = internalSearchField(clazz, fieldName);
+ if (result != null) {
+ return result;
+ }
+ currentClass = currentClass.getSuperclass();
+ } while (currentClass != null);
+
+ throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + " and superclasses: " + fieldName);
+ }
+
+ /**
+ * Search a field of any accessibility within the class, but not its superclasses.
+ * <p>
+ * If a field is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that.
+ *
+ * @param clazz The only class to search for the field
+ * @param fieldName The name of the field
+ * @return The field
+ * @throws NullPointerException if clazz or fieldName is null
+ * @throws IllegalArgumentException if the field does not exist
+ */
+ public static Field restrictedSearchField(Class<?> clazz, String fieldName) {
+ Field result = internalSearchField(clazz, fieldName);
+ if (result == null) {
+ throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + ": " + fieldName);
+ }
+ return result;
+ }
+
+ /**
+ * Searches for a field using the given search method.
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search in/from
+ * @param fieldName Name of the field
+ * @return The field
+ * @throws NullPointerException if clazz or fieldName is null
+ * @throws IllegalArgumentException if the field is not found
+ */
+ public static Field searchField(int modifiers, Class<?> clazz, String fieldName) {
+ Field result;
+ if ((modifiers & DEEP_SEARCH) != 0) {
+ result = deepSearchField(clazz, fieldName);
+ } else {
+ result = restrictedSearchField(clazz, fieldName);
+ }
+ if ((modifiers & REMOVE_FINAL) != 0) {
+ removeFinalModifier(result);
+ }
+ return result;
+ }
+
+ /**
+ * @return The same as {@link #restrictedSearchField(Class, String)}, but returns null instead of throwing IllegalArgumentException
+ * @see #restrictedSearchField(Class, String)
+ */
+ private static Field internalSearchField(Class<?> clazz, String fieldName) {
+ Field result;
+ try {
+ // throws NullPointerException if either clazz or fieldName are null.
+ result = clazz.getDeclaredField(fieldName);
+ } catch (NoSuchFieldException | SecurityException ex) {
+ return null;
+ }
+
+ if (!result.isAccessible()) try {
+ result.setAccessible(true);
+ } catch (SecurityException ignored) {
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Attempts to remove existing final modifier of the given field
+ * This method should always return true.
+ *
+ * @param field The field whose final modifier to remove
+ * @return true if the field most definitely has no final modifier after this call
+ * @throws NullPointerException if field is null
+ */
+ public static boolean removeFinalModifier(Field field) {
+ Objects.requireNonNull(field);
+ try {
+ int modifiers = (int) fieldModifiersField.get(field);
+ if (modifiers != (modifiers &= ~Modifier.FINAL)) {
+ fieldModifiersField.set(field, modifiers);
+ }
+ return true;
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets field value of the field named fieldName and the given instance
+ * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param instance The instance whose field value to get
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #deepSearchField(Class, String)
+ * @see #getFieldValue(Class, String, Object)
+ */
+ public static <T> T getFieldValue(Object instance, String fieldName) {
+ return getFieldValue(deepSearchField(instance.getClass(), fieldName), instance);
+ }
+
+ /**
+ * Gets field value of the field named fieldName and the given instance
+ * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search for the field
+ * @param instance The instance whose field value to get
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ * @see #getFieldValue(Field, Object)
+ */
+ public static <T> T getFieldValue(Class<?> clazz, String fieldName, Object instance) {
+ return getFieldValue(restrictedSearchField(clazz, fieldName), instance);
+ }
+
+ /**
+ * Gets field value of the field named fieldName and the given instance
+ * To find the field, {@link #searchField(int, Class, String)} is used.
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search for the field
+ * @param instance The instance whose field value to get
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #searchField(int, Class, String)
+ * @see #getFieldValue(Field, Object)
+ */
+ public static <T> T getFieldValue(int modifiers, Class<?> clazz, String fieldName, Object instance) {
+ return getFieldValue(searchField(modifiers, clazz, fieldName), instance);
+ }
+
+ /**
+ * Gets field value of the given field and the given instance
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param field the field
+ * @param instance The instance whose field value to get
+ * @param <T> The expected/known field type
+ * @return The field value
+ */
+ public static <T> T getFieldValue(Field field, Object instance) {
+ return exceptionHandler.supplySafe(() -> (T) field.get(instance));
+ }
+
+ /**
+ * Gets static field value of the field named fieldName
+ * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ * @see #getStaticFieldValue(Field)
+ */
+ public static <T> T getStaticFieldValue(Class<?> clazz, String fieldName) {
+ return getStaticFieldValue(restrictedSearchField(clazz, fieldName));
+ }
+
+ /**
+ * Gets static field value of the field named fieldName
+ * To find the field, {@link #searchField(int, Class, String)} is used.
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #deepSearchField(Class, String)
+ * @see #getStaticFieldValue(Field)
+ */
+ public static <T> T getStaticFieldValue(int modifiers, Class<?> clazz, String fieldName) {
+ return getStaticFieldValue(searchField(modifiers, clazz, fieldName));
+ }
+
+ /**
+ * Gets static field value
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ * <p>
+ * Equivalent to the call {@code getFieldValue(field, (Object) null)}
+ *
+ * @param field the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @see #getFieldValue(Field, Object)
+ */
+ public static <T> T getStaticFieldValue(Field field) {
+ return getFieldValue(field, (Object) null);
+ }
+
+ /**
+ * Sets field value of the field named fieldName and the given instance
+ * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param instance The instance whose field value to set
+ * @param fieldName the name of the field
+ * @param newValue the new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #deepSearchField(Class, String)
+ * @see #setFieldValue(Class, String, Object, Object)
+ */
+ public static void setFieldValue(Object instance, String fieldName, Object newValue) {
+ setFieldValue(deepSearchField(instance.getClass(), fieldName), instance, newValue);
+ }
+
+ /**
+ * Sets field value of the field named fieldName and the given instance
+ * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param instance The field owner
+ * @param newValue The new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ * @see #setFieldValue(Field, Object, Object)
+ */
+ public static void setFieldValue(Class<?> clazz, String fieldName, Object instance, Object newValue) {
+ setFieldValue(restrictedSearchField(clazz, fieldName), instance, newValue);
+ }
+
+ /**
+ * Sets field value of the field named fieldName and the given instance
+ * To find the field, {@link #searchField(int, Class, String)} is used.
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search for the field
+ * @param instance The instance whose field value to set
+ * @param fieldName the name of the field
+ * @param newValue The new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #searchField(int, Class, String)
+ * @see #setFieldValue(Field, Object, Object)
+ */
+ public static void setFieldValue(int modifiers, Class<?> clazz, String fieldName, Object instance, Object newValue) {
+ setFieldValue(searchField(modifiers, clazz, fieldName), instance, newValue);
+ }
+
+ /**
+ * Sets a field value
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param field The field
+ * @param instance The field owner
+ * @param newValue The new field value
+ */
+ public static void setFieldValue(Field field, Object instance, Object newValue) {
+ exceptionHandler.runSafe(() -> field.set(instance, newValue));
+ }
+
+ /**
+ * Sets static field value of the field name fieldName
+ * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param newValue The new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ * @see #setStaticFieldValue(Field, Object)
+ */
+ public static void setStaticFieldValue(Class<?> clazz, String fieldName, Object newValue) {
+ setStaticFieldValue(restrictedSearchField(clazz, fieldName), newValue);
+ }
+
+ /**
+ * Sets static field value of the field named fieldName
+ * To find the field, {@link #searchField(int, Class, String)} is used.
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param newValue The new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #searchField(int, Class, String)
+ * @see #setStaticFieldValue(Field, Object)
+ */
+ public static void setStaticFieldValue(int modifiers, Class<?> clazz, String fieldName, Object newValue) {
+ setStaticFieldValue(searchField(modifiers, clazz, fieldName), newValue);
+ }
+
+ /**
+ * Sets a static field value
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param field The field
+ * @param newValue The new field value
+ */
+ public static void setStaticFieldValue(Field field, Object newValue) {
+ setFieldValue(field, (Object) null, newValue);
+ }
+
+ /*
+ ### METHOD METHODS ###
+ */
+
+ /**
+ * Search a method of any accessibility within the class or any of its superclasses.
+ * The first method with the given name that is found will be returned.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param clazz The lowest class in the ladder to start searching from
+ * @param methodName The name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz is null or methodName is null
+ * @throws IllegalArgumentException if the method doesn't exist
+ * @see #restrictedSearchMethod(Class, String, Class[])
+ */
+ public static Method deepSearchMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ return deepSearchMethod(0, clazz, methodName, parameterTypes);
+ }
+
+ /**
+ * Search a method of any accessibility within the class or any of its superclasses.
+ * The first method with the given name that is found will be returned.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS}
+ * @param clazz The lowest class in the ladder to start searching from
+ * @param methodName The name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz is null or methodName is null
+ * @throws IllegalArgumentException if the method doesn't exist
+ * @see #restrictedSearchMethod(Class, String, Class[])
+ */
+ public static Method deepSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ Class<?> currentClass = clazz;
+ Method result;
+ do {
+ // throws NPE if class or methodName is null
+ result = internalSearchMethod(modifiers, currentClass, methodName, parameterTypes);
+ if (result != null) {
+ return result;
+ }
+ currentClass = currentClass.getSuperclass();
+ } while (currentClass != null);
+
+ throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + " and superclasses: " + methodName);
+ }
+
+ /**
+ * Search a method of any accessibility within the class, but not its superclasses.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param clazz The only class to search for the method
+ * @param methodName The name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method does not exist
+ */
+ public static Method restrictedSearchMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ return restrictedSearchMethod(0, clazz, methodName, parameterTypes);
+ }
+
+ /**
+ * Search a method of any accessibility within the class, but not its superclasses.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS}
+ * @param clazz The only class to search for the method
+ * @param methodName The name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method does not exist
+ */
+ public static Method restrictedSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ Method result = internalSearchMethod(modifiers, clazz, methodName, parameterTypes);
+ if (result == null) {
+ throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + ": " + methodName);
+ }
+ return result;
+ }
+
+ /**
+ * Searches for a method using the given search method.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ */
+ public static Method searchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ if ((modifiers & DEEP_SEARCH) != 0) {
+ return deepSearchMethod(modifiers, clazz, methodName, parameterTypes);
+ } else {
+ return restrictedSearchMethod(modifiers, clazz, methodName, parameterTypes);
+ }
+ }
+
+ /**
+ * @return The same as {@link #restrictedSearchMethod(Class, String, Class[]) }, but returns null instead of throwing IllegalArgumentException
+ * @see #restrictedSearchMethod(Class, String, Class[])
+ */
+ private static Method internalSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ Method result = null;
+
+ if ((modifiers & IGNORE_PARAMS) != 0) {
+
+ // throws NullPointerException if either clazz or methodName are null.
+ methodName = methodName.intern();
+ for (Method method : clazz.getDeclaredMethods()) {
+ // all method names are interned. Identity comparison is much faster.
+ if (method.getName() == methodName) {
+ result = method;
+ break;
+ }
+ }
+
+ if (result == null) {
+ return null;
+ }
+
+ } else {
+
+ try {
+ // throws NullPointerException if either clazz or methodName are null.
+ result = clazz.getDeclaredMethod(methodName, parameterTypes);
+ } catch (NoSuchMethodException | SecurityException ex) {
+ return null;
+ }
+
+ }
+
+ if (!result.isAccessible()) try {
+ result.setAccessible(true);
+ } catch (SecurityException ignored) {
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Invokes the method named methodName with the given instance and arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
+ * modifiers {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}, and the class {@link Object#getClass() instance.getClass()}
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param methodName Name of the method
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if instance or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeMethod(Object instance, String methodName, Object... args) {
+ return invokeMethod(searchMethod(DEEP_SEARCH | IGNORE_PARAMS, instance.getClass(), methodName), instance, args);
+ }
+
+ /**
+ * Invokes the method named methodName with the given instance and arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
+ * as well as the modifier {@link #IGNORE_PARAMS}
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeMethod(Class<?> clazz, String methodName, Object instance, Object... args) {
+ return invokeMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), instance, args);
+ }
+
+ /**
+ * Invokes the method named methodName with the given instance and arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
+ * For this search, the result of calling {@link Object#getClass() instance.getClass()} is used.
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
+ * @param methodName Name of the method
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if instance or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeMethod(int modifiers, Object instance, String methodName, Object... args) {
+ return invokeMethod(searchMethod(modifiers, instance.getClass(), methodName), instance, args);
+ }
+
+ /**
+ * Invokes the method named methodName with the given instance and arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeMethod(int modifiers, Class<?> clazz, String methodName, Object instance, Object... args) {
+ return invokeMethod(searchMethod(modifiers, clazz, methodName), instance, args);
+ }
+
+ /**
+ * Invokes the method with the given instance and arguments
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param method The method to invoke
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ */
+ public static <T> T invokeMethod(Method method, Object instance, Object... args) {
+ return exceptionHandler.supplySafe(() -> (T) method.invoke(instance, args));
+ }
+
+ /**
+ * Invokes the static method named methodName with the given arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
+ * as well as the modifier {@link #IGNORE_PARAMS}
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeStaticMethod(Method, Object...)
+ */
+ public static <T> T invokeStaticMethod(Class<?> clazz, String methodName, Object... args) {
+ return invokeStaticMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), args);
+ }
+
+ /**
+ * Invokes the static method named methodName with the given arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeStaticMethod(Method, Object...)
+ */
+ public static <T> T invokeStaticMethod(int modifiers, Class<?> clazz, String methodName, Object... args) {
+ return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args);
+ }
+
+ /**
+ * Invokes the static method with the given arguments
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param method The method to invoke
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeStaticMethod(Method method, Object... args) {
+ return invokeMethod(method, (Object) null, args);
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java b/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java index 6596b65..d4486ed 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java +++ b/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java @@ -1,59 +1,59 @@ -package io.dico.dicore; - -import org.bukkit.configuration.ConfigurationSection; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class SetBasedWhitelist implements Whitelist { - private final Set set; - private final boolean blacklist; - - public SetBasedWhitelist(Object[] array, boolean blacklist) { - this(Arrays.asList(array), blacklist); - } - - public SetBasedWhitelist(ConfigurationSection section, Function<String, ?> parser) { - this(section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toList()), - section.getBoolean("blacklist", false)); - } - - @SuppressWarnings("unchecked") - public SetBasedWhitelist(Collection collection, boolean blacklist) { - Set set; - if (collection.isEmpty()) { - set = Collections.emptySet(); - } else if (collection.iterator().next() instanceof Enum) { - set = EnumSet.copyOf(collection); - } else if (collection instanceof Set) { - set = (Set) collection; - } else { - set = new HashSet<>(collection); - } - - this.set = set; - this.blacklist = blacklist; - } - - @Override - public boolean isWhitelisted(Object o) { - return blacklist != set.contains(o); - } - - @SuppressWarnings("unchecked") - @Override - public String toString() { - return (blacklist ? "Blacklist" : "Whitelist") + "{" - + String.join(", ", (CharSequence[]) set.stream().map(String::valueOf).toArray(String[]::new)) + "}"; - } - - public Set getSet() { - return set; - } - - public boolean isBlacklist() { - return blacklist; - } - -} +package io.dico.dicore;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class SetBasedWhitelist implements Whitelist {
+ private final Set set;
+ private final boolean blacklist;
+
+ public SetBasedWhitelist(Object[] array, boolean blacklist) {
+ this(Arrays.asList(array), blacklist);
+ }
+
+ public SetBasedWhitelist(ConfigurationSection section, Function<String, ?> parser) {
+ this(section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toList()),
+ section.getBoolean("blacklist", false));
+ }
+
+ @SuppressWarnings("unchecked")
+ public SetBasedWhitelist(Collection collection, boolean blacklist) {
+ Set set;
+ if (collection.isEmpty()) {
+ set = Collections.emptySet();
+ } else if (collection.iterator().next() instanceof Enum) {
+ set = EnumSet.copyOf(collection);
+ } else if (collection instanceof Set) {
+ set = (Set) collection;
+ } else {
+ set = new HashSet<>(collection);
+ }
+
+ this.set = set;
+ this.blacklist = blacklist;
+ }
+
+ @Override
+ public boolean isWhitelisted(Object o) {
+ return blacklist != set.contains(o);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public String toString() {
+ return (blacklist ? "Blacklist" : "Whitelist") + "{"
+ + String.join(", ", (CharSequence[]) set.stream().map(String::valueOf).toArray(String[]::new)) + "}";
+ }
+
+ public Set getSet() {
+ return set;
+ }
+
+ public boolean isBlacklist() {
+ return blacklist;
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java b/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java index 44659fa..afcae02 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java +++ b/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java @@ -1,408 +1,408 @@ -package io.dico.dicore; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.entity.*; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.Attachable; -import org.bukkit.material.MaterialData; -import org.bukkit.projectiles.ProjectileSource; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Predicate; - -public class SpigotUtil { - - private SpigotUtil() { - throw new UnsupportedOperationException(); - } - - public static World matchWorld(String input) { - try { - UUID uid = UUID.fromString(input); - World world = Bukkit.getWorld(uid); - if (world != null) { - return world; - } - } catch (IllegalArgumentException ignored) { - } - - World result = Bukkit.getWorld(input); - if (result == null) { - input = input.toLowerCase().replace("_", "").replaceAll("[-_]", ""); - for (World world : Bukkit.getWorlds()) { - if (world.getName().toLowerCase().equals(input)) { - result = world; - break; - } - } - } - - return result; - } - - public static Block getSupportingBlock(Block block) { - MaterialData data = block.getState().getData(); - if (data instanceof Attachable) { - BlockFace attachedOn = ((Attachable) data).getAttachedFace(); - return block.getRelative(attachedOn); - } - return null; - } - - public static boolean isItemPresent(ItemStack stack) { - return stack != null && stack.getType() != Material.AIR && stack.getAmount() > 0; - } - - public static boolean removeItems(Inventory from, ItemStack item, int amount) { - for (Map.Entry<Integer, ? extends ItemStack> entry : from.all(item.getType()).entrySet()) { - ItemStack stack = entry.getValue(); - if (item.isSimilar(stack)) { - amount -= stack.getAmount(); - int stackAmount = -Math.min(0, amount); - if (stackAmount == 0) { - from.setItem(entry.getKey(), null); - } else { - stack.setAmount(stackAmount); - } - } - } - return amount <= 0; - } - - public static BlockFace yawToFace(float yaw) { - if ((yaw %= 360) < 0) - yaw += 360; - if (45 <= yaw && yaw < 135) - return BlockFace.WEST; - if (135 <= yaw && yaw < 225) - return BlockFace.NORTH; - if (225 <= yaw && yaw < 315) - return BlockFace.EAST; - return BlockFace.SOUTH; - } - - public static void addItems(InventoryHolder entity, ItemStack... items) { - Location dropLocation; - if (entity instanceof Entity) { - dropLocation = ((Entity) entity).getLocation(); - } else if (entity instanceof BlockState) { - dropLocation = ((BlockState) entity).getLocation().add(0.5, 1, 0.5); - } else { - throw new IllegalArgumentException("Can't find location of this InventoryHolder: " + entity); - } - World world = dropLocation.getWorld(); - for (ItemStack toDrop : entity.getInventory().addItem(items).values()) { - world.dropItemNaturally(dropLocation, toDrop); - } - } - - public static String asJsonString(Object object) { - return asJsonString(null, object, 0); - } - - public static String asJsonString(String key, Object object, int indentation) { - String indent = new String(new char[indentation * 2]).replace('\0', ' '); - StringBuilder builder = new StringBuilder(indent); - if (key != null) { - builder.append(key).append(": "); - } - if (object instanceof ConfigurationSerializable) { - object = ((ConfigurationSerializable) object).serialize(); - } - if (object instanceof Map) { - builder.append("{\n"); - Map<?, ?> map = (Map) object; - for (Map.Entry entry : map.entrySet()) { - builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1)); - } - builder.append(indent).append("}"); - } else if (object instanceof List) { - builder.append("[\n"); - List list = (List) object; - for (Object entry : list) { - builder.append(asJsonString(null, entry, indentation + 1)); - } - builder.append(indent).append("]"); - } else { - builder.append(String.valueOf(object)); - } - return builder.append(",\n").toString(); - } - - public static String asJsonString(String key, Object object, int indentation, BiConsumer<List<?>, StringBuilder> listHeader) { - String indent = new String(new char[indentation * 2]).replace('\0', ' '); - StringBuilder builder = new StringBuilder(indent); - if (key != null) { - builder.append(key).append(": "); - } - if (object instanceof ConfigurationSerializable) { - object = ((ConfigurationSerializable) object).serialize(); - } - if (object instanceof Map) { - builder.append("{\n"); - Map<?, ?> map = (Map) object; - for (Map.Entry entry : map.entrySet()) { - builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1, listHeader)); - } - builder.append(indent).append("}"); - } else if (object instanceof List) { - builder.append("["); - List list = (List) object; - listHeader.accept(list, builder); - builder.append("\n"); - for (Object entry : list) { - builder.append(asJsonString(null, entry, indentation + 1, listHeader)); - } - builder.append(indent).append("]"); - } else { - builder.append(String.valueOf(object)); - } - return builder.append(",\n").toString(); - } - - public static BlockFace estimateDirectionTo(Location from, Location to) { - double dx = from.getX() - to.getX(); - double dz = from.getZ() - to.getZ(); - - boolean xGreater = Math.abs(dx) - Math.abs(dz) > 0; - double f = xGreater ? 2 / Math.abs(dx) : 2 / Math.abs(dz); - dx *= f; - dz *= f; - - double other = Math.abs(xGreater ? dz : dx); - - if (other <= .5) { - return xGreater ? (dx < 0 ? BlockFace.WEST : BlockFace.EAST) : (dz < 0 ? BlockFace.NORTH : BlockFace.SOUTH); - } - - if (other < 1.5) { - if (xGreater) { - return dx < 0 ? (dz < 0 ? BlockFace.WEST_NORTH_WEST : BlockFace.WEST_SOUTH_WEST) : (dz < 0 ? BlockFace.EAST_NORTH_EAST : BlockFace.EAST_SOUTH_EAST); - } - return dx < 0 ? (dz < 0 ? BlockFace.NORTH_NORTH_WEST : BlockFace.SOUTH_SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_NORTH_EAST : BlockFace.SOUTH_SOUTH_EAST); - } - - return dx < 0 ? (dz < 0 ? BlockFace.NORTH_WEST : BlockFace.SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_EAST : BlockFace.SOUTH_EAST); - } - - public static Entity findEntityFromDamager(Entity damager, EntityType searched) { - if (damager.getType() == searched) { - return damager; - } - - if (damager instanceof Projectile) { - ProjectileSource shooter = ((Projectile) damager).getShooter(); - if (shooter instanceof Entity && ((Entity) shooter).getType() == searched) { - return (Entity) shooter; - } - return null; - } - - if (damager.getType() == EntityType.PRIMED_TNT) { - Entity source = ((TNTPrimed) damager).getSource(); - if (source.getType() == searched) { - return source; - } - } - - return null; - } - - public static int xpForNextLevel(int currentLevel) { - if (currentLevel >= 30) { - return 112 + (currentLevel - 30) * 9; - } - - if (currentLevel >= 15) { - return 37 + (currentLevel - 15) * 5; - } - - return 7 + currentLevel * 2; - } - - public static int removeExp(Player entity, int xp) { - int total = entity.getTotalExperience(); - if (xp > total) { - xp = total; - } - - int level = entity.getLevel(); - if (level < 0) { - return 0; - } - - int removed = 0; - int xpForNextLevel = xpForNextLevel(level); - int current = (int) entity.getExp() * xpForNextLevel; - - if (xp > current) { - xp -= current; - total -= current; - removed += current; - - if (level == 0) { - entity.setExp(0F); - entity.setTotalExperience(total); - return removed; - } - } else { - current -= xp; - total -= xp; - removed += xp; - - entity.setExp((float) current / xpForNextLevel); - entity.setTotalExperience(total); - return removed; - } - - do { - xpForNextLevel = xpForNextLevel(--level); - if (xpForNextLevel >= xp) { - total -= xp; - removed += xp; - - entity.setExp(1F / xpForNextLevel * (xpForNextLevel - xp)); - entity.setTotalExperience(total); - entity.setLevel(level); - return removed; - } - - xp -= xpForNextLevel; - total -= xpForNextLevel; - removed += xpForNextLevel; - } while (level > 0); - - entity.setExp(0F); - entity.setTotalExperience(0); - entity.setLevel(0); - return removed; - } - - public static int getTotalExp(Player entity) { - int rv = 0; - int level = Math.min(entity.getLevel(), 20000); - for (int i = 0; i < level; i++) { - rv += xpForNextLevel(i); - } - rv += Math.min(1F, Math.max(0F, entity.getExp())) * xpForNextLevel(level); - return rv; - } - - public static double getTotalExpLevels(Player entity) { - int xp = entity.getTotalExperience(); - - int level = 0; - while (xp > 0 && level < 20000) { - int needed = xpForNextLevel(level); - if (needed > xp) { - return level + ((double) xp / needed); - } - xp -= needed; - level++; - } - - return level; - } - - public static int getNearbyPlayerCount(Player origin, double range, Predicate<Player> predicate) { - List<Entity> entities = origin.getNearbyEntities(range, range, range); - int result = 0; - for (Entity entity : entities) { - if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) { - result++; - } - } - return result; - } - - public static void getNearbyPlayers(Player origin, double range, Collection<Player> collection, Predicate<Player> predicate) { - List<Entity> entities = origin.getNearbyEntities(range, range, range); - for (Entity entity : entities) { - if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) { - collection.add((Player) entity); - } - } - } - - public static void forEachNearbyPlayer(Player origin, double range, Consumer<Player> action) { - List<Entity> entities = origin.getNearbyEntities(range, range, range); - for (Entity entity : entities) { - if (entity.getType() == EntityType.PLAYER) { - action.accept((Player) entity); - } - } - } - - public static double distanceSquared(Location first, Location second) { - double dx = first.getX() - second.getX(); - double dy = first.getY() - second.getY(); - double dz = first.getZ() - second.getZ(); - - return dx * dx + dy * dy + dz * dz; - } - - - public static <T extends Entity> Iterator<T> findNearbyEntities(Entity origin, boolean includeSelf, Predicate<Entity> predicate, double horizontalRange, double verticalRange) { - Objects.requireNonNull(origin); - return new Iterator<T>() { - Entity next; - List<Entity> nearby; - int index = 0; - int size; - - { - if (includeSelf) { - next = origin; - } else { - next = findNext(); - } - } - - Entity findNext() { - if (nearby == null) { - nearby = origin.getNearbyEntities(horizontalRange, verticalRange, horizontalRange); - size = nearby.size(); - } - - while (index < size) { - Entity e = nearby.get(index++); - if (predicate.test(e)) { - return e; - } - } - - return null; - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public T next() { - if (next == null) { - throw new NoSuchElementException(); - } - Entity result = next; - next = findNext(); - //noinspection unchecked - return (T) result; - } - - }; - } - - -} +package io.dico.dicore;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.BlockState;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.entity.*;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.material.Attachable;
+import org.bukkit.material.MaterialData;
+import org.bukkit.projectiles.ProjectileSource;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class SpigotUtil {
+
+ private SpigotUtil() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static World matchWorld(String input) {
+ try {
+ UUID uid = UUID.fromString(input);
+ World world = Bukkit.getWorld(uid);
+ if (world != null) {
+ return world;
+ }
+ } catch (IllegalArgumentException ignored) {
+ }
+
+ World result = Bukkit.getWorld(input);
+ if (result == null) {
+ input = input.toLowerCase().replace("_", "").replaceAll("[-_]", "");
+ for (World world : Bukkit.getWorlds()) {
+ if (world.getName().toLowerCase().equals(input)) {
+ result = world;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static Block getSupportingBlock(Block block) {
+ MaterialData data = block.getState().getData();
+ if (data instanceof Attachable) {
+ BlockFace attachedOn = ((Attachable) data).getAttachedFace();
+ return block.getRelative(attachedOn);
+ }
+ return null;
+ }
+
+ public static boolean isItemPresent(ItemStack stack) {
+ return stack != null && stack.getType() != Material.AIR && stack.getAmount() > 0;
+ }
+
+ public static boolean removeItems(Inventory from, ItemStack item, int amount) {
+ for (Map.Entry<Integer, ? extends ItemStack> entry : from.all(item.getType()).entrySet()) {
+ ItemStack stack = entry.getValue();
+ if (item.isSimilar(stack)) {
+ amount -= stack.getAmount();
+ int stackAmount = -Math.min(0, amount);
+ if (stackAmount == 0) {
+ from.setItem(entry.getKey(), null);
+ } else {
+ stack.setAmount(stackAmount);
+ }
+ }
+ }
+ return amount <= 0;
+ }
+
+ public static BlockFace yawToFace(float yaw) {
+ if ((yaw %= 360) < 0)
+ yaw += 360;
+ if (45 <= yaw && yaw < 135)
+ return BlockFace.WEST;
+ if (135 <= yaw && yaw < 225)
+ return BlockFace.NORTH;
+ if (225 <= yaw && yaw < 315)
+ return BlockFace.EAST;
+ return BlockFace.SOUTH;
+ }
+
+ public static void addItems(InventoryHolder entity, ItemStack... items) {
+ Location dropLocation;
+ if (entity instanceof Entity) {
+ dropLocation = ((Entity) entity).getLocation();
+ } else if (entity instanceof BlockState) {
+ dropLocation = ((BlockState) entity).getLocation().add(0.5, 1, 0.5);
+ } else {
+ throw new IllegalArgumentException("Can't find location of this InventoryHolder: " + entity);
+ }
+ World world = dropLocation.getWorld();
+ for (ItemStack toDrop : entity.getInventory().addItem(items).values()) {
+ world.dropItemNaturally(dropLocation, toDrop);
+ }
+ }
+
+ public static String asJsonString(Object object) {
+ return asJsonString(null, object, 0);
+ }
+
+ public static String asJsonString(String key, Object object, int indentation) {
+ String indent = new String(new char[indentation * 2]).replace('\0', ' ');
+ StringBuilder builder = new StringBuilder(indent);
+ if (key != null) {
+ builder.append(key).append(": ");
+ }
+ if (object instanceof ConfigurationSerializable) {
+ object = ((ConfigurationSerializable) object).serialize();
+ }
+ if (object instanceof Map) {
+ builder.append("{\n");
+ Map<?, ?> map = (Map) object;
+ for (Map.Entry entry : map.entrySet()) {
+ builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1));
+ }
+ builder.append(indent).append("}");
+ } else if (object instanceof List) {
+ builder.append("[\n");
+ List list = (List) object;
+ for (Object entry : list) {
+ builder.append(asJsonString(null, entry, indentation + 1));
+ }
+ builder.append(indent).append("]");
+ } else {
+ builder.append(String.valueOf(object));
+ }
+ return builder.append(",\n").toString();
+ }
+
+ public static String asJsonString(String key, Object object, int indentation, BiConsumer<List<?>, StringBuilder> listHeader) {
+ String indent = new String(new char[indentation * 2]).replace('\0', ' ');
+ StringBuilder builder = new StringBuilder(indent);
+ if (key != null) {
+ builder.append(key).append(": ");
+ }
+ if (object instanceof ConfigurationSerializable) {
+ object = ((ConfigurationSerializable) object).serialize();
+ }
+ if (object instanceof Map) {
+ builder.append("{\n");
+ Map<?, ?> map = (Map) object;
+ for (Map.Entry entry : map.entrySet()) {
+ builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1, listHeader));
+ }
+ builder.append(indent).append("}");
+ } else if (object instanceof List) {
+ builder.append("[");
+ List list = (List) object;
+ listHeader.accept(list, builder);
+ builder.append("\n");
+ for (Object entry : list) {
+ builder.append(asJsonString(null, entry, indentation + 1, listHeader));
+ }
+ builder.append(indent).append("]");
+ } else {
+ builder.append(String.valueOf(object));
+ }
+ return builder.append(",\n").toString();
+ }
+
+ public static BlockFace estimateDirectionTo(Location from, Location to) {
+ double dx = from.getX() - to.getX();
+ double dz = from.getZ() - to.getZ();
+
+ boolean xGreater = Math.abs(dx) - Math.abs(dz) > 0;
+ double f = xGreater ? 2 / Math.abs(dx) : 2 / Math.abs(dz);
+ dx *= f;
+ dz *= f;
+
+ double other = Math.abs(xGreater ? dz : dx);
+
+ if (other <= .5) {
+ return xGreater ? (dx < 0 ? BlockFace.WEST : BlockFace.EAST) : (dz < 0 ? BlockFace.NORTH : BlockFace.SOUTH);
+ }
+
+ if (other < 1.5) {
+ if (xGreater) {
+ return dx < 0 ? (dz < 0 ? BlockFace.WEST_NORTH_WEST : BlockFace.WEST_SOUTH_WEST) : (dz < 0 ? BlockFace.EAST_NORTH_EAST : BlockFace.EAST_SOUTH_EAST);
+ }
+ return dx < 0 ? (dz < 0 ? BlockFace.NORTH_NORTH_WEST : BlockFace.SOUTH_SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_NORTH_EAST : BlockFace.SOUTH_SOUTH_EAST);
+ }
+
+ return dx < 0 ? (dz < 0 ? BlockFace.NORTH_WEST : BlockFace.SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_EAST : BlockFace.SOUTH_EAST);
+ }
+
+ public static Entity findEntityFromDamager(Entity damager, EntityType searched) {
+ if (damager.getType() == searched) {
+ return damager;
+ }
+
+ if (damager instanceof Projectile) {
+ ProjectileSource shooter = ((Projectile) damager).getShooter();
+ if (shooter instanceof Entity && ((Entity) shooter).getType() == searched) {
+ return (Entity) shooter;
+ }
+ return null;
+ }
+
+ if (damager.getType() == EntityType.PRIMED_TNT) {
+ Entity source = ((TNTPrimed) damager).getSource();
+ if (source.getType() == searched) {
+ return source;
+ }
+ }
+
+ return null;
+ }
+
+ public static int xpForNextLevel(int currentLevel) {
+ if (currentLevel >= 30) {
+ return 112 + (currentLevel - 30) * 9;
+ }
+
+ if (currentLevel >= 15) {
+ return 37 + (currentLevel - 15) * 5;
+ }
+
+ return 7 + currentLevel * 2;
+ }
+
+ public static int removeExp(Player entity, int xp) {
+ int total = entity.getTotalExperience();
+ if (xp > total) {
+ xp = total;
+ }
+
+ int level = entity.getLevel();
+ if (level < 0) {
+ return 0;
+ }
+
+ int removed = 0;
+ int xpForNextLevel = xpForNextLevel(level);
+ int current = (int) entity.getExp() * xpForNextLevel;
+
+ if (xp > current) {
+ xp -= current;
+ total -= current;
+ removed += current;
+
+ if (level == 0) {
+ entity.setExp(0F);
+ entity.setTotalExperience(total);
+ return removed;
+ }
+ } else {
+ current -= xp;
+ total -= xp;
+ removed += xp;
+
+ entity.setExp((float) current / xpForNextLevel);
+ entity.setTotalExperience(total);
+ return removed;
+ }
+
+ do {
+ xpForNextLevel = xpForNextLevel(--level);
+ if (xpForNextLevel >= xp) {
+ total -= xp;
+ removed += xp;
+
+ entity.setExp(1F / xpForNextLevel * (xpForNextLevel - xp));
+ entity.setTotalExperience(total);
+ entity.setLevel(level);
+ return removed;
+ }
+
+ xp -= xpForNextLevel;
+ total -= xpForNextLevel;
+ removed += xpForNextLevel;
+ } while (level > 0);
+
+ entity.setExp(0F);
+ entity.setTotalExperience(0);
+ entity.setLevel(0);
+ return removed;
+ }
+
+ public static int getTotalExp(Player entity) {
+ int rv = 0;
+ int level = Math.min(entity.getLevel(), 20000);
+ for (int i = 0; i < level; i++) {
+ rv += xpForNextLevel(i);
+ }
+ rv += Math.min(1F, Math.max(0F, entity.getExp())) * xpForNextLevel(level);
+ return rv;
+ }
+
+ public static double getTotalExpLevels(Player entity) {
+ int xp = entity.getTotalExperience();
+
+ int level = 0;
+ while (xp > 0 && level < 20000) {
+ int needed = xpForNextLevel(level);
+ if (needed > xp) {
+ return level + ((double) xp / needed);
+ }
+ xp -= needed;
+ level++;
+ }
+
+ return level;
+ }
+
+ public static int getNearbyPlayerCount(Player origin, double range, Predicate<Player> predicate) {
+ List<Entity> entities = origin.getNearbyEntities(range, range, range);
+ int result = 0;
+ for (Entity entity : entities) {
+ if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) {
+ result++;
+ }
+ }
+ return result;
+ }
+
+ public static void getNearbyPlayers(Player origin, double range, Collection<Player> collection, Predicate<Player> predicate) {
+ List<Entity> entities = origin.getNearbyEntities(range, range, range);
+ for (Entity entity : entities) {
+ if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) {
+ collection.add((Player) entity);
+ }
+ }
+ }
+
+ public static void forEachNearbyPlayer(Player origin, double range, Consumer<Player> action) {
+ List<Entity> entities = origin.getNearbyEntities(range, range, range);
+ for (Entity entity : entities) {
+ if (entity.getType() == EntityType.PLAYER) {
+ action.accept((Player) entity);
+ }
+ }
+ }
+
+ public static double distanceSquared(Location first, Location second) {
+ double dx = first.getX() - second.getX();
+ double dy = first.getY() - second.getY();
+ double dz = first.getZ() - second.getZ();
+
+ return dx * dx + dy * dy + dz * dz;
+ }
+
+
+ public static <T extends Entity> Iterator<T> findNearbyEntities(Entity origin, boolean includeSelf, Predicate<Entity> predicate, double horizontalRange, double verticalRange) {
+ Objects.requireNonNull(origin);
+ return new Iterator<T>() {
+ Entity next;
+ List<Entity> nearby;
+ int index = 0;
+ int size;
+
+ {
+ if (includeSelf) {
+ next = origin;
+ } else {
+ next = findNext();
+ }
+ }
+
+ Entity findNext() {
+ if (nearby == null) {
+ nearby = origin.getNearbyEntities(horizontalRange, verticalRange, horizontalRange);
+ size = nearby.size();
+ }
+
+ while (index < size) {
+ Entity e = nearby.get(index++);
+ if (predicate.test(e)) {
+ return e;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public T next() {
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+ Entity result = next;
+ next = findNext();
+ //noinspection unchecked
+ return (T) result;
+ }
+
+ };
+ }
+
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java b/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java index 62a389e..fb76755 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java +++ b/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java @@ -1,473 +1,473 @@ -package io.dico.dicore; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; - -public class StringUtil { - - public static String capitalize(String input) { - if (input.length() > 0) { - char first = input.charAt(0); - if (first != (first = Character.toUpperCase(first))) { - char[] result = input.toCharArray(); - result[0] = first; - return String.valueOf(result); - } - } - return input; - } - - /** - * Capitalizes the first character of the string or the first character of each word - * - * @param input the string to capitalize - * @param spaceChar the character separating each word. If @code '\0' is passed, only the first character of - * the input is capitalized. - * @return the capitalized string - */ - public static String capitalize(String input, char spaceChar) { - if (spaceChar == '\0') { - return capitalize(input); - } - - char[] result = null; - boolean capitalize = true; - for (int n = input.length(), i = 0; i < n; i++) { - char c = input.charAt(i); - if (capitalize && c != (c = Character.toUpperCase(c))) { - if (result == null) result = input.toCharArray(); - result[i] = c; - } - capitalize = c == spaceChar; - } - return result != null ? String.valueOf(result) : input; - } - - public static String capitalize(String input, char spaceChar, char newSpaceChar) { - if (newSpaceChar == '\0') { - return capitalize(input, spaceChar); - } - - char[] result = null; - boolean capitalize = true; - for (int n = input.length(), i = 0; i < n; i++) { - char c = input.charAt(i); - if (capitalize && c != (c = Character.toUpperCase(c))) { - if (result == null) result = input.toCharArray(); - result[i] = c; - } - if (capitalize = c == spaceChar) { - if (result == null) result = input.toCharArray(); - result[i] = newSpaceChar; - } - } - return result != null ? String.valueOf(result) : input; - } - - /** - * Returns a lowercase version of the input with _ replaced with a space. - * Mainly used for making names of enum constants readable. - * - * @param input - * @return a humanified version of @code input - */ - public static String humanify(String input) { - return input == null ? null : input.toLowerCase().replace('_', ' '); - } - - /** - * Enumerate the given items, separating them by ", " and finally by " and " - * - * @param words the items to enumerate (it's not really enumerating....) - * @return the enumerated string - */ - public static String enumerate(String... words) { - StringBuilder result = new StringBuilder(); - int size = words.length; - int secondLastIndex = size - 2; - for (int i = 0; i < size; i++) { - String word = words[i]; - if (word.isEmpty()) - continue; - result.append(word); - if (i < secondLastIndex) - result.append(", "); - else if (i == secondLastIndex) - result.append(" and "); - } - return result.toString(); - } - - public static String enumerate(String list, String regex) { - return enumerate(list.split(regex)); - } - - /** - * Return a formatted string of the length in millis, containing days, hours and minutes. - * - * @param length The delay in milliseconds - * @return the formatted string - */ - public static String getTimeLength(long length) { - int minute = 60000; // in millis - int hour = 60 * minute; - int day = 24 * hour; - - int minutes = (int) ((length / minute) % 60); - int hours = (int) ((length / hour) % 24); - int days = (int) (length / day); //returns floor - - String result = ""; // It will be splitted at "|" - if (days != 0) - result += days + " days|"; - if (hours != 0) - result += hours + " hours|"; - if (minutes != 0) - result += minutes + " minutes|"; - return enumerate(result.split("\\|")); - } - - /** - * Return a formatted String to represent the given time length, in the given units - * - * @param sourceAmount Amount of delay - * @param sourceUnit Unit of delay - * @param ifEmpty the String to return if the - * @param displayedUnits units displayed - * @return the formatted string - * @throws IllegalArgumentException if there are no displayed units - */ - public static String getTimeLength(long sourceAmount, TimeUnit sourceUnit, String ifEmpty, TimeUnit... displayedUnits) { - if (displayedUnits.length == 0) { - throw new IllegalArgumentException("No displayed units"); - } - Arrays.sort(displayedUnits, Collections.reverseOrder(TimeUnit::compareTo)); // sort by opposite of enum declaration order (largest -> smallest) - List<String> segments = new ArrayList<>(displayedUnits.length); - for (TimeUnit unit : displayedUnits) { - long displayedAmount = unit.convert(sourceAmount, sourceUnit); - sourceAmount -= sourceUnit.convert(displayedAmount, unit); - if (displayedAmount > 0) { - String unitWord = unit.name().toLowerCase(); // plural - if (displayedAmount == 1) { - unitWord = unitWord.substring(0, unitWord.length() - 1); // remove s at the end - } - segments.add(displayedAmount + " " + unitWord); - } - } - return segments.isEmpty() ? ifEmpty : enumerate(segments.toArray(new String[segments.size()])); - } - - /** - * Returns the delay represented by a ban-like delay representation, in milliseconds - * Example: "5d2h5m3s" for 5 days, 2 hours, 5 minutes and 3 seconds. - * <p> - * Supported characters are s, m, h, d, w. - * Negative numbers are supported. - * - * @param input The input string - * @return The delay in milliseconds - * @throws IllegalArgumentException if the input string isn't properly formatted, or any non-digit character isn't recognized (capitals are not recognized). - */ - public static long getTimeLength(String input) { //if -1: error - long count = 0; - int i = 0; - while (i < input.length()) { - int num = 0; - char unit = '\0'; - boolean negate; - if (negate = input.charAt(i) == '-') { - i++; - } - do { - char c = input.charAt(i); - int digit = c - '0'; - if (0 <= digit && digit < 10) { - num = 10 * num + digit; - } else { - unit = c; - break; - } - } while (i < input.length()); - - long unitTime = getUnitTime(unit); - if (unitTime == -1) - throw new IllegalArgumentException(); - if (negate) { - unitTime = -unitTime; - } - count += (num * unitTime); - } - return count; - } - - /** - * Returns the time represented by the given unit character in milliseconds. - * <p> - * 's' -> 1000 - * 'm' -> 1000 * 60 - * 'h' -> 1000 * 60 * 60 - * 'd' -> 1000 * 60 * 60 * 24 - * 'w' -> 1000 * 60 * 60 * 24 * 7 - * anything else -> -1 - * - * @param unit The unit character, as shown above - * @return the millisecond delay represented by the unit - */ - public static long getUnitTime(char unit) { //if -1: no value found - switch (Character.toLowerCase(unit)) { - case 's': - return 1000; - case 'm': - return 1000 * 60; - case 'h': - return 1000 * 60 * 60; - case 'd': - return 1000 * 60 * 60 * 24; - case 'w': - return 1000 * 60 * 60 * 24 * 7; - default: - return -1; - } - } - - /** - * Computes a binary representation of the value. - * The returned representation always displays 64 bits. - * Every 8 bits, the digits are seperated by an _ - * The representation is prefixed by 0b. - * <p> - * Example: 0b00000000_11111111_00000001_11110000_00001111_11001100_00001111_10111010 - * - * @param entry the value to represent in binary - * @return A binary representation of the long value - */ - public static String toBinaryString(long entry) { - String binary = Long.toBinaryString(entry); - String binary64 = String.valueOf(new char[64 - binary.length()]).replace('\0', '0') + binary; - String withUnderscores = String.join("_", IntStream.range(0, 8).mapToObj(x -> binary64.substring(x * 8, x * 8 + 8)).toArray(String[]::new)); - return "0b" + withUnderscores; - } - - /** - * Turns a generic java classname into a name formatted properly to be an enum constant. - * - * @param name The string value I'd describe as a generic java classname (so we have CapitalCase) - * @return An enum constant version of it (ENUM_FORMAT: CAPITAL_CASE) - */ - public static String toEnumFormat(String name) { - StringBuilder result = new StringBuilder(name.length() + 2); - - boolean capital = true; - for (int i = 0, n = name.length(); i < n; i++) { - char c = name.charAt(i); - if (capital) { - capital = Character.isUpperCase(c); - } else if (Character.isUpperCase(c)) { - capital = true; - result.append('_'); - } - result.append(capital ? c : Character.toUpperCase(c)); - } - - return result.toString(); - } - - /** - * Replaces any occurrence of toReplace with another string. - * Any colours that occured before the occurence of toReplace, are copied to the end of the replacement. - * - * @param target The String to query - * @param toReplace The sequence to replace - * @param with the replacing sequence - * @return the result - */ - public static String replaceKeepColours(String target, String toReplace, String with) { - int index = -toReplace.length(); - while ((index = target.indexOf(toReplace, index + toReplace.length())) != -1) { - String start = target.substring(0, index); - Formatting coloursBefore = Formatting.getFormats(start); - String after; - try { - after = target.substring(index + toReplace.length()); - } catch (IndexOutOfBoundsException e) { - after = ""; - } - target = start + with + coloursBefore + after; - } - return target; - } - - public static String replParam(String target, String param, Object repl) { - return replParam(target, param, repl, false); - } - - public static String replParams(String target, String[] params, Object[] repls) { - return replParams(target, params, repls, false, false); - } - - public static boolean replParams(String[] target, String[] params, Object[] repls) { - return replParams(target, 0, target.length, params, repls); - } - - public static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls) { - return replParams(target, from, to, params, repls, false); - } - - public static boolean replParams(List<String> target, String[] params, Object[] repls) { - return replParams(target, 0, target.size(), params, repls); - } - - public static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls) { - return replParams(target, from, to, params, repls, false); - } - - public static String replParamAndTranslate(String target, String param, Object repl) { - return replParam(target, param, repl, true); - } - - public static String replParamsAndTranslate(String target, String[] params, Object[] repls) { - return replParams(target, params, repls, false, true); - } - - public static boolean replParamsAndTranslate(String[] target, String[] params, Object[] repls) { - return replParamsAndTranslate(target, 0, target.length, params, repls); - } - - public static boolean replParamsAndTranslate(String[] target, int from, int to, String[] params, Object[] repls) { - return replParams(target, from, to, params, repls, true); - } - - public static boolean replParamsAndTranslate(List<String> target, String[] params, Object[] repls) { - return replParamsAndTranslate(target, 0, target.size(), params, repls); - } - - public static boolean replParamsAndTranslate(List<String> target, int from, int to, String[] params, Object[] repls) { - return replParams(target, from, to, params, repls, true); - } - - private static String replParam(String target, String param, Object replacementObj, boolean translate) { - int idx = target.indexOf(param, 0); - if (idx == -1) { - return translate ? Formatting.translate(target) : target; - } - - String rep = replacementObj.toString(); - StringBuilder builder = new StringBuilder(target); - do { - builder.replace(idx, idx + param.length(), rep); - idx = builder.indexOf(param, idx + rep.length()); - } while (idx != -1); - - if (translate) { - Formatting.translate(builder); - } - - return builder.toString(); - } - - @SuppressWarnings("StringEquality") - private static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls, boolean translate) { - if (from < 0 || to < from || to > target.length) { - throw new IllegalArgumentException("Invalid from-to for array size " + target.length + ": " + from + "-" + to); - } - - boolean change = false; - for (int i = from; i < to; i++) { - String val = target[i]; - if (val != (val = replParams(val, params, repls, true, translate))) { - target[i] = val; - change = true; - } - } - return change; - } - - @SuppressWarnings("StringEquality") - private static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls, boolean translate) { - if (from < 0 || to < from || to > target.size()) { - throw new IllegalArgumentException("Invalid from-to for list size " + target.size() + ": " + from + "-" + to); - } - - boolean change = false; - if (target instanceof RandomAccess) { - for (int i = from; i < to; i++) { - String val = target.get(i); - if (val != (val = replParams(val, params, repls, true, translate))) { - target.set(i, val); - change = true; - } - } - } else { - ListIterator<String> itr = target.listIterator(from); - for (int n = to - from, i = 0; i < n && itr.hasNext(); i++) { - String val = itr.next(); - if (val != (val = replParams(val, params, repls, true, translate))) { - itr.set(val); - change = true; - } - } - } - return change; - } - - private static String replParams(String target, String[] params, Object[] repls, boolean updateRepls, boolean translate) { - int n = params.length; - if (n != repls.length) { - throw new IllegalArgumentException(); - } - - String param = null; - int idx = -1; - int i; - for (i = 0; i < n; i++) { - param = params[i]; - if (param == null) { - continue; - } - idx = target.indexOf(param, 0); - if (idx != -1) { - break; - } - } - - if (idx == -1) { - return translate ? Formatting.translate(target) : target; - } - - String repl = repls[i].toString(); - if (updateRepls) { - repls[i] = repl; - } - - StringBuilder builder = new StringBuilder(target); - do { - builder.replace(idx, idx + param.length(), repl); - idx = builder.indexOf(param, idx + repl.length()); - } while (idx != -1); - - for (i++; i < n; i++) { - param = params[i]; - if (param == null || (idx = builder.indexOf(param, 0)) == -1) { - continue; - } - - repl = repls[i].toString(); - if (updateRepls) { - repls[i] = repl; - } - - do { - builder.replace(idx, idx + param.length(), repl); - idx = builder.indexOf(param, idx + repl.length()); - } while (idx != -1); - } - - if (translate) { - Formatting.translate(builder); - } - - return builder.toString(); - } - -} +package io.dico.dicore;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+public class StringUtil {
+
+ public static String capitalize(String input) {
+ if (input.length() > 0) {
+ char first = input.charAt(0);
+ if (first != (first = Character.toUpperCase(first))) {
+ char[] result = input.toCharArray();
+ result[0] = first;
+ return String.valueOf(result);
+ }
+ }
+ return input;
+ }
+
+ /**
+ * Capitalizes the first character of the string or the first character of each word
+ *
+ * @param input the string to capitalize
+ * @param spaceChar the character separating each word. If @code '\0' is passed, only the first character of
+ * the input is capitalized.
+ * @return the capitalized string
+ */
+ public static String capitalize(String input, char spaceChar) {
+ if (spaceChar == '\0') {
+ return capitalize(input);
+ }
+
+ char[] result = null;
+ boolean capitalize = true;
+ for (int n = input.length(), i = 0; i < n; i++) {
+ char c = input.charAt(i);
+ if (capitalize && c != (c = Character.toUpperCase(c))) {
+ if (result == null) result = input.toCharArray();
+ result[i] = c;
+ }
+ capitalize = c == spaceChar;
+ }
+ return result != null ? String.valueOf(result) : input;
+ }
+
+ public static String capitalize(String input, char spaceChar, char newSpaceChar) {
+ if (newSpaceChar == '\0') {
+ return capitalize(input, spaceChar);
+ }
+
+ char[] result = null;
+ boolean capitalize = true;
+ for (int n = input.length(), i = 0; i < n; i++) {
+ char c = input.charAt(i);
+ if (capitalize && c != (c = Character.toUpperCase(c))) {
+ if (result == null) result = input.toCharArray();
+ result[i] = c;
+ }
+ if (capitalize = c == spaceChar) {
+ if (result == null) result = input.toCharArray();
+ result[i] = newSpaceChar;
+ }
+ }
+ return result != null ? String.valueOf(result) : input;
+ }
+
+ /**
+ * Returns a lowercase version of the input with _ replaced with a space.
+ * Mainly used for making names of enum constants readable.
+ *
+ * @param input
+ * @return a humanified version of @code input
+ */
+ public static String humanify(String input) {
+ return input == null ? null : input.toLowerCase().replace('_', ' ');
+ }
+
+ /**
+ * Enumerate the given items, separating them by ", " and finally by " and "
+ *
+ * @param words the items to enumerate (it's not really enumerating....)
+ * @return the enumerated string
+ */
+ public static String enumerate(String... words) {
+ StringBuilder result = new StringBuilder();
+ int size = words.length;
+ int secondLastIndex = size - 2;
+ for (int i = 0; i < size; i++) {
+ String word = words[i];
+ if (word.isEmpty())
+ continue;
+ result.append(word);
+ if (i < secondLastIndex)
+ result.append(", ");
+ else if (i == secondLastIndex)
+ result.append(" and ");
+ }
+ return result.toString();
+ }
+
+ public static String enumerate(String list, String regex) {
+ return enumerate(list.split(regex));
+ }
+
+ /**
+ * Return a formatted string of the length in millis, containing days, hours and minutes.
+ *
+ * @param length The delay in milliseconds
+ * @return the formatted string
+ */
+ public static String getTimeLength(long length) {
+ int minute = 60000; // in millis
+ int hour = 60 * minute;
+ int day = 24 * hour;
+
+ int minutes = (int) ((length / minute) % 60);
+ int hours = (int) ((length / hour) % 24);
+ int days = (int) (length / day); //returns floor
+
+ String result = ""; // It will be splitted at "|"
+ if (days != 0)
+ result += days + " days|";
+ if (hours != 0)
+ result += hours + " hours|";
+ if (minutes != 0)
+ result += minutes + " minutes|";
+ return enumerate(result.split("\\|"));
+ }
+
+ /**
+ * Return a formatted String to represent the given time length, in the given units
+ *
+ * @param sourceAmount Amount of delay
+ * @param sourceUnit Unit of delay
+ * @param ifEmpty the String to return if the
+ * @param displayedUnits units displayed
+ * @return the formatted string
+ * @throws IllegalArgumentException if there are no displayed units
+ */
+ public static String getTimeLength(long sourceAmount, TimeUnit sourceUnit, String ifEmpty, TimeUnit... displayedUnits) {
+ if (displayedUnits.length == 0) {
+ throw new IllegalArgumentException("No displayed units");
+ }
+ Arrays.sort(displayedUnits, Collections.reverseOrder(TimeUnit::compareTo)); // sort by opposite of enum declaration order (largest -> smallest)
+ List<String> segments = new ArrayList<>(displayedUnits.length);
+ for (TimeUnit unit : displayedUnits) {
+ long displayedAmount = unit.convert(sourceAmount, sourceUnit);
+ sourceAmount -= sourceUnit.convert(displayedAmount, unit);
+ if (displayedAmount > 0) {
+ String unitWord = unit.name().toLowerCase(); // plural
+ if (displayedAmount == 1) {
+ unitWord = unitWord.substring(0, unitWord.length() - 1); // remove s at the end
+ }
+ segments.add(displayedAmount + " " + unitWord);
+ }
+ }
+ return segments.isEmpty() ? ifEmpty : enumerate(segments.toArray(new String[segments.size()]));
+ }
+
+ /**
+ * Returns the delay represented by a ban-like delay representation, in milliseconds
+ * Example: "5d2h5m3s" for 5 days, 2 hours, 5 minutes and 3 seconds.
+ * <p>
+ * Supported characters are s, m, h, d, w.
+ * Negative numbers are supported.
+ *
+ * @param input The input string
+ * @return The delay in milliseconds
+ * @throws IllegalArgumentException if the input string isn't properly formatted, or any non-digit character isn't recognized (capitals are not recognized).
+ */
+ public static long getTimeLength(String input) { //if -1: error
+ long count = 0;
+ int i = 0;
+ while (i < input.length()) {
+ int num = 0;
+ char unit = '\0';
+ boolean negate;
+ if (negate = input.charAt(i) == '-') {
+ i++;
+ }
+ do {
+ char c = input.charAt(i);
+ int digit = c - '0';
+ if (0 <= digit && digit < 10) {
+ num = 10 * num + digit;
+ } else {
+ unit = c;
+ break;
+ }
+ } while (i < input.length());
+
+ long unitTime = getUnitTime(unit);
+ if (unitTime == -1)
+ throw new IllegalArgumentException();
+ if (negate) {
+ unitTime = -unitTime;
+ }
+ count += (num * unitTime);
+ }
+ return count;
+ }
+
+ /**
+ * Returns the time represented by the given unit character in milliseconds.
+ * <p>
+ * 's' -> 1000
+ * 'm' -> 1000 * 60
+ * 'h' -> 1000 * 60 * 60
+ * 'd' -> 1000 * 60 * 60 * 24
+ * 'w' -> 1000 * 60 * 60 * 24 * 7
+ * anything else -> -1
+ *
+ * @param unit The unit character, as shown above
+ * @return the millisecond delay represented by the unit
+ */
+ public static long getUnitTime(char unit) { //if -1: no value found
+ switch (Character.toLowerCase(unit)) {
+ case 's':
+ return 1000;
+ case 'm':
+ return 1000 * 60;
+ case 'h':
+ return 1000 * 60 * 60;
+ case 'd':
+ return 1000 * 60 * 60 * 24;
+ case 'w':
+ return 1000 * 60 * 60 * 24 * 7;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Computes a binary representation of the value.
+ * The returned representation always displays 64 bits.
+ * Every 8 bits, the digits are seperated by an _
+ * The representation is prefixed by 0b.
+ * <p>
+ * Example: 0b00000000_11111111_00000001_11110000_00001111_11001100_00001111_10111010
+ *
+ * @param entry the value to represent in binary
+ * @return A binary representation of the long value
+ */
+ public static String toBinaryString(long entry) {
+ String binary = Long.toBinaryString(entry);
+ String binary64 = String.valueOf(new char[64 - binary.length()]).replace('\0', '0') + binary;
+ String withUnderscores = String.join("_", IntStream.range(0, 8).mapToObj(x -> binary64.substring(x * 8, x * 8 + 8)).toArray(String[]::new));
+ return "0b" + withUnderscores;
+ }
+
+ /**
+ * Turns a generic java classname into a name formatted properly to be an enum constant.
+ *
+ * @param name The string value I'd describe as a generic java classname (so we have CapitalCase)
+ * @return An enum constant version of it (ENUM_FORMAT: CAPITAL_CASE)
+ */
+ public static String toEnumFormat(String name) {
+ StringBuilder result = new StringBuilder(name.length() + 2);
+
+ boolean capital = true;
+ for (int i = 0, n = name.length(); i < n; i++) {
+ char c = name.charAt(i);
+ if (capital) {
+ capital = Character.isUpperCase(c);
+ } else if (Character.isUpperCase(c)) {
+ capital = true;
+ result.append('_');
+ }
+ result.append(capital ? c : Character.toUpperCase(c));
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Replaces any occurrence of toReplace with another string.
+ * Any colours that occured before the occurence of toReplace, are copied to the end of the replacement.
+ *
+ * @param target The String to query
+ * @param toReplace The sequence to replace
+ * @param with the replacing sequence
+ * @return the result
+ */
+ public static String replaceKeepColours(String target, String toReplace, String with) {
+ int index = -toReplace.length();
+ while ((index = target.indexOf(toReplace, index + toReplace.length())) != -1) {
+ String start = target.substring(0, index);
+ Formatting coloursBefore = Formatting.getFormats(start);
+ String after;
+ try {
+ after = target.substring(index + toReplace.length());
+ } catch (IndexOutOfBoundsException e) {
+ after = "";
+ }
+ target = start + with + coloursBefore + after;
+ }
+ return target;
+ }
+
+ public static String replParam(String target, String param, Object repl) {
+ return replParam(target, param, repl, false);
+ }
+
+ public static String replParams(String target, String[] params, Object[] repls) {
+ return replParams(target, params, repls, false, false);
+ }
+
+ public static boolean replParams(String[] target, String[] params, Object[] repls) {
+ return replParams(target, 0, target.length, params, repls);
+ }
+
+ public static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls) {
+ return replParams(target, from, to, params, repls, false);
+ }
+
+ public static boolean replParams(List<String> target, String[] params, Object[] repls) {
+ return replParams(target, 0, target.size(), params, repls);
+ }
+
+ public static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls) {
+ return replParams(target, from, to, params, repls, false);
+ }
+
+ public static String replParamAndTranslate(String target, String param, Object repl) {
+ return replParam(target, param, repl, true);
+ }
+
+ public static String replParamsAndTranslate(String target, String[] params, Object[] repls) {
+ return replParams(target, params, repls, false, true);
+ }
+
+ public static boolean replParamsAndTranslate(String[] target, String[] params, Object[] repls) {
+ return replParamsAndTranslate(target, 0, target.length, params, repls);
+ }
+
+ public static boolean replParamsAndTranslate(String[] target, int from, int to, String[] params, Object[] repls) {
+ return replParams(target, from, to, params, repls, true);
+ }
+
+ public static boolean replParamsAndTranslate(List<String> target, String[] params, Object[] repls) {
+ return replParamsAndTranslate(target, 0, target.size(), params, repls);
+ }
+
+ public static boolean replParamsAndTranslate(List<String> target, int from, int to, String[] params, Object[] repls) {
+ return replParams(target, from, to, params, repls, true);
+ }
+
+ private static String replParam(String target, String param, Object replacementObj, boolean translate) {
+ int idx = target.indexOf(param, 0);
+ if (idx == -1) {
+ return translate ? Formatting.translate(target) : target;
+ }
+
+ String rep = replacementObj.toString();
+ StringBuilder builder = new StringBuilder(target);
+ do {
+ builder.replace(idx, idx + param.length(), rep);
+ idx = builder.indexOf(param, idx + rep.length());
+ } while (idx != -1);
+
+ if (translate) {
+ Formatting.translate(builder);
+ }
+
+ return builder.toString();
+ }
+
+ @SuppressWarnings("StringEquality")
+ private static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls, boolean translate) {
+ if (from < 0 || to < from || to > target.length) {
+ throw new IllegalArgumentException("Invalid from-to for array size " + target.length + ": " + from + "-" + to);
+ }
+
+ boolean change = false;
+ for (int i = from; i < to; i++) {
+ String val = target[i];
+ if (val != (val = replParams(val, params, repls, true, translate))) {
+ target[i] = val;
+ change = true;
+ }
+ }
+ return change;
+ }
+
+ @SuppressWarnings("StringEquality")
+ private static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls, boolean translate) {
+ if (from < 0 || to < from || to > target.size()) {
+ throw new IllegalArgumentException("Invalid from-to for list size " + target.size() + ": " + from + "-" + to);
+ }
+
+ boolean change = false;
+ if (target instanceof RandomAccess) {
+ for (int i = from; i < to; i++) {
+ String val = target.get(i);
+ if (val != (val = replParams(val, params, repls, true, translate))) {
+ target.set(i, val);
+ change = true;
+ }
+ }
+ } else {
+ ListIterator<String> itr = target.listIterator(from);
+ for (int n = to - from, i = 0; i < n && itr.hasNext(); i++) {
+ String val = itr.next();
+ if (val != (val = replParams(val, params, repls, true, translate))) {
+ itr.set(val);
+ change = true;
+ }
+ }
+ }
+ return change;
+ }
+
+ private static String replParams(String target, String[] params, Object[] repls, boolean updateRepls, boolean translate) {
+ int n = params.length;
+ if (n != repls.length) {
+ throw new IllegalArgumentException();
+ }
+
+ String param = null;
+ int idx = -1;
+ int i;
+ for (i = 0; i < n; i++) {
+ param = params[i];
+ if (param == null) {
+ continue;
+ }
+ idx = target.indexOf(param, 0);
+ if (idx != -1) {
+ break;
+ }
+ }
+
+ if (idx == -1) {
+ return translate ? Formatting.translate(target) : target;
+ }
+
+ String repl = repls[i].toString();
+ if (updateRepls) {
+ repls[i] = repl;
+ }
+
+ StringBuilder builder = new StringBuilder(target);
+ do {
+ builder.replace(idx, idx + param.length(), repl);
+ idx = builder.indexOf(param, idx + repl.length());
+ } while (idx != -1);
+
+ for (i++; i < n; i++) {
+ param = params[i];
+ if (param == null || (idx = builder.indexOf(param, 0)) == -1) {
+ continue;
+ }
+
+ repl = repls[i].toString();
+ if (updateRepls) {
+ repls[i] = repl;
+ }
+
+ do {
+ builder.replace(idx, idx + param.length(), repl);
+ idx = builder.indexOf(param, idx + repl.length());
+ } while (idx != -1);
+ }
+
+ if (translate) {
+ Formatting.translate(builder);
+ }
+
+ return builder.toString();
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java b/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java index 5f8f4e2..e467d91 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java @@ -1,91 +1,91 @@ -package io.dico.dicore; - -import org.bukkit.configuration.ConfigurationSection; - -import java.util.*; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -@FunctionalInterface -public interface Whitelist extends Predicate { - - Whitelist EVERYTHING = item -> true; - Whitelist NOTHING = item -> false; - Whitelist NOT_NULL = Objects::nonNull; - - static <T> Predicate<T> everythingAsPredicate() { - return (Predicate<T>) EVERYTHING; - } - - static <T> Predicate<T> nothingAsPredicate() { - return (Predicate<T>) NOTHING; - } - - static <T> Predicate<T> notNullAsPredicate() { - return (Predicate<T>) NOT_NULL; - } - - static Whitelist only(Object item) { - return item::equals; - } - - static Whitelist not(Object item) { - return o -> !item.equals(o); - } - - static Whitelist only(Object item1, Object item2) { - return item -> item1.equals(item) || item2.equals(item); - } - - static Whitelist not(Object item1, Object item2) { - return item -> !(item1.equals(item) || item2.equals(item)); - } - - static Whitelist only(Object[] objects) { - return new SetBasedWhitelist(objects, false); - } - - static Whitelist not(Object[] objects) { - return new SetBasedWhitelist(objects, true); - } - - static Whitelist fromConfig(ConfigurationSection section, Function<String, ?> parser) { - if (section == null) { - return NOTHING; - } - boolean blacklist = section.getBoolean("blacklist", false); - Set list = section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toSet()); - switch (list.size()) { - case 0: - return blacklist ? EVERYTHING : NOTHING; - case 1: { - Iterator iterator = list.iterator(); - return blacklist ? not(iterator.next()) : only(iterator.next()); - } - case 2: { - Iterator iterator = list.iterator(); - return blacklist ? not(iterator.next(), iterator.next()) : only(iterator.next(), iterator.next()); - } - default: - Object item = list.iterator().next(); - if (item instanceof Enum) { - list = EnumSet.copyOf(list); - } - return new SetBasedWhitelist(list, blacklist); - } - } - - static void copyIntoConfig(ConfigurationSection target, Function<Object, String> mapper, boolean blacklist, Object... objects) { - target.set("blacklist", blacklist); - target.set("listed", Arrays.stream(objects).map(mapper).unordered().distinct().collect(Collectors.toList())); - } - - @Override - default boolean test(Object o) { - return isWhitelisted(o); - } - - boolean isWhitelisted(Object o); -} - +package io.dico.dicore;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+@FunctionalInterface
+public interface Whitelist extends Predicate {
+
+ Whitelist EVERYTHING = item -> true;
+ Whitelist NOTHING = item -> false;
+ Whitelist NOT_NULL = Objects::nonNull;
+
+ static <T> Predicate<T> everythingAsPredicate() {
+ return (Predicate<T>) EVERYTHING;
+ }
+
+ static <T> Predicate<T> nothingAsPredicate() {
+ return (Predicate<T>) NOTHING;
+ }
+
+ static <T> Predicate<T> notNullAsPredicate() {
+ return (Predicate<T>) NOT_NULL;
+ }
+
+ static Whitelist only(Object item) {
+ return item::equals;
+ }
+
+ static Whitelist not(Object item) {
+ return o -> !item.equals(o);
+ }
+
+ static Whitelist only(Object item1, Object item2) {
+ return item -> item1.equals(item) || item2.equals(item);
+ }
+
+ static Whitelist not(Object item1, Object item2) {
+ return item -> !(item1.equals(item) || item2.equals(item));
+ }
+
+ static Whitelist only(Object[] objects) {
+ return new SetBasedWhitelist(objects, false);
+ }
+
+ static Whitelist not(Object[] objects) {
+ return new SetBasedWhitelist(objects, true);
+ }
+
+ static Whitelist fromConfig(ConfigurationSection section, Function<String, ?> parser) {
+ if (section == null) {
+ return NOTHING;
+ }
+ boolean blacklist = section.getBoolean("blacklist", false);
+ Set list = section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toSet());
+ switch (list.size()) {
+ case 0:
+ return blacklist ? EVERYTHING : NOTHING;
+ case 1: {
+ Iterator iterator = list.iterator();
+ return blacklist ? not(iterator.next()) : only(iterator.next());
+ }
+ case 2: {
+ Iterator iterator = list.iterator();
+ return blacklist ? not(iterator.next(), iterator.next()) : only(iterator.next(), iterator.next());
+ }
+ default:
+ Object item = list.iterator().next();
+ if (item instanceof Enum) {
+ list = EnumSet.copyOf(list);
+ }
+ return new SetBasedWhitelist(list, blacklist);
+ }
+ }
+
+ static void copyIntoConfig(ConfigurationSection target, Function<Object, String> mapper, boolean blacklist, Object... objects) {
+ target.set("blacklist", blacklist);
+ target.set("listed", Arrays.stream(objects).map(mapper).unordered().distinct().collect(Collectors.toList()));
+ }
+
+ @Override
+ default boolean test(Object o) {
+ return isWhitelisted(o);
+ }
+
+ boolean isWhitelisted(Object o);
+}
+
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java index 93584ea..378424d 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java @@ -1,46 +1,46 @@ -package io.dico.dicore.event; - -import io.dico.dicore.InterfaceChain; - -public interface ChainedListener<T> extends InterfaceChain<SimpleListener<T>, ChainedListener<T>>, SimpleListener<T> { - - @Override - default ChainedListener<T> getEmptyInstance() { - return ChainedListeners.empty(); - } - - @Override - default ChainedListener<T> withElement(SimpleListener<T> element) { - if (element == null) { - return this; - } - - int count = getElementCount() + 1; - return new ChainedListener<T>() { - @Override - public void accept(T event) { - try { - ChainedListener.this.accept(event); - } finally { - element.accept(event); - } - } - - @Override - public ChainedListener<T> withoutLastNode() { - return ChainedListener.this; - } - - @Override - public SimpleListener<T> getDelegateOfLastNode() { - return element; - } - - @Override - public int getElementCount() { - return count; - } - }; - } - -} +package io.dico.dicore.event;
+
+import io.dico.dicore.InterfaceChain;
+
+public interface ChainedListener<T> extends InterfaceChain<SimpleListener<T>, ChainedListener<T>>, SimpleListener<T> {
+
+ @Override
+ default ChainedListener<T> getEmptyInstance() {
+ return ChainedListeners.empty();
+ }
+
+ @Override
+ default ChainedListener<T> withElement(SimpleListener<T> element) {
+ if (element == null) {
+ return this;
+ }
+
+ int count = getElementCount() + 1;
+ return new ChainedListener<T>() {
+ @Override
+ public void accept(T event) {
+ try {
+ ChainedListener.this.accept(event);
+ } finally {
+ element.accept(event);
+ }
+ }
+
+ @Override
+ public ChainedListener<T> withoutLastNode() {
+ return ChainedListener.this;
+ }
+
+ @Override
+ public SimpleListener<T> getDelegateOfLastNode() {
+ return element;
+ }
+
+ @Override
+ public int getElementCount() {
+ return count;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java index 86b8448..875c131 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java @@ -1,55 +1,55 @@ -package io.dico.dicore.event; - -import io.dico.dicore.InterfaceChain; - -public interface ChainedListenerHandle extends InterfaceChain<ListenerHandle, ChainedListenerHandle>, ListenerHandle { - - @Override - default ChainedListenerHandle getEmptyInstance() { - return ChainedListenerHandles.empty(); - } - - @Override - default ChainedListenerHandle withElement(ListenerHandle element) { - if (element == null) { - return this; - } - - int count = getElementCount() + 1; - return new ChainedListenerHandle() { - @Override - public void register() { - try { - ChainedListenerHandle.this.register(); - } finally { - element.register(); - } - } - - @Override - public void unregister() { - try { - ChainedListenerHandle.this.unregister(); - } finally { - element.unregister(); - } - } - - @Override - public ChainedListenerHandle withoutLastNode() { - return ChainedListenerHandle.this; - } - - @Override - public ListenerHandle getDelegateOfLastNode() { - return element; - } - - @Override - public int getElementCount() { - return count; - } - }; - } - -} +package io.dico.dicore.event;
+
+import io.dico.dicore.InterfaceChain;
+
+public interface ChainedListenerHandle extends InterfaceChain<ListenerHandle, ChainedListenerHandle>, ListenerHandle {
+
+ @Override
+ default ChainedListenerHandle getEmptyInstance() {
+ return ChainedListenerHandles.empty();
+ }
+
+ @Override
+ default ChainedListenerHandle withElement(ListenerHandle element) {
+ if (element == null) {
+ return this;
+ }
+
+ int count = getElementCount() + 1;
+ return new ChainedListenerHandle() {
+ @Override
+ public void register() {
+ try {
+ ChainedListenerHandle.this.register();
+ } finally {
+ element.register();
+ }
+ }
+
+ @Override
+ public void unregister() {
+ try {
+ ChainedListenerHandle.this.unregister();
+ } finally {
+ element.unregister();
+ }
+ }
+
+ @Override
+ public ChainedListenerHandle withoutLastNode() {
+ return ChainedListenerHandle.this;
+ }
+
+ @Override
+ public ListenerHandle getDelegateOfLastNode() {
+ return element;
+ }
+
+ @Override
+ public int getElementCount() {
+ return count;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java index 0be0508..15c4553 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java @@ -1,63 +1,63 @@ -package io.dico.dicore.event; - -public class ChainedListenerHandles { - - private ChainedListenerHandles() { - - } - - private static final ChainedListenerHandle empty = new ChainedListenerHandle() { - @Override - public void register() { - - } - - public void unregister() { - - } - - @Override - public ChainedListenerHandle withElement(ListenerHandle other) { - return ChainedListenerHandles.singleton(other); - } - - @Override - public int getElementCount() { - return 0; - } - - @Override - public ListenerHandle getDelegateOfLastNode() { - return null; - } - }; - - public static ChainedListenerHandle empty() { - return empty; - } - - public static ChainedListenerHandle singleton(ListenerHandle element) { - if (element instanceof ChainedListenerHandle) { - return (ChainedListenerHandle) element; - } - if (element == null) { - return empty(); - } - return new ChainedListenerHandle() { - @Override - public void register() { - element.register(); - } - - public void unregister() { - element.unregister(); - } - - @Override - public ListenerHandle getDelegateOfLastNode() { - return element; - } - }; - } - -} +package io.dico.dicore.event;
+
+public class ChainedListenerHandles {
+
+ private ChainedListenerHandles() {
+
+ }
+
+ private static final ChainedListenerHandle empty = new ChainedListenerHandle() {
+ @Override
+ public void register() {
+
+ }
+
+ public void unregister() {
+
+ }
+
+ @Override
+ public ChainedListenerHandle withElement(ListenerHandle other) {
+ return ChainedListenerHandles.singleton(other);
+ }
+
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ public ListenerHandle getDelegateOfLastNode() {
+ return null;
+ }
+ };
+
+ public static ChainedListenerHandle empty() {
+ return empty;
+ }
+
+ public static ChainedListenerHandle singleton(ListenerHandle element) {
+ if (element instanceof ChainedListenerHandle) {
+ return (ChainedListenerHandle) element;
+ }
+ if (element == null) {
+ return empty();
+ }
+ return new ChainedListenerHandle() {
+ @Override
+ public void register() {
+ element.register();
+ }
+
+ public void unregister() {
+ element.unregister();
+ }
+
+ @Override
+ public ListenerHandle getDelegateOfLastNode() {
+ return element;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java index 1d2bcd4..44050a2 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java @@ -1,56 +1,56 @@ -package io.dico.dicore.event; - -@SuppressWarnings("unchecked") -public class ChainedListeners { - - private static final ChainedListener<?> empty = new ChainedListener<Object>() { - @Override - public void accept(Object event) { - - } - - @Override - public ChainedListener<Object> withElement(SimpleListener other) { - return ChainedListeners.singleton(other); - } - - @Override - public int getElementCount() { - return 0; - } - - @Override - public SimpleListener<Object> getDelegateOfLastNode() { - return null; - } - }; - - private ChainedListeners() { - - } - - public static <T> ChainedListener<T> empty() { - return (ChainedListener<T>) empty; - } - - public static <T> ChainedListener<T> singleton(SimpleListener<T> element) { - if (element instanceof ChainedListener) { - return (ChainedListener<T>) element; - } - if (element == null) { - return empty(); - } - return new ChainedListener<T>() { - @Override - public void accept(T event) { - element.accept(event); - } - - @Override - public SimpleListener getDelegateOfLastNode() { - return element; - } - }; - } - -} +package io.dico.dicore.event;
+
+@SuppressWarnings("unchecked")
+public class ChainedListeners {
+
+ private static final ChainedListener<?> empty = new ChainedListener<Object>() {
+ @Override
+ public void accept(Object event) {
+
+ }
+
+ @Override
+ public ChainedListener<Object> withElement(SimpleListener other) {
+ return ChainedListeners.singleton(other);
+ }
+
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ public SimpleListener<Object> getDelegateOfLastNode() {
+ return null;
+ }
+ };
+
+ private ChainedListeners() {
+
+ }
+
+ public static <T> ChainedListener<T> empty() {
+ return (ChainedListener<T>) empty;
+ }
+
+ public static <T> ChainedListener<T> singleton(SimpleListener<T> element) {
+ if (element instanceof ChainedListener) {
+ return (ChainedListener<T>) element;
+ }
+ if (element == null) {
+ return empty();
+ }
+ return new ChainedListener<T>() {
+ @Override
+ public void accept(T event) {
+ element.accept(event);
+ }
+
+ @Override
+ public SimpleListener getDelegateOfLastNode() {
+ return element;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java b/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java index 56265bb..fa290c0 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java @@ -1,91 +1,91 @@ -package io.dico.dicore.event; - -import org.bukkit.event.Cancellable; -import org.bukkit.event.EventPriority; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.function.Consumer; - -public final class HandlerList<T> { - private final List<Listener<T>> source = new ArrayList<>(); - private Listener<T>[] listeners = newArray(0); - - public void refresh() { - source.sort(Comparator.comparingInt(l -> l.getPriority().ordinal())); - listeners = source.toArray(newArray(source.size())); - } - - @SuppressWarnings("unchecked") - private static <T> Listener<T>[] newArray(int length) { - return new Listener[length]; - } - - public void register(Listener<T> listener) { - if (!source.contains(listener) && source.add(listener)) { - refresh(); - } - } - - public ListenerHandle getListenerHandle(Listener<T> listener) { - return new ListenerHandle() { - @Override - public void register() { - HandlerList.this.register(listener); - } - - @Override - public void unregister() { - HandlerList.this.unregister(listener); - } - }; - } - - public void register(EventPriority priority, Consumer<T> listener) { - register(new Listener<T>() { - @Override - public EventPriority getPriority() { - return priority; - } - - @Override - public void accept(T event) { - listener.accept(event); - } - }); - } - - public List<Listener<T>> getRegistrations() { - return Collections.unmodifiableList(source); - } - - public void unregister(Listener<T> listener) { - if (source.remove(listener)) { - refresh(); - } - } - - public void callEvent(T event) { - if (event instanceof Cancellable) { - Cancellable c = (Cancellable) event; - boolean cancelled = c.isCancelled(); - for (Listener<T> listener : listeners) { - if (listener.listensToCancelledState(cancelled)) { - //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName()); - listener.accept(event); - cancelled = c.isCancelled(); - } /*else { - EnchantsPlugin.getInstance().debug("Listener does not listen to cancelled state of " + cancelled + ": " + listener.getClass().getSimpleName()); - }*/ - } - } else { - for (Listener<T> listener : listeners) { - //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName()); - listener.accept(event); - } - } - } - -} +package io.dico.dicore.event;
+
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.EventPriority;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Consumer;
+
+public final class HandlerList<T> {
+ private final List<Listener<T>> source = new ArrayList<>();
+ private Listener<T>[] listeners = newArray(0);
+
+ public void refresh() {
+ source.sort(Comparator.comparingInt(l -> l.getPriority().ordinal()));
+ listeners = source.toArray(newArray(source.size()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> Listener<T>[] newArray(int length) {
+ return new Listener[length];
+ }
+
+ public void register(Listener<T> listener) {
+ if (!source.contains(listener) && source.add(listener)) {
+ refresh();
+ }
+ }
+
+ public ListenerHandle getListenerHandle(Listener<T> listener) {
+ return new ListenerHandle() {
+ @Override
+ public void register() {
+ HandlerList.this.register(listener);
+ }
+
+ @Override
+ public void unregister() {
+ HandlerList.this.unregister(listener);
+ }
+ };
+ }
+
+ public void register(EventPriority priority, Consumer<T> listener) {
+ register(new Listener<T>() {
+ @Override
+ public EventPriority getPriority() {
+ return priority;
+ }
+
+ @Override
+ public void accept(T event) {
+ listener.accept(event);
+ }
+ });
+ }
+
+ public List<Listener<T>> getRegistrations() {
+ return Collections.unmodifiableList(source);
+ }
+
+ public void unregister(Listener<T> listener) {
+ if (source.remove(listener)) {
+ refresh();
+ }
+ }
+
+ public void callEvent(T event) {
+ if (event instanceof Cancellable) {
+ Cancellable c = (Cancellable) event;
+ boolean cancelled = c.isCancelled();
+ for (Listener<T> listener : listeners) {
+ if (listener.listensToCancelledState(cancelled)) {
+ //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName());
+ listener.accept(event);
+ cancelled = c.isCancelled();
+ } /*else {
+ EnchantsPlugin.getInstance().debug("Listener does not listen to cancelled state of " + cancelled + ": " + listener.getClass().getSimpleName());
+ }*/
+ }
+ } else {
+ for (Listener<T> listener : listeners) {
+ //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName());
+ listener.accept(event);
+ }
+ }
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java b/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java index c3e5413..064cd28 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java @@ -1,17 +1,17 @@ -package io.dico.dicore.event; - -import org.bukkit.event.EventPriority; - -public interface Listener<T> { - - default EventPriority getPriority() { - return EventPriority.NORMAL; - } - - default boolean listensToCancelledState(boolean cancelled) { - return !cancelled; - } - - void accept(T event); - -} +package io.dico.dicore.event;
+
+import org.bukkit.event.EventPriority;
+
+public interface Listener<T> {
+
+ default EventPriority getPriority() {
+ return EventPriority.NORMAL;
+ }
+
+ default boolean listensToCancelledState(boolean cancelled) {
+ return !cancelled;
+ }
+
+ void accept(T event);
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java b/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java index 6042954..fbedd9f 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java @@ -1,9 +1,9 @@ -package io.dico.dicore.event; - -public interface ListenerHandle { - - void register(); - - void unregister(); - -} +package io.dico.dicore.event;
+
+public interface ListenerHandle {
+
+ void register();
+
+ void unregister();
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java b/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java index 9688975..4e49a55 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java @@ -1,7 +1,7 @@ -package io.dico.dicore.event; - -public interface SimpleListener<T> { - - void accept(T event); - -} +package io.dico.dicore.event;
+
+public interface SimpleListener<T> {
+
+ void accept(T event);
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java index e95b65e..2777125 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java @@ -1,203 +1,203 @@ -package io.dico.dicore.exceptions; - -import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable; -import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier; -import io.dico.dicore.exceptions.checkedfunctions.CheckedFunctionalObject; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.sql.SQLException; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Supplier; - -@FunctionalInterface -public interface ExceptionHandler { - - /** - * Handle the given exception according to this handler's implementation - * - * @param ex The exception to be handled - * @throws NullPointerException if ex is null, unless the implementation specifies otherwise - * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise - */ - void handle(Throwable ex); - - /** - * Handle the given exception according to this handler's implementation - * This method is intended for use by {@link CheckedFunctionalObject} and subinterfaces. - * It supplies exception handlers the option to acquire more information, by overriding this method and calling it from {@link #handle(Throwable)} - * - * @param ex The exception to be handled - * @param args Any arguments passed, this is used by {@link CheckedFunctionalObject} and subinterfaces. - * @return {@code null} (unless specified otherwise by the implementation) - * @throws NullPointerException if ex is null, unless the implementation specifies otherwise - * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise - */ - default Object handleGenericException(Throwable ex, Object... args) { - handle(ex); - return null; - } - - /** - * @return true if this {@link ExceptionHandler}'s {@link #handleGenericException(Throwable, Object...)} method is <b>never</b> expected to throw - * an unchecked exception other than {@link Error} - */ - default boolean isSafe() { - return true; - } - - /** - * Runs the given checked action, handling any thrown exceptions using this exception handler. - * <p> - * Any exceptions thrown by this handler are delegated to the caller. - * - * @param action The action to run - * @throws NullPointerException if action is null - */ - default void runSafe(CheckedRunnable<? extends Throwable> action) { - Objects.requireNonNull(action); - try { - action.checkedRun(); - } catch (Throwable ex) { - handle(ex); - } - } - - /** - * Computes the result of the given checked supplier, handling any thrown exceptions using this exception handler. - * <p> - * Any exceptions thrown by this handler are delegated to the caller. - * - * @param action The supplier whose result to compute - * @param <T> generic type parameter for the supplier and the result type of this method - * @return The result of this computation, or null if an error occurred - * @throws NullPointerException if action is null - */ - - default <T> T supplySafe(CheckedSupplier<T, ? extends Throwable> action) { - Objects.requireNonNull(action); - try { - return action.checkedGet(); - } catch (Throwable ex) { - handle(ex); - return null; - } - } - - /** - * @param action The action to wrap - * @return A runnable that wraps the given action using this handler's {@link #runSafe(CheckedRunnable)} method. - * @see #runSafe(CheckedRunnable) - */ - default Runnable safeRunnable(CheckedRunnable<? extends Throwable> action) { - return () -> runSafe(action); - } - - /** - * @param supplier The computation to wrap - * @return A supplier that wraps the given computation using this handler's {@link #supplySafe(CheckedSupplier)} method. - * @see #supplySafe(CheckedSupplier) - */ - default <T> Supplier<T> safeSupplier(CheckedSupplier<T, ? extends Throwable> supplier) { - return () -> supplySafe(supplier); - } - - /** - * Logs the given exception as an error to {@code out} - * <p> - * Format: Error occurred while {@code failedActivityDescription}, followed by additional details and a stack trace - * - * @param out The consumer to accept the error message, for instance {@code {@link java.util.logging.Logger logger}::warning}. - * @param failedActivityDescription A description of the activity that was being executed when the exception was thrown - * @param ex The exception that was thrown - * @throws NullPointerException if any argument is null - */ - static void log(Consumer<String> out, String failedActivityDescription, Throwable ex) { - if (out == null || failedActivityDescription == null || ex == null) { - throw new NullPointerException(); - } - - StringWriter msg = new StringWriter(1024); - msg.append("Error occurred while ").append(failedActivityDescription).append(':'); - - if (ex instanceof SQLException) { - SQLException sqlex = (SQLException) ex; - msg.append('\n').append("Error code: ").append(Integer.toString(sqlex.getErrorCode())); - msg.append('\n').append("SQL State: ").append(sqlex.getSQLState()); - } - - msg.append('\n').append("=======START STACK======="); - try (PrintWriter pw = new PrintWriter(msg)) { - ex.printStackTrace(pw); - } - msg.append('\n').append("========END STACK========"); - - out.accept(msg.toString()); - } - - /** - * @param activityDescription The activity description - * @return An ExceptionHandler that prints to {@link System#out} - * @see #log(Consumer, String) - */ - static ExceptionHandler log(String activityDescription) { - // A method reference would cache the current value in System.out - // This would update if the value in System.out is changed (by for example, applying a PrintStream that handles colours). - return log(msg -> System.out.println(msg), activityDescription); - } - - /** - * @param out The Consumer to be passed to {@link #log(Consumer, String, Throwable)} - * @param activityDescription The activity description to be passed to {@link #log(Consumer, String, Throwable)} - * @return An ExceptionHandler that passes exceptions with given arguments to {@link #log(Consumer, String, Throwable)} - * @see #log(Consumer, String, Throwable) - */ - static ExceptionHandler log(Consumer<String> out, String activityDescription) { - return ex -> log(out, activityDescription, ex); - } - - /** - * This ExceptionHandler turns any Throwable into an unchecked exception, then throws it again. - */ - ExceptionHandler UNCHECKED = new ExceptionHandler() { - @Override - public void handle(Throwable ex) { - errorFilter(ex); - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - throw new RuntimeException(ex); - } - - @Override - public boolean isSafe() { - return false; - } - }; - - /** - * This ExceptionHandler suppresses all exceptions, - * apart from {@link Error} because that is not usually supposed to be caught - */ - ExceptionHandler SUPPRESS = ExceptionHandler::errorFilter; - - /** - * This ExceptionHandler calls {@link Throwable#printStackTrace()} unless it is a {@link NullPointerException} or {@link Error}. - */ - ExceptionHandler PRINT_UNLESS_NP = ex -> { - errorFilter(ex); - if (!(ex instanceof NullPointerException)) { - ex.printStackTrace(); - } - }; - - static void errorFilter(Throwable ex) { - if (ex instanceof Error) { - throw (Error) ex; - } else if (ex == null) { - throw new NullPointerException(); - } - } - -} +package io.dico.dicore.exceptions;
+
+import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedFunctionalObject;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.SQLException;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+@FunctionalInterface
+public interface ExceptionHandler {
+
+ /**
+ * Handle the given exception according to this handler's implementation
+ *
+ * @param ex The exception to be handled
+ * @throws NullPointerException if ex is null, unless the implementation specifies otherwise
+ * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise
+ */
+ void handle(Throwable ex);
+
+ /**
+ * Handle the given exception according to this handler's implementation
+ * This method is intended for use by {@link CheckedFunctionalObject} and subinterfaces.
+ * It supplies exception handlers the option to acquire more information, by overriding this method and calling it from {@link #handle(Throwable)}
+ *
+ * @param ex The exception to be handled
+ * @param args Any arguments passed, this is used by {@link CheckedFunctionalObject} and subinterfaces.
+ * @return {@code null} (unless specified otherwise by the implementation)
+ * @throws NullPointerException if ex is null, unless the implementation specifies otherwise
+ * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise
+ */
+ default Object handleGenericException(Throwable ex, Object... args) {
+ handle(ex);
+ return null;
+ }
+
+ /**
+ * @return true if this {@link ExceptionHandler}'s {@link #handleGenericException(Throwable, Object...)} method is <b>never</b> expected to throw
+ * an unchecked exception other than {@link Error}
+ */
+ default boolean isSafe() {
+ return true;
+ }
+
+ /**
+ * Runs the given checked action, handling any thrown exceptions using this exception handler.
+ * <p>
+ * Any exceptions thrown by this handler are delegated to the caller.
+ *
+ * @param action The action to run
+ * @throws NullPointerException if action is null
+ */
+ default void runSafe(CheckedRunnable<? extends Throwable> action) {
+ Objects.requireNonNull(action);
+ try {
+ action.checkedRun();
+ } catch (Throwable ex) {
+ handle(ex);
+ }
+ }
+
+ /**
+ * Computes the result of the given checked supplier, handling any thrown exceptions using this exception handler.
+ * <p>
+ * Any exceptions thrown by this handler are delegated to the caller.
+ *
+ * @param action The supplier whose result to compute
+ * @param <T> generic type parameter for the supplier and the result type of this method
+ * @return The result of this computation, or null if an error occurred
+ * @throws NullPointerException if action is null
+ */
+
+ default <T> T supplySafe(CheckedSupplier<T, ? extends Throwable> action) {
+ Objects.requireNonNull(action);
+ try {
+ return action.checkedGet();
+ } catch (Throwable ex) {
+ handle(ex);
+ return null;
+ }
+ }
+
+ /**
+ * @param action The action to wrap
+ * @return A runnable that wraps the given action using this handler's {@link #runSafe(CheckedRunnable)} method.
+ * @see #runSafe(CheckedRunnable)
+ */
+ default Runnable safeRunnable(CheckedRunnable<? extends Throwable> action) {
+ return () -> runSafe(action);
+ }
+
+ /**
+ * @param supplier The computation to wrap
+ * @return A supplier that wraps the given computation using this handler's {@link #supplySafe(CheckedSupplier)} method.
+ * @see #supplySafe(CheckedSupplier)
+ */
+ default <T> Supplier<T> safeSupplier(CheckedSupplier<T, ? extends Throwable> supplier) {
+ return () -> supplySafe(supplier);
+ }
+
+ /**
+ * Logs the given exception as an error to {@code out}
+ * <p>
+ * Format: Error occurred while {@code failedActivityDescription}, followed by additional details and a stack trace
+ *
+ * @param out The consumer to accept the error message, for instance {@code {@link java.util.logging.Logger logger}::warning}.
+ * @param failedActivityDescription A description of the activity that was being executed when the exception was thrown
+ * @param ex The exception that was thrown
+ * @throws NullPointerException if any argument is null
+ */
+ static void log(Consumer<String> out, String failedActivityDescription, Throwable ex) {
+ if (out == null || failedActivityDescription == null || ex == null) {
+ throw new NullPointerException();
+ }
+
+ StringWriter msg = new StringWriter(1024);
+ msg.append("Error occurred while ").append(failedActivityDescription).append(':');
+
+ if (ex instanceof SQLException) {
+ SQLException sqlex = (SQLException) ex;
+ msg.append('\n').append("Error code: ").append(Integer.toString(sqlex.getErrorCode()));
+ msg.append('\n').append("SQL State: ").append(sqlex.getSQLState());
+ }
+
+ msg.append('\n').append("=======START STACK=======");
+ try (PrintWriter pw = new PrintWriter(msg)) {
+ ex.printStackTrace(pw);
+ }
+ msg.append('\n').append("========END STACK========");
+
+ out.accept(msg.toString());
+ }
+
+ /**
+ * @param activityDescription The activity description
+ * @return An ExceptionHandler that prints to {@link System#out}
+ * @see #log(Consumer, String)
+ */
+ static ExceptionHandler log(String activityDescription) {
+ // A method reference would cache the current value in System.out
+ // This would update if the value in System.out is changed (by for example, applying a PrintStream that handles colours).
+ return log(msg -> System.out.println(msg), activityDescription);
+ }
+
+ /**
+ * @param out The Consumer to be passed to {@link #log(Consumer, String, Throwable)}
+ * @param activityDescription The activity description to be passed to {@link #log(Consumer, String, Throwable)}
+ * @return An ExceptionHandler that passes exceptions with given arguments to {@link #log(Consumer, String, Throwable)}
+ * @see #log(Consumer, String, Throwable)
+ */
+ static ExceptionHandler log(Consumer<String> out, String activityDescription) {
+ return ex -> log(out, activityDescription, ex);
+ }
+
+ /**
+ * This ExceptionHandler turns any Throwable into an unchecked exception, then throws it again.
+ */
+ ExceptionHandler UNCHECKED = new ExceptionHandler() {
+ @Override
+ public void handle(Throwable ex) {
+ errorFilter(ex);
+ if (ex instanceof RuntimeException) {
+ throw (RuntimeException) ex;
+ }
+ throw new RuntimeException(ex);
+ }
+
+ @Override
+ public boolean isSafe() {
+ return false;
+ }
+ };
+
+ /**
+ * This ExceptionHandler suppresses all exceptions,
+ * apart from {@link Error} because that is not usually supposed to be caught
+ */
+ ExceptionHandler SUPPRESS = ExceptionHandler::errorFilter;
+
+ /**
+ * This ExceptionHandler calls {@link Throwable#printStackTrace()} unless it is a {@link NullPointerException} or {@link Error}.
+ */
+ ExceptionHandler PRINT_UNLESS_NP = ex -> {
+ errorFilter(ex);
+ if (!(ex instanceof NullPointerException)) {
+ ex.printStackTrace();
+ }
+ };
+
+ static void errorFilter(Throwable ex) {
+ if (ex instanceof Error) {
+ throw (Error) ex;
+ } else if (ex == null) {
+ throw new NullPointerException();
+ }
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java index badf05b..bfcafe8 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java @@ -1,69 +1,69 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.BiConsumer; - -/** - * checked mimic of {@link BiConsumer} - * - * @param <TParam1> - * @param <TParam2> - * @param <TException> - */ -@FunctionalInterface -public interface CheckedBiConsumer<TParam1, TParam2, TException extends Throwable> - extends CheckedFunctionalObject<Void, TException>, BiConsumer<TParam1, TParam2> { - - /** - * The consuming action - * - * @param t the first argument to consume - * @param u the second argument to consume - * @throws TException if an exception occurs - */ - void checkedAccept(TParam1 t, TParam2 u) throws TException; - - /** - * unchecked wrapper for {@link #checkedAccept(Object, Object)} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @param t the first input - * @param u the second input - * @see #checkedAccept(Object, Object) - * @see #resultOnError(Throwable, Object...) - */ - @Override - default void accept(TParam1 t, TParam2 u) { - try { - checkedAccept(t, u); - } catch (Throwable ex) { - handleGenericException(ex, t, u); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedBiConsumer<TParam1, TParam2, TException> handleExceptionsWith(ExceptionHandler handler) { - return new CheckedBiConsumer<TParam1, TParam2, TException>() { - @Override - public void checkedAccept(TParam1 t, TParam2 u) throws TException { - CheckedBiConsumer.this.checkedAccept(t, u); - } - - @Override - public Void handleGenericException(Throwable thrown, Object... args) { - handler.handleGenericException(thrown, args); - return null; - } - - @Override - public CheckedBiConsumer<TParam1, TParam2, TException> handleExceptionsWith(ExceptionHandler handler) { - return CheckedBiConsumer.this.handleExceptionsWith(handler); - } - }; - } - -} +package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.BiConsumer;
+
+/**
+ * checked mimic of {@link BiConsumer}
+ *
+ * @param <TParam1>
+ * @param <TParam2>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedBiConsumer<TParam1, TParam2, TException extends Throwable>
+ extends CheckedFunctionalObject<Void, TException>, BiConsumer<TParam1, TParam2> {
+
+ /**
+ * The consuming action
+ *
+ * @param t the first argument to consume
+ * @param u the second argument to consume
+ * @throws TException if an exception occurs
+ */
+ void checkedAccept(TParam1 t, TParam2 u) throws TException;
+
+ /**
+ * unchecked wrapper for {@link #checkedAccept(Object, Object)}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @param t the first input
+ * @param u the second input
+ * @see #checkedAccept(Object, Object)
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default void accept(TParam1 t, TParam2 u) {
+ try {
+ checkedAccept(t, u);
+ } catch (Throwable ex) {
+ handleGenericException(ex, t, u);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedBiConsumer<TParam1, TParam2, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedBiConsumer<TParam1, TParam2, TException>() {
+ @Override
+ public void checkedAccept(TParam1 t, TParam2 u) throws TException {
+ CheckedBiConsumer.this.checkedAccept(t, u);
+ }
+
+ @Override
+ public Void handleGenericException(Throwable thrown, Object... args) {
+ handler.handleGenericException(thrown, args);
+ return null;
+ }
+
+ @Override
+ public CheckedBiConsumer<TParam1, TParam2, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedBiConsumer.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java index 9b39325..11fca3f 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java @@ -1,76 +1,76 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.BiFunction; - -/** - * checked mimic of {@link BiFunction} - * - * @param <TParam1> - * @param <TParam2> - * @param <TResult> - * @param <TException> - */ -@FunctionalInterface -public interface CheckedBiFunction<TParam1, TParam2, TResult, TException extends Throwable> - extends CheckedFunctionalObject<TResult, TException>, BiFunction<TParam1, TParam2, TResult> { - - /** - * the functional method - * - * @param t the first input - * @param u the second input - * @return the function output - * @throws TException if an exception occurs - */ - TResult checkedApply(TParam1 t, TParam2 u) throws TException; - - /** - * unchecked wrapper for {@link #checkedApply(Object, Object)} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @param t the first input - * @param u the second input - * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown - * @see #checkedApply(Object, Object) - * @see #resultOnError(Throwable, Object...) - */ - @Override - default TResult apply(TParam1 t, TParam2 u) { - try { - return checkedApply(t, u); - } catch (Throwable ex) { - return handleGenericException(ex, t, u); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedBiFunction<TParam1, TParam2, TResult, TException> handleExceptionsWith(ExceptionHandler handler) { - return new CheckedBiFunction<TParam1, TParam2, TResult, TException>() { - @Override - public TResult checkedApply(TParam1 t, TParam2 u) throws TException { - return CheckedBiFunction.this.checkedApply(t, u); - } - - @Override - @SuppressWarnings("unchecked") - public TResult handleGenericException(Throwable thrown, Object... args) { - Object result = handler.handleGenericException(thrown, args); - try { - return (TResult) result; - } catch (Exception ex) { - return null; - } - } - - @Override - public CheckedBiFunction<TParam1, TParam2, TResult, TException> handleExceptionsWith(ExceptionHandler handler) { - return CheckedBiFunction.this.handleExceptionsWith(handler); - } - }; - } -} +package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.BiFunction;
+
+/**
+ * checked mimic of {@link BiFunction}
+ *
+ * @param <TParam1>
+ * @param <TParam2>
+ * @param <TResult>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedBiFunction<TParam1, TParam2, TResult, TException extends Throwable>
+ extends CheckedFunctionalObject<TResult, TException>, BiFunction<TParam1, TParam2, TResult> {
+
+ /**
+ * the functional method
+ *
+ * @param t the first input
+ * @param u the second input
+ * @return the function output
+ * @throws TException if an exception occurs
+ */
+ TResult checkedApply(TParam1 t, TParam2 u) throws TException;
+
+ /**
+ * unchecked wrapper for {@link #checkedApply(Object, Object)}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @param t the first input
+ * @param u the second input
+ * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown
+ * @see #checkedApply(Object, Object)
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default TResult apply(TParam1 t, TParam2 u) {
+ try {
+ return checkedApply(t, u);
+ } catch (Throwable ex) {
+ return handleGenericException(ex, t, u);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedBiFunction<TParam1, TParam2, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedBiFunction<TParam1, TParam2, TResult, TException>() {
+ @Override
+ public TResult checkedApply(TParam1 t, TParam2 u) throws TException {
+ return CheckedBiFunction.this.checkedApply(t, u);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public TResult handleGenericException(Throwable thrown, Object... args) {
+ Object result = handler.handleGenericException(thrown, args);
+ try {
+ return (TResult) result;
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public CheckedBiFunction<TParam1, TParam2, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedBiFunction.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java index fa48ae8..abe6d53 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java @@ -1,67 +1,67 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Consumer; - -/** - * checked mimic of {@link Consumer} - * - * @param <TParam> - * @param <TException> - */ -@FunctionalInterface -public interface CheckedConsumer<TParam, TException extends Throwable> - extends CheckedFunctionalObject<Void, TException>, Consumer<TParam> { - - /** - * The consuming action - * - * @param t the argument to consume - * @throws TException if an error occurs - */ - void checkedAccept(TParam t) throws TException; - - /** - * Unchecked version of {@link #checkedAccept(Object)} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @param t the argument to consume - * @see #checkedAccept(Object) - * @see #resultOnError(Throwable, Object...) - */ - @Override - default void accept(TParam t) { - try { - checkedAccept(t); - } catch (Throwable ex) { - handleGenericException(ex, t); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedConsumer<TParam, TException> handleExceptionsWith(ExceptionHandler handler) { - return new CheckedConsumer<TParam, TException>() { - @Override - public void checkedAccept(TParam t) throws TException { - CheckedConsumer.this.checkedAccept(t); - } - - @Override - @SuppressWarnings("unchecked") - public Void handleGenericException(Throwable thrown, Object... args) { - handler.handleGenericException(thrown, args); - return null; - } - - @Override - public CheckedConsumer<TParam, TException> handleExceptionsWith(ExceptionHandler handler) { - return CheckedConsumer.this.handleExceptionsWith(handler); - } - }; - } - -} +package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.Consumer;
+
+/**
+ * checked mimic of {@link Consumer}
+ *
+ * @param <TParam>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedConsumer<TParam, TException extends Throwable>
+ extends CheckedFunctionalObject<Void, TException>, Consumer<TParam> {
+
+ /**
+ * The consuming action
+ *
+ * @param t the argument to consume
+ * @throws TException if an error occurs
+ */
+ void checkedAccept(TParam t) throws TException;
+
+ /**
+ * Unchecked version of {@link #checkedAccept(Object)}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @param t the argument to consume
+ * @see #checkedAccept(Object)
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default void accept(TParam t) {
+ try {
+ checkedAccept(t);
+ } catch (Throwable ex) {
+ handleGenericException(ex, t);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedConsumer<TParam, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedConsumer<TParam, TException>() {
+ @Override
+ public void checkedAccept(TParam t) throws TException {
+ CheckedConsumer.this.checkedAccept(t);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Void handleGenericException(Throwable thrown, Object... args) {
+ handler.handleGenericException(thrown, args);
+ return null;
+ }
+
+ @Override
+ public CheckedConsumer<TParam, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedConsumer.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java index 9ff806a..48ac207 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java @@ -1,74 +1,74 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Function; - -/** - * checked mimic of {@link Function} - * - * @param <TParam> - * @param <TResult> - * @param <TException> - */ -@FunctionalInterface -public interface CheckedFunction<TParam, TResult, TException extends Throwable> - extends CheckedFunctionalObject<TResult, TException>, Function<TParam, TResult> { - - /** - * the functional method - * - * @param t the input - * @return the function output - * @throws TException if an exception occurs - */ - TResult checkedApply(TParam t) throws TException; - - /** - * unchecked wrapper for {@link #checkedApply(Object)} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @param t the input - * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown - * @see #checkedApply(Object) - * @see #resultOnError(Throwable, Object...) - */ - @Override - default TResult apply(TParam t) { - try { - return checkedApply(t); - } catch (Throwable ex) { - return handleGenericException(ex, t); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedFunction<TParam, TResult, TException> handleExceptionsWith(ExceptionHandler handler) { - return new CheckedFunction<TParam, TResult, TException>() { - @Override - public TResult checkedApply(TParam t) throws TException { - return CheckedFunction.this.checkedApply(t); - } - - @Override - @SuppressWarnings("unchecked") - public TResult handleGenericException(Throwable thrown, Object... args) { - Object result = handler.handleGenericException(thrown, args); - try { - return (TResult) result; - } catch (Exception ex) { - return null; - } - } - - @Override - public CheckedFunction<TParam, TResult, TException> handleExceptionsWith(ExceptionHandler handler) { - return CheckedFunction.this.handleExceptionsWith(handler); - } - }; - } - -} +package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.Function;
+
+/**
+ * checked mimic of {@link Function}
+ *
+ * @param <TParam>
+ * @param <TResult>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedFunction<TParam, TResult, TException extends Throwable>
+ extends CheckedFunctionalObject<TResult, TException>, Function<TParam, TResult> {
+
+ /**
+ * the functional method
+ *
+ * @param t the input
+ * @return the function output
+ * @throws TException if an exception occurs
+ */
+ TResult checkedApply(TParam t) throws TException;
+
+ /**
+ * unchecked wrapper for {@link #checkedApply(Object)}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @param t the input
+ * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown
+ * @see #checkedApply(Object)
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default TResult apply(TParam t) {
+ try {
+ return checkedApply(t);
+ } catch (Throwable ex) {
+ return handleGenericException(ex, t);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedFunction<TParam, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedFunction<TParam, TResult, TException>() {
+ @Override
+ public TResult checkedApply(TParam t) throws TException {
+ return CheckedFunction.this.checkedApply(t);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public TResult handleGenericException(Throwable thrown, Object... args) {
+ Object result = handler.handleGenericException(thrown, args);
+ try {
+ return (TResult) result;
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public CheckedFunction<TParam, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedFunction.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java index e84b33d..d9d7a53 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java @@ -1,85 +1,85 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -/** - * Base interface for all checked functional interfaces - * Most subinterfaces will mimic interfaces in the package {@link java.util.function} - * <p> - * Checked functional interfaces are functions with throws declarations. - * The name comes from the fact that they can throw <b>checked</b> exceptions. - * <p> - * They extend their non-checked counterparts, whose methods are implemented by - * returning the result of {@link #resultOnError(TException, Object...)} when an exception is thrown - * <p> - * Made public to allow more specialized checked functional interfaces to subclass it. - * Making primitive versions shouldn't provide a significant performance increase because we're checking for exceptions, - * the performance impact of which is probably a few magnitudes larger. Don't quote me on this. - * - * @param <TResult> The return type of this functional interface's method - * @param <TException> The type of exception that might be thrown - */ -public interface CheckedFunctionalObject<TResult, TException extends Throwable> extends ExceptionHandler { - - /** - * {@inheritDoc} - * - * @param ex The exception to be handled - */ - @Override - default void handle(Throwable ex) { - handleGenericException(ex); - } - - /** - * {@inheritDoc} - * <p> - * Method to handle exceptions thrown by the default implementations of subinterfaces. - * Since you can't catch a type parameter as exception, this code is in place to take care of it. - * <p> - * If the thrown exception is not a TException, an unchecked version is thrown by calling this method. - * - * @param thrown The thrown exception - * @param args the arguments supplied to the method that threw the exception, if any. These are not guaranteed to be given if parameters are present. - * @return The result computed by {@link #resultOnError(Throwable, Object...)} - * @see #resultOnError(Throwable, Object...) - */ - @Override - @SuppressWarnings("unchecked") - default TResult handleGenericException(Throwable thrown, Object... args) { - - // check if the throwable is a TException - TException castedException; - try { - castedException = (TException) thrown; - } catch (ClassCastException ex) { - // if not, throw an unchecked version of it - ExceptionHandler.UNCHECKED.handleGenericException(thrown); - // this code is never reached. - return null; - } - - // if it is a TException, use resultOnError to compute result. - return resultOnError(castedException, args); - } - - /** - * This method handles the exceptions thrown by the checked method of this object, when called by its unchecked wrapper. - * - * @param ex The exception thrown - * @param args The (typed) arguments passed, if any - * @return The result to return, if any - */ - default TResult resultOnError(TException ex, Object... args) { - return null; - } - - /** - * Creates a new functional object that uses the given exception handler to handle exceptions. - * - * @param handler The handler to handle exceptions - * @return a new functional object that uses the given exception handler to handle exceptions - */ - CheckedFunctionalObject<TResult, TException> handleExceptionsWith(ExceptionHandler handler); - -} +package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+/**
+ * Base interface for all checked functional interfaces
+ * Most subinterfaces will mimic interfaces in the package {@link java.util.function}
+ * <p>
+ * Checked functional interfaces are functions with throws declarations.
+ * The name comes from the fact that they can throw <b>checked</b> exceptions.
+ * <p>
+ * They extend their non-checked counterparts, whose methods are implemented by
+ * returning the result of {@link #resultOnError(TException, Object...)} when an exception is thrown
+ * <p>
+ * Made public to allow more specialized checked functional interfaces to subclass it.
+ * Making primitive versions shouldn't provide a significant performance increase because we're checking for exceptions,
+ * the performance impact of which is probably a few magnitudes larger. Don't quote me on this.
+ *
+ * @param <TResult> The return type of this functional interface's method
+ * @param <TException> The type of exception that might be thrown
+ */
+public interface CheckedFunctionalObject<TResult, TException extends Throwable> extends ExceptionHandler {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param ex The exception to be handled
+ */
+ @Override
+ default void handle(Throwable ex) {
+ handleGenericException(ex);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Method to handle exceptions thrown by the default implementations of subinterfaces.
+ * Since you can't catch a type parameter as exception, this code is in place to take care of it.
+ * <p>
+ * If the thrown exception is not a TException, an unchecked version is thrown by calling this method.
+ *
+ * @param thrown The thrown exception
+ * @param args the arguments supplied to the method that threw the exception, if any. These are not guaranteed to be given if parameters are present.
+ * @return The result computed by {@link #resultOnError(Throwable, Object...)}
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ default TResult handleGenericException(Throwable thrown, Object... args) {
+
+ // check if the throwable is a TException
+ TException castedException;
+ try {
+ castedException = (TException) thrown;
+ } catch (ClassCastException ex) {
+ // if not, throw an unchecked version of it
+ ExceptionHandler.UNCHECKED.handleGenericException(thrown);
+ // this code is never reached.
+ return null;
+ }
+
+ // if it is a TException, use resultOnError to compute result.
+ return resultOnError(castedException, args);
+ }
+
+ /**
+ * This method handles the exceptions thrown by the checked method of this object, when called by its unchecked wrapper.
+ *
+ * @param ex The exception thrown
+ * @param args The (typed) arguments passed, if any
+ * @return The result to return, if any
+ */
+ default TResult resultOnError(TException ex, Object... args) {
+ return null;
+ }
+
+ /**
+ * Creates a new functional object that uses the given exception handler to handle exceptions.
+ *
+ * @param handler The handler to handle exceptions
+ * @return a new functional object that uses the given exception handler to handle exceptions
+ */
+ CheckedFunctionalObject<TResult, TException> handleExceptionsWith(ExceptionHandler handler);
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java index 55de6f8..110c988 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java @@ -1,62 +1,62 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -/** - * checked mimic of {@link Runnable} - * - * @param <TException> - */ -@FunctionalInterface -public interface CheckedRunnable<TException extends Throwable> - extends CheckedFunctionalObject<Void, TException>, Runnable { - - /** - * The runnable action - * - * @throws TException if an exception occurs - */ - void checkedRun() throws TException; - - /** - * Unchecked version of {@link #checkedRun()} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @see #checkedRun() - * @see #resultOnError(Throwable, Object...) - */ - @Override - default void run() { - try { - checkedRun(); - } catch (Throwable ex) { - handleGenericException(ex); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedRunnable<TException> handleExceptionsWith(ExceptionHandler handler) { - return new CheckedRunnable<TException>() { - @Override - public void checkedRun() throws TException { - CheckedRunnable.this.checkedRun(); - } - - @Override - @SuppressWarnings("unchecked") - public Void handleGenericException(Throwable thrown, Object... args) { - handler.handleGenericException(thrown, args); - return null; - } - - @Override - public CheckedRunnable<TException> handleExceptionsWith(ExceptionHandler handler) { - return CheckedRunnable.this.handleExceptionsWith(handler); - } - }; - } - -} +package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+/**
+ * checked mimic of {@link Runnable}
+ *
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedRunnable<TException extends Throwable>
+ extends CheckedFunctionalObject<Void, TException>, Runnable {
+
+ /**
+ * The runnable action
+ *
+ * @throws TException if an exception occurs
+ */
+ void checkedRun() throws TException;
+
+ /**
+ * Unchecked version of {@link #checkedRun()}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @see #checkedRun()
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default void run() {
+ try {
+ checkedRun();
+ } catch (Throwable ex) {
+ handleGenericException(ex);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedRunnable<TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedRunnable<TException>() {
+ @Override
+ public void checkedRun() throws TException {
+ CheckedRunnable.this.checkedRun();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Void handleGenericException(Throwable thrown, Object... args) {
+ handler.handleGenericException(thrown, args);
+ return null;
+ }
+
+ @Override
+ public CheckedRunnable<TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedRunnable.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java index dec2e7e..7820428 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java @@ -1,70 +1,70 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Supplier; - -/** - * checked mimic of {@link Supplier} - * - * @param <TResult> - * @param <TException> - */ -@FunctionalInterface -public interface CheckedSupplier<TResult, TException extends Throwable> - extends CheckedFunctionalObject<TResult, TException>, Supplier<TResult> { - - /** - * The computation - * - * @return the result of this computation - * @throws TException if an error occurs - */ - TResult checkedGet() throws TException; - - /** - * Unchecked version of {@link #checkedGet()} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @return the result of this computation - * @see #checkedGet() - * @see #resultOnError(Throwable, Object...) - */ - @Override - default TResult get() { - try { - return checkedGet(); - } catch (Throwable ex) { - return handleGenericException(ex); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedSupplier<TResult, TException> handleExceptionsWith(ExceptionHandler handler) { - return new CheckedSupplier<TResult, TException>() { - @Override - public TResult checkedGet() throws TException { - return CheckedSupplier.this.checkedGet(); - } - - @Override - @SuppressWarnings("unchecked") - public TResult handleGenericException(Throwable thrown, Object... args) { - Object result = handler.handleGenericException(thrown, args); - try { - return (TResult) result; - } catch (Exception ex) { - return null; - } - } - - @Override - public CheckedSupplier<TResult, TException> handleExceptionsWith(ExceptionHandler handler) { - return CheckedSupplier.this.handleExceptionsWith(handler); - } - }; - } -} +package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.Supplier;
+
+/**
+ * checked mimic of {@link Supplier}
+ *
+ * @param <TResult>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedSupplier<TResult, TException extends Throwable>
+ extends CheckedFunctionalObject<TResult, TException>, Supplier<TResult> {
+
+ /**
+ * The computation
+ *
+ * @return the result of this computation
+ * @throws TException if an error occurs
+ */
+ TResult checkedGet() throws TException;
+
+ /**
+ * Unchecked version of {@link #checkedGet()}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @return the result of this computation
+ * @see #checkedGet()
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default TResult get() {
+ try {
+ return checkedGet();
+ } catch (Throwable ex) {
+ return handleGenericException(ex);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedSupplier<TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedSupplier<TResult, TException>() {
+ @Override
+ public TResult checkedGet() throws TException {
+ return CheckedSupplier.this.checkedGet();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public TResult handleGenericException(Throwable thrown, Object... args) {
+ Object result = handler.handleGenericException(thrown, args);
+ try {
+ return (TResult) result;
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public CheckedSupplier<TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedSupplier.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java index b400cce..43db459 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java +++ b/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java @@ -1,108 +1,108 @@ -package io.dico.dicore.task; - -import org.bukkit.Bukkit; -import org.bukkit.plugin.Plugin; - -import java.util.NoSuchElementException; - -public abstract class BaseTask<T> { - private boolean running = false; - private Integer taskId = null; - private long workTime = 5L; - private int workCount; - - public void start(Plugin plugin, int delay, int period, long workTime) { - doStartChecks(); - this.workTime = workTime; - workCount = 0; - running = true; - - if (delay == -1) { - run(); - if (!running) { - return; - } - delay = period; - } - - taskId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this::run, delay, period); - } - - public void startImmediately(Plugin plugin, int period, long workTime) { - start(plugin, -1, period, workTime); - } - - protected void doStartChecks() { - if (isRunning()) { - throw new IllegalStateException("Can't start when already running"); - } - } - - public void start(Plugin plugin) { - start(plugin, -1, 20, 5L); - } - - protected void onFinish(boolean early) { - } - - protected long getWorkTime() { - return workTime; - } - - protected abstract boolean process(T object); - - private void run() { - workCount++; - final long stop = System.currentTimeMillis() + getWorkTime(); - do { - if (!processNext()) { - return; - } - } while (System.currentTimeMillis() < stop); - } - - public int getTaskId() { - return running ? taskId : -1; - } - - public int getWorkCount() { - return workCount; - } - - public boolean isRunning() { - return running; - } - - protected abstract T supply() throws NoSuchElementException; - - private void cancelTask(boolean early) { - if (taskId != null) { - Bukkit.getScheduler().cancelTask(taskId); - } - running = false; - taskId = null; - onFinish(early); - } - - private boolean processNext() { - T object; - try { - object = supply(); - } catch (NoSuchElementException e) { - cancelTask(false); - return false; - } - - try { - if (process(object)) { - return true; - } - } catch (RuntimeException e) { - e.printStackTrace(); - } - - cancelTask(true); - return false; - } - -} +package io.dico.dicore.task;
+
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.Plugin;
+
+import java.util.NoSuchElementException;
+
+public abstract class BaseTask<T> {
+ private boolean running = false;
+ private Integer taskId = null;
+ private long workTime = 5L;
+ private int workCount;
+
+ public void start(Plugin plugin, int delay, int period, long workTime) {
+ doStartChecks();
+ this.workTime = workTime;
+ workCount = 0;
+ running = true;
+
+ if (delay == -1) {
+ run();
+ if (!running) {
+ return;
+ }
+ delay = period;
+ }
+
+ taskId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this::run, delay, period);
+ }
+
+ public void startImmediately(Plugin plugin, int period, long workTime) {
+ start(plugin, -1, period, workTime);
+ }
+
+ protected void doStartChecks() {
+ if (isRunning()) {
+ throw new IllegalStateException("Can't start when already running");
+ }
+ }
+
+ public void start(Plugin plugin) {
+ start(plugin, -1, 20, 5L);
+ }
+
+ protected void onFinish(boolean early) {
+ }
+
+ protected long getWorkTime() {
+ return workTime;
+ }
+
+ protected abstract boolean process(T object);
+
+ private void run() {
+ workCount++;
+ final long stop = System.currentTimeMillis() + getWorkTime();
+ do {
+ if (!processNext()) {
+ return;
+ }
+ } while (System.currentTimeMillis() < stop);
+ }
+
+ public int getTaskId() {
+ return running ? taskId : -1;
+ }
+
+ public int getWorkCount() {
+ return workCount;
+ }
+
+ public boolean isRunning() {
+ return running;
+ }
+
+ protected abstract T supply() throws NoSuchElementException;
+
+ private void cancelTask(boolean early) {
+ if (taskId != null) {
+ Bukkit.getScheduler().cancelTask(taskId);
+ }
+ running = false;
+ taskId = null;
+ onFinish(early);
+ }
+
+ private boolean processNext() {
+ T object;
+ try {
+ object = supply();
+ } catch (NoSuchElementException e) {
+ cancelTask(false);
+ return false;
+ }
+
+ try {
+ if (process(object)) {
+ return true;
+ }
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+
+ cancelTask(true);
+ return false;
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java index e58814c..3ff7be7 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java +++ b/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java @@ -1,120 +1,120 @@ -package io.dico.dicore.task; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.BiPredicate; -import java.util.function.Consumer; -import java.util.function.Predicate; - -public abstract class IteratorTask<T> extends BaseTask<T> { - - private Iterator<? extends T> iterator; - - public IteratorTask() { - } - - @SuppressWarnings("unchecked") - public IteratorTask(Iterable<? extends T> iterable, boolean clone) { - refresh(iterable, clone); - } - - public IteratorTask(Iterable<? extends T> iterable) { - this(iterable, false); - } - - public IteratorTask(Iterator<? extends T> iterator) { - refresh(iterator); - } - - @Override - protected void doStartChecks() { - super.doStartChecks(); - if (iterator == null) { - throw new IllegalStateException("An iterator must be supplied first"); - } - } - - protected final void refresh(Iterable<? extends T> iterable, boolean clone) { - if (clone) { - Collection<T> collection; - if (!(iterable instanceof Collection)) { - collection = new LinkedList<>(); - for (T next : iterable) { - collection.add(next); - } - } else { - collection = new ArrayList((Collection) iterable); - } - iterator = collection.iterator(); - } else { - iterator = iterable.iterator(); - } - } - - protected final void refresh(Iterator<? extends T> iterator) { - Objects.requireNonNull(iterator); - this.iterator = iterator; - } - - @Override - protected T supply() { - return iterator.next(); - } - - protected void remove() { - iterator.remove(); - } - - // One argument: The processed object - - public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, Consumer<T> processor) { - return create(iterable, false, processor); - } - - public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, Consumer<T> processor) { - return create(iterable, clone, object -> { - processor.accept(object); - return true; - }); - } - - public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, Predicate<T> processor) { - return create(iterable, false, processor); - } - - public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, Predicate<T> processor) { - return new IteratorTask<T>(iterable, clone) { - @Override - protected boolean process(T object) { - return processor.test(object); - } - }; - } - - // Two arguments: the processed object, and a runnable to remove it from the iterator. - - public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, BiConsumer<T, Runnable> processor) { - return create(iterable, false, processor); - } - - public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, BiConsumer<T, Runnable> processor) { - return create(iterable, clone, (object, runnable) -> { - processor.accept(object, runnable); - return true; - }); - } - - public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, BiPredicate<T, Runnable> processor) { - return create(iterable, false, processor); - } - - public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, BiPredicate<T, Runnable> processor) { - return new IteratorTask<T>(iterable, clone) { - @Override - protected boolean process(T object) { - return processor.test(object, this::remove); - } - }; - } - -} +package io.dico.dicore.task;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public abstract class IteratorTask<T> extends BaseTask<T> {
+
+ private Iterator<? extends T> iterator;
+
+ public IteratorTask() {
+ }
+
+ @SuppressWarnings("unchecked")
+ public IteratorTask(Iterable<? extends T> iterable, boolean clone) {
+ refresh(iterable, clone);
+ }
+
+ public IteratorTask(Iterable<? extends T> iterable) {
+ this(iterable, false);
+ }
+
+ public IteratorTask(Iterator<? extends T> iterator) {
+ refresh(iterator);
+ }
+
+ @Override
+ protected void doStartChecks() {
+ super.doStartChecks();
+ if (iterator == null) {
+ throw new IllegalStateException("An iterator must be supplied first");
+ }
+ }
+
+ protected final void refresh(Iterable<? extends T> iterable, boolean clone) {
+ if (clone) {
+ Collection<T> collection;
+ if (!(iterable instanceof Collection)) {
+ collection = new LinkedList<>();
+ for (T next : iterable) {
+ collection.add(next);
+ }
+ } else {
+ collection = new ArrayList((Collection) iterable);
+ }
+ iterator = collection.iterator();
+ } else {
+ iterator = iterable.iterator();
+ }
+ }
+
+ protected final void refresh(Iterator<? extends T> iterator) {
+ Objects.requireNonNull(iterator);
+ this.iterator = iterator;
+ }
+
+ @Override
+ protected T supply() {
+ return iterator.next();
+ }
+
+ protected void remove() {
+ iterator.remove();
+ }
+
+ // One argument: The processed object
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, Consumer<T> processor) {
+ return create(iterable, false, processor);
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, Consumer<T> processor) {
+ return create(iterable, clone, object -> {
+ processor.accept(object);
+ return true;
+ });
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, Predicate<T> processor) {
+ return create(iterable, false, processor);
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, Predicate<T> processor) {
+ return new IteratorTask<T>(iterable, clone) {
+ @Override
+ protected boolean process(T object) {
+ return processor.test(object);
+ }
+ };
+ }
+
+ // Two arguments: the processed object, and a runnable to remove it from the iterator.
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, BiConsumer<T, Runnable> processor) {
+ return create(iterable, false, processor);
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, BiConsumer<T, Runnable> processor) {
+ return create(iterable, clone, (object, runnable) -> {
+ processor.accept(object, runnable);
+ return true;
+ });
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, BiPredicate<T, Runnable> processor) {
+ return create(iterable, false, processor);
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, BiPredicate<T, Runnable> processor) {
+ return new IteratorTask<T>(iterable, clone) {
+ @Override
+ protected boolean process(T object) {
+ return processor.test(object, this::remove);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java index 1298c4c..6204bee 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java +++ b/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java @@ -1,19 +1,19 @@ -package io.dico.dicore.task; - -import java.util.NoSuchElementException; - -public abstract class VoidTask extends BaseTask<Void> { - - @Override - protected final boolean process(Void object) { - return process(); - } - - @Override - protected final Void supply() throws NoSuchElementException { - return null; - } - - protected abstract boolean process(); - -} +package io.dico.dicore.task;
+
+import java.util.NoSuchElementException;
+
+public abstract class VoidTask extends BaseTask<Void> {
+
+ @Override
+ protected final boolean process(Void object) {
+ return process();
+ }
+
+ @Override
+ protected final Void supply() throws NoSuchElementException {
+ return null;
+ }
+
+ protected abstract boolean process();
+
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d2c45a4..4f19e53 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +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 +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
@@ -1,172 +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" "$@" +#!/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" "$@"
diff --git a/gradlew.bat b/gradlew.bat index f955316..e95643d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +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 +@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
diff --git a/javaStyle.xml b/javaStyle.xml index ec482f0..3a6a183 100644 --- a/javaStyle.xml +++ b/javaStyle.xml @@ -1,39 +1,39 @@ -<code_scheme name="Dico" version="173"> - <option name="RIGHT_MARGIN" value="150" /> - <option name="FORMATTER_TAGS_ENABLED" value="true" /> - <JavaCodeStyleSettings> - <option name="REPLACE_INSTANCEOF" value="true" /> - <option name="REPLACE_CAST" value="true" /> - <option name="ANNOTATION_PARAMETER_WRAP" value="1" /> - <option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" /> - </JavaCodeStyleSettings> - <XML> - <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" /> - </XML> - <codeStyleSettings language="JAVA"> - <option name="METHOD_ANNOTATION_WRAP" value="1" /> - <option name="FIELD_ANNOTATION_WRAP" value="1" /> - <option name="VARIABLE_ANNOTATION_WRAP" value="1" /> - <arrangement> - <groups> - <group> - <type>GETTERS_AND_SETTERS</type> - <order>KEEP</order> - </group> - <group> - <type>OVERRIDDEN_METHODS</type> - <order>KEEP</order> - </group> - <group> - <type>DEPENDENT_METHODS</type> - <order>DEPTH_FIRST</order> - </group> - </groups> - </arrangement> - </codeStyleSettings> - <codeStyleSettings language="Scala"> - <indentOptions> - <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> - </indentOptions> - </codeStyleSettings> +<code_scheme name="Dico" version="173">
+ <option name="RIGHT_MARGIN" value="150" />
+ <option name="FORMATTER_TAGS_ENABLED" value="true" />
+ <JavaCodeStyleSettings>
+ <option name="REPLACE_INSTANCEOF" value="true" />
+ <option name="REPLACE_CAST" value="true" />
+ <option name="ANNOTATION_PARAMETER_WRAP" value="1" />
+ <option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" />
+ </JavaCodeStyleSettings>
+ <XML>
+ <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+ </XML>
+ <codeStyleSettings language="JAVA">
+ <option name="METHOD_ANNOTATION_WRAP" value="1" />
+ <option name="FIELD_ANNOTATION_WRAP" value="1" />
+ <option name="VARIABLE_ANNOTATION_WRAP" value="1" />
+ <arrangement>
+ <groups>
+ <group>
+ <type>GETTERS_AND_SETTERS</type>
+ <order>KEEP</order>
+ </group>
+ <group>
+ <type>OVERRIDDEN_METHODS</type>
+ <order>KEEP</order>
+ </group>
+ <group>
+ <type>DEPENDENT_METHODS</type>
+ <order>DEPTH_FIRST</order>
+ </group>
+ </groups>
+ </arrangement>
+ </codeStyleSettings>
+ <codeStyleSettings language="Scala">
+ <indentOptions>
+ <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
+ </indentOptions>
+ </codeStyleSettings>
</code_scheme>
\ No newline at end of file diff --git a/permissions.md b/permissions.md new file mode 100644 index 0000000..e058fb3 --- /dev/null +++ b/permissions.md @@ -0,0 +1,71 @@ +# Permission Nodes + +Node|Description +---|--- +parcels.command.home | Access to `/p home` and aliases (example 1) +parcels.command.option.interact.gates | Access to `/p option interact gates` and aliases (example 2) +parcels.admin.bypass.ban | Ability to enter plots that listed a player as banned +parcels.admin.bypass.build | Ability to build anywhere +parcels.admin.bypass.gamemode | Be exempt from the world-specific gamemode enforcement +parcels.command.home.others | Ability to use `/p home` for plots other than your own +parcels.admin.manage | Admin rights. Required for staff commands and operations. + +## All Commands + +* parcel +* parcel goto +* parcel clear +* parcel goto_fake +* parcel claim +* parcel auto +* parcel tp +* parcel home +* parcel info +* parcel setbiome +* parcel ban +* parcel disallow +* parcel distrust +* parcel allow +* parcel unban +* parcel entrust +* parcel option +* parcel option interact +* parcel option interact buttons +* parcel option interact levers +* parcel option interact pressure_plates +* parcel option interact redstone +* parcel option interact containers +* parcel option interact gates +* parcel global +* parcel global ban +* parcel global disallow +* parcel global distrust +* parcel global allow +* parcel global unban +* parcel global entrust +* parcel global list +* parcel admin +* parcel admin swap +* parcel admin reset +* parcel admin setowner +* parcel admin dispose +* parcel admin update_all_owner_signs +* parcel admin global +* parcel admin global ban +* parcel admin global disallow +* parcel admin global distrust +* parcel admin global allow +* parcel admin global unban +* parcel admin global entrust +* parcel admin global list +* parcel debug +* parcel debug permissions +* parcel debug reloadoptions +* parcel debug privilege +* parcel debug complete_jobs +* parcel debug jobs +* parcel debug tpworld +* parcel debug make_mess +* parcel debug hasperm +* parcel debug message +* parcel debug directionality diff --git a/settings.gradle.kts b/settings.gradle.kts index 2977cab..ef05e0d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,6 @@ pluginManagement.repositories { - maven("http://dl.bintray.com/kotlin/kotlin-eap") + maven("https://dl.bintray.com/kotlin/kotlin-eap/") + maven("https://dl.bintray.com/kotlin/kotlin-dev/") mavenCentral() maven("https://plugins.gradle.org/m2/") } diff --git a/src/main/kotlin/io/dico/parcels2/Interactable.kt b/src/main/kotlin/io/dico/parcels2/Interactable.kt index c301340..f25b796 100644 --- a/src/main/kotlin/io/dico/parcels2/Interactable.kt +++ b/src/main/kotlin/io/dico/parcels2/Interactable.kt @@ -1,174 +1,174 @@ -package io.dico.parcels2 - -import io.dico.parcels2.util.math.ceilDiv -import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix -import org.bukkit.Material -import java.util.EnumMap - -class Interactables -private constructor( - val id: Int, - val name: String, - val interactableByDefault: Boolean, - vararg val materials: Material -) { - - companion object { - val classesById: List<Interactables> - val classesByName: Map<String, Interactables> - val listedMaterials: Map<Material, Int> - - init { - val array = getClassesArray() - classesById = array.asList() - classesByName = mapOf(*array.map { it.name to it }.toTypedArray()) - listedMaterials = EnumMap(mapOf(*array.flatMap { clazz -> clazz.materials.map { it to clazz.id } }.toTypedArray())) - } - - operator fun get(material: Material): Interactables? { - val id = listedMaterials[material] ?: return null - return classesById[id] - } - - operator fun get(name: String): Interactables { - return classesByName[name] ?: throw IllegalArgumentException("Interactables class does not exist: $name") - } - - operator fun get(id: Int): Interactables { - return classesById[id] - } - - private fun getClassesArray() = run { - var id = 0 - @Suppress("UNUSED_CHANGED_VALUE") - arrayOf( - Interactables( - id++, "buttons", true, - Material.STONE_BUTTON, - *getMaterialsWithWoodTypePrefix("BUTTON") - ), - - Interactables( - id++, "levers", true, - Material.LEVER - ), - - Interactables( - id++, "pressure_plates", true, - Material.STONE_PRESSURE_PLATE, - *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"), - Material.HEAVY_WEIGHTED_PRESSURE_PLATE, - Material.LIGHT_WEIGHTED_PRESSURE_PLATE - ), - - Interactables( - id++, "redstone", false, - Material.COMPARATOR, - Material.REPEATER - ), - - Interactables( - id++, "containers", false, - Material.CHEST, - Material.TRAPPED_CHEST, - Material.DISPENSER, - Material.DROPPER, - Material.HOPPER, - Material.FURNACE - ), - - Interactables( - id++, "gates", true, - *getMaterialsWithWoodTypePrefix("DOOR"), - *getMaterialsWithWoodTypePrefix("TRAPDOOR"), - *getMaterialsWithWoodTypePrefix("FENCE_GATE") - ) - ) - } - - } - -} - -val Parcel?.effectiveInteractableConfig: InteractableConfiguration - get() = this?.interactableConfig ?: pathInteractableConfig - -val pathInteractableConfig: InteractableConfiguration = run { - val data = BitmaskInteractableConfiguration().apply { - Interactables.classesById.forEach { - setInteractable(it, false) - } - } - object : InteractableConfiguration by data { - override fun setInteractable(clazz: Interactables, interactable: Boolean) = - throw IllegalStateException("pathInteractableConfig is immutable") - - override fun clear() = - throw IllegalStateException("pathInteractableConfig is immutable") - - override fun copyFrom(other: InteractableConfiguration) = - throw IllegalStateException("pathInteractableConfig is immutable") - } -} - -interface InteractableConfiguration { - val interactableClasses: List<Interactables> get() = Interactables.classesById.filter { isInteractable(it) } - - fun isInteractable(material: Material): Boolean - fun isInteractable(clazz: Interactables): Boolean - fun isDefault(): Boolean - - fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean - fun clear(): Boolean - fun copyFrom(other: InteractableConfiguration) = - Interactables.classesById.fold(false) { cur, elem -> setInteractable(elem, other.isInteractable(elem) || cur) } - - operator fun invoke(material: Material) = isInteractable(material) - operator fun invoke(className: String) = isInteractable(Interactables[className]) -} - -fun InteractableConfiguration.isInteractable(clazz: Interactables?) = clazz != null && isInteractable(clazz) - -class BitmaskInteractableConfiguration : InteractableConfiguration { - val bitmaskArray = IntArray(Interactables.classesById.size ceilDiv Int.SIZE_BITS) - - private fun isBitSet(classId: Int): Boolean { - val idx = classId.ushr(5) - return idx < bitmaskArray.size && bitmaskArray[idx].and(0x1.shl(classId.and(0x1F))) != 0 - } - - override fun isInteractable(material: Material): Boolean { - val classId = Interactables.listedMaterials[material] ?: return false - return isBitSet(classId) != Interactables.classesById[classId].interactableByDefault - } - - override fun isInteractable(clazz: Interactables): Boolean { - return isBitSet(clazz.id) != clazz.interactableByDefault - } - - override fun isDefault(): Boolean { - for (x in bitmaskArray) { - if (x != 0) return false - } - return true - } - - override fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean { - val idx = clazz.id.ushr(5) - if (idx >= bitmaskArray.size) return false - val bit = 0x1.shl(clazz.id.and(0x1F)) - val oldBitmask = bitmaskArray[idx] - bitmaskArray[idx] = if (interactable != clazz.interactableByDefault) oldBitmask.or(bit) else oldBitmask.and(bit.inv()) - return bitmaskArray[idx] != oldBitmask - } - - override fun clear(): Boolean { - var change = false - for (i in bitmaskArray.indices) { - if (!change && bitmaskArray[i] != 0) change = true - bitmaskArray[i] = 0 - } - return change - } - +package io.dico.parcels2
+
+import io.dico.parcels2.util.math.ceilDiv
+import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
+import org.bukkit.Material
+import java.util.EnumMap
+
+class Interactables
+private constructor(
+ val id: Int,
+ val name: String,
+ val interactableByDefault: Boolean,
+ vararg val materials: Material
+) {
+
+ companion object {
+ val classesById: List<Interactables>
+ val classesByName: Map<String, Interactables>
+ val listedMaterials: Map<Material, Int>
+
+ init {
+ val array = getClassesArray()
+ classesById = array.asList()
+ classesByName = mapOf(*array.map { it.name to it }.toTypedArray())
+ listedMaterials = EnumMap(mapOf(*array.flatMap { clazz -> clazz.materials.map { it to clazz.id } }.toTypedArray()))
+ }
+
+ operator fun get(material: Material): Interactables? {
+ val id = listedMaterials[material] ?: return null
+ return classesById[id]
+ }
+
+ operator fun get(name: String): Interactables {
+ return classesByName[name] ?: throw IllegalArgumentException("Interactables class does not exist: $name")
+ }
+
+ operator fun get(id: Int): Interactables {
+ return classesById[id]
+ }
+
+ private fun getClassesArray() = run {
+ var id = 0
+ @Suppress("UNUSED_CHANGED_VALUE")
+ arrayOf(
+ Interactables(
+ id++, "buttons", true,
+ Material.STONE_BUTTON,
+ *getMaterialsWithWoodTypePrefix("BUTTON")
+ ),
+
+ Interactables(
+ id++, "levers", true,
+ Material.LEVER
+ ),
+
+ Interactables(
+ id++, "pressure_plates", true,
+ Material.STONE_PRESSURE_PLATE,
+ *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
+ Material.HEAVY_WEIGHTED_PRESSURE_PLATE,
+ Material.LIGHT_WEIGHTED_PRESSURE_PLATE
+ ),
+
+ Interactables(
+ id++, "redstone", false,
+ Material.COMPARATOR,
+ Material.REPEATER
+ ),
+
+ Interactables(
+ id++, "containers", false,
+ Material.CHEST,
+ Material.TRAPPED_CHEST,
+ Material.DISPENSER,
+ Material.DROPPER,
+ Material.HOPPER,
+ Material.FURNACE
+ ),
+
+ Interactables(
+ id++, "gates", true,
+ *getMaterialsWithWoodTypePrefix("DOOR"),
+ *getMaterialsWithWoodTypePrefix("TRAPDOOR"),
+ *getMaterialsWithWoodTypePrefix("FENCE_GATE")
+ )
+ )
+ }
+
+ }
+
+}
+
+val Parcel?.effectiveInteractableConfig: InteractableConfiguration
+ get() = this?.interactableConfig ?: pathInteractableConfig
+
+val pathInteractableConfig: InteractableConfiguration = run {
+ val data = BitmaskInteractableConfiguration().apply {
+ Interactables.classesById.forEach {
+ setInteractable(it, false)
+ }
+ }
+ object : InteractableConfiguration by data {
+ override fun setInteractable(clazz: Interactables, interactable: Boolean) =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+
+ override fun clear() =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+
+ override fun copyFrom(other: InteractableConfiguration) =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+ }
+}
+
+interface InteractableConfiguration {
+ val interactableClasses: List<Interactables> get() = Interactables.classesById.filter { isInteractable(it) }
+
+ fun isInteractable(material: Material): Boolean
+ fun isInteractable(clazz: Interactables): Boolean
+ fun isDefault(): Boolean
+
+ fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean
+ fun clear(): Boolean
+ fun copyFrom(other: InteractableConfiguration) =
+ Interactables.classesById.fold(false) { cur, elem -> setInteractable(elem, other.isInteractable(elem) || cur) }
+
+ operator fun invoke(material: Material) = isInteractable(material)
+ operator fun invoke(className: String) = isInteractable(Interactables[className])
+}
+
+fun InteractableConfiguration.isInteractable(clazz: Interactables?) = clazz != null && isInteractable(clazz)
+
+class BitmaskInteractableConfiguration : InteractableConfiguration {
+ val bitmaskArray = IntArray(Interactables.classesById.size ceilDiv Int.SIZE_BITS)
+
+ private fun isBitSet(classId: Int): Boolean {
+ val idx = classId.ushr(5)
+ return idx < bitmaskArray.size && bitmaskArray[idx].and(0x1.shl(classId.and(0x1F))) != 0
+ }
+
+ override fun isInteractable(material: Material): Boolean {
+ val classId = Interactables.listedMaterials[material] ?: return false
+ return isBitSet(classId) != Interactables.classesById[classId].interactableByDefault
+ }
+
+ override fun isInteractable(clazz: Interactables): Boolean {
+ return isBitSet(clazz.id) != clazz.interactableByDefault
+ }
+
+ override fun isDefault(): Boolean {
+ for (x in bitmaskArray) {
+ if (x != 0) return false
+ }
+ return true
+ }
+
+ override fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean {
+ val idx = clazz.id.ushr(5)
+ if (idx >= bitmaskArray.size) return false
+ val bit = 0x1.shl(clazz.id.and(0x1F))
+ val oldBitmask = bitmaskArray[idx]
+ bitmaskArray[idx] = if (interactable != clazz.interactableByDefault) oldBitmask.or(bit) else oldBitmask.and(bit.inv())
+ return bitmaskArray[idx] != oldBitmask
+ }
+
+ override fun clear(): Boolean {
+ var change = false
+ for (i in bitmaskArray.indices) {
+ if (!change && bitmaskArray[i] != 0) change = true
+ bitmaskArray[i] = 0
+ }
+ return change
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt b/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt index 10da0da..ebbe334 100644 --- a/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt +++ b/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt @@ -1,6 +1,8 @@ package io.dico.parcels2 +import io.dico.parcels2.util.PluginAware import io.dico.parcels2.util.math.clampMin +import io.dico.parcels2.util.scheduleRepeating import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart.LAZY @@ -74,7 +76,12 @@ interface Job : JobAndScopeMembersUnion { * * if [asCompletionListener] is true, [onCompleted] is called with the same [block] */ - fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean = true, block: JobUpdateLister): Job + fun onProgressUpdate( + minDelay: Int, + minInterval: Int, + asCompletionListener: Boolean = true, + block: JobUpdateLister + ): Job /** * Calls the given [block] when this job completes, with the progress value 1.0. @@ -138,7 +145,11 @@ interface JobInternal : Job, JobScope { * There is a configurable maxiumum amount of milliseconds that can be allocated to all jobs together in each server tick * This object attempts to split that maximum amount of milliseconds equally between all jobs */ -class BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJobtimeOptions) : JobDispatcher { +class BukkitJobDispatcher( + private val plugin: PluginAware, + private val scope: CoroutineScope, + var options: TickJobtimeOptions +) : JobDispatcher { // The currently registered bukkit scheduler task private var bukkitTask: BukkitTask? = null // The jobs. @@ -146,18 +157,18 @@ class BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJo override val jobs: List<Job> = _jobs override fun dispatch(function: JobFunction): Job { - val job: JobInternal = JobImpl(plugin, function) + val job: JobInternal = JobImpl(scope, function) if (bukkitTask == null) { val completed = job.resume(options.jobTime.toLong()) if (completed) return job - bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickCoroutineJobs() } + bukkitTask = plugin.scheduleRepeating(options.tickInterval) { tickJobs() } } _jobs.addFirst(job) return job } - private fun tickCoroutineJobs() { + private fun tickJobs() { val jobs = _jobs if (jobs.isEmpty()) return val tickStartTime = System.currentTimeMillis() @@ -237,7 +248,12 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal { } } - override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: JobUpdateLister): Job { + override fun onProgressUpdate( + minDelay: Int, + minInterval: Int, + asCompletionListener: Boolean, + block: JobUpdateLister + ): Job { onProgressUpdate?.let { throw IllegalStateException() } if (asCompletionListener) onCompleted(block) if (isComplete) return this @@ -296,7 +312,11 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal { if (isStarted) { continuation?.let { continuation = null - it.resume(Unit) + + wrapExternalCall { + it.resume(Unit) + } + return continuation == null } return true @@ -304,34 +324,45 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal { isStarted = true startTimeOrElapsedTime = System.currentTimeMillis() - coroutine.start() + + wrapExternalCall { + coroutine.start() + } return continuation == null } + private inline fun wrapExternalCall(block: () -> Unit) { + try { + block() + } catch (ex: Throwable) { + logger.error("Job $coroutine generated an exception", ex) + } + } + override suspend fun awaitCompletion() { coroutine.join() } private fun delegateProgress(curPortion: Double, portion: Double): JobScope = - DelegateScope(progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0)) + DelegateScope(this, progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0)) override fun delegateProgress(portion: Double): JobScope = delegateProgress(1.0, portion) - private inner class DelegateScope(val progressStart: Double, val portion: Double) : JobScope { + private class DelegateScope(val parent: JobImpl, val progressStart: Double, val portion: Double) : JobScope { override val elapsedTime: Long - get() = this@JobImpl.elapsedTime + get() = parent.elapsedTime override suspend fun markSuspensionPoint() = - this@JobImpl.markSuspensionPoint() + parent.markSuspensionPoint() override val progress: Double - get() = (this@JobImpl.progress - progressStart) / portion + get() = (parent.progress - progressStart) / portion override fun setProgress(progress: Double) = - this@JobImpl.setProgress(progressStart + progress * portion) + parent.setProgress(progressStart + progress * portion) override fun delegateProgress(portion: Double): JobScope = - this@JobImpl.delegateProgress(this.portion, portion) + parent.delegateProgress(this.portion, portion) } } diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt index 4f89fe0..3527e15 100644 --- a/src/main/kotlin/io/dico/parcels2/Parcel.kt +++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt @@ -1,66 +1,66 @@ -package io.dico.parcels2 - -import io.dico.parcels2.util.math.Vec2i -import io.dico.parcels2.util.math.Vec3i -import org.bukkit.Location -import org.joda.time.DateTime -import java.util.UUID - -/** - * Parcel implementation of ParcelData will update the database when changes are made. - * To change the data without updating the database, defer to the data delegate instance. - * - * This should be used for example in database query callbacks. - * However, this implementation is intentionally not thread-safe. - * Therefore, database query callbacks should schedule their updates using the bukkit scheduler. - */ -interface Parcel : ParcelData, Privileges { - val id: ParcelId - val world: ParcelWorld - val pos: Vec2i - val x: Int - val z: Int - val data: ParcelDataHolder - val infoString: String - val hasBlockVisitors: Boolean - val globalPrivileges: GlobalPrivileges? - - override val keyOfOwner: PlayerProfile.Real? - get() = owner as? PlayerProfile.Real - - fun copyData(newData: ParcelDataHolder, callerIsDatabase: Boolean = false) - - fun dispose() = copyData(ParcelDataHolder()) - - fun updateOwnerSign(force: Boolean = false) - - val homeLocation: Location get() = world.blockManager.getHomeLocation(id) -} - - - -interface ParcelData : RawPrivileges { - var owner: PlayerProfile? - val lastClaimTime: DateTime? - var isOwnerSignOutdated: Boolean - var interactableConfig: InteractableConfiguration - - //fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean - - fun isOwner(uuid: UUID): Boolean { - return owner?.uuid == uuid - } - - fun isOwner(profile: PlayerProfile?): Boolean { - return owner == profile - } -} - -class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf()) - : ParcelData, PrivilegesHolder(addedMap) { - override var owner: PlayerProfile? = null - override var lastClaimTime: DateTime? = null - override var isOwnerSignOutdated = false - override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration() -} - +package io.dico.parcels2
+
+import io.dico.parcels2.util.math.Vec2i
+import io.dico.parcels2.util.math.Vec3i
+import org.bukkit.Location
+import org.joda.time.DateTime
+import java.util.UUID
+
+/**
+ * Parcel implementation of ParcelData will update the database when changes are made.
+ * To change the data without updating the database, defer to the data delegate instance.
+ *
+ * This should be used for example in database query callbacks.
+ * However, this implementation is intentionally not thread-safe.
+ * Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
+ */
+interface Parcel : ParcelData, Privileges {
+ val id: ParcelId
+ val world: ParcelWorld
+ val pos: Vec2i
+ val x: Int
+ val z: Int
+ val data: ParcelDataHolder
+ val infoString: String
+ val hasBlockVisitors: Boolean
+ val globalPrivileges: GlobalPrivileges?
+
+ override val keyOfOwner: PlayerProfile.Real?
+ get() = owner as? PlayerProfile.Real
+
+ fun copyData(newData: ParcelDataHolder, callerIsDatabase: Boolean = false)
+
+ fun dispose() = copyData(ParcelDataHolder())
+
+ fun updateOwnerSign(force: Boolean = false)
+
+ val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
+}
+
+
+
+interface ParcelData : RawPrivileges {
+ var owner: PlayerProfile?
+ val lastClaimTime: DateTime?
+ var isOwnerSignOutdated: Boolean
+ var interactableConfig: InteractableConfiguration
+
+ //fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
+
+ fun isOwner(uuid: UUID): Boolean {
+ return owner?.uuid == uuid
+ }
+
+ fun isOwner(profile: PlayerProfile?): Boolean {
+ return owner == profile
+ }
+}
+
+class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf())
+ : ParcelData, PrivilegesHolder(addedMap) {
+ override var owner: PlayerProfile? = null
+ override var lastClaimTime: DateTime? = null
+ override var isOwnerSignOutdated = false
+ override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration()
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt index 109c5dc..ada6d12 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt @@ -56,7 +56,7 @@ interface ParcelBlockManager { fun getRegion(parcel: ParcelId): Region - fun getEntities(parcel: ParcelId): Collection<Entity> + fun getEntities(region: Region): Collection<Entity> fun isParcelInfoSectionLoaded(parcel: ParcelId): Boolean @@ -92,8 +92,7 @@ inline fun ParcelBlockManager.tryDoBlockOperation( abstract class ParcelBlockManagerBase : ParcelBlockManager { - override fun getEntities(parcel: ParcelId): Collection<Entity> { - val region = getRegion(parcel) + override fun getEntities(region: Region): Collection<Entity> { val center = region.center val centerLoc = Location(world, center.x, center.y, center.z) val centerDist = (center - region.origin).add(0.2, 0.2, 0.2) diff --git a/src/main/kotlin/io/dico/parcels2/ParcelId.kt b/src/main/kotlin/io/dico/parcels2/ParcelId.kt index eef7129..77a1835 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelId.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelId.kt @@ -1,56 +1,56 @@ -@file:Suppress("FunctionName") - -package io.dico.parcels2 - -import io.dico.parcels2.util.math.Vec2i -import org.bukkit.Bukkit -import org.bukkit.World -import java.util.UUID - -/** - * Used by storage backing options to encompass the identity of a world - * Does NOT support equality operator. - */ -interface ParcelWorldId { - val name: String - val uid: UUID? - fun equals(id: ParcelWorldId): Boolean = name == id.name || (uid != null && uid == id.uid) - - val bukkitWorld: World? get() = Bukkit.getWorld(name) ?: uid?.let { Bukkit.getWorld(it) } -} - -fun ParcelWorldId.parcelWorldIdToString() = "ParcelWorld($name)" - -fun ParcelWorldId(worldName: String, worldUid: UUID? = null): ParcelWorldId = ParcelWorldIdImpl(worldName, worldUid) -fun ParcelWorldId(world: World) = ParcelWorldId(world.name, world.uid) - -/** - * Used by storage backing options to encompass the location of a parcel - * Does NOT support equality operator. - */ -interface ParcelId { - val worldId: ParcelWorldId - val x: Int - val z: Int - val pos: Vec2i get() = Vec2i(x, z) - val idString get() = "$x,$z" - fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId) -} - -fun ParcelId.parcelIdToString() = "Parcel(${worldId.name},$idString)" - -fun ParcelId(worldId: ParcelWorldId, pos: Vec2i) = ParcelId(worldId, pos.x, pos.z) -fun ParcelId(worldName: String, worldUid: UUID?, pos: Vec2i) = ParcelId(worldName, worldUid, pos.x, pos.z) -fun ParcelId(worldName: String, worldUid: UUID?, x: Int, z: Int) = ParcelId(ParcelWorldId(worldName, worldUid), x, z) -fun ParcelId(worldId: ParcelWorldId, x: Int, z: Int): ParcelId = ParcelIdImpl(worldId, x, z) - -private class ParcelWorldIdImpl(override val name: String, - override val uid: UUID?) : ParcelWorldId { - override fun toString() = parcelWorldIdToString() -} - -private class ParcelIdImpl(override val worldId: ParcelWorldId, - override val x: Int, - override val z: Int) : ParcelId { - override fun toString() = parcelIdToString() -} +@file:Suppress("FunctionName")
+
+package io.dico.parcels2
+
+import io.dico.parcels2.util.math.Vec2i
+import org.bukkit.Bukkit
+import org.bukkit.World
+import java.util.UUID
+
+/**
+ * Used by storage backing options to encompass the identity of a world
+ * Does NOT support equality operator.
+ */
+interface ParcelWorldId {
+ val name: String
+ val uid: UUID?
+ fun equals(id: ParcelWorldId): Boolean = name == id.name || (uid != null && uid == id.uid)
+
+ val bukkitWorld: World? get() = Bukkit.getWorld(name) ?: uid?.let { Bukkit.getWorld(it) }
+}
+
+fun ParcelWorldId.parcelWorldIdToString() = "ParcelWorld($name)"
+
+fun ParcelWorldId(worldName: String, worldUid: UUID? = null): ParcelWorldId = ParcelWorldIdImpl(worldName, worldUid)
+fun ParcelWorldId(world: World) = ParcelWorldId(world.name, world.uid)
+
+/**
+ * Used by storage backing options to encompass the location of a parcel
+ * Does NOT support equality operator.
+ */
+interface ParcelId {
+ val worldId: ParcelWorldId
+ val x: Int
+ val z: Int
+ val pos: Vec2i get() = Vec2i(x, z)
+ val idString get() = "$x,$z"
+ fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId)
+}
+
+fun ParcelId.parcelIdToString() = "Parcel(${worldId.name},$idString)"
+
+fun ParcelId(worldId: ParcelWorldId, pos: Vec2i) = ParcelId(worldId, pos.x, pos.z)
+fun ParcelId(worldName: String, worldUid: UUID?, pos: Vec2i) = ParcelId(worldName, worldUid, pos.x, pos.z)
+fun ParcelId(worldName: String, worldUid: UUID?, x: Int, z: Int) = ParcelId(ParcelWorldId(worldName, worldUid), x, z)
+fun ParcelId(worldId: ParcelWorldId, x: Int, z: Int): ParcelId = ParcelIdImpl(worldId, x, z)
+
+private class ParcelWorldIdImpl(override val name: String,
+ override val uid: UUID?) : ParcelWorldId {
+ override fun toString() = parcelWorldIdToString()
+}
+
+private class ParcelIdImpl(override val worldId: ParcelWorldId,
+ override val x: Int,
+ override val z: Int) : ParcelId {
+ override fun toString() = parcelIdToString()
+}
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt index c31b11a..054d9d6 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt @@ -83,7 +83,7 @@ interface ParcelContainer { fun getParcelById(id: ParcelId): Parcel? - fun nextEmptyParcel(): Parcel? + suspend fun nextEmptyParcel(): Parcel? } diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt index a6ebcd8..2ffef06 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt @@ -13,9 +13,10 @@ import io.dico.parcels2.options.Options import io.dico.parcels2.options.optionsMapper import io.dico.parcels2.storage.Storage import io.dico.parcels2.util.MainThreadDispatcher -import io.dico.parcels2.util.PluginScheduler +import io.dico.parcels2.util.PluginAware import io.dico.parcels2.util.ext.tryCreate import io.dico.parcels2.util.isServerThread +import io.dico.parcels2.util.scheduleRepeating import kotlinx.coroutines.CoroutineScope import org.bukkit.Bukkit import org.bukkit.generator.ChunkGenerator @@ -29,7 +30,7 @@ import kotlin.coroutines.CoroutineContext val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin") private inline val plogger get() = logger -class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler { +class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginAware { lateinit var optionsFile: File; private set lateinit var options: Options; private set lateinit var parcelProvider: ParcelProvider; private set @@ -43,7 +44,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler { override val coroutineContext: CoroutineContext = MainThreadDispatcher(this) override val plugin: Plugin get() = this - val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, options.tickJobtime) } + val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, this, options.tickJobtime) } override fun onEnable() { plogger.info("Is server thread: ${isServerThread()}") @@ -147,7 +148,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler { } } - scheduleRepeating(100, 5, entityTracker::tick) + scheduleRepeating(5, delay = 100, task = entityTracker::tick) } }
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt index e3e0f55..b73f7ba 100644 --- a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt +++ b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt @@ -3,11 +3,13 @@ package io.dico.parcels2 import io.dico.parcels2.storage.Storage +import io.dico.parcels2.util.checkPlayerNameValid import io.dico.parcels2.util.ext.PLAYER_NAME_PLACEHOLDER import io.dico.parcels2.util.ext.isValid import io.dico.parcels2.util.ext.uuid import io.dico.parcels2.util.getOfflinePlayer import io.dico.parcels2.util.getPlayerName +import io.dico.parcels2.util.isPlayerNameValid import org.bukkit.Bukkit import org.bukkit.OfflinePlayer import java.util.UUID @@ -32,7 +34,7 @@ interface PlayerProfile { companion object { fun safe(uuid: UUID?, name: String?): PlayerProfile? { - if (uuid != null) return Real(uuid, name) + if (uuid != null) return Real(uuid, if (name != null && !isPlayerNameValid(name)) null else name) if (name != null) return invoke(name) return null } @@ -47,7 +49,7 @@ interface PlayerProfile { } operator fun invoke(name: String): PlayerProfile { - if (name == Star.name) return Star + if (name equalsIgnoreCase Star.name) return Star return Fake(name) } @@ -60,12 +62,17 @@ interface PlayerProfile { return RealImpl(player.uuid, null) } - fun byName(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile { + fun byName(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile? { if (!allowReal) { if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true") return Fake(input) } + if (!isPlayerNameValid(input)) { + if (!allowFake) return null + return Fake(input) + } + if (input == Star.name) return Star return getOfflinePlayer(input)?.let { PlayerProfile(it) } ?: Unresolved(input) @@ -92,19 +99,19 @@ interface PlayerProfile { companion object { fun byName(name: String): PlayerProfile { - if (name == Star.name) return Star + if (name equalsIgnoreCase Star.name) return Star return Unresolved(name) } operator fun invoke(uuid: UUID, name: String?): Real { - if (name == Star.name || uuid == Star.uuid) return Star + if (name equalsIgnoreCase Star.name || uuid == Star.uuid) return Star return RealImpl(uuid, name) } fun safe(uuid: UUID?, name: String?): Real? { - if (name == Star.name || uuid == Star.uuid) return Star + if (name equalsIgnoreCase Star.name || uuid == Star.uuid) return Star if (uuid == null) return null - return RealImpl(uuid, name) + return RealImpl(uuid, if (name != null && !isPlayerNameValid(name)) null else name) } } @@ -130,7 +137,7 @@ interface PlayerProfile { override val nameOrBukkitName: String get() = name override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean { - return allowNameMatch && player.name == name + return allowNameMatch && player.name equalsIgnoreCase name } override fun toString() = "${javaClass.simpleName}($name)" @@ -138,13 +145,17 @@ interface PlayerProfile { class Fake(name: String) : NameOnly(name) { override fun equals(other: PlayerProfile): Boolean { - return other is Fake && other.name == name + return other is Fake && other.name equalsIgnoreCase name } } class Unresolved(name: String) : NameOnly(name) { + init { + checkPlayerNameValid(name) + } + override fun equals(other: PlayerProfile): Boolean { - return other is Unresolved && name == other.name + return other is Unresolved && name equalsIgnoreCase other.name } suspend fun tryResolveSuspendedly(storage: Storage): Real? { @@ -171,11 +182,24 @@ interface PlayerProfile { } private class RealImpl(override val uuid: UUID, override val name: String?) : BaseImpl(), Real { + init { + name?.let { checkPlayerNameValid(it) } + } + override fun toString() = "Real($notNullName)" } } +private infix fun String?.equalsIgnoreCase(other: String): Boolean { + if (this == null) return false + if (length != other.length) return false + repeat(length) { i -> + if (this[i].toLowerCase() != other[i].toLowerCase()) return false + } + return true +} + suspend fun PlayerProfile.resolved(storage: Storage, resolveToFake: Boolean = false): PlayerProfile? = when (this) { is PlayerProfile.Unresolved -> tryResolveSuspendedly(storage) diff --git a/src/main/kotlin/io/dico/parcels2/Privilege.kt b/src/main/kotlin/io/dico/parcels2/Privilege.kt index 135d7c1..892ed4c 100644 --- a/src/main/kotlin/io/dico/parcels2/Privilege.kt +++ b/src/main/kotlin/io/dico/parcels2/Privilege.kt @@ -1,125 +1,125 @@ -package io.dico.parcels2 - -import io.dico.parcels2.Privilege.DEFAULT -import java.util.Collections - -typealias PrivilegeKey = PlayerProfile.Real -typealias PrivilegeMap = Map<PrivilegeKey, Privilege> -typealias MutablePrivilegeMap = MutableMap<PrivilegeKey, Privilege> - -@Suppress("FunctionName") -fun MutablePrivilegeMap(): MutablePrivilegeMap = hashMapOf() - -@Suppress("UNCHECKED_CAST") -val EmptyPrivilegeMap = Collections.emptyMap<Any, Any>() as MutablePrivilegeMap - -enum class Privilege( - val number: Int, - val transient: Boolean = false -) { - BANNED(1), - DEFAULT(2), - CAN_BUILD(3), - CAN_MANAGE(4), - - OWNER(-1, transient = true), - ADMIN(-1, transient = true); - - fun implies(other: Privilege): Boolean = - when { - other > DEFAULT -> this >= other - other == DEFAULT -> this == other - else -> this <= other - } - - fun isChangeInDirection(positiveDirection: Boolean, update: Privilege): Boolean = - if (positiveDirection) update > this - else update < this - - fun requireNonTransient(): Privilege { - if (transient) { - throw IllegalArgumentException("Transient privilege $this is invalid") - } - return this - } - - companion object { - fun getByNumber(id: Int) = - when (id) { - 1 -> BANNED - 2 -> DEFAULT - 3 -> CAN_BUILD - 4 -> CAN_MANAGE - else -> null - } - } -} - -interface RawPrivileges { - val privilegeMap: PrivilegeMap - var privilegeOfStar: Privilege - - fun getRawStoredPrivilege(key: PrivilegeKey): Privilege - fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean - fun hasAnyDeclaredPrivileges(): Boolean -} - -open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = EmptyPrivilegeMap) : RawPrivileges { - private var _privilegeOfStar: Privilege = DEFAULT - - override /*open*/ var privilegeOfStar: Privilege - get() = _privilegeOfStar - set(value) = run { _privilegeOfStar = value } - - protected val isEmpty - inline get() = privilegeMap === EmptyPrivilegeMap - - override fun getRawStoredPrivilege(key: PrivilegeKey) = - if (key.isStar) _privilegeOfStar - else privilegeMap.getOrDefault(key, _privilegeOfStar) - - override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean { - privilege.requireNonTransient() - - if (key.isStar) { - if (_privilegeOfStar == privilege) return false - _privilegeOfStar = privilege - return true - } - - if (isEmpty) { - if (privilege == DEFAULT) return false - privilegeMap = MutablePrivilegeMap() - } - - return if (privilege == DEFAULT) privilegeMap.remove(key) != null - else privilegeMap.put(key, privilege) != privilege - } - - override fun hasAnyDeclaredPrivileges(): Boolean { - return privilegeMap.isNotEmpty() || privilegeOfStar != DEFAULT - } - - fun copyPrivilegesFrom(other: PrivilegesHolder) { - privilegeMap = other.privilegeMap - privilegeOfStar = other.privilegeOfStar - } - -} - -private fun <K, V> MutableMap<K, V>.put(key: K, value: V, override: Boolean) { - if (override) this[key] = value - else putIfAbsent(key, value) -} - -fun RawPrivileges.filterProfilesWithPrivilegeTo(map: MutableMap<PrivilegeKey, Privilege>, privilege: Privilege) { - if (privilegeOfStar.implies(privilege)) { - map.putIfAbsent(PlayerProfile.Star, privilegeOfStar) - } - - for ((profile, declaredPrivilege) in privilegeMap) { - if (declaredPrivilege.implies(privilege)) { - map.putIfAbsent(profile, declaredPrivilege) - } - } -} +package io.dico.parcels2
+
+import io.dico.parcels2.Privilege.DEFAULT
+import java.util.Collections
+
+typealias PrivilegeKey = PlayerProfile.Real
+typealias PrivilegeMap = Map<PrivilegeKey, Privilege>
+typealias MutablePrivilegeMap = MutableMap<PrivilegeKey, Privilege>
+
+@Suppress("FunctionName")
+fun MutablePrivilegeMap(): MutablePrivilegeMap = hashMapOf()
+
+@Suppress("UNCHECKED_CAST")
+val EmptyPrivilegeMap = Collections.emptyMap<Any, Any>() as MutablePrivilegeMap
+
+enum class Privilege(
+ val number: Int,
+ val transient: Boolean = false
+) {
+ BANNED(1),
+ DEFAULT(2),
+ CAN_BUILD(3),
+ CAN_MANAGE(4),
+
+ OWNER(-1, transient = true),
+ ADMIN(-1, transient = true);
+
+ fun implies(other: Privilege): Boolean =
+ when {
+ other > DEFAULT -> this >= other
+ other == DEFAULT -> this == other
+ else -> this <= other
+ }
+
+ fun isChangeInDirection(positiveDirection: Boolean, update: Privilege): Boolean =
+ if (positiveDirection) update > this
+ else update < this
+
+ fun requireNonTransient(): Privilege {
+ if (transient) {
+ throw IllegalArgumentException("Transient privilege $this is invalid")
+ }
+ return this
+ }
+
+ companion object {
+ fun getByNumber(id: Int) =
+ when (id) {
+ 1 -> BANNED
+ 2 -> DEFAULT
+ 3 -> CAN_BUILD
+ 4 -> CAN_MANAGE
+ else -> null
+ }
+ }
+}
+
+interface RawPrivileges {
+ val privilegeMap: PrivilegeMap
+ var privilegeOfStar: Privilege
+
+ fun getRawStoredPrivilege(key: PrivilegeKey): Privilege
+ fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean
+ fun hasAnyDeclaredPrivileges(): Boolean
+}
+
+open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = EmptyPrivilegeMap) : RawPrivileges {
+ private var _privilegeOfStar: Privilege = DEFAULT
+
+ override /*open*/ var privilegeOfStar: Privilege
+ get() = _privilegeOfStar
+ set(value) = run { _privilegeOfStar = value }
+
+ protected val isEmpty
+ inline get() = privilegeMap === EmptyPrivilegeMap
+
+ override fun getRawStoredPrivilege(key: PrivilegeKey) =
+ if (key.isStar) _privilegeOfStar
+ else privilegeMap.getOrDefault(key, _privilegeOfStar)
+
+ override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ privilege.requireNonTransient()
+
+ if (key.isStar) {
+ if (_privilegeOfStar == privilege) return false
+ _privilegeOfStar = privilege
+ return true
+ }
+
+ if (isEmpty) {
+ if (privilege == DEFAULT) return false
+ privilegeMap = MutablePrivilegeMap()
+ }
+
+ return if (privilege == DEFAULT) privilegeMap.remove(key) != null
+ else privilegeMap.put(key, privilege) != privilege
+ }
+
+ override fun hasAnyDeclaredPrivileges(): Boolean {
+ return privilegeMap.isNotEmpty() || privilegeOfStar != DEFAULT
+ }
+
+ fun copyPrivilegesFrom(other: PrivilegesHolder) {
+ privilegeMap = other.privilegeMap
+ privilegeOfStar = other.privilegeOfStar
+ }
+
+}
+
+private fun <K, V> MutableMap<K, V>.put(key: K, value: V, override: Boolean) {
+ if (override) this[key] = value
+ else putIfAbsent(key, value)
+}
+
+fun RawPrivileges.filterProfilesWithPrivilegeTo(map: MutableMap<PrivilegeKey, Privilege>, privilege: Privilege) {
+ if (privilegeOfStar.implies(privilege)) {
+ map.putIfAbsent(PlayerProfile.Star, privilegeOfStar)
+ }
+
+ for ((profile, declaredPrivilege) in privilegeMap) {
+ if (declaredPrivilege.implies(privilege)) {
+ map.putIfAbsent(profile, declaredPrivilege)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/Privileges.kt b/src/main/kotlin/io/dico/parcels2/Privileges.kt index 632f1a4..6cdd6d0 100644 --- a/src/main/kotlin/io/dico/parcels2/Privileges.kt +++ b/src/main/kotlin/io/dico/parcels2/Privileges.kt @@ -1,64 +1,64 @@ -package io.dico.parcels2 - -import io.dico.parcels2.PrivilegeChangeResult.* -import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE -import io.dico.parcels2.util.ext.PERM_BAN_BYPASS -import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE -import org.bukkit.OfflinePlayer -import org.bukkit.entity.Player - -interface Privileges : RawPrivileges { - val keyOfOwner: PlayerProfile.Real? - - fun getStoredPrivilege(key: PrivilegeKey): Privilege { - return if (key == keyOfOwner) Privilege.OWNER - else getRawStoredPrivilege(key) - } - - override var privilegeOfStar: Privilege - get() = getStoredPrivilege(PlayerProfile.Star) - set(value) { - setRawStoredPrivilege(PlayerProfile.Star, value) - } -} - -val OfflinePlayer.privilegeKey: PrivilegeKey - inline get() = PlayerProfile.nameless(this) - -fun Privileges.getEffectivePrivilege(player: OfflinePlayer, adminPerm: String): Privilege = - if (player is Player && player.hasPermission(adminPerm)) Privilege.ADMIN - else getStoredPrivilege(player.privilegeKey) - -fun Privileges.canManage(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_ADMIN_MANAGE) >= Privilege.CAN_MANAGE -fun Privileges.canManageFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_MANAGE -fun Privileges.canBuild(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BUILD_ANYWHERE) >= Privilege.CAN_BUILD -fun Privileges.canBuildFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_BUILD -fun Privileges.canEnter(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BAN_BYPASS) >= Privilege.DEFAULT -fun Privileges.canEnterFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.DEFAULT - -enum class PrivilegeChangeResult { - SUCCESS, FAIL, FAIL_OWNER -} - -fun Privileges.changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult = - when { - key == keyOfOwner -> FAIL_OWNER - getRawStoredPrivilege(key).isChangeInDirection(positive, update) && setRawStoredPrivilege(key, update) -> SUCCESS - else -> FAIL - } - -fun Privileges.allowManage(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_MANAGE) -fun Privileges.disallowManage(key: PrivilegeKey) = changePrivilege(key, false, Privilege.CAN_BUILD) -fun Privileges.allowBuild(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_BUILD) -fun Privileges.disallowBuild(key: PrivilegeKey) = changePrivilege(key, false, Privilege.DEFAULT) -fun Privileges.allowEnter(key: PrivilegeKey) = changePrivilege(key, true, Privilege.DEFAULT) -fun Privileges.disallowEnter(key: PrivilegeKey) = changePrivilege(key, false, Privilege.BANNED) - -interface GlobalPrivileges : RawPrivileges, Privileges { - override val keyOfOwner: PlayerProfile.Real -} - -interface GlobalPrivilegesManager { - operator fun get(owner: PlayerProfile.Real): GlobalPrivileges - operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey) -} +package io.dico.parcels2
+
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
+import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
+import org.bukkit.OfflinePlayer
+import org.bukkit.entity.Player
+
+interface Privileges : RawPrivileges {
+ val keyOfOwner: PlayerProfile.Real?
+
+ fun getStoredPrivilege(key: PrivilegeKey): Privilege {
+ return if (key == keyOfOwner) Privilege.OWNER
+ else getRawStoredPrivilege(key)
+ }
+
+ override var privilegeOfStar: Privilege
+ get() = getStoredPrivilege(PlayerProfile.Star)
+ set(value) {
+ setRawStoredPrivilege(PlayerProfile.Star, value)
+ }
+}
+
+val OfflinePlayer.privilegeKey: PrivilegeKey
+ inline get() = PlayerProfile.nameless(this)
+
+fun Privileges.getEffectivePrivilege(player: OfflinePlayer, adminPerm: String): Privilege =
+ if (player is Player && player.hasPermission(adminPerm)) Privilege.ADMIN
+ else getStoredPrivilege(player.privilegeKey)
+
+fun Privileges.canManage(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_ADMIN_MANAGE) >= Privilege.CAN_MANAGE
+fun Privileges.canManageFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_MANAGE
+fun Privileges.canBuild(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BUILD_ANYWHERE) >= Privilege.CAN_BUILD
+fun Privileges.canBuildFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_BUILD
+fun Privileges.canEnter(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BAN_BYPASS) >= Privilege.DEFAULT
+fun Privileges.canEnterFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.DEFAULT
+
+enum class PrivilegeChangeResult {
+ SUCCESS, FAIL, FAIL_OWNER
+}
+
+fun Privileges.changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult =
+ when {
+ key == keyOfOwner -> FAIL_OWNER
+ getRawStoredPrivilege(key).isChangeInDirection(positive, update) && setRawStoredPrivilege(key, update) -> SUCCESS
+ else -> FAIL
+ }
+
+fun Privileges.allowManage(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_MANAGE)
+fun Privileges.disallowManage(key: PrivilegeKey) = changePrivilege(key, false, Privilege.CAN_BUILD)
+fun Privileges.allowBuild(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_BUILD)
+fun Privileges.disallowBuild(key: PrivilegeKey) = changePrivilege(key, false, Privilege.DEFAULT)
+fun Privileges.allowEnter(key: PrivilegeKey) = changePrivilege(key, true, Privilege.DEFAULT)
+fun Privileges.disallowEnter(key: PrivilegeKey) = changePrivilege(key, false, Privilege.BANNED)
+
+interface GlobalPrivileges : RawPrivileges, Privileges {
+ override val keyOfOwner: PlayerProfile.Real
+}
+
+interface GlobalPrivilegesManager {
+ operator fun get(owner: PlayerProfile.Real): GlobalPrivileges
+ operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt index 8f2c565..83567c5 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt @@ -1,61 +1,61 @@ -package io.dico.parcels2.blockvisitor - -import io.dico.parcels2.util.math.Vec3i -import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix -import io.dico.parcels2.util.ext.getMaterialsWithWoolColorPrefix -import org.bukkit.Material -import org.bukkit.Material.* -import org.bukkit.block.BlockFace -import org.bukkit.block.data.BlockData -import org.bukkit.block.data.Directional -import java.util.EnumSet - -private val attachables = EnumSet.of( - REPEATER, COMPARATOR, - *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"), - STONE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE, - *getMaterialsWithWoodTypePrefix("BUTTON"), - STONE_BUTTON, LEVER, - *getMaterialsWithWoodTypePrefix("DOOR"), IRON_DOOR, - ACTIVATOR_RAIL, POWERED_RAIL, DETECTOR_RAIL, RAIL, - PISTON, STICKY_PISTON, - REDSTONE_TORCH, REDSTONE_WALL_TORCH, REDSTONE_WIRE, - TRIPWIRE, TRIPWIRE_HOOK, - - BROWN_MUSHROOM, RED_MUSHROOM, CACTUS, CARROT, COCOA, - WHEAT, DEAD_BUSH, CHORUS_FLOWER, DANDELION, SUGAR_CANE, - TALL_GRASS, TALL_SEAGRASS, NETHER_WART, MELON_STEM, - PUMPKIN_STEM, SUNFLOWER, POTATO, LILY_PAD, VINE, - *getMaterialsWithWoodTypePrefix("SAPLING"), - - SAND, RED_SAND, DRAGON_EGG, ANVIL, - *getMaterialsWithWoolColorPrefix("CONCRETE_POWDER"), - - *getMaterialsWithWoolColorPrefix("CARPET"), - CAKE, FIRE, - FLOWER_POT, - LADDER, - // NETHER_PORTAL, fuck nether portals - FLOWER_POT, - SNOW, - TORCH, WALL_TORCH, - *getMaterialsWithWoolColorPrefix("BANNER"), - *getMaterialsWithWoolColorPrefix("WALL_BANNER"), - SIGN, WALL_SIGN -) - -fun isAttachable(type: Material) = attachables.contains(type) - -fun getSupportingBlock(data: BlockData): Vec3i = when (data) { - //is MultipleFacing -> // fuck it xD this is good enough - - is Directional -> Vec3i.convert(when (data.material) { - // exceptions - COCOA -> data.facing - OAK_DOOR, BIRCH_DOOR, SPRUCE_DOOR, JUNGLE_DOOR, ACACIA_DOOR, DARK_OAK_DOOR, IRON_DOOR -> BlockFace.DOWN - - else -> data.facing.oppositeFace - }) - - else -> Vec3i.down -} +package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.util.math.Vec3i
+import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
+import io.dico.parcels2.util.ext.getMaterialsWithWoolColorPrefix
+import org.bukkit.Material
+import org.bukkit.Material.*
+import org.bukkit.block.BlockFace
+import org.bukkit.block.data.BlockData
+import org.bukkit.block.data.Directional
+import java.util.EnumSet
+
+private val attachables = EnumSet.of(
+ REPEATER, COMPARATOR,
+ *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
+ STONE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE,
+ *getMaterialsWithWoodTypePrefix("BUTTON"),
+ STONE_BUTTON, LEVER,
+ *getMaterialsWithWoodTypePrefix("DOOR"), IRON_DOOR,
+ ACTIVATOR_RAIL, POWERED_RAIL, DETECTOR_RAIL, RAIL,
+ PISTON, STICKY_PISTON,
+ REDSTONE_TORCH, REDSTONE_WALL_TORCH, REDSTONE_WIRE,
+ TRIPWIRE, TRIPWIRE_HOOK,
+
+ BROWN_MUSHROOM, RED_MUSHROOM, CACTUS, CARROT, COCOA,
+ WHEAT, DEAD_BUSH, CHORUS_FLOWER, DANDELION, SUGAR_CANE,
+ TALL_GRASS, TALL_SEAGRASS, NETHER_WART, MELON_STEM,
+ PUMPKIN_STEM, SUNFLOWER, POTATO, LILY_PAD, VINE,
+ *getMaterialsWithWoodTypePrefix("SAPLING"),
+
+ SAND, RED_SAND, DRAGON_EGG, ANVIL,
+ *getMaterialsWithWoolColorPrefix("CONCRETE_POWDER"),
+
+ *getMaterialsWithWoolColorPrefix("CARPET"),
+ CAKE, FIRE,
+ FLOWER_POT,
+ LADDER,
+ // NETHER_PORTAL, fuck nether portals
+ FLOWER_POT,
+ SNOW,
+ TORCH, WALL_TORCH,
+ *getMaterialsWithWoolColorPrefix("BANNER"),
+ *getMaterialsWithWoolColorPrefix("WALL_BANNER"),
+ SIGN, WALL_SIGN
+)
+
+fun isAttachable(type: Material) = attachables.contains(type)
+
+fun getSupportingBlock(data: BlockData): Vec3i = when (data) {
+ //is MultipleFacing -> // fuck it xD this is good enough
+
+ is Directional -> Vec3i.convert(when (data.material) {
+ // exceptions
+ COCOA -> data.facing
+ OAK_DOOR, BIRCH_DOOR, SPRUCE_DOOR, JUNGLE_DOOR, ACACIA_DOOR, DARK_OAK_DOOR, IRON_DOOR -> BlockFace.DOWN
+
+ else -> data.facing.oppositeFace
+ })
+
+ else -> Vec3i.down
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Entities.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Entities.kt new file mode 100644 index 0000000..d9ea09f --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Entities.kt @@ -0,0 +1,38 @@ +package io.dico.parcels2.blockvisitor + +import io.dico.parcels2.util.math.Vec3d +import org.bukkit.Location +import org.bukkit.World +import org.bukkit.entity.Entity +import org.bukkit.entity.Minecart + +/* +open class EntityCopy<T : Entity>(entity: T) { + val type = entity.type + + @Suppress("UNCHECKED_CAST") + fun spawn(world: World, position: Vec3d): T { + val entity = world.spawnEntity(Location(null, position.x, position.y, position.z), type) as T + setAttributes(entity) + return entity + } + + open fun setAttributes(entity: T) {} +} + +open class MinecartCopy<T : Minecart>(entity: T) : EntityCopy<T>(entity) { + val damage = entity.damage + val maxSpeed = entity.maxSpeed + val isSlowWhenEmpty = entity.isSlowWhenEmpty + val flyingVelocityMod = entity.flyingVelocityMod + val derailedVelocityMod = entity.derailedVelocityMod + val displayBlockData = entity.displayBlockData + val displayBlockOffset = entity.displayBlockOffset + + override fun setAttributes(entity: T) { + super.setAttributes(entity) + entity.damage = damage + entity.displayBlockData = displayBlockData + entity.displayBlockOffset = displayBlockOffset + } +}*/
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt index 3f7e070..ddfec27 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt @@ -1,38 +1,38 @@ -package io.dico.parcels2.blockvisitor - -import org.bukkit.block.Block -import org.bukkit.block.BlockState -import org.bukkit.block.Sign -import kotlin.reflect.KClass - -interface ExtraBlockChange { - fun update(block: Block) -} - -abstract class BlockStateChange<T : BlockState> : ExtraBlockChange { - abstract val stateClass: KClass<T> - - abstract fun update(state: T) - - override fun update(block: Block) { - val state = block.state - if (stateClass.isInstance(state)) { - @Suppress("UNCHECKED_CAST") - update(state as T) - } - } -} - -class SignStateChange(state: Sign) : BlockStateChange<Sign>() { - val lines = state.lines - - override val stateClass: KClass<Sign> - get() = Sign::class - - override fun update(state: Sign) { - for (i in lines.indices) { - val line = lines[i] - state.setLine(i, line) - } - } -} +package io.dico.parcels2.blockvisitor
+
+import org.bukkit.block.Block
+import org.bukkit.block.BlockState
+import org.bukkit.block.Sign
+import kotlin.reflect.KClass
+
+interface ExtraBlockChange {
+ fun update(block: Block)
+}
+
+abstract class BlockStateChange<T : BlockState> : ExtraBlockChange {
+ abstract val stateClass: KClass<T>
+
+ abstract fun update(state: T)
+
+ override fun update(block: Block) {
+ val state = block.state
+ if (stateClass.isInstance(state)) {
+ @Suppress("UNCHECKED_CAST")
+ update(state as T)
+ }
+ }
+}
+
+class SignStateChange(state: Sign) : BlockStateChange<Sign>() {
+ val lines = state.lines
+
+ override val stateClass: KClass<Sign>
+ get() = Sign::class
+
+ override fun update(state: Sign) {
+ for (i in lines.indices) {
+ val line = lines[i]
+ state.setLine(i, line)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt index b749b36..6525655 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt @@ -1,323 +1,323 @@ -package io.dico.parcels2.blockvisitor - -import io.dico.parcels2.util.math.Dimension -import io.dico.parcels2.util.math.Region -import io.dico.parcels2.util.math.Vec3i -import io.dico.parcels2.util.math.clampMax - -private typealias Scope = SequenceScope<Vec3i> -/* -class ParcelTraverser( - val parcelProvider: ParcelProvider, - val delegate: RegionTraverser, - scope: CoroutineScope -) : RegionTraverser(), CoroutineScope by scope { - - class OccupiedException(parcelId: ParcelId) : Exception("Parcel $parcelId is occupied") - - /** - * Traverse the blocks of parcel's land - * The iterator must be exhausted, else the permit to traverse it will not be reclaimed. - * - * @throws OccupiedException if a parcel is maintained with the given parcel id and an - * iterator exists for it that has not been exhausted - */ - fun traverseParcel(parcelId: ParcelId): Iterator<Vec3i> { - val world = parcelProvider.getWorldById(parcelId.worldId) - ?: throw IllegalArgumentException() - val parcel = parcelProvider.getParcelById(parcelId) - - val medium = if (parcel != null) { - if (parcel.hasBlockVisitors || parcel !is ParcelImpl) { - throw OccupiedException(parcelId) - } - parcel.hasBlockVisitors = true - TraverserMedium { parcel.hasBlockVisitors = false } - } else { - TraverserMedium.DoNothing - } - - val region = world.blockManager.getRegion(parcelId) - return traverseRegion(region, world.world.maxHeight, medium) - } - - override suspend fun Scope.build(region: Region, medium: TraverserMedium) { - with(delegate) { - return build(region, medium) - } - } - -} - -@Suppress("FunctionName") -inline fun TraverserMedium(crossinline whenComplete: () -> Unit) = - object : TraverserMedium { - override fun iterationCompleted() { - whenComplete() - } - } - -/** - * An object that is able to communicate with an iterator returned by [RegionTraverser] - * - */ -interface TraverserMedium { - - /** - * Called by the traverser during first [Iterator.hasNext] call that returns false - */ - fun iterationCompleted() - - /** - * The default [TraverserMedium], which does nothing. - */ - object DoNothing : TraverserMedium { - override fun iterationCompleted() {} - } -}*/ - -sealed class RegionTraverser { - - /** - * Get an iterator traversing [region] using this traverser. - * Depending on the implementation, [region] might be traversed in a specific order and direction. - */ - fun traverseRegion( - region: Region, - worldHeight: Int = 256/*, - medium: TraverserMedium = TraverserMedium.DoNothing*/ - ): Iterator<Vec3i> = iterator { build(validify(region, worldHeight)/*, medium*/) } - - abstract suspend fun Scope.build(region: Region/*, medium: TraverserMedium = TraverserMedium.DoNothing*/) - - companion object { - val upward = Directional(TraverseDirection(1, 1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X)) - val downward = Directional(TraverseDirection(1, -1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X)) - val toClear get() = downward - val toFill get() = upward - - /** - * The returned [RegionTraverser] will traverse the regions - * * below and including absolute level [y] first, in [upward] direction. - * * above absolute level [y] last, in [downward] direction. - */ - fun convergingTo(y: Int) = Slicing(y, upward, downward, true) - - /** - * The returned [RegionTraverser] will traverse the regions - * * above absolute level [y] first, in [upward] direction. - * * below and including absolute level [y] second, in [downward] direction. - */ - fun separatingFrom(y: Int) = Slicing(y, downward, upward, false) - - private fun validify(region: Region, worldHeight: Int): Region { - if (region.origin.y < 0) { - val origin = region.origin withY 0 - val size = region.size.withY((region.size.y + region.origin.y).clampMax(worldHeight)) - return Region(origin, size) - } - - if (region.origin.y + region.size.y > worldHeight) { - val size = region.size.withY(worldHeight - region.origin.y) - return Region(region.origin, size) - } - - return region - } - - } - - class Directional( - val direction: TraverseDirection, - val order: TraverseOrder - ) : RegionTraverser() { - - private inline fun iterate(max: Int, increasing: Boolean, action: (Int) -> Unit) { - for (i in 0..max) { - action(if (increasing) i else max - i) - } - } - - override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) { - val order = order - val (primary, secondary, tertiary) = order.toArray() - val (origin, size) = region - - val maxOfPrimary = size[primary] - 1 - val maxOfSecondary = size[secondary] - 1 - val maxOfTertiary = size[tertiary] - 1 - - val isPrimaryIncreasing = direction.isIncreasing(primary) - val isSecondaryIncreasing = direction.isIncreasing(secondary) - val isTertiaryIncreasing = direction.isIncreasing(tertiary) - - iterate(maxOfPrimary, isPrimaryIncreasing) { p -> - iterate(maxOfSecondary, isSecondaryIncreasing) { s -> - iterate(maxOfTertiary, isTertiaryIncreasing) { t -> - yield(order.add(origin, p, s, t)) - } - } - } - - /*medium.iterationCompleted()*/ - } - - } - - class Slicing( - val bottomSectionMaxY: Int, - val bottomTraverser: RegionTraverser, - val topTraverser: RegionTraverser, - val bottomFirst: Boolean = true - ) : RegionTraverser() { - - private fun slice(region: Region, atY: Int): Pair<Region, Region?> { - if (atY < region.size.y + 1) { - val bottom = Region(region.origin, region.size.withY(atY + 1)) - val top = Region(region.origin.withY(atY + 1), region.size.addY(-atY - 1)) - return bottom to top - } - return region to null - } - - override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) { - val (bottom, top) = slice(region, bottomSectionMaxY) - - if (bottomFirst) { - with(bottomTraverser) { build(bottom) } - top?.let { with(topTraverser) { build(it) } } - } else { - top?.let { with(topTraverser) { build(it) } } - with(bottomTraverser) { build(bottom) } - } - - /*medium.iterationCompleted()*/ - } - } - - /** - * Returns [Directional] instance that would be responsible for - * emitting the given position if it is contained in a region. - * [Directional] instance has a set order and direction - */ - fun childForPosition(position: Vec3i): Directional { - var cur = this - while (true) { - when (cur) { - /*is ParcelTraverser -> cur = cur.delegate*/ - is Directional -> return cur - is Slicing -> - cur = - if (position.y <= cur.bottomSectionMaxY) cur.bottomTraverser - else cur.topTraverser - } - } - } - - /** - * Returns true if and only if this traverser would visit the given - * [block] position before the given [current] position. - * If at least one of [block] and [current] is not contained in a - * region being traversed the result is undefined. - */ - fun comesFirst(current: Vec3i, block: Vec3i): Boolean { - var cur = this - while (true) { - when (cur) { - /*is ParcelTraverser -> cur = cur.delegate*/ - is Directional -> return cur.direction.comesFirst(current, block) - is Slicing -> { - val border = cur.bottomSectionMaxY - cur = when { - current.y <= border && block.y <= border -> cur.bottomTraverser - current.y <= border -> return !cur.bottomFirst - block.y <= border -> return cur.bottomFirst - else -> cur.topTraverser - } - } - } - } - } - -} - -object TraverseOrderFactory { - private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3 - - fun createWith(primary: Dimension, secondary: Dimension): TraverseOrder { - // tertiary is implicit - if (primary == secondary) throw IllegalArgumentException() - return TraverseOrder(primary, isSwap(primary, secondary)) - } -} - -inline class TraverseOrder(val orderNum: Int) { - constructor(first: Dimension, swap: Boolean) - : this(if (swap) first.ordinal + 3 else first.ordinal) - - @Suppress("NOTHING_TO_INLINE") - private inline fun element(index: Int) = Dimension[(orderNum + index) % 3] - - private val swap inline get() = orderNum >= 3 - - /** - * The slowest changing dimension - */ - val primary: Dimension get() = element(0) - - /** - * Second slowest changing dimension - */ - val secondary: Dimension get() = element(if (swap) 2 else 1) - - /** - * Dimension that changes every block - */ - val tertiary: Dimension get() = element(if (swap) 1 else 2) - - /** - * All 3 dimensions in this order - */ - fun toArray() = arrayOf(primary, secondary, tertiary) - - fun add(vec: Vec3i, p: Int, s: Int, t: Int): Vec3i = - // optimize this, will be called lots - when (orderNum) { - 0 -> vec.add(p, s, t) // xyz - 1 -> vec.add(t, p, s) // yzx - 2 -> vec.add(s, t, p) // zxy - 3 -> vec.add(p, t, s) // xzy - 4 -> vec.add(s, p, t) // yxz - 5 -> vec.add(t, s, p) // zyx - else -> error("Invalid orderNum $orderNum") - } -} - -inline class TraverseDirection(val bits: Int) { - fun isIncreasing(dimension: Dimension) = (1 shl dimension.ordinal) and bits != 0 - - fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean = - if (isIncreasing(dimension)) - block[dimension] <= current[dimension] - else - block[dimension] >= current[dimension] - - fun comesFirst(current: Vec3i, block: Vec3i) = - comesFirst(current, block, Dimension.X) - && comesFirst(current, block, Dimension.Y) - && comesFirst(current, block, Dimension.Z) - - companion object { - operator fun invoke(x: Int, y: Int, z: Int) = invoke(Vec3i(x, y, z)) - - operator fun invoke(block: Vec3i): TraverseDirection { - if (block.x == 0 || block.y == 0 || block.z == 0) throw IllegalArgumentException() - var bits = 0 - if (block.x > 0) bits = bits or 1 - if (block.y > 0) bits = bits or 2 - if (block.z > 0) bits = bits or 4 - return TraverseDirection(bits) - } - } - -} +package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.util.math.Dimension
+import io.dico.parcels2.util.math.Region
+import io.dico.parcels2.util.math.Vec3i
+import io.dico.parcels2.util.math.clampMax
+
+private typealias Scope = SequenceScope<Vec3i>
+/*
+class ParcelTraverser(
+ val parcelProvider: ParcelProvider,
+ val delegate: RegionTraverser,
+ scope: CoroutineScope
+) : RegionTraverser(), CoroutineScope by scope {
+
+ class OccupiedException(parcelId: ParcelId) : Exception("Parcel $parcelId is occupied")
+
+ /**
+ * Traverse the blocks of parcel's land
+ * The iterator must be exhausted, else the permit to traverse it will not be reclaimed.
+ *
+ * @throws OccupiedException if a parcel is maintained with the given parcel id and an
+ * iterator exists for it that has not been exhausted
+ */
+ fun traverseParcel(parcelId: ParcelId): Iterator<Vec3i> {
+ val world = parcelProvider.getWorldById(parcelId.worldId)
+ ?: throw IllegalArgumentException()
+ val parcel = parcelProvider.getParcelById(parcelId)
+
+ val medium = if (parcel != null) {
+ if (parcel.hasBlockVisitors || parcel !is ParcelImpl) {
+ throw OccupiedException(parcelId)
+ }
+ parcel.hasBlockVisitors = true
+ TraverserMedium { parcel.hasBlockVisitors = false }
+ } else {
+ TraverserMedium.DoNothing
+ }
+
+ val region = world.blockManager.getRegion(parcelId)
+ return traverseRegion(region, world.world.maxHeight, medium)
+ }
+
+ override suspend fun Scope.build(region: Region, medium: TraverserMedium) {
+ with(delegate) {
+ return build(region, medium)
+ }
+ }
+
+}
+
+@Suppress("FunctionName")
+inline fun TraverserMedium(crossinline whenComplete: () -> Unit) =
+ object : TraverserMedium {
+ override fun iterationCompleted() {
+ whenComplete()
+ }
+ }
+
+/**
+ * An object that is able to communicate with an iterator returned by [RegionTraverser]
+ *
+ */
+interface TraverserMedium {
+
+ /**
+ * Called by the traverser during first [Iterator.hasNext] call that returns false
+ */
+ fun iterationCompleted()
+
+ /**
+ * The default [TraverserMedium], which does nothing.
+ */
+ object DoNothing : TraverserMedium {
+ override fun iterationCompleted() {}
+ }
+}*/
+
+sealed class RegionTraverser {
+
+ /**
+ * Get an iterator traversing [region] using this traverser.
+ * Depending on the implementation, [region] might be traversed in a specific order and direction.
+ */
+ fun traverseRegion(
+ region: Region,
+ worldHeight: Int = 256/*,
+ medium: TraverserMedium = TraverserMedium.DoNothing*/
+ ): Iterator<Vec3i> = iterator { build(validify(region, worldHeight)/*, medium*/) }
+
+ abstract suspend fun Scope.build(region: Region/*, medium: TraverserMedium = TraverserMedium.DoNothing*/)
+
+ companion object {
+ val upward = Directional(TraverseDirection(1, 1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
+ val downward = Directional(TraverseDirection(1, -1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
+ val toClear get() = downward
+ val toFill get() = upward
+
+ /**
+ * The returned [RegionTraverser] will traverse the regions
+ * * below and including absolute level [y] first, in [upward] direction.
+ * * above absolute level [y] last, in [downward] direction.
+ */
+ fun convergingTo(y: Int) = Slicing(y, upward, downward, true)
+
+ /**
+ * The returned [RegionTraverser] will traverse the regions
+ * * above absolute level [y] first, in [upward] direction.
+ * * below and including absolute level [y] second, in [downward] direction.
+ */
+ fun separatingFrom(y: Int) = Slicing(y, downward, upward, false)
+
+ private fun validify(region: Region, worldHeight: Int): Region {
+ if (region.origin.y < 0) {
+ val origin = region.origin withY 0
+ val size = region.size.withY((region.size.y + region.origin.y).clampMax(worldHeight))
+ return Region(origin, size)
+ }
+
+ if (region.origin.y + region.size.y > worldHeight) {
+ val size = region.size.withY(worldHeight - region.origin.y)
+ return Region(region.origin, size)
+ }
+
+ return region
+ }
+
+ }
+
+ class Directional(
+ val direction: TraverseDirection,
+ val order: TraverseOrder
+ ) : RegionTraverser() {
+
+ private inline fun iterate(max: Int, increasing: Boolean, action: (Int) -> Unit) {
+ for (i in 0..max) {
+ action(if (increasing) i else max - i)
+ }
+ }
+
+ override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
+ val order = order
+ val (primary, secondary, tertiary) = order.toArray()
+ val (origin, size) = region
+
+ val maxOfPrimary = size[primary] - 1
+ val maxOfSecondary = size[secondary] - 1
+ val maxOfTertiary = size[tertiary] - 1
+
+ val isPrimaryIncreasing = direction.isIncreasing(primary)
+ val isSecondaryIncreasing = direction.isIncreasing(secondary)
+ val isTertiaryIncreasing = direction.isIncreasing(tertiary)
+
+ iterate(maxOfPrimary, isPrimaryIncreasing) { p ->
+ iterate(maxOfSecondary, isSecondaryIncreasing) { s ->
+ iterate(maxOfTertiary, isTertiaryIncreasing) { t ->
+ yield(order.add(origin, p, s, t))
+ }
+ }
+ }
+
+ /*medium.iterationCompleted()*/
+ }
+
+ }
+
+ class Slicing(
+ val bottomSectionMaxY: Int,
+ val bottomTraverser: RegionTraverser,
+ val topTraverser: RegionTraverser,
+ val bottomFirst: Boolean = true
+ ) : RegionTraverser() {
+
+ private fun slice(region: Region, atY: Int): Pair<Region, Region?> {
+ if (atY < region.size.y + 1) {
+ val bottom = Region(region.origin, region.size.withY(atY + 1))
+ val top = Region(region.origin.withY(atY + 1), region.size.addY(-atY - 1))
+ return bottom to top
+ }
+ return region to null
+ }
+
+ override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
+ val (bottom, top) = slice(region, bottomSectionMaxY)
+
+ if (bottomFirst) {
+ with(bottomTraverser) { build(bottom) }
+ top?.let { with(topTraverser) { build(it) } }
+ } else {
+ top?.let { with(topTraverser) { build(it) } }
+ with(bottomTraverser) { build(bottom) }
+ }
+
+ /*medium.iterationCompleted()*/
+ }
+ }
+
+ /**
+ * Returns [Directional] instance that would be responsible for
+ * emitting the given position if it is contained in a region.
+ * [Directional] instance has a set order and direction
+ */
+ fun childForPosition(position: Vec3i): Directional {
+ var cur = this
+ while (true) {
+ when (cur) {
+ /*is ParcelTraverser -> cur = cur.delegate*/
+ is Directional -> return cur
+ is Slicing ->
+ cur =
+ if (position.y <= cur.bottomSectionMaxY) cur.bottomTraverser
+ else cur.topTraverser
+ }
+ }
+ }
+
+ /**
+ * Returns true if and only if this traverser would visit the given
+ * [block] position before the given [current] position.
+ * If at least one of [block] and [current] is not contained in a
+ * region being traversed the result is undefined.
+ */
+ fun comesFirst(current: Vec3i, block: Vec3i): Boolean {
+ var cur = this
+ while (true) {
+ when (cur) {
+ /*is ParcelTraverser -> cur = cur.delegate*/
+ is Directional -> return cur.direction.comesFirst(current, block)
+ is Slicing -> {
+ val border = cur.bottomSectionMaxY
+ cur = when {
+ current.y <= border && block.y <= border -> cur.bottomTraverser
+ current.y <= border -> return !cur.bottomFirst
+ block.y <= border -> return cur.bottomFirst
+ else -> cur.topTraverser
+ }
+ }
+ }
+ }
+ }
+
+}
+
+object TraverseOrderFactory {
+ private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3
+
+ fun createWith(primary: Dimension, secondary: Dimension): TraverseOrder {
+ // tertiary is implicit
+ if (primary == secondary) throw IllegalArgumentException()
+ return TraverseOrder(primary, isSwap(primary, secondary))
+ }
+}
+
+inline class TraverseOrder(val orderNum: Int) {
+ constructor(first: Dimension, swap: Boolean)
+ : this(if (swap) first.ordinal + 3 else first.ordinal)
+
+ @Suppress("NOTHING_TO_INLINE")
+ private inline fun element(index: Int) = Dimension[(orderNum + index) % 3]
+
+ private val swap inline get() = orderNum >= 3
+
+ /**
+ * The slowest changing dimension
+ */
+ val primary: Dimension get() = element(0)
+
+ /**
+ * Second slowest changing dimension
+ */
+ val secondary: Dimension get() = element(if (swap) 2 else 1)
+
+ /**
+ * Dimension that changes every block
+ */
+ val tertiary: Dimension get() = element(if (swap) 1 else 2)
+
+ /**
+ * All 3 dimensions in this order
+ */
+ fun toArray() = arrayOf(primary, secondary, tertiary)
+
+ fun add(vec: Vec3i, p: Int, s: Int, t: Int): Vec3i =
+ // optimize this, will be called lots
+ when (orderNum) {
+ 0 -> vec.add(p, s, t) // xyz
+ 1 -> vec.add(t, p, s) // yzx
+ 2 -> vec.add(s, t, p) // zxy
+ 3 -> vec.add(p, t, s) // xzy
+ 4 -> vec.add(s, p, t) // yxz
+ 5 -> vec.add(t, s, p) // zyx
+ else -> error("Invalid orderNum $orderNum")
+ }
+}
+
+inline class TraverseDirection(val bits: Int) {
+ fun isIncreasing(dimension: Dimension) = (1 shl dimension.ordinal) and bits != 0
+
+ fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean =
+ if (isIncreasing(dimension))
+ block[dimension] <= current[dimension]
+ else
+ block[dimension] >= current[dimension]
+
+ fun comesFirst(current: Vec3i, block: Vec3i) =
+ comesFirst(current, block, Dimension.X)
+ && comesFirst(current, block, Dimension.Y)
+ && comesFirst(current, block, Dimension.Z)
+
+ companion object {
+ operator fun invoke(x: Int, y: Int, z: Int) = invoke(Vec3i(x, y, z))
+
+ operator fun invoke(block: Vec3i): TraverseDirection {
+ if (block.x == 0 || block.y == 0 || block.z == 0) throw IllegalArgumentException()
+ var bits = 0
+ if (block.x > 0) bits = bits or 1
+ if (block.y > 0) bits = bits or 2
+ if (block.z > 0) bits = bits or 4
+ return TraverseDirection(bits)
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt index df3cfab..39b4345 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt @@ -1,121 +1,121 @@ -package io.dico.parcels2.blockvisitor - -import io.dico.parcels2.JobFunction -import io.dico.parcels2.JobScope -import io.dico.parcels2.util.math.Region -import io.dico.parcels2.util.math.Vec3i -import io.dico.parcels2.util.math.get -import org.bukkit.Bukkit -import org.bukkit.Material -import org.bukkit.World -import org.bukkit.block.Sign -import org.bukkit.block.data.BlockData - -private val air = Bukkit.createBlockData(Material.AIR) - -class Schematic { - val size: Vec3i get() = _size!! - private var _size: Vec3i? = null - set(value) { - field?.let { throw IllegalStateException() } - field = value - } - - private var blockDatas: Array<BlockData?>? = null - private val extra = mutableListOf<Pair<Vec3i, ExtraBlockChange>>() - private var isLoaded = false; private set - private val traverser: RegionTraverser = RegionTraverser.upward - - suspend fun JobScope.load(world: World, region: Region) { - _size = region.size - - val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it } - val blocks = traverser.traverseRegion(region) - val total = region.blockCount.toDouble() - - loop@ for ((index, vec) in blocks.withIndex()) { - markSuspensionPoint() - setProgress(index / total) - - val block = world[vec] - if (block.y > 255) continue - val blockData = block.blockData - data[index] = blockData - - val extraChange = when (blockData.material) { - Material.SIGN, - Material.WALL_SIGN -> SignStateChange(block.state as Sign) - else -> continue@loop - } - - extra += (vec - region.origin) to extraChange - } - - isLoaded = true - } - - suspend fun JobScope.paste(world: World, position: Vec3i) { - if (!isLoaded) throw IllegalStateException() - - val region = Region(position, _size!!) - val blocks = traverser.traverseRegion(region, worldHeight = world.maxHeight) - val blockDatas = blockDatas!! - var postponed = hashMapOf<Vec3i, BlockData>() - - val total = region.blockCount.toDouble() - var processed = 0 - - for ((index, vec) in blocks.withIndex()) { - markSuspensionPoint() - setProgress(index / total) - - val block = world[vec] - val type = blockDatas[index] ?: air - if (type !== air && isAttachable(type.material)) { - val supportingBlock = vec + getSupportingBlock(type) - - if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) { - block.blockData = type - setProgress(++processed / total) - } else { - postponed[vec] = type - } - - } else { - block.blockData = type - setProgress(++processed / total) - } - } - - while (!postponed.isEmpty()) { - markSuspensionPoint() - val newMap = hashMapOf<Vec3i, BlockData>() - for ((vec, type) in postponed) { - val supportingBlock = vec + getSupportingBlock(type) - if (supportingBlock in postponed && supportingBlock != vec) { - newMap[vec] = type - } else { - world[vec].blockData = type - setProgress(++processed / total) - } - } - postponed = newMap - } - - // Should be negligible so we don't track progress - for ((vec, extraChange) in extra) { - markSuspensionPoint() - val block = world[position + vec] - extraChange.update(block) - } - } - - fun getLoadTask(world: World, region: Region): JobFunction = { - load(world, region) - } - - fun getPasteTask(world: World, position: Vec3i): JobFunction = { - paste(world, position) - } - -} +package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.JobFunction
+import io.dico.parcels2.JobScope
+import io.dico.parcels2.util.math.Region
+import io.dico.parcels2.util.math.Vec3i
+import io.dico.parcels2.util.math.get
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.World
+import org.bukkit.block.Sign
+import org.bukkit.block.data.BlockData
+
+private val air = Bukkit.createBlockData(Material.AIR)
+
+class Schematic {
+ val size: Vec3i get() = _size!!
+ private var _size: Vec3i? = null
+ set(value) {
+ field?.let { throw IllegalStateException() }
+ field = value
+ }
+
+ private var blockDatas: Array<BlockData?>? = null
+ private val extra = mutableListOf<Pair<Vec3i, ExtraBlockChange>>()
+ private var isLoaded = false; private set
+ private val traverser: RegionTraverser = RegionTraverser.upward
+
+ suspend fun JobScope.load(world: World, region: Region) {
+ _size = region.size
+
+ val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it }
+ val blocks = traverser.traverseRegion(region)
+ val total = region.blockCount.toDouble()
+
+ loop@ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ setProgress(index / total)
+
+ val block = world[vec]
+ if (block.y > 255) continue
+ val blockData = block.blockData
+ data[index] = blockData
+
+ val extraChange = when (blockData.material) {
+ Material.SIGN,
+ Material.WALL_SIGN -> SignStateChange(block.state as Sign)
+ else -> continue@loop
+ }
+
+ extra += (vec - region.origin) to extraChange
+ }
+
+ isLoaded = true
+ }
+
+ suspend fun JobScope.paste(world: World, position: Vec3i) {
+ if (!isLoaded) throw IllegalStateException()
+
+ val region = Region(position, _size!!)
+ val blocks = traverser.traverseRegion(region, worldHeight = world.maxHeight)
+ val blockDatas = blockDatas!!
+ var postponed = hashMapOf<Vec3i, BlockData>()
+
+ val total = region.blockCount.toDouble()
+ var processed = 0
+
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ setProgress(index / total)
+
+ val block = world[vec]
+ val type = blockDatas[index] ?: air
+ if (type !== air && isAttachable(type.material)) {
+ val supportingBlock = vec + getSupportingBlock(type)
+
+ if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) {
+ block.blockData = type
+ setProgress(++processed / total)
+ } else {
+ postponed[vec] = type
+ }
+
+ } else {
+ block.blockData = type
+ setProgress(++processed / total)
+ }
+ }
+
+ while (!postponed.isEmpty()) {
+ markSuspensionPoint()
+ val newMap = hashMapOf<Vec3i, BlockData>()
+ for ((vec, type) in postponed) {
+ val supportingBlock = vec + getSupportingBlock(type)
+ if (supportingBlock in postponed && supportingBlock != vec) {
+ newMap[vec] = type
+ } else {
+ world[vec].blockData = type
+ setProgress(++processed / total)
+ }
+ }
+ postponed = newMap
+ }
+
+ // Should be negligible so we don't track progress
+ for ((vec, extraChange) in extra) {
+ markSuspensionPoint()
+ val block = world[position + vec]
+ extraChange.update(block)
+ }
+ }
+
+ fun getLoadTask(world: World, region: Region): JobFunction = {
+ load(world, region)
+ }
+
+ fun getPasteTask(world: World, position: Vec3i): JobFunction = {
+ paste(world, position)
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt index eaa9f57..32f4299 100644 --- a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt +++ b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt @@ -1,64 +1,64 @@ -package io.dico.parcels2.command - -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 -import io.dico.parcels2.util.ext.hasPermAdminManage -import io.dico.parcels2.util.ext.parcelLimit -import org.bukkit.entity.Player -import org.bukkit.plugin.Plugin -import java.lang.reflect.Method - -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") - } - - protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) { - if (player.hasPermAdminManage) return - val numOwnedParcels = plugin.storage.getOwnedParcels(PlayerProfile(player)).await() - .filter { it.worldId.equals(world.id) }.size - - val limit = player.parcelLimit - if (numOwnedParcels >= limit) { - err("You have enough plots for now") - } - } - - protected suspend fun toPrivilegeKey(profile: PlayerProfile): PrivilegeKey = when (profile) { - is Real -> profile - is Unresolved -> profile.tryResolveSuspendedly(plugin.storage) - ?: throw CommandException() - else -> throw CommandException() - } - - protected fun areYouSureMessage(context: ExecutionContext): String { - val command = (context.route + context.original).joinToString(" ") + " -sure" - return "Are you sure? You cannot undo this action!\n" + - "Run \"/$command\" if you want to go through with this." - } - - protected fun Job.reportProgressUpdates(context: ExecutionContext, action: String): Job = - onProgressUpdate(1000, 1000) { progress, elapsedTime -> - val alt = context.getFormat(EMessageType.NUMBER) - val main = context.getFormat(EMessageType.INFORMATIVE) - context.sendMessage( - EMessageType.INFORMATIVE, false, "$action progress: $alt%.02f$main%%, $alt%.2f${main}s elapsed" - .format(progress * 100, elapsedTime / 1000.0) - ) - } -} - +package io.dico.parcels2.command
+
+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
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import io.dico.parcels2.util.ext.parcelLimit
+import org.bukkit.entity.Player
+import org.bukkit.plugin.Plugin
+import java.lang.reflect.Method
+
+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")
+ }
+
+ protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) {
+ if (player.hasPermAdminManage) return
+ val numOwnedParcels = plugin.storage.getOwnedParcels(PlayerProfile(player)).await()
+ .filter { it.worldId.equals(world.id) }.size
+
+ val limit = player.parcelLimit
+ if (numOwnedParcels >= limit) {
+ err("You have enough plots for now")
+ }
+ }
+
+ protected suspend fun toPrivilegeKey(profile: PlayerProfile): PrivilegeKey = when (profile) {
+ is Real -> profile
+ is Unresolved -> profile.tryResolveSuspendedly(plugin.storage)
+ ?: throw CommandException()
+ else -> throw CommandException()
+ }
+
+ protected fun areYouSureMessage(context: ExecutionContext): String {
+ val command = (context.route + context.original).joinToString(" ") + " -sure"
+ return "Are you sure? You cannot undo this action!\n" +
+ "Run \"/$command\" if you want to go through with this."
+ }
+
+ protected fun Job.reportProgressUpdates(context: ExecutionContext, action: String): Job =
+ onProgressUpdate(1000, 1000) { progress, elapsedTime ->
+ val alt = context.getFormat(EMessageType.NUMBER)
+ val main = context.getFormat(EMessageType.INFORMATIVE)
+ context.sendMessage(
+ EMessageType.INFORMATIVE, false, "$action progress: $alt%.02f$main%%, $alt%.2f${main}s elapsed"
+ .format(progress * 100, elapsedTime / 1000.0)
+ )
+ }
+}
+
fun err(message: String): Nothing = throw CommandException(message)
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt index e700d6d..38adc43 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt @@ -1,95 +1,95 @@ -package io.dico.parcels2.command - -import io.dico.dicore.command.CommandException -import io.dico.dicore.command.ExecutionContext -import io.dico.dicore.command.Validate -import io.dico.dicore.command.annotation.Cmd -import io.dico.dicore.command.annotation.Flag -import io.dico.parcels2.* -import io.dico.parcels2.command.ParcelTarget.TargetKind -import io.dico.parcels2.defaultimpl.DefaultParcelContainer -import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE - -class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { - - @Cmd("setowner") - @RequireParcelPrivilege(Privilege.ADMIN) - suspend fun ParcelScope.cmdSetowner(@ProfileKind(ProfileKind.ANY) target: PlayerProfile): Any? { - val profile = target.resolved(plugin.storage, resolveToFake = true)!! - parcel.owner = profile - - val fakeString = if (profile.isFake) " (fake)" else "" - return "${profile.notNullName}$fakeString is the new owner of (${parcel.id.idString})" - } - - @Cmd("update_all_owner_signs") - fun cmdUpdateAllOwnerSigns(context: ExecutionContext): Any? { - Validate.isAuthorized(context.sender, PERM_ADMIN_MANAGE) - plugin.jobDispatcher.dispatch { - fun getParcelCount(world: ParcelWorld) = (world.options.axisLimit * 2 + 1).let { it * it } - val parcelCount = plugin.parcelProvider.worlds.values.sumBy { getParcelCount(it) }.toDouble() - var processed = 0 - for (world in plugin.parcelProvider.worlds.values) { - markSuspensionPoint() - - val container = world.container as? DefaultParcelContainer - if (container == null) { - processed += getParcelCount(world) - setProgress(processed / parcelCount) - continue - } - - for (parcel in container.getAllParcels()) { - parcel.updateOwnerSign(force = true) - processed++ - setProgress(processed / parcelCount) - } - } - }.reportProgressUpdates(context, "Updating") - return null - } - - @Cmd("dispose") - @RequireParcelPrivilege(Privilege.ADMIN) - fun ParcelScope.cmdDispose(): Any? { - parcel.dispose() - return "Data of (${parcel.id.idString}) has been disposed" - } - - @Cmd("reset") - @RequireParcelPrivilege(Privilege.ADMIN) - fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? { - Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel") - if (!sure) return areYouSureMessage(context) - - parcel.dispose() - world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Reset") - return "Data of (${parcel.id.idString}) has been disposed" - } - - @Cmd("swap") - @RequireParcelPrivilege(Privilege.ADMIN) - fun ParcelScope.cmdSwap( - context: ExecutionContext, - @TargetKind(TargetKind.ID) target: ParcelTarget, - @Flag sure: Boolean - ): Any? { - Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel") - if (!sure) return areYouSureMessage(context) - - val parcel2 = (target as ParcelTarget.ByID).getParcel() - ?: throw CommandException("Invalid parcel target") - - // Validate.isTrue(parcel2.world == world, "Parcel must be in the same world") - Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in that parcel") - - val data = parcel.data - parcel.copyData(parcel2.data) - parcel2.copyData(data) - - val job = plugin.parcelProvider.swapParcels(parcel.id, parcel2.id)?.reportProgressUpdates(context, "Swap") - Validate.notNull(job, "A process is already running in some parcel (internal error)") - return null - } - +package io.dico.parcels2.command
+
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Flag
+import io.dico.parcels2.*
+import io.dico.parcels2.command.ParcelTarget.TargetKind
+import io.dico.parcels2.defaultimpl.DefaultParcelContainer
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+
+class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ @Cmd("setowner")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ suspend fun ParcelScope.cmdSetowner(@ProfileKind(ProfileKind.ANY) target: PlayerProfile): Any? {
+ val profile = target.resolved(plugin.storage, resolveToFake = true)!!
+ parcel.owner = profile
+
+ val fakeString = if (profile.isFake) " (fake)" else ""
+ return "${profile.notNullName}$fakeString is the new owner of (${parcel.id.idString})"
+ }
+
+ @Cmd("update_all_owner_signs")
+ fun cmdUpdateAllOwnerSigns(context: ExecutionContext): Any? {
+ Validate.isAuthorized(context.sender, PERM_ADMIN_MANAGE)
+ plugin.jobDispatcher.dispatch {
+ fun getParcelCount(world: ParcelWorld) = (world.options.axisLimit * 2 + 1).let { it * it }
+ val parcelCount = plugin.parcelProvider.worlds.values.sumBy { getParcelCount(it) }.toDouble()
+ var processed = 0
+ for (world in plugin.parcelProvider.worlds.values) {
+ markSuspensionPoint()
+
+ val container = world.container as? DefaultParcelContainer
+ if (container == null) {
+ processed += getParcelCount(world)
+ setProgress(processed / parcelCount)
+ continue
+ }
+
+ for (parcel in container.getAllParcels()) {
+ parcel.updateOwnerSign(force = true)
+ processed++
+ setProgress(processed / parcelCount)
+ }
+ }
+ }.reportProgressUpdates(context, "Updating")
+ return null
+ }
+
+ @Cmd("dispose")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdDispose(): Any? {
+ parcel.dispose()
+ return "Data of (${parcel.id.idString}) has been disposed"
+ }
+
+ @Cmd("reset")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+ if (!sure) return areYouSureMessage(context)
+
+ parcel.dispose()
+ world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Reset")
+ return "Data of (${parcel.id.idString}) has been disposed"
+ }
+
+ @Cmd("swap")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdSwap(
+ context: ExecutionContext,
+ @TargetKind(TargetKind.ID) target: ParcelTarget,
+ @Flag sure: Boolean
+ ): Any? {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+ if (!sure) return areYouSureMessage(context)
+
+ val parcel2 = (target as ParcelTarget.ByID).getParcel()
+ ?: throw CommandException("Invalid parcel target")
+
+ // Validate.isTrue(parcel2.world == world, "Parcel must be in the same world")
+ Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in that parcel")
+
+ val data = parcel.data
+ parcel.copyData(parcel2.data)
+ parcel2.copyData(data)
+
+ val job = plugin.parcelProvider.swapParcels(parcel.id, parcel2.id)?.reportProgressUpdates(context, "Swap")
+ Validate.notNull(job, "A process is already running in some parcel (internal error)")
+ return null
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt index cee3e62..a2bd8d1 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt @@ -1,132 +1,132 @@ -@file:Suppress("NON_EXHAUSTIVE_WHEN") - -package io.dico.parcels2.command - -import io.dico.dicore.command.ExecutionContext -import io.dico.dicore.command.Validate -import io.dico.dicore.command.annotation.Cmd -import io.dico.dicore.command.annotation.Desc -import io.dico.parcels2.* -import io.dico.parcels2.Privilege.BANNED -import io.dico.parcels2.Privilege.CAN_BUILD -import io.dico.parcels2.PrivilegeChangeResult.* -import io.dico.parcels2.defaultimpl.InfoBuilder -import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE -import org.bukkit.OfflinePlayer - -class CommandsAdminPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { - private val data - inline get() = plugin.globalPrivileges - - private fun checkContext(context: ExecutionContext, owner: OfflinePlayer, changing: Boolean = true): OfflinePlayer { - if (changing) { - checkConnected("have privileges changed") - } - val sender = context.sender - if (sender !== owner) { - Validate.isAuthorized(sender, PERM_ADMIN_MANAGE) - } - return owner - } - - @Cmd("list", aliases = ["l"]) - @Desc( - "List globally declared privileges, players you", - "allowed to build on or banned from all your parcels", - shortVersion = "lists globally declared privileges" - ) - fun cmdList(context: ExecutionContext, owner: OfflinePlayer): Any? { - checkContext(context, owner, changing = false) - val map = plugin.globalPrivileges[owner] - Validate.isTrue(map.hasAnyDeclaredPrivileges(), "This user has not declared any global privileges") - - return StringBuilder().apply { - with(InfoBuilder) { - appendProfilesWithPrivilege("Globally Allowed", map, null, CAN_BUILD) - appendProfilesWithPrivilege("Globally Banned", map, null, BANNED) - } - }.toString() - } - - @Cmd("entrust") - @Desc( - "Allows a player to manage globally", - shortVersion = "allows a player to manage globally" - ) - suspend fun cmdEntrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? = - when (data[checkContext(context, owner)].allowManage(toPrivilegeKey(player))) { - FAIL_OWNER -> err("The target cannot be the owner themselves") - FAIL -> err("${player.name} is already allowed to manage globally") - SUCCESS -> "${player.name} is now allowed to manage globally" - } - - @Cmd("distrust") - @Desc( - "Disallows a player to manage globally,", - "they will still be able to build", - shortVersion = "disallows a player to manage globally" - ) - suspend fun cmdDistrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? = - when (data[checkContext(context, owner)].disallowManage(toPrivilegeKey(player))) { - FAIL_OWNER -> err("The target cannot be the owner themselves") - FAIL -> err("${player.name} is not currently allowed to manage globally") - SUCCESS -> "${player.name} is not allowed to manage globally anymore" - } - - @Cmd("allow", aliases = ["add", "permit"]) - @Desc( - "Globally allows a player to build on all", - "the parcels that you own.", - shortVersion = "globally allows a player to build on your parcels" - ) - suspend fun cmdAllow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? = - when (data[checkContext(context, owner)].allowBuild(toPrivilegeKey(player))) { - FAIL_OWNER -> err("The target cannot be the owner themselves") - FAIL -> err("${player.name} is already allowed globally") - SUCCESS -> "${player.name} is now allowed to build globally" - } - - @Cmd("disallow", aliases = ["remove", "forbid"]) - @Desc( - "Globally disallows a player to build on", - "the parcels that you own.", - "If the player is allowed to build on specific", - "parcels, they can still build there.", - shortVersion = "globally disallows a player to build on your parcels" - ) - suspend fun cmdDisallow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? = - when (data[checkContext(context, owner)].disallowBuild(toPrivilegeKey(player))) { - FAIL_OWNER -> err("The target cannot be the owner themselves") - FAIL -> err("${player.name} is not currently allowed globally") - SUCCESS -> "${player.name} is not allowed to build globally anymore" - } - - @Cmd("ban", aliases = ["deny"]) - @Desc( - "Globally bans a player from all the parcels", - "that you own, making them unable to enter.", - shortVersion = "globally bans a player from your parcels" - ) - suspend fun cmdBan(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? = - when (data[checkContext(context, owner)].disallowEnter(toPrivilegeKey(player))) { - FAIL_OWNER -> err("The target cannot be the owner themselves") - FAIL -> err("${player.name} is already banned globally") - SUCCESS -> "${player.name} is now banned globally" - } - - @Cmd("unban", aliases = ["undeny"]) - @Desc( - "Globally unbans a player from all the parcels", - "that you own, they can enter again.", - "If the player is banned from specific parcels,", - "they will still be banned there.", - shortVersion = "globally unbans a player from your parcels" - ) - suspend fun cmdUnban(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? = - when (data[checkContext(context, owner)].allowEnter(toPrivilegeKey(player))) { - FAIL_OWNER -> err("The target cannot be the owner themselves") - FAIL -> err("${player.name} is not currently banned globally") - SUCCESS -> "${player.name} is not banned globally anymore" - } - +@file:Suppress("NON_EXHAUSTIVE_WHEN")
+
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.BANNED
+import io.dico.parcels2.Privilege.CAN_BUILD
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.defaultimpl.InfoBuilder
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import org.bukkit.OfflinePlayer
+
+class CommandsAdminPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+ private val data
+ inline get() = plugin.globalPrivileges
+
+ private fun checkContext(context: ExecutionContext, owner: OfflinePlayer, changing: Boolean = true): OfflinePlayer {
+ if (changing) {
+ checkConnected("have privileges changed")
+ }
+ val sender = context.sender
+ if (sender !== owner) {
+ Validate.isAuthorized(sender, PERM_ADMIN_MANAGE)
+ }
+ return owner
+ }
+
+ @Cmd("list", aliases = ["l"])
+ @Desc(
+ "List globally declared privileges, players you",
+ "allowed to build on or banned from all your parcels",
+ shortVersion = "lists globally declared privileges"
+ )
+ fun cmdList(context: ExecutionContext, owner: OfflinePlayer): Any? {
+ checkContext(context, owner, changing = false)
+ val map = plugin.globalPrivileges[owner]
+ Validate.isTrue(map.hasAnyDeclaredPrivileges(), "This user has not declared any global privileges")
+
+ return StringBuilder().apply {
+ with(InfoBuilder) {
+ appendProfilesWithPrivilege("Globally Allowed", map, null, CAN_BUILD)
+ appendProfilesWithPrivilege("Globally Banned", map, null, BANNED)
+ }
+ }.toString()
+ }
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage globally",
+ shortVersion = "allows a player to manage globally"
+ )
+ suspend fun cmdEntrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowManage(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already allowed to manage globally")
+ SUCCESS -> "${player.name} is now allowed to manage globally"
+ }
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage globally,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage globally"
+ )
+ suspend fun cmdDistrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowManage(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently allowed to manage globally")
+ SUCCESS -> "${player.name} is not allowed to manage globally anymore"
+ }
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Globally allows a player to build on all",
+ "the parcels that you own.",
+ shortVersion = "globally allows a player to build on your parcels"
+ )
+ suspend fun cmdAllow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowBuild(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already allowed globally")
+ SUCCESS -> "${player.name} is now allowed to build globally"
+ }
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Globally disallows a player to build on",
+ "the parcels that you own.",
+ "If the player is allowed to build on specific",
+ "parcels, they can still build there.",
+ shortVersion = "globally disallows a player to build on your parcels"
+ )
+ suspend fun cmdDisallow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowBuild(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently allowed globally")
+ SUCCESS -> "${player.name} is not allowed to build globally anymore"
+ }
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Globally bans a player from all the parcels",
+ "that you own, making them unable to enter.",
+ shortVersion = "globally bans a player from your parcels"
+ )
+ suspend fun cmdBan(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowEnter(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already banned globally")
+ SUCCESS -> "${player.name} is now banned globally"
+ }
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Globally unbans a player from all the parcels",
+ "that you own, they can enter again.",
+ "If the player is banned from specific parcels,",
+ "they will still be banned there.",
+ shortVersion = "globally unbans a player from your parcels"
+ )
+ suspend fun cmdUnban(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowEnter(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently banned globally")
+ SUCCESS -> "${player.name} is not banned globally anymore"
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt index e0bde7d..3f166ad 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt @@ -1,162 +1,162 @@ -package io.dico.parcels2.command - -import io.dico.dicore.Formatting -import io.dico.dicore.command.* -import io.dico.dicore.command.IContextFilter.Priority.PERMISSION -import io.dico.dicore.command.annotation.Cmd -import io.dico.dicore.command.annotation.PreprocessArgs -import io.dico.dicore.command.annotation.RequireParameters -import io.dico.dicore.command.parameter.ArgumentBuffer -import io.dico.parcels2.* -import io.dico.parcels2.blockvisitor.RegionTraverser -import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE -import io.dico.parcels2.util.ext.PERM_BAN_BYPASS -import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE -import kotlinx.coroutines.launch -import org.bukkit.Bukkit -import org.bukkit.Material -import org.bukkit.block.BlockFace -import org.bukkit.block.data.Directional -import org.bukkit.command.CommandSender -import org.bukkit.entity.Player -import java.util.Random - -class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { - - @Cmd("reloadoptions") - fun reloadOptions() { - plugin.loadOptions() - } - - @Cmd("tpworld") - fun tpWorld(sender: Player, worldName: String): String { - if (worldName == "list") { - return Bukkit.getWorlds().joinToString("\n- ", "- ", "") - } - val world = Bukkit.getWorld(worldName) ?: err("World $worldName is not loaded") - sender.teleport(world.spawnLocation) - return "Teleported you to $worldName spawn" - } - - @Cmd("make_mess") - @RequireParcelPrivilege(Privilege.OWNER) - fun ParcelScope.cmdMakeMess(context: ExecutionContext) { - Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel") - - val server = plugin.server - val blockDatas = arrayOf( - server.createBlockData(Material.BLUE_WOOL), - server.createBlockData(Material.LIME_WOOL), - server.createBlockData(Material.GLASS), - server.createBlockData(Material.STONE_SLAB), - server.createBlockData(Material.STONE), - server.createBlockData(Material.QUARTZ_BLOCK), - server.createBlockData(Material.BROWN_CONCRETE) - ) - val random = Random() - - world.blockManager.tryDoBlockOperation(plugin.parcelProvider, parcel.id, traverser = RegionTraverser.upward) { block -> - block.blockData = blockDatas[random.nextInt(7)] - }?.onProgressUpdate(1000, 1000) { progress, elapsedTime -> - context.sendMessage( - EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed" - .format(progress * 100, elapsedTime / 1000.0) - ) - } - } - - @Cmd("directionality", aliases = ["dir"]) - fun cmdDirectionality(sender: Player, context: ExecutionContext, material: Material): Any? { - val senderLoc = sender.location - val block = senderLoc.add(senderLoc.direction.setY(0).normalize().multiply(2).toLocation(sender.world)).block - - val blockData = Bukkit.createBlockData(material) - if (blockData is Directional) { - blockData.facing = BlockFace.SOUTH - } - - block.blockData = blockData - return if (blockData is Directional) "The block is facing south" else "The block is not directional, however it implements " + - blockData.javaClass.interfaces!!.contentToString() - } - - @Cmd("jobs") - fun cmdJobs(): Any? { - val workers = plugin.jobDispatcher.jobs - println(workers.map { it.coroutine }.joinToString(separator = "\n")) - return "Task count: ${workers.size}" - } - - @Cmd("complete_jobs") - fun cmdCompleteJobs(): Any? = cmdJobs().also { - plugin.launch { plugin.jobDispatcher.completeAllTasks() } - } - - @Cmd("message") - @PreprocessArgs - fun cmdMessage(sender: CommandSender, message: String): Any? { - // testing @PreprocessArgs which merges "hello there" into a single argument - sender.sendMessage(Formatting.translate(message)) - return null - } - - @Cmd("hasperm") - fun cmdHasperm(target: Player, permission: String): Any? { - return target.hasPermission(permission).toString() - } - - @Cmd("permissions") - fun cmdPermissions(context: ExecutionContext, of: Player, vararg address: String): Any? { - val target = context.address.dispatcherForTree.getDeepChild(ArgumentBuffer(address)) - Validate.isTrue(target.depth == address.size && target.hasCommand(), "Not found: /${address.joinToString(separator = " ")}") - return getPermissionsOf(target).joinToString(separator = "\n") { "$it: ${of.hasPermission(it)}" } - } - - @Cmd("privilege") - @RequireParameters(1) - suspend fun ParcelScope.cmdPrivilege(target: PlayerProfile, adminPerm: String?): Any? { - val key = toPrivilegeKey(target) - - val perm = when (adminPerm) { - "none" -> null - "build" -> PERM_BUILD_ANYWHERE - "manage", null -> PERM_ADMIN_MANAGE - "enter" -> PERM_BAN_BYPASS - else -> err("adminPerm should be build, manager or enter") - } - - val privilege = if (perm == null) { - parcel.getStoredPrivilege(key) - } else { - if (key is PlayerProfile.Star) err("* can't have permissions") - parcel.getEffectivePrivilege(key.player!!, perm) - } - - return privilege.toString() - } - - 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 - } - +package io.dico.parcels2.command
+
+import io.dico.dicore.Formatting
+import io.dico.dicore.command.*
+import io.dico.dicore.command.IContextFilter.Priority.PERMISSION
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.PreprocessArgs
+import io.dico.dicore.command.annotation.RequireParameters
+import io.dico.dicore.command.parameter.ArgumentBuffer
+import io.dico.parcels2.*
+import io.dico.parcels2.blockvisitor.RegionTraverser
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
+import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
+import kotlinx.coroutines.launch
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.block.BlockFace
+import org.bukkit.block.data.Directional
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+import java.util.Random
+
+class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ @Cmd("reloadoptions")
+ fun reloadOptions() {
+ plugin.loadOptions()
+ }
+
+ @Cmd("tpworld")
+ fun tpWorld(sender: Player, worldName: String): String {
+ if (worldName == "list") {
+ return Bukkit.getWorlds().joinToString("\n- ", "- ", "")
+ }
+ val world = Bukkit.getWorld(worldName) ?: err("World $worldName is not loaded")
+ sender.teleport(world.spawnLocation)
+ return "Teleported you to $worldName spawn"
+ }
+
+ @Cmd("make_mess")
+ @RequireParcelPrivilege(Privilege.OWNER)
+ fun ParcelScope.cmdMakeMess(context: ExecutionContext) {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+
+ val server = plugin.server
+ val blockDatas = arrayOf(
+ server.createBlockData(Material.BLUE_WOOL),
+ server.createBlockData(Material.LIME_WOOL),
+ server.createBlockData(Material.GLASS),
+ server.createBlockData(Material.STONE_SLAB),
+ server.createBlockData(Material.STONE),
+ server.createBlockData(Material.QUARTZ_BLOCK),
+ server.createBlockData(Material.BROWN_CONCRETE)
+ )
+ val random = Random()
+
+ world.blockManager.tryDoBlockOperation(plugin.parcelProvider, parcel.id, traverser = RegionTraverser.upward) { block ->
+ block.blockData = blockDatas[random.nextInt(7)]
+ }?.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
+ context.sendMessage(
+ EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
+ .format(progress * 100, elapsedTime / 1000.0)
+ )
+ }
+ }
+
+ @Cmd("directionality", aliases = ["dir"])
+ fun cmdDirectionality(sender: Player, context: ExecutionContext, material: Material): Any? {
+ val senderLoc = sender.location
+ val block = senderLoc.add(senderLoc.direction.setY(0).normalize().multiply(2).toLocation(sender.world)).block
+
+ val blockData = Bukkit.createBlockData(material)
+ if (blockData is Directional) {
+ blockData.facing = BlockFace.SOUTH
+ }
+
+ block.blockData = blockData
+ return if (blockData is Directional) "The block is facing south" else "The block is not directional, however it implements " +
+ blockData.javaClass.interfaces!!.contentToString()
+ }
+
+ @Cmd("jobs")
+ fun cmdJobs(): Any? {
+ val workers = plugin.jobDispatcher.jobs
+ println(workers.map { it.coroutine }.joinToString(separator = "\n"))
+ return "Task count: ${workers.size}"
+ }
+
+ @Cmd("complete_jobs")
+ fun cmdCompleteJobs(): Any? = cmdJobs().also {
+ plugin.launch { plugin.jobDispatcher.completeAllTasks() }
+ }
+
+ @Cmd("message")
+ @PreprocessArgs
+ fun cmdMessage(sender: CommandSender, message: String): Any? {
+ // testing @PreprocessArgs which merges "hello there" into a single argument
+ sender.sendMessage(Formatting.translate(message))
+ return null
+ }
+
+ @Cmd("hasperm")
+ fun cmdHasperm(target: Player, permission: String): Any? {
+ return target.hasPermission(permission).toString()
+ }
+
+ @Cmd("permissions")
+ fun cmdPermissions(context: ExecutionContext, of: Player, vararg address: String): Any? {
+ val target = context.address.dispatcherForTree.getDeepChild(ArgumentBuffer(address))
+ Validate.isTrue(target.depth == address.size && target.hasCommand(), "Not found: /${address.joinToString(separator = " ")}")
+ return getPermissionsOf(target).joinToString(separator = "\n") { "$it: ${of.hasPermission(it)}" }
+ }
+
+ @Cmd("privilege")
+ @RequireParameters(1)
+ suspend fun ParcelScope.cmdPrivilege(target: PlayerProfile, adminPerm: String?): Any? {
+ val key = toPrivilegeKey(target)
+
+ val perm = when (adminPerm) {
+ "none" -> null
+ "build" -> PERM_BUILD_ANYWHERE
+ "manage", null -> PERM_ADMIN_MANAGE
+ "enter" -> PERM_BAN_BYPASS
+ else -> err("adminPerm should be build, manager or enter")
+ }
+
+ val privilege = if (perm == null) {
+ parcel.getStoredPrivilege(key)
+ } else {
+ if (key is PlayerProfile.Star) err("* can't have permissions")
+ parcel.getEffectivePrivilege(key.player!!, perm)
+ }
+
+ return privilege.toString()
+ }
+
+ 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
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt index c918b80..9929a4c 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt @@ -113,6 +113,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab return "Enjoy your new parcel!" } + /* @Cmd("unclaim") @Desc("Unclaims this parcel") @RequireParcelPrivilege(Privilege.OWNER) @@ -120,7 +121,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab checkConnected("be unclaimed") parcel.dispose() return "Your parcel has been disposed" - } + }*/ @Cmd("clear") @RequireParcelPrivilege(Privilege.OWNER) diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt index 3b4234c..33ffb8b 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt @@ -1,77 +1,77 @@ -package io.dico.parcels2.command - -import io.dico.dicore.command.ExecutionContext -import io.dico.dicore.command.annotation.Cmd -import io.dico.dicore.command.annotation.Desc -import io.dico.parcels2.ParcelsPlugin -import io.dico.parcels2.PlayerProfile -import org.bukkit.entity.Player - -class CommandsPrivilegesGlobal(plugin: ParcelsPlugin, - val adminVersion: CommandsAdminPrivilegesGlobal) : AbstractParcelCommands(plugin) { - @Cmd("list", aliases = ["l"]) - @Desc( - "List globally declared privileges, players you", - "allowed to build on or banned from all your parcels", - shortVersion = "lists globally declared privileges" - ) - fun cmdList(sender: Player, context: ExecutionContext) = - adminVersion.cmdList(context, sender) - - @Cmd("entrust") - @Desc( - "Allows a player to manage globally", - shortVersion = "allows a player to manage globally" - ) - suspend fun cmdEntrust(sender: Player, context: ExecutionContext, player: PlayerProfile) = - adminVersion.cmdEntrust(context, sender, player) - - @Cmd("distrust") - @Desc( - "Disallows a player to manage globally,", - "they will still be able to build", - shortVersion = "disallows a player to manage globally" - ) - suspend fun cmdDistrust(sender: Player, context: ExecutionContext, player: PlayerProfile) = - adminVersion.cmdDistrust(context, sender, player) - - @Cmd("allow", aliases = ["add", "permit"]) - @Desc( - "Globally allows a player to build on all", - "the parcels that you own.", - shortVersion = "globally allows a player to build on your parcels" - ) - suspend fun cmdAllow(sender: Player, context: ExecutionContext, player: PlayerProfile) = - adminVersion.cmdAllow(context, sender, player) - - @Cmd("disallow", aliases = ["remove", "forbid"]) - @Desc( - "Globally disallows a player to build on", - "the parcels that you own.", - "If the player is allowed to build on specific", - "parcels, they can still build there.", - shortVersion = "globally disallows a player to build on your parcels" - ) - suspend fun cmdDisallow(sender: Player, context: ExecutionContext, player: PlayerProfile) = - adminVersion.cmdDisallow(context, sender, player) - - @Cmd("ban", aliases = ["deny"]) - @Desc( - "Globally bans a player from all the parcels", - "that you own, making them unable to enter.", - shortVersion = "globally bans a player from your parcels" - ) - suspend fun cmdBan(sender: Player, context: ExecutionContext, player: PlayerProfile) = - adminVersion.cmdBan(context, sender, player) - - @Cmd("unban", aliases = ["undeny"]) - @Desc( - "Globally unbans a player from all the parcels", - "that you own, they can enter again.", - "If the player is banned from specific parcels,", - "they will still be banned there.", - shortVersion = "globally unbans a player from your parcels" - ) - suspend fun cmdUnban(sender: Player, context: ExecutionContext, player: PlayerProfile) = - adminVersion.cmdUnban(context, sender, player) +package io.dico.parcels2.command
+
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.PlayerProfile
+import org.bukkit.entity.Player
+
+class CommandsPrivilegesGlobal(plugin: ParcelsPlugin,
+ val adminVersion: CommandsAdminPrivilegesGlobal) : AbstractParcelCommands(plugin) {
+ @Cmd("list", aliases = ["l"])
+ @Desc(
+ "List globally declared privileges, players you",
+ "allowed to build on or banned from all your parcels",
+ shortVersion = "lists globally declared privileges"
+ )
+ fun cmdList(sender: Player, context: ExecutionContext) =
+ adminVersion.cmdList(context, sender)
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage globally",
+ shortVersion = "allows a player to manage globally"
+ )
+ suspend fun cmdEntrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdEntrust(context, sender, player)
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage globally,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage globally"
+ )
+ suspend fun cmdDistrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdDistrust(context, sender, player)
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Globally allows a player to build on all",
+ "the parcels that you own.",
+ shortVersion = "globally allows a player to build on your parcels"
+ )
+ suspend fun cmdAllow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdAllow(context, sender, player)
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Globally disallows a player to build on",
+ "the parcels that you own.",
+ "If the player is allowed to build on specific",
+ "parcels, they can still build there.",
+ shortVersion = "globally disallows a player to build on your parcels"
+ )
+ suspend fun cmdDisallow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdDisallow(context, sender, player)
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Globally bans a player from all the parcels",
+ "that you own, making them unable to enter.",
+ shortVersion = "globally bans a player from your parcels"
+ )
+ suspend fun cmdBan(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdBan(context, sender, player)
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Globally unbans a player from all the parcels",
+ "that you own, they can enter again.",
+ "If the player is banned from specific parcels,",
+ "they will still be banned there.",
+ shortVersion = "globally unbans a player from your parcels"
+ )
+ suspend fun cmdUnban(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdUnban(context, sender, player)
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt index 21910b1..e3aba22 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt @@ -1,144 +1,144 @@ -package io.dico.parcels2.command - -import io.dico.dicore.command.Validate -import io.dico.dicore.command.annotation.Cmd -import io.dico.dicore.command.annotation.Desc -import io.dico.parcels2.* -import io.dico.parcels2.PrivilegeChangeResult.* -import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE -import io.dico.parcels2.util.ext.hasPermAdminManage -import org.bukkit.entity.Player - -class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { - - private fun ParcelScope.checkPrivilege(sender: Player, key: PrivilegeKey) { - val senderPrivilege = parcel.getEffectivePrivilege(sender, PERM_ADMIN_MANAGE) - val targetPrivilege = parcel.getStoredPrivilege(key) - Validate.isTrue(senderPrivilege > targetPrivilege, "You may not change the privilege of ${key.notNullName}") - } - - private fun ParcelScope.checkOwned(sender: Player) { - Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned") - } - - @Cmd("entrust") - @Desc( - "Allows a player to manage this parcel", - shortVersion = "allows a player to manage this parcel" - ) - @RequireParcelPrivilege(Privilege.OWNER) - suspend fun ParcelScope.cmdEntrust(sender: Player, player: PlayerProfile): Any? { - checkConnected("have privileges changed") - checkOwned(sender) - - val key = toPrivilegeKey(player) - return when (parcel.allowManage(key)) { - FAIL_OWNER -> err("The target already owns the parcel") - FAIL -> err("${player.name} is already allowed to manage this parcel") - SUCCESS -> "${player.name} is now allowed to manage this parcel" - } - } - - @Cmd("distrust") - @Desc( - "Disallows a player to manage this parcel,", - "they will still be able to build", - shortVersion = "disallows a player to manage this parcel" - ) - @RequireParcelPrivilege(Privilege.OWNER) - suspend fun ParcelScope.cmdDistrust(sender: Player, player: PlayerProfile): Any? { - checkConnected("have privileges changed") - checkOwned(sender) - - val key = toPrivilegeKey(player) - return when (parcel.disallowManage(key)) { - FAIL_OWNER -> err("The target owns the parcel and can't be distrusted") - FAIL -> err("${player.name} is not currently allowed to manage this parcel") - SUCCESS -> "${player.name} is not allowed to manage this parcel anymore" - } - } - - @Cmd("allow", aliases = ["add", "permit"]) - @Desc( - "Allows a player to build on this parcel", - shortVersion = "allows a player to build on this parcel" - ) - @RequireParcelPrivilege(Privilege.CAN_MANAGE) - suspend fun ParcelScope.cmdAllow(sender: Player, player: PlayerProfile): Any? { - checkConnected("have privileges changed") - checkOwned(sender) - - val key = toPrivilegeKey(player) - checkPrivilege(sender, key) - - return when (parcel.allowBuild(key)) { - FAIL_OWNER -> err("The target already owns the parcel") - FAIL -> err("${player.name} is already allowed to build on this parcel") - SUCCESS -> "${player.name} is now allowed to build on this parcel" - } - } - - @Cmd("disallow", aliases = ["remove", "forbid"]) - @Desc( - "Disallows a player to build on this parcel,", - "they won't be allowed to anymore", - shortVersion = "disallows a player to build on this parcel" - ) - @RequireParcelPrivilege(Privilege.CAN_MANAGE) - suspend fun ParcelScope.cmdDisallow(sender: Player, player: PlayerProfile): Any? { - checkConnected("have privileges changed") - checkOwned(sender) - - val key = toPrivilegeKey(player) - checkPrivilege(sender, key) - - return when (parcel.disallowBuild(key)) { - FAIL_OWNER -> err("The target owns the parcel") - FAIL -> err("${player.name} is not currently allowed to build on this parcel") - SUCCESS -> "${player.name} is not allowed to build on this parcel anymore" - } - } - - @Cmd("ban", aliases = ["deny"]) - @Desc( - "Bans a player from this parcel,", - "making them unable to enter", - shortVersion = "bans a player from this parcel" - ) - @RequireParcelPrivilege(Privilege.CAN_MANAGE) - suspend fun ParcelScope.cmdBan(sender: Player, player: PlayerProfile): Any? { - checkConnected("have privileges changed") - checkOwned(sender) - - val key = toPrivilegeKey(player) - checkPrivilege(sender, key) - - return when (parcel.disallowEnter(key)) { - FAIL_OWNER -> err("The target owns the parcel") - FAIL -> err("${player.name} is already banned from this parcel") - SUCCESS -> "${player.name} is now banned from this parcel" - } - } - - @Cmd("unban", aliases = ["undeny"]) - @Desc( - "Unbans a player from this parcel,", - "they will be able to enter it again", - shortVersion = "unbans a player from this parcel" - ) - @RequireParcelPrivilege(Privilege.CAN_MANAGE) - suspend fun ParcelScope.cmdUnban(sender: Player, player: PlayerProfile): Any? { - checkConnected("have privileges changed") - checkOwned(sender) - - val key = toPrivilegeKey(player) - checkPrivilege(sender, key) - - return when (parcel.allowEnter(key)) { - FAIL_OWNER -> err("The target owns the parcel") - FAIL -> err("${player.name} is not currently banned from this parcel") - SUCCESS -> "${player.name} is not banned from this parcel anymore" - } - } - +package io.dico.parcels2.command
+
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.*
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import org.bukkit.entity.Player
+
+class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ private fun ParcelScope.checkPrivilege(sender: Player, key: PrivilegeKey) {
+ val senderPrivilege = parcel.getEffectivePrivilege(sender, PERM_ADMIN_MANAGE)
+ val targetPrivilege = parcel.getStoredPrivilege(key)
+ Validate.isTrue(senderPrivilege > targetPrivilege, "You may not change the privilege of ${key.notNullName}")
+ }
+
+ private fun ParcelScope.checkOwned(sender: Player) {
+ Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
+ }
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage this parcel",
+ shortVersion = "allows a player to manage this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.OWNER)
+ suspend fun ParcelScope.cmdEntrust(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ return when (parcel.allowManage(key)) {
+ FAIL_OWNER -> err("The target already owns the parcel")
+ FAIL -> err("${player.name} is already allowed to manage this parcel")
+ SUCCESS -> "${player.name} is now allowed to manage this parcel"
+ }
+ }
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage this parcel,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.OWNER)
+ suspend fun ParcelScope.cmdDistrust(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ return when (parcel.disallowManage(key)) {
+ FAIL_OWNER -> err("The target owns the parcel and can't be distrusted")
+ FAIL -> err("${player.name} is not currently allowed to manage this parcel")
+ SUCCESS -> "${player.name} is not allowed to manage this parcel anymore"
+ }
+ }
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Allows a player to build on this parcel",
+ shortVersion = "allows a player to build on this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdAllow(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.allowBuild(key)) {
+ FAIL_OWNER -> err("The target already owns the parcel")
+ FAIL -> err("${player.name} is already allowed to build on this parcel")
+ SUCCESS -> "${player.name} is now allowed to build on this parcel"
+ }
+ }
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Disallows a player to build on this parcel,",
+ "they won't be allowed to anymore",
+ shortVersion = "disallows a player to build on this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdDisallow(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.disallowBuild(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is not currently allowed to build on this parcel")
+ SUCCESS -> "${player.name} is not allowed to build on this parcel anymore"
+ }
+ }
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Bans a player from this parcel,",
+ "making them unable to enter",
+ shortVersion = "bans a player from this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdBan(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.disallowEnter(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is already banned from this parcel")
+ SUCCESS -> "${player.name} is now banned from this parcel"
+ }
+ }
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Unbans a player from this parcel,",
+ "they will be able to enter it again",
+ shortVersion = "unbans a player from this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdUnban(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.allowEnter(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is not currently banned from this parcel")
+ SUCCESS -> "${player.name} is not banned from this parcel anymore"
+ }
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt index 15548b4..e41fd37 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt @@ -1,68 +1,68 @@ -package io.dico.parcels2.command - -import io.dico.dicore.command.CommandException -import io.dico.dicore.command.ExecutionContext -import io.dico.dicore.command.registration.reflect.ICommandReceiver -import io.dico.dicore.command.Validate -import io.dico.parcels2.* -import io.dico.parcels2.Privilege.* -import io.dico.parcels2.util.ext.hasPermAdminManage -import io.dico.parcels2.util.ext.uuid -import org.bukkit.entity.Player -import java.lang.reflect.Method -import kotlin.reflect.full.extensionReceiverParameter -import kotlin.reflect.full.findAnnotation -import kotlin.reflect.jvm.jvmErasure -import kotlin.reflect.jvm.kotlinFunction - -@Target(AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.RUNTIME) -annotation class RequireParcelPrivilege(val privilege: Privilege) - -/* -@Target(AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.RUNTIME) -annotation class SuspensionTimeout(val millis: Int) -*/ - -open class WorldScope(val world: ParcelWorld) : ICommandReceiver -open class ParcelScope(val parcel: Parcel) : WorldScope(parcel.world) { - fun checkCanManage(player: Player, action: String) = Validate.isTrue(parcel.canManage(player), "You must own this parcel to $action") -} - -fun getParcelCommandReceiver(parcelProvider: ParcelProvider, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver { - val player = context.sender as Player - val function = method.kotlinFunction!! - val receiverType = function.extensionReceiverParameter!!.type - val require = function.findAnnotation<RequireParcelPrivilege>() - - return when (receiverType.jvmErasure) { - ParcelScope::class -> ParcelScope(parcelProvider.getParcelRequired(player, require?.privilege)) - WorldScope::class -> WorldScope(parcelProvider.getWorldRequired(player, require?.privilege == ADMIN)) - else -> throw InternalError("Invalid command receiver type") - } -} - -fun ParcelProvider.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld { - if (admin) Validate.isTrue(player.hasPermAdminManage, "You must have admin rights to use that command") - return getWorld(player.world) - ?: throw CommandException("You must be in a parcel world to use that command") -} - -fun ParcelProvider.getParcelRequired(player: Player, privilege: Privilege? = null): Parcel { - val parcel = getWorldRequired(player, admin = privilege == ADMIN).getParcelAt(player) - ?: throw CommandException("You must be in a parcel to use that command") - - if (!player.hasPermAdminManage) { - @Suppress("NON_EXHAUSTIVE_WHEN") - when (privilege) { - OWNER -> - Validate.isTrue(parcel.isOwner(player.uuid), "You must own this parcel to use that command") - CAN_MANAGE -> - Validate.isTrue(parcel.canManage(player), "You must have management privileges on this parcel to use that command") - } - } - - return parcel -} - +package io.dico.parcels2.command
+
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.registration.reflect.ICommandReceiver
+import io.dico.dicore.command.Validate
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.*
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import io.dico.parcels2.util.ext.uuid
+import org.bukkit.entity.Player
+import java.lang.reflect.Method
+import kotlin.reflect.full.extensionReceiverParameter
+import kotlin.reflect.full.findAnnotation
+import kotlin.reflect.jvm.jvmErasure
+import kotlin.reflect.jvm.kotlinFunction
+
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class RequireParcelPrivilege(val privilege: Privilege)
+
+/*
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SuspensionTimeout(val millis: Int)
+*/
+
+open class WorldScope(val world: ParcelWorld) : ICommandReceiver
+open class ParcelScope(val parcel: Parcel) : WorldScope(parcel.world) {
+ fun checkCanManage(player: Player, action: String) = Validate.isTrue(parcel.canManage(player), "You must own this parcel to $action")
+}
+
+fun getParcelCommandReceiver(parcelProvider: ParcelProvider, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver {
+ val player = context.sender as Player
+ val function = method.kotlinFunction!!
+ val receiverType = function.extensionReceiverParameter!!.type
+ val require = function.findAnnotation<RequireParcelPrivilege>()
+
+ return when (receiverType.jvmErasure) {
+ ParcelScope::class -> ParcelScope(parcelProvider.getParcelRequired(player, require?.privilege))
+ WorldScope::class -> WorldScope(parcelProvider.getWorldRequired(player, require?.privilege == ADMIN))
+ else -> throw InternalError("Invalid command receiver type")
+ }
+}
+
+fun ParcelProvider.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld {
+ if (admin) Validate.isTrue(player.hasPermAdminManage, "You must have admin rights to use that command")
+ return getWorld(player.world)
+ ?: throw CommandException("You must be in a parcel world to use that command")
+}
+
+fun ParcelProvider.getParcelRequired(player: Player, privilege: Privilege? = null): Parcel {
+ val parcel = getWorldRequired(player, admin = privilege == ADMIN).getParcelAt(player)
+ ?: throw CommandException("You must be in a parcel to use that command")
+
+ if (!player.hasPermAdminManage) {
+ @Suppress("NON_EXHAUSTIVE_WHEN")
+ when (privilege) {
+ OWNER ->
+ Validate.isTrue(parcel.isOwner(player.uuid), "You must own this parcel to use that command")
+ CAN_MANAGE ->
+ Validate.isTrue(parcel.canManage(player), "You must have management privileges on this parcel to use that command")
+ }
+ }
+
+ return parcel
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt index 23087dc..fbe4333 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt @@ -1,56 +1,56 @@ -package io.dico.parcels2.command - -import io.dico.dicore.command.* -import io.dico.dicore.command.parameter.type.ParameterTypes -import io.dico.parcels2.Interactables -import io.dico.parcels2.ParcelsPlugin -import io.dico.parcels2.Privilege -import org.bukkit.command.CommandSender -import org.bukkit.entity.Player - -class ParcelOptionsInteractCommand(val plugin: ParcelsPlugin) : Command() { - - init { - setShortDescription("View and/or change the setting") - setDescription(shortDescription) - addContextFilter(IContextFilter.PLAYER_ONLY) - addContextFilter(IContextFilter.INHERIT_PERMISSIONS) - addParameter("allowed", "new setting", ParameterTypes.BOOLEAN) - requiredParameters(0) - } - - override fun execute(sender: CommandSender, context: ExecutionContext): String? { - if (!plugin.storage.isConnected) err("Parcels cannot have their options changed right now because of a database error") - - val interactableClass = Interactables[context.address.mainKey] - val allowed: Boolean? = context.get("allowed") - - val parcel = plugin.parcelProvider.getParcelRequired(sender as Player, - if (allowed == null) Privilege.DEFAULT else Privilege.CAN_MANAGE) - - if (allowed == null) { - val setting = parcel.interactableConfig.isInteractable(interactableClass) - val default = setting == interactableClass.interactableByDefault - - val canColor = context.address.chatHandler.getChatFormatForType(EMessageType.BAD_NEWS) - val cannotColor = context.address.chatHandler.getChatFormatForType(EMessageType.GOOD_NEWS) - val resetColor = context.address.chatHandler.getChatFormatForType(EMessageType.RESULT) - - val settingString = (if (setting) "${canColor}can" else "${cannotColor}cannot") + resetColor - val defaultString = if (default) " (default)" else "" - - return "Players $settingString interact with ${interactableClass.name} on this parcel$defaultString" - } - - val change = parcel.interactableConfig.setInteractable(interactableClass, allowed) - - val interactableClassName = interactableClass.name - return when { - allowed && change -> "Other players can now interact with $interactableClassName" - allowed && !change -> err("Other players could already interact with $interactableClassName") - change -> "Other players can not interact with $interactableClassName anymore" - else -> err("Other players were not allowed to interact with $interactableClassName") - } - } - -} +package io.dico.parcels2.command
+
+import io.dico.dicore.command.*
+import io.dico.dicore.command.parameter.type.ParameterTypes
+import io.dico.parcels2.Interactables
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.Privilege
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+
+class ParcelOptionsInteractCommand(val plugin: ParcelsPlugin) : Command() {
+
+ init {
+ setShortDescription("View and/or change the setting")
+ setDescription(shortDescription)
+ addContextFilter(IContextFilter.PLAYER_ONLY)
+ addContextFilter(IContextFilter.INHERIT_PERMISSIONS)
+ addParameter("allowed", "new setting", ParameterTypes.BOOLEAN)
+ requiredParameters(0)
+ }
+
+ override fun execute(sender: CommandSender, context: ExecutionContext): String? {
+ if (!plugin.storage.isConnected) err("Parcels cannot have their options changed right now because of a database error")
+
+ val interactableClass = Interactables[context.address.mainKey]
+ val allowed: Boolean? = context.get("allowed")
+
+ val parcel = plugin.parcelProvider.getParcelRequired(sender as Player,
+ if (allowed == null) Privilege.DEFAULT else Privilege.CAN_MANAGE)
+
+ if (allowed == null) {
+ val setting = parcel.interactableConfig.isInteractable(interactableClass)
+ val default = setting == interactableClass.interactableByDefault
+
+ val canColor = context.address.chatHandler.getChatFormatForType(EMessageType.BAD_NEWS)
+ val cannotColor = context.address.chatHandler.getChatFormatForType(EMessageType.GOOD_NEWS)
+ val resetColor = context.address.chatHandler.getChatFormatForType(EMessageType.RESULT)
+
+ val settingString = (if (setting) "${canColor}can" else "${cannotColor}cannot") + resetColor
+ val defaultString = if (default) " (default)" else ""
+
+ return "Players $settingString interact with ${interactableClass.name} on this parcel$defaultString"
+ }
+
+ val change = parcel.interactableConfig.setInteractable(interactableClass, allowed)
+
+ val interactableClassName = interactableClass.name
+ return when {
+ allowed && change -> "Other players can now interact with $interactableClassName"
+ allowed && !change -> err("Other players could already interact with $interactableClassName")
+ change -> "Other players can not interact with $interactableClassName anymore"
+ else -> err("Other players were not allowed to interact with $interactableClassName")
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt index c7083a1..1b20f72 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt @@ -6,7 +6,6 @@ import io.dico.dicore.command.parameter.Parameter import io.dico.dicore.command.parameter.type.ParameterConfig import io.dico.dicore.command.parameter.type.ParameterType import io.dico.parcels2.* -import io.dico.parcels2.command.ProfileKind.Companion.ANY import io.dico.parcels2.command.ProfileKind.Companion.FAKE import io.dico.parcels2.command.ProfileKind.Companion.REAL import org.bukkit.Location @@ -53,6 +52,7 @@ annotation class ProfileKind(val kind: Int) { const val REAL = 1 const val FAKE = 2 const val ANY = REAL or FAKE + const val ALLOW_INVALID = 4 override fun toParameterInfo(annotation: ProfileKind): Int { return annotation.kind @@ -62,13 +62,20 @@ annotation class ProfileKind(val kind: Int) { class ProfileParameterType : ParameterType<PlayerProfile, Int>(PlayerProfile::class.java, ProfileKind) { - override fun parse(parameter: Parameter<PlayerProfile, Int>, sender: CommandSender, buffer: ArgumentBuffer): PlayerProfile { + override fun parse(parameter: Parameter<PlayerProfile, Int>, sender: CommandSender, buffer: ArgumentBuffer): PlayerProfile? { val info = parameter.paramInfo ?: REAL val allowReal = (info and REAL) != 0 val allowFake = (info and FAKE) != 0 val input = buffer.next()!! - return PlayerProfile.byName(input, allowReal, allowFake) + + val profile = PlayerProfile.byName(input, allowReal, allowFake) + + if (profile == null && (info and ProfileKind.ALLOW_INVALID) == 0) { + invalidInput(parameter, "\'$input\' is not a valid player name") + } + + return profile } override fun complete( diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt index 8891912..9ba3c25 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt @@ -25,7 +25,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef abstract suspend fun getParcelSuspend(storage: Storage): Parcel? - class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) { + class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : + ParcelTarget(world, parsedKind, isDefault) { override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel() fun getParcel() = id?.let { world.getParcelById(it) } val isPath: Boolean get() = id == null @@ -78,7 +79,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef const val DEFAULT_KIND = REAL - const val PREFER_OWNED_FOR_DEFAULT = 8 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default + const val PREFER_OWNED_FOR_DEFAULT = + 8 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default // instead of parcel that the player is in override fun toParameterInfo(annotation: TargetKind): Int { @@ -90,7 +92,11 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, TargetKind) { - override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget { + override fun parse( + parameter: Parameter<ParcelTarget, Int>, + sender: CommandSender, + buffer: ArgumentBuffer + ): ParcelTarget { var input = buffer.next()!! val worldString = input.substringBefore("/", missingDelimiterValue = "") input = input.substringAfter("/") @@ -100,18 +106,26 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef parcelProvider.getWorld(player.world) ?: invalidInput(parameter, "You cannot omit the world if you're not in a parcel world") } else { - parcelProvider.getWorld(worldString) ?: invalidInput(parameter, "$worldString is not a parcel world") + parcelProvider.getWorld(worldString) + ?: invalidInput(parameter, "$worldString is not a parcel world") } val kind = parameter.paramInfo ?: DEFAULT_KIND if (input.contains(',')) { - if (kind and ID == 0) invalidInput(parameter, "You must specify a parcel by OWNER, that is, an owner and index") + if (kind and ID == 0) invalidInput(parameter, + "You must specify a parcel by OWNER, that is, an owner and index") return ByID(world, getId(parameter, input), kind, false) } - if (kind and OWNER == 0) invalidInput(parameter, "You must specify a parcel by ID, that is, the x and z component separated by a comma") + if (kind and OWNER == 0) invalidInput(parameter, + "You must specify a parcel by ID, that is, the x and z component separated by a comma") val (owner, index) = getHomeIndex(parameter, kind, sender, input) - return ByOwner(world, owner, index, kind, false, onResolveFailure = { invalidInput(parameter, "The player $input does not exist") }) + return ByOwner(world, + owner, + index, + kind, + false, + onResolveFailure = { invalidInput(parameter, "The player $input does not exist") }) } private fun getId(parameter: Parameter<*, *>, input: String): Vec2i { @@ -124,7 +138,12 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef return Vec2i(x, z) } - private fun getHomeIndex(parameter: Parameter<*, *>, kind: Int, sender: CommandSender, input: String): Pair<PlayerProfile, Int> { + private fun getHomeIndex( + parameter: Parameter<*, *>, + kind: Int, + sender: CommandSender, + input: String + ): Pair<PlayerProfile, Int> { val splitIdx = input.indexOf(':') val ownerString: String val index: Int? @@ -155,10 +174,11 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef ?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer") } - val owner = if (ownerString.isEmpty()) + val owner = (if (ownerString.isEmpty()) PlayerProfile(requirePlayer(sender, parameter, "the player")) else - PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0) + PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0)) + ?: invalidInput(parameter, "\'$ownerString\' is not a valid player name") return owner to (index ?: 0) } @@ -168,7 +188,11 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef return sender } - override fun getDefaultValue(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget? { + override fun getDefaultValue( + parameter: Parameter<ParcelTarget, Int>, + sender: CommandSender, + buffer: ArgumentBuffer + ): ParcelTarget? { val kind = parameter.paramInfo ?: DEFAULT_KIND val useLocation = when { kind and REAL == REAL -> kind and PREFER_OWNED_FOR_DEFAULT == 0 @@ -178,7 +202,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef } val player = requirePlayer(sender, parameter, "the parcel") - val world = parcelProvider.getWorld(player.world) ?: invalidInput(parameter, "You must be in a parcel world to omit the parcel") + val world = parcelProvider.getWorld(player.world) ?: invalidInput(parameter, + "You must be in a parcel world to omit the parcel") if (useLocation) { val id = player.location.let { world.getParcelIdAt(it.x.floor(), it.z.floor())?.pos } return ByID(world, id, kind, true) diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt index b616f77..52f1104 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt @@ -1,24 +1,24 @@ -package io.dico.parcels2.command - -import io.dico.dicore.Formatting -import io.dico.dicore.command.EMessageType -import io.dico.dicore.command.ExecutionContext -import io.dico.dicore.command.chat.AbstractChatHandler -import io.dico.parcels2.util.ext.plus - -class ParcelsChatHandler : AbstractChatHandler() { - - override fun getMessagePrefixForType(type: EMessageType?): String { - return Formatting.RED + "[Parcels] " - } - - override fun createMessage(context: ExecutionContext, type: EMessageType, message: String?): String? { - if (message.isNullOrEmpty()) return null - var result = getChatFormatForType(type) + message - if (context.address.mainKey != "info") { - result = getMessagePrefixForType(type) + result - } - return result - } - +package io.dico.parcels2.command
+
+import io.dico.dicore.Formatting
+import io.dico.dicore.command.EMessageType
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.chat.AbstractChatHandler
+import io.dico.parcels2.util.ext.plus
+
+class ParcelsChatHandler : AbstractChatHandler() {
+
+ override fun getMessagePrefixForType(type: EMessageType?): String {
+ return Formatting.RED + "[Parcels] "
+ }
+
+ override fun createMessage(context: ExecutionContext, type: EMessageType, message: String?): String? {
+ if (message.isNullOrEmpty()) return null
+ var result = getChatFormatForType(type) + message
+ if (context.address.mainKey != "info") {
+ result = getMessagePrefixForType(type) + result
+ }
+ return result
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt index 1193af3..f3cd8d7 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt @@ -45,7 +45,7 @@ class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer { } } - override fun nextEmptyParcel(): Parcel? { + override suspend fun nextEmptyParcel(): Parcel? { return walkInCircle().find { it.owner == null } } diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt index 9e43c05..73b6b4d 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt @@ -11,6 +11,7 @@ import org.bukkit.block.BlockFace import org.bukkit.block.Skull import org.bukkit.block.data.type.Slab import org.bukkit.block.data.type.WallSign +import org.bukkit.entity.Player import java.util.Random private val airType = Bukkit.createBlockData(Material.AIR) @@ -285,17 +286,29 @@ class DefaultParcelGenerator( val floorType = o.floorType val fillType = o.fillType - for ((index, vec) in blocks.withIndex()) { - markSuspensionPoint() - val y = vec.y - val blockType = when { - y > floorHeight -> airType - y == floorHeight -> floorType - else -> fillType + delegateWork(0.95) { + for ((index, vec) in blocks.withIndex()) { + markSuspensionPoint() + val y = vec.y + val blockType = when { + y > floorHeight -> airType + y == floorHeight -> floorType + else -> fillType + } + world[vec].blockData = blockType + setProgress((index + 1) / blockCount) + } + } + + delegateWork { + val entities = getEntities(region) + for ((index, entity) in entities.withIndex()) { + if (entity is Player) continue + entity.remove() + setProgress((index + 1) / entities.size.toDouble()) } - world[vec].blockData = blockType - setProgress((index + 1) / blockCount) } + } override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> { diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt index 769cee6..670dd94 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt @@ -1,28 +1,28 @@ -@file:Suppress("UNCHECKED_CAST") - -package io.dico.parcels2.defaultimpl - -import io.dico.parcels2.* -import io.dico.parcels2.util.ext.alsoIfTrue -import java.util.Collections - -class GlobalPrivilegesManagerImpl(val plugin: ParcelsPlugin) : GlobalPrivilegesManager { - private val map = mutableMapOf<PlayerProfile, GlobalPrivileges>() - - override fun get(owner: PlayerProfile.Real): GlobalPrivileges { - return map[owner] ?: GlobalPrivilegesImpl(owner).also { map[owner] = it } - } - - private inner class GlobalPrivilegesImpl(override val keyOfOwner: PlayerProfile.Real) : PrivilegesHolder(), GlobalPrivileges { - override var privilegeOfStar: Privilege - get() = super<GlobalPrivileges>.privilegeOfStar - set(value) = run { super<GlobalPrivileges>.privilegeOfStar = value } - - override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean { - return super.setRawStoredPrivilege(key, privilege).alsoIfTrue { - plugin.storage.setGlobalPrivilege(keyOfOwner, key, privilege) - } - } - } - +@file:Suppress("UNCHECKED_CAST")
+
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.*
+import io.dico.parcels2.util.ext.alsoIfTrue
+import java.util.Collections
+
+class GlobalPrivilegesManagerImpl(val plugin: ParcelsPlugin) : GlobalPrivilegesManager {
+ private val map = mutableMapOf<PlayerProfile, GlobalPrivileges>()
+
+ override fun get(owner: PlayerProfile.Real): GlobalPrivileges {
+ return map[owner] ?: GlobalPrivilegesImpl(owner).also { map[owner] = it }
+ }
+
+ private inner class GlobalPrivilegesImpl(override val keyOfOwner: PlayerProfile.Real) : PrivilegesHolder(), GlobalPrivileges {
+ override var privilegeOfStar: Privilege
+ get() = super<GlobalPrivileges>.privilegeOfStar
+ set(value) = run { super<GlobalPrivileges>.privilegeOfStar = value }
+
+ override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ return super.setRawStoredPrivilege(key, privilege).alsoIfTrue {
+ plugin.storage.setGlobalPrivilege(keyOfOwner, key, privilege)
+ }
+ }
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt index a3704c7..99df82a 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt @@ -1,88 +1,88 @@ -package io.dico.parcels2.defaultimpl - -import io.dico.dicore.Formatting -import io.dico.parcels2.Privilege -import io.dico.parcels2.PrivilegeKey -import io.dico.parcels2.RawPrivileges -import io.dico.parcels2.filterProfilesWithPrivilegeTo - -object InfoBuilder { - val infoStringColor1 = Formatting.GREEN - val infoStringColor2 = Formatting.AQUA - - inline fun StringBuilder.appendField(field: StringBuilder.() -> Unit, value: StringBuilder.() -> Unit) { - append(infoStringColor1) - field() - append(": ") - append(infoStringColor2) - value() - append(' ') - } - - inline fun StringBuilder.appendField(name: String, value: StringBuilder.() -> Unit) { - appendField({ append(name) }, value) - } - - inline fun StringBuilder.appendFieldWithCount(name: String, count: Int, value: StringBuilder.() -> Unit) { - appendField({ - append(name) - append('(') - append(infoStringColor2) - append(count) - append(infoStringColor1) - append(')') - }, value) - } - - fun StringBuilder.appendProfilesWithPrivilege(fieldName: String, local: RawPrivileges, global: RawPrivileges?, privilege: Privilege) { - val map = linkedMapOf<PrivilegeKey, Privilege>() - local.filterProfilesWithPrivilegeTo(map, privilege) - val localCount = map.size - global?.filterProfilesWithPrivilegeTo(map, privilege) - appendPrivilegeProfiles(fieldName, map, localCount) - } - - fun StringBuilder.appendPrivilegeProfiles(fieldName: String, map: LinkedHashMap<PrivilegeKey, Privilege>, localCount: Int) { - if (map.isEmpty()) return - - appendFieldWithCount(fieldName, map.size) { - // first [localCount] entries are local - val separator = "$infoStringColor1, $infoStringColor2" - val iterator = map.iterator() - - if (localCount != 0) { - appendPrivilegeEntry(false, iterator.next().toPair()) - repeat(localCount - 1) { - append(separator) - appendPrivilegeEntry(false, iterator.next().toPair()) - } - - } else if (iterator.hasNext()) { - // ensure there is never a leading or trailing separator - appendPrivilegeEntry(true, iterator.next().toPair()) - } - - iterator.forEach { next -> - append(separator) - appendPrivilegeEntry(true, next.toPair()) - } - } - } - - fun StringBuilder.appendPrivilegeEntry(global: Boolean, pair: Pair<PrivilegeKey, Privilege>) { - val (key, priv) = pair - - append(key.notNullName) - - // suffix. Maybe T should be M for mod or something. T means they have CAN_MANAGE privilege. - append( - when { - global && priv == Privilege.CAN_MANAGE -> " (G) (T)" - global -> " (G)" - priv == Privilege.CAN_MANAGE -> " (T)" - else -> "" - } - ) - } - +package io.dico.parcels2.defaultimpl
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.Privilege
+import io.dico.parcels2.PrivilegeKey
+import io.dico.parcels2.RawPrivileges
+import io.dico.parcels2.filterProfilesWithPrivilegeTo
+
+object InfoBuilder {
+ val infoStringColor1 = Formatting.GREEN
+ val infoStringColor2 = Formatting.AQUA
+
+ inline fun StringBuilder.appendField(field: StringBuilder.() -> Unit, value: StringBuilder.() -> Unit) {
+ append(infoStringColor1)
+ field()
+ append(": ")
+ append(infoStringColor2)
+ value()
+ append(' ')
+ }
+
+ inline fun StringBuilder.appendField(name: String, value: StringBuilder.() -> Unit) {
+ appendField({ append(name) }, value)
+ }
+
+ inline fun StringBuilder.appendFieldWithCount(name: String, count: Int, value: StringBuilder.() -> Unit) {
+ appendField({
+ append(name)
+ append('(')
+ append(infoStringColor2)
+ append(count)
+ append(infoStringColor1)
+ append(')')
+ }, value)
+ }
+
+ fun StringBuilder.appendProfilesWithPrivilege(fieldName: String, local: RawPrivileges, global: RawPrivileges?, privilege: Privilege) {
+ val map = linkedMapOf<PrivilegeKey, Privilege>()
+ local.filterProfilesWithPrivilegeTo(map, privilege)
+ val localCount = map.size
+ global?.filterProfilesWithPrivilegeTo(map, privilege)
+ appendPrivilegeProfiles(fieldName, map, localCount)
+ }
+
+ fun StringBuilder.appendPrivilegeProfiles(fieldName: String, map: LinkedHashMap<PrivilegeKey, Privilege>, localCount: Int) {
+ if (map.isEmpty()) return
+
+ appendFieldWithCount(fieldName, map.size) {
+ // first [localCount] entries are local
+ val separator = "$infoStringColor1, $infoStringColor2"
+ val iterator = map.iterator()
+
+ if (localCount != 0) {
+ appendPrivilegeEntry(false, iterator.next().toPair())
+ repeat(localCount - 1) {
+ append(separator)
+ appendPrivilegeEntry(false, iterator.next().toPair())
+ }
+
+ } else if (iterator.hasNext()) {
+ // ensure there is never a leading or trailing separator
+ appendPrivilegeEntry(true, iterator.next().toPair())
+ }
+
+ iterator.forEach { next ->
+ append(separator)
+ appendPrivilegeEntry(true, next.toPair())
+ }
+ }
+ }
+
+ fun StringBuilder.appendPrivilegeEntry(global: Boolean, pair: Pair<PrivilegeKey, Privilege>) {
+ val (key, priv) = pair
+
+ append(key.notNullName)
+
+ // suffix. Maybe T should be M for mod or something. T means they have CAN_MANAGE privilege.
+ append(
+ when {
+ global && priv == Privilege.CAN_MANAGE -> " (G) (T)"
+ global -> " (G)"
+ priv == Privilege.CAN_MANAGE -> " (T)"
+ else -> ""
+ }
+ )
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt index 48a7fee..7748fc7 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt @@ -2,12 +2,18 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.* import io.dico.parcels2.blockvisitor.Schematic +import io.dico.parcels2.util.math.Region +import io.dico.parcels2.util.math.Vec3d +import io.dico.parcels2.util.math.Vec3i import io.dico.parcels2.util.schedule import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.bukkit.Bukkit +import org.bukkit.World import org.bukkit.WorldCreator +import org.bukkit.entity.Entity +import org.bukkit.util.Vector import org.joda.time.DateTime class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { @@ -45,7 +51,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { private fun loadWorlds0() { if (Bukkit.getWorlds().isEmpty()) { - plugin.schedule(::loadWorlds0) + plugin.schedule { loadWorlds0() } plugin.logger.warning("Scheduling to load worlds in the next tick because no bukkit worlds are loaded yet") return } @@ -64,7 +70,8 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { WorldCreator(worldName).generator(generator).createWorld() } - parcelWorld = ParcelWorldImpl(plugin, bukkitWorld, generator, worldOptions.runtime,::DefaultParcelContainer) + parcelWorld = + ParcelWorldImpl(plugin, bukkitWorld, generator, worldOptions.runtime, ::DefaultParcelContainer) if (!worldExists) { val time = DateTime.now() @@ -73,7 +80,8 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { newlyCreatedWorlds.add(parcelWorld) } else { GlobalScope.launch(context = Dispatchers.Unconfined) { - parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: DateTime.now() + parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: + DateTime.now() } } @@ -84,7 +92,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { } private fun loadStoredData(newlyCreatedWorlds: Collection<ParcelWorld> = emptyList()) { - plugin.launch(Dispatchers.Default) { + plugin.launch { val migration = plugin.options.migration if (migration.enabled) { migration.instance?.newInstance()?.apply { @@ -155,10 +163,32 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { } override fun swapParcels(parcelId1: ParcelId, parcelId2: ParcelId): Job? { - val blockManager1 = getWorldById(parcelId1.worldId)?.blockManager ?: return null - val blockManager2 = getWorldById(parcelId2.worldId)?.blockManager ?: return null + val world1 = getWorldById(parcelId1.worldId) ?: return null + val world2 = getWorldById(parcelId2.worldId) ?: return null + val blockManager1 = world1.blockManager + val blockManager2 = world2.blockManager + + class CopyTarget(val world: World, val region: Region) + class CopySource(val origin: Vec3i, val schematic: Schematic, val entities: Collection<Entity>) + + suspend fun JobScope.copy(source: CopySource, target: CopyTarget) { + with(source.schematic) { paste(target.world, target.region.origin) } + + for (entity in source.entities) { + entity.velocity = Vector(0, 0, 0) + val location = entity.location + location.world = target.world + val coords = target.region.origin + (Vec3d(entity.location) - source.origin) + coords.copyInto(location) + entity.teleport(location) + } + } return trySubmitBlockVisitor(Permit(), parcelId1, parcelId2) { + val temporaryParcel = world1.nextEmptyParcel() + ?: world2.nextEmptyParcel() + ?: return@trySubmitBlockVisitor + var region1 = blockManager1.getRegion(parcelId1) var region2 = blockManager2.getRegion(parcelId2) @@ -168,10 +198,41 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { region2 = region2.withSize(size) } - val schematicOf1 = delegateWork(0.25) { Schematic().apply { load(blockManager1.world, region1) } } - val schematicOf2 = delegateWork(0.25) { Schematic().apply { load(blockManager2.world, region2) } } - delegateWork(0.25) { with(schematicOf1) { paste(blockManager2.world, region2.origin) } } - delegateWork(0.25) { with(schematicOf2) { paste(blockManager1.world, region1.origin) } } + // Teleporting entities safely requires a different approach: + // * Copy schematic1 into temporary location + // * Teleport entities1 into temporary location + // * Copy schematic2 into parcel1 + // * Teleport entities2 into parcel1 + // * Copy schematic1 into parcel2 + // * Teleport entities1 into parcel2 + // * Clear temporary location + + lateinit var source1: CopySource + lateinit var source2: CopySource + + delegateWork(0.30) { + val schematicOf1 = delegateWork(0.50) { Schematic().apply { load(blockManager1.world, region1) } } + val schematicOf2 = delegateWork(0.50) { Schematic().apply { load(blockManager2.world, region2) } } + + source1 = CopySource(region1.origin, schematicOf1, blockManager1.getEntities(region1)) + source2 = CopySource(region2.origin, schematicOf2, blockManager2.getEntities(region2)) + } + + val target1 = CopyTarget(blockManager1.world, region1) + val target2 = CopyTarget(blockManager2.world, region2) + val targetTemp = CopyTarget( + temporaryParcel.world.world, + temporaryParcel.world.blockManager.getRegion(temporaryParcel.id) + ) + + delegateWork { + delegateWork(1.0 / 3.0) { copy(source1, targetTemp) } + delegateWork(1.0 / 3.0) { copy(source2, target1) } + delegateWork(1.0 / 3.0) { copy(source1, target2) } + } + + // Separate job. Whatever + temporaryParcel.world.blockManager.clearParcel(temporaryParcel.id) } } diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt index 531a25f..cb322d4 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt @@ -69,7 +69,7 @@ class ParcelWorldImpl( override fun getParcelById(id: ParcelId): Parcel? = container.getParcelById(id) - override fun nextEmptyParcel(): Parcel? = container.nextEmptyParcel() + override suspend fun nextEmptyParcel(): Parcel? = container.nextEmptyParcel() override fun toString() = parcelWorldIdToString() } diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt index 198e0e7..78b8b03 100644 --- a/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt +++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt @@ -1,61 +1,61 @@ -package io.dico.parcels2.listener - -import io.dico.parcels2.Parcel -import io.dico.parcels2.ParcelProvider -import io.dico.parcels2.util.ext.editLoop -import org.bukkit.entity.Entity - -class ParcelEntityTracker(val parcelProvider: ParcelProvider) { - val map = mutableMapOf<Entity, Parcel?>() - - fun untrack(entity: Entity) { - map.remove(entity) - } - - fun track(entity: Entity, parcel: Parcel?) { - map[entity] = parcel - } - - /* - * Tracks entities. If the entity is dead, they are removed from the collection. - * If the entity is found to have left the parcel it was created in, it will be removed from the world and from the list. - * If it is still in the parcel it was created in, and it is on the ground, it is removed from the list. - * - * Start after 5 seconds, run every 0.25 seconds - */ - fun tick() { - map.editLoop { entity, parcel -> - if (entity.isDead) { - remove(); return@editLoop - } - - if (parcel != null && parcel.hasBlockVisitors) { - remove() - - val newParcel = parcelProvider.getParcelAt(entity.location) - if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) { - entity.remove() - } - - return@editLoop - } - - val newParcel = parcelProvider.getParcelAt(entity.location) - if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) { - remove() - entity.remove() - } - } - } - - fun swapParcels(parcel1: Parcel, parcel2: Parcel) { - map.editLoop { -> - if (value === parcel1) { - value = parcel2 - } else if (value === parcel2) { - value = parcel1 - } - } - } - +package io.dico.parcels2.listener
+
+import io.dico.parcels2.Parcel
+import io.dico.parcels2.ParcelProvider
+import io.dico.parcels2.util.ext.editLoop
+import org.bukkit.entity.Entity
+
+class ParcelEntityTracker(val parcelProvider: ParcelProvider) {
+ val map = mutableMapOf<Entity, Parcel?>()
+
+ fun untrack(entity: Entity) {
+ map.remove(entity)
+ }
+
+ fun track(entity: Entity, parcel: Parcel?) {
+ map[entity] = parcel
+ }
+
+ /*
+ * Tracks entities. If the entity is dead, they are removed from the collection.
+ * If the entity is found to have left the parcel it was created in, it will be removed from the world and from the list.
+ * If it is still in the parcel it was created in, and it is on the ground, it is removed from the list.
+ *
+ * Start after 5 seconds, run every 0.25 seconds
+ */
+ fun tick() {
+ map.editLoop { entity, parcel ->
+ if (entity.isDead) {
+ remove(); return@editLoop
+ }
+
+ if (parcel != null && parcel.hasBlockVisitors) {
+ remove()
+
+ val newParcel = parcelProvider.getParcelAt(entity.location)
+ if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
+ entity.remove()
+ }
+
+ return@editLoop
+ }
+
+ val newParcel = parcelProvider.getParcelAt(entity.location)
+ if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
+ remove()
+ entity.remove()
+ }
+ }
+ }
+
+ fun swapParcels(parcel1: Parcel, parcel2: Parcel) {
+ map.editLoop { ->
+ if (value === parcel1) {
+ value = parcel2
+ } else if (value === parcel2) {
+ value = parcel1
+ }
+ }
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt index 9c9bdc2..e87dd68 100644 --- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt +++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt @@ -10,6 +10,7 @@ import io.dico.parcels2.storage.Storage import io.dico.parcels2.util.ext.* import io.dico.parcels2.util.math.* import org.bukkit.Location +import org.bukkit.Material import org.bukkit.Material.* import org.bukkit.World import org.bukkit.block.Biome @@ -209,7 +210,7 @@ class ParcelListeners( * Prevents player from using beds in HELL or SKY biomes if explosions are disabled. */ @Suppress("NON_EXHAUSTIVE_WHEN") - @field:ListenerMarker(priority = NORMAL) + @field:ListenerMarker(priority = NORMAL, ignoreCancelled = false) val onPlayerInteractEvent = RegistratorListener<PlayerInteractEvent> l@{ event -> val user = event.player val world = parcelProvider.getWorld(user.world) ?: return@l @@ -223,6 +224,7 @@ class ParcelListeners( when (event.action) { Action.RIGHT_CLICK_BLOCK -> run { + if (event.isCancelled) return@l val type = clickedBlock.type val interactableClass = Interactables[type] @@ -259,9 +261,15 @@ class ParcelListeners( } Action.RIGHT_CLICK_AIR -> onPlayerRightClick(event, world, parcel) - Action.PHYSICAL -> if (!canBuildOnArea(user, parcel) && !(parcel != null && parcel.interactableConfig("pressure_plates"))) { - user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel") - event.isCancelled = true; return@l + Action.PHYSICAL -> if (!event.isCancelled && !canBuildOnArea(user, parcel)) { + if (clickedBlock.type == Material.TURTLE_EGG) { + event.isCancelled = true; return@l + } + + if (!(parcel != null && parcel.interactableConfig("pressure_plates"))) { + user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel") + event.isCancelled = true; return@l + } } } } @@ -437,13 +445,15 @@ class ParcelListeners( @field:ListenerMarker(priority = NORMAL) val onEntitySpawnEvent = RegistratorListener<EntitySpawnEvent> l@{ event -> val world = parcelProvider.getWorld(event.entity.world) ?: return@l - if (event.entity is Creature && world.options.blockMobSpawning) { + if (event.entity is Mob && world.options.blockMobSpawning) { event.isCancelled = true } else if (world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) { event.isCancelled = true } } + + /* * Prevents minecarts/boats from moving outside a plot */ @@ -471,7 +481,7 @@ class ParcelListeners( @field:ListenerMarker(priority = NORMAL) val onEntityDamageByEntityEvent = RegistratorListener<EntityDamageByEntityEvent> l@{ event -> val world = parcelProvider.getWorld(event.entity.world) ?: return@l - if (world.options.disableExplosions && event.damager is ExplosiveMinecart || event.damager is Creeper) { + if (world.options.disableExplosions && (event.damager is ExplosiveMinecart || event.damager is Creeper)) { event.isCancelled = true; return@l } @@ -538,6 +548,14 @@ class ParcelListeners( event.blocks.removeIf { world.getParcelAt(it.block) !== area } } + @field:ListenerMarker(priority = NORMAL) + val onBlockGrowEvent = RegistratorListener<BlockGrowEvent> l@{ event -> + val (world, area) = getWorldAndArea(event.block) ?: return@l + if (area == null) { + event.isCancelled = true + } + } + /* * Prevents dispensers/droppers from dispensing out of parcels */ diff --git a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt index 4d35a53..5bab29a 100644 --- a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt +++ b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt @@ -1,13 +1,13 @@ package io.dico.parcels2.listener import com.sk89q.worldedit.EditSession.Stage.BEFORE_REORDER -import com.sk89q.worldedit.Vector -import com.sk89q.worldedit.Vector2D import com.sk89q.worldedit.WorldEdit import com.sk89q.worldedit.bukkit.WorldEditPlugin import com.sk89q.worldedit.event.extent.EditSessionEvent import com.sk89q.worldedit.extent.AbstractDelegateExtent import com.sk89q.worldedit.extent.Extent +import com.sk89q.worldedit.math.BlockVector2 +import com.sk89q.worldedit.math.BlockVector3 import com.sk89q.worldedit.util.eventbus.EventHandler.Priority.VERY_EARLY import com.sk89q.worldedit.util.eventbus.Subscribe import com.sk89q.worldedit.world.biome.BaseBiome @@ -57,14 +57,13 @@ class WorldEditListener(val parcels: ParcelsPlugin, val worldEdit: WorldEdit) { return false } - override fun setBlock(location: Vector, block: BlockStateHolder<*>): Boolean { - return canBuild(location.blockX, location.blockZ) && super.setBlock(location, block) - } - - override fun setBiome(coord: Vector2D, biome: BaseBiome): Boolean { + override fun setBiome(coord: BlockVector2, biome: BaseBiome): Boolean { return canBuild(coord.blockX, coord.blockZ) && super.setBiome(coord, biome) } + override fun <T : BlockStateHolder<T>> setBlock(location: BlockVector3, block: T): Boolean { + return canBuild(location.blockX, location.blockZ) && super.setBlock(location, block) + } } companion object { diff --git a/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt b/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt index d0626dc..a6a57e5 100644 --- a/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt +++ b/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt @@ -1,36 +1,36 @@ -package io.dico.parcels2.options - -import io.dico.parcels2.ParcelGenerator -import io.dico.parcels2.defaultimpl.DefaultParcelGenerator -import org.bukkit.Bukkit -import org.bukkit.Material -import org.bukkit.block.Biome -import org.bukkit.block.data.BlockData -import kotlin.reflect.KClass - -object GeneratorOptionsFactories : PolymorphicOptionsFactories<ParcelGenerator>("name", GeneratorOptions::class, DefaultGeneratorOptionsFactory()) - -class GeneratorOptions (name: String = "default", options: Any = DefaultGeneratorOptions()) : PolymorphicOptions<ParcelGenerator>(name, options, GeneratorOptionsFactories) { - fun newInstance(worldName: String) = factory.newInstance(key, options, worldName) -} - -private class DefaultGeneratorOptionsFactory : PolymorphicOptionsFactory<ParcelGenerator> { - override val supportedKeys: List<String> = listOf("default") - override val optionsClass: KClass<out Any> get() = DefaultGeneratorOptions::class - - override fun newInstance(key: String, options: Any, vararg extra: Any?): ParcelGenerator { - return DefaultParcelGenerator(extra.first() as String, options as DefaultGeneratorOptions) - } -} - -class DefaultGeneratorOptions(val defaultBiome: Biome = Biome.JUNGLE, - val wallType: BlockData = Bukkit.createBlockData(Material.STONE_SLAB), - val floorType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK), - val fillType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK), - val pathMainType: BlockData = Bukkit.createBlockData(Material.SANDSTONE), - val pathAltType: BlockData = Bukkit.createBlockData(Material.REDSTONE_BLOCK), - val parcelSize: Int = 101, - val pathSize: Int = 9, - val floorHeight: Int = 64, - val offsetX: Int = 0, +package io.dico.parcels2.options
+
+import io.dico.parcels2.ParcelGenerator
+import io.dico.parcels2.defaultimpl.DefaultParcelGenerator
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.block.Biome
+import org.bukkit.block.data.BlockData
+import kotlin.reflect.KClass
+
+object GeneratorOptionsFactories : PolymorphicOptionsFactories<ParcelGenerator>("name", GeneratorOptions::class, DefaultGeneratorOptionsFactory())
+
+class GeneratorOptions (name: String = "default", options: Any = DefaultGeneratorOptions()) : PolymorphicOptions<ParcelGenerator>(name, options, GeneratorOptionsFactories) {
+ fun newInstance(worldName: String) = factory.newInstance(key, options, worldName)
+}
+
+private class DefaultGeneratorOptionsFactory : PolymorphicOptionsFactory<ParcelGenerator> {
+ override val supportedKeys: List<String> = listOf("default")
+ override val optionsClass: KClass<out Any> get() = DefaultGeneratorOptions::class
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): ParcelGenerator {
+ return DefaultParcelGenerator(extra.first() as String, options as DefaultGeneratorOptions)
+ }
+}
+
+class DefaultGeneratorOptions(val defaultBiome: Biome = Biome.JUNGLE,
+ val wallType: BlockData = Bukkit.createBlockData(Material.STONE_SLAB),
+ val floorType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
+ val fillType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
+ val pathMainType: BlockData = Bukkit.createBlockData(Material.SANDSTONE),
+ val pathAltType: BlockData = Bukkit.createBlockData(Material.REDSTONE_BLOCK),
+ val parcelSize: Int = 101,
+ val pathSize: Int = 9,
+ val floorHeight: Int = 64,
+ val offsetX: Int = 0,
val offsetZ: Int = 0)
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt b/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt index 5e36099..7dd752e 100644 --- a/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt +++ b/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt @@ -1,22 +1,22 @@ -package io.dico.parcels2.options - -import io.dico.parcels2.storage.migration.Migration -import io.dico.parcels2.storage.migration.plotme.PlotmeMigration -import kotlin.reflect.KClass - -object MigrationOptionsFactories : PolymorphicOptionsFactories<Migration>("kind", MigrationOptions::class, PlotmeMigrationFactory()) - -class MigrationOptions(kind: String = "plotme-0.17", options: Any = PlotmeMigrationOptions()) : SimplePolymorphicOptions<Migration>(kind, options, MigrationOptionsFactories) - -private class PlotmeMigrationFactory : PolymorphicOptionsFactory<Migration> { - override val supportedKeys = listOf("plotme-0.17") - override val optionsClass: KClass<out Any> get() = PlotmeMigrationOptions::class - - override fun newInstance(key: String, options: Any, vararg extra: Any?): Migration { - return PlotmeMigration(options as PlotmeMigrationOptions) - } -} - -class PlotmeMigrationOptions(val worldsFromTo: Map<String, String> = mapOf("plotworld" to "parcels"), - val storage: StorageOptions = StorageOptions(options = DataConnectionOptions(database = "plotme")), +package io.dico.parcels2.options
+
+import io.dico.parcels2.storage.migration.Migration
+import io.dico.parcels2.storage.migration.plotme.PlotmeMigration
+import kotlin.reflect.KClass
+
+object MigrationOptionsFactories : PolymorphicOptionsFactories<Migration>("kind", MigrationOptions::class, PlotmeMigrationFactory())
+
+class MigrationOptions(kind: String = "plotme-0.17", options: Any = PlotmeMigrationOptions()) : SimplePolymorphicOptions<Migration>(kind, options, MigrationOptionsFactories)
+
+private class PlotmeMigrationFactory : PolymorphicOptionsFactory<Migration> {
+ override val supportedKeys = listOf("plotme-0.17")
+ override val optionsClass: KClass<out Any> get() = PlotmeMigrationOptions::class
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): Migration {
+ return PlotmeMigration(options as PlotmeMigrationOptions)
+ }
+}
+
+class PlotmeMigrationOptions(val worldsFromTo: Map<String, String> = mapOf("plotworld" to "parcels"),
+ val storage: StorageOptions = StorageOptions(options = DataConnectionOptions(database = "plotme")),
val tableNamesUppercase: Boolean = false)
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/options/Options.kt b/src/main/kotlin/io/dico/parcels2/options/Options.kt index 35d48ba..412c783 100644 --- a/src/main/kotlin/io/dico/parcels2/options/Options.kt +++ b/src/main/kotlin/io/dico/parcels2/options/Options.kt @@ -1,58 +1,58 @@ -package io.dico.parcels2.options - -import io.dico.parcels2.TickJobtimeOptions -import org.bukkit.GameMode -import org.bukkit.Material -import java.io.Reader -import java.io.Writer -import java.util.EnumSet - -class Options { - var worlds: Map<String, WorldOptions> = hashMapOf() - private set - var storage: StorageOptions = StorageOptions() - var tickJobtime: TickJobtimeOptions = TickJobtimeOptions(20, 1) - var migration = MigrationOptionsHolder() - - fun addWorld(name: String, - generatorOptions: GeneratorOptions? = null, - worldOptions: RuntimeWorldOptions? = null) { - val optionsHolder = WorldOptions( - generatorOptions ?: GeneratorOptions(), - worldOptions ?: RuntimeWorldOptions() - ) - - (worlds as MutableMap).put(name, optionsHolder) - } - - fun writeTo(writer: Writer) = optionsMapper.writeValue(writer, this) - - fun mergeFrom(reader: Reader) = optionsMapper.readerForUpdating(this).readValue<Options>(reader) - - override fun toString(): String = optionsMapper.writeValueAsString(this) - -} - -class WorldOptions(val generator: GeneratorOptions, - var runtime: RuntimeWorldOptions = RuntimeWorldOptions()) - -class RuntimeWorldOptions(var gameMode: GameMode? = GameMode.CREATIVE, - var dayTime: Boolean = true, - var noWeather: Boolean = true, - var preventWeatherBlockChanges: Boolean = true, - var preventBlockSpread: Boolean = true, // TODO - var dropEntityItems: Boolean = true, - var doTileDrops: Boolean = false, - var disableExplosions: Boolean = true, - var blockPortalCreation: Boolean = true, - var blockMobSpawning: Boolean = true, - var blockedItems: Set<Material> = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL), - var axisLimit: Int = 10) - -class DataFileOptions(val location: String = "/flatfile-storage/") - -class MigrationOptionsHolder { - var enabled = false - var disableWhenComplete = true - var instance: MigrationOptions? = MigrationOptions() +package io.dico.parcels2.options
+
+import io.dico.parcels2.TickJobtimeOptions
+import org.bukkit.GameMode
+import org.bukkit.Material
+import java.io.Reader
+import java.io.Writer
+import java.util.EnumSet
+
+class Options {
+ var worlds: Map<String, WorldOptions> = hashMapOf()
+ private set
+ var storage: StorageOptions = StorageOptions()
+ var tickJobtime: TickJobtimeOptions = TickJobtimeOptions(20, 1)
+ var migration = MigrationOptionsHolder()
+
+ fun addWorld(name: String,
+ generatorOptions: GeneratorOptions? = null,
+ worldOptions: RuntimeWorldOptions? = null) {
+ val optionsHolder = WorldOptions(
+ generatorOptions ?: GeneratorOptions(),
+ worldOptions ?: RuntimeWorldOptions()
+ )
+
+ (worlds as MutableMap).put(name, optionsHolder)
+ }
+
+ fun writeTo(writer: Writer) = optionsMapper.writeValue(writer, this)
+
+ fun mergeFrom(reader: Reader) = optionsMapper.readerForUpdating(this).readValue<Options>(reader)
+
+ override fun toString(): String = optionsMapper.writeValueAsString(this)
+
+}
+
+class WorldOptions(val generator: GeneratorOptions,
+ var runtime: RuntimeWorldOptions = RuntimeWorldOptions())
+
+class RuntimeWorldOptions(var gameMode: GameMode? = GameMode.CREATIVE,
+ var dayTime: Boolean = true,
+ var noWeather: Boolean = true,
+ var preventWeatherBlockChanges: Boolean = true,
+ var preventBlockSpread: Boolean = true, // TODO
+ var dropEntityItems: Boolean = true,
+ var doTileDrops: Boolean = false,
+ var disableExplosions: Boolean = true,
+ var blockPortalCreation: Boolean = true,
+ var blockMobSpawning: Boolean = true,
+ var blockedItems: Set<Material> = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL),
+ var axisLimit: Int = 10)
+
+class DataFileOptions(val location: String = "/flatfile-storage/")
+
+class MigrationOptionsHolder {
+ var enabled = false
+ var disableWhenComplete = true
+ var instance: MigrationOptions? = MigrationOptions()
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt b/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt index 671a25f..d741617 100644 --- a/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt +++ b/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt @@ -1,66 +1,66 @@ -package io.dico.parcels2.options - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.PropertyNamingStrategy -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.deser.std.StdDeserializer -import com.fasterxml.jackson.databind.ser.std.StdSerializer -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory -import com.fasterxml.jackson.module.kotlin.KotlinModule -import org.bukkit.Bukkit -import org.bukkit.block.data.BlockData - -val optionsMapper = ObjectMapper(YAMLFactory()).apply { - propertyNamingStrategy = PropertyNamingStrategy.KEBAB_CASE - - val kotlinModule = KotlinModule() - - with(kotlinModule) { - /* - setSerializerModifier(object : BeanSerializerModifier() { - @Suppress("UNCHECKED_CAST") - override fun modifySerializer(config: SerializationConfig?, beanDesc: BeanDescription, serializer: JsonSerializer<*>): JsonSerializer<*> { - - val newSerializer = if (GeneratorOptions::class.isSuperclassOf(beanDesc.beanClass.kotlin)) { - GeneratorOptionsSerializer(serializer as JsonSerializer<GeneratorOptions>) - } else { - serializer - } - - return super.modifySerializer(config, beanDesc, newSerializer) - } - })*/ - - addSerializer(BlockDataSerializer()) - addDeserializer(BlockData::class.java, BlockDataDeserializer()) - - GeneratorOptionsFactories.registerSerialization(this) - StorageOptionsFactories.registerSerialization(this) - MigrationOptionsFactories.registerSerialization(this) - } - - registerModule(kotlinModule) -} - -private class BlockDataSerializer : StdSerializer<BlockData>(BlockData::class.java) { - - override fun serialize(value: BlockData, gen: JsonGenerator, provider: SerializerProvider) { - gen.writeString(value.asString) - } - -} - -private class BlockDataDeserializer : StdDeserializer<BlockData>(BlockData::class.java) { - - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): BlockData? { - try { - return Bukkit.createBlockData(p.valueAsString) - } catch (ex: Exception) { - throw RuntimeException("Exception occurred at ${p.currentLocation}", ex) - } - } - -} +package io.dico.parcels2.options
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.PropertyNamingStrategy
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer
+import com.fasterxml.jackson.databind.ser.std.StdSerializer
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
+import com.fasterxml.jackson.module.kotlin.KotlinModule
+import org.bukkit.Bukkit
+import org.bukkit.block.data.BlockData
+
+val optionsMapper = ObjectMapper(YAMLFactory()).apply {
+ propertyNamingStrategy = PropertyNamingStrategy.KEBAB_CASE
+
+ val kotlinModule = KotlinModule()
+
+ with(kotlinModule) {
+ /*
+ setSerializerModifier(object : BeanSerializerModifier() {
+ @Suppress("UNCHECKED_CAST")
+ override fun modifySerializer(config: SerializationConfig?, beanDesc: BeanDescription, serializer: JsonSerializer<*>): JsonSerializer<*> {
+
+ val newSerializer = if (GeneratorOptions::class.isSuperclassOf(beanDesc.beanClass.kotlin)) {
+ GeneratorOptionsSerializer(serializer as JsonSerializer<GeneratorOptions>)
+ } else {
+ serializer
+ }
+
+ return super.modifySerializer(config, beanDesc, newSerializer)
+ }
+ })*/
+
+ addSerializer(BlockDataSerializer())
+ addDeserializer(BlockData::class.java, BlockDataDeserializer())
+
+ GeneratorOptionsFactories.registerSerialization(this)
+ StorageOptionsFactories.registerSerialization(this)
+ MigrationOptionsFactories.registerSerialization(this)
+ }
+
+ registerModule(kotlinModule)
+}
+
+private class BlockDataSerializer : StdSerializer<BlockData>(BlockData::class.java) {
+
+ override fun serialize(value: BlockData, gen: JsonGenerator, provider: SerializerProvider) {
+ gen.writeString(value.asString)
+ }
+
+}
+
+private class BlockDataDeserializer : StdDeserializer<BlockData>(BlockData::class.java) {
+
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext): BlockData? {
+ try {
+ return Bukkit.createBlockData(p.valueAsString)
+ } catch (ex: Exception) {
+ throw RuntimeException("Exception occurred at ${p.currentLocation}", ex)
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt b/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt index aa60f39..f65efa1 100644 --- a/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt +++ b/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt @@ -1,89 +1,89 @@ -package io.dico.parcels2.options - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.module.SimpleModule -import com.fasterxml.jackson.databind.ser.std.StdSerializer -import io.dico.parcels2.logger -import kotlin.reflect.KClass - -abstract class PolymorphicOptions<T : Any>(val key: String, - val options: Any, - factories: PolymorphicOptionsFactories<T>) { - val factory = factories.getFactory(key)!! -} - -abstract class SimplePolymorphicOptions<T : Any>(key: String, options: Any, factories: PolymorphicOptionsFactories<T>) - : PolymorphicOptions<T>(key, options, factories) { - fun newInstance(): T = factory.newInstance(key, options) -} - -interface PolymorphicOptionsFactory<T : Any> { - val supportedKeys: List<String> - val optionsClass: KClass<out Any> - fun newInstance(key: String, options: Any, vararg extra: Any?): T -} - -@Suppress("UNCHECKED_CAST") -abstract class PolymorphicOptionsFactories<T : Any>(val serializeKeyAs: String, - rootClass: KClass<out PolymorphicOptions<T>>, - vararg defaultFactories: PolymorphicOptionsFactory<T>) { - val rootClass = rootClass as KClass<PolymorphicOptions<T>> - private val map: MutableMap<String, PolymorphicOptionsFactory<T>> = linkedMapOf() - val availableKeys: Collection<String> get() = map.keys - - fun registerFactory(factory: PolymorphicOptionsFactory<T>) = factory.supportedKeys.forEach { map.putIfAbsent(it.toLowerCase(), factory) } - - fun getFactory(key: String): PolymorphicOptionsFactory<T>? = map[key.toLowerCase()] - - fun registerSerialization(module: SimpleModule) { - module.addSerializer(PolymorphicOptionsSerializer(this)) - module.addDeserializer(rootClass.java, PolymorphicOptionsDeserializer(this)) - } - - init { - defaultFactories.forEach { registerFactory(it) } - } -} - - -private class PolymorphicOptionsDeserializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : JsonDeserializer<PolymorphicOptions<T>>() { - - override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): PolymorphicOptions<T> { - val node = p.readValueAsTree<JsonNode>() - val key = node.get(factories.serializeKeyAs).asText() - val factory = getFactory(key) - val optionsNode = node.get("options") - val options = p.codec.treeToValue(optionsNode, factory.optionsClass.java) - return factories.rootClass.constructors.first().call(key, options) - } - - private fun getFactory(key: String): PolymorphicOptionsFactory<T> { - factories.getFactory(key)?.let { return it } - - logger.warn("Unknown ${factories.rootClass.simpleName} ${factories.serializeKeyAs}: $key. " + - "\nAvailable options: ${factories.availableKeys}") - - val default = factories.getFactory(factories.availableKeys.first()) - ?: throw IllegalStateException("No default ${factories.rootClass.simpleName} factory registered.") - return default - } - -} - -private class PolymorphicOptionsSerializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : StdSerializer<PolymorphicOptions<T>>(factories.rootClass.java) { - - override fun serialize(value: PolymorphicOptions<T>, gen: JsonGenerator, sp: SerializerProvider?) { - with(gen) { - writeStartObject() - writeStringField(factories.serializeKeyAs, value.key) - writeFieldName("options") - writeObject(value.options) - writeEndObject() - } - } +package io.dico.parcels2.options
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.JsonDeserializer
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.module.SimpleModule
+import com.fasterxml.jackson.databind.ser.std.StdSerializer
+import io.dico.parcels2.logger
+import kotlin.reflect.KClass
+
+abstract class PolymorphicOptions<T : Any>(val key: String,
+ val options: Any,
+ factories: PolymorphicOptionsFactories<T>) {
+ val factory = factories.getFactory(key)!!
+}
+
+abstract class SimplePolymorphicOptions<T : Any>(key: String, options: Any, factories: PolymorphicOptionsFactories<T>)
+ : PolymorphicOptions<T>(key, options, factories) {
+ fun newInstance(): T = factory.newInstance(key, options)
+}
+
+interface PolymorphicOptionsFactory<T : Any> {
+ val supportedKeys: List<String>
+ val optionsClass: KClass<out Any>
+ fun newInstance(key: String, options: Any, vararg extra: Any?): T
+}
+
+@Suppress("UNCHECKED_CAST")
+abstract class PolymorphicOptionsFactories<T : Any>(val serializeKeyAs: String,
+ rootClass: KClass<out PolymorphicOptions<T>>,
+ vararg defaultFactories: PolymorphicOptionsFactory<T>) {
+ val rootClass = rootClass as KClass<PolymorphicOptions<T>>
+ private val map: MutableMap<String, PolymorphicOptionsFactory<T>> = linkedMapOf()
+ val availableKeys: Collection<String> get() = map.keys
+
+ fun registerFactory(factory: PolymorphicOptionsFactory<T>) = factory.supportedKeys.forEach { map.putIfAbsent(it.toLowerCase(), factory) }
+
+ fun getFactory(key: String): PolymorphicOptionsFactory<T>? = map[key.toLowerCase()]
+
+ fun registerSerialization(module: SimpleModule) {
+ module.addSerializer(PolymorphicOptionsSerializer(this))
+ module.addDeserializer(rootClass.java, PolymorphicOptionsDeserializer(this))
+ }
+
+ init {
+ defaultFactories.forEach { registerFactory(it) }
+ }
+}
+
+
+private class PolymorphicOptionsDeserializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : JsonDeserializer<PolymorphicOptions<T>>() {
+
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): PolymorphicOptions<T> {
+ val node = p.readValueAsTree<JsonNode>()
+ val key = node.get(factories.serializeKeyAs).asText()
+ val factory = getFactory(key)
+ val optionsNode = node.get("options")
+ val options = p.codec.treeToValue(optionsNode, factory.optionsClass.java)
+ return factories.rootClass.constructors.first().call(key, options)
+ }
+
+ private fun getFactory(key: String): PolymorphicOptionsFactory<T> {
+ factories.getFactory(key)?.let { return it }
+
+ logger.warn("Unknown ${factories.rootClass.simpleName} ${factories.serializeKeyAs}: $key. " +
+ "\nAvailable options: ${factories.availableKeys}")
+
+ val default = factories.getFactory(factories.availableKeys.first())
+ ?: throw IllegalStateException("No default ${factories.rootClass.simpleName} factory registered.")
+ return default
+ }
+
+}
+
+private class PolymorphicOptionsSerializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : StdSerializer<PolymorphicOptions<T>>(factories.rootClass.java) {
+
+ override fun serialize(value: PolymorphicOptions<T>, gen: JsonGenerator, sp: SerializerProvider?) {
+ with(gen) {
+ writeStartObject()
+ writeStringField(factories.serializeKeyAs, value.key)
+ writeFieldName("options")
+ writeObject(value.options)
+ writeEndObject()
+ }
+ }
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt b/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt index 3d68701..29aab7a 100644 --- a/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt +++ b/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt @@ -1,59 +1,59 @@ -package io.dico.parcels2.options - -import com.zaxxer.hikari.HikariDataSource -import io.dico.parcels2.logger -import io.dico.parcels2.storage.Storage -import io.dico.parcels2.storage.BackedStorage -import io.dico.parcels2.storage.exposed.ExposedBacking -import io.dico.parcels2.storage.getHikariConfig -import javax.sql.DataSource - -object StorageOptionsFactories : PolymorphicOptionsFactories<Storage>("dialect", StorageOptions::class, ConnectionStorageFactory()) - -class StorageOptions(dialect: String = "mariadb", options: Any = DataConnectionOptions()) : SimplePolymorphicOptions<Storage>(dialect, options, StorageOptionsFactories) { - - fun getDataSourceFactory(): DataSourceFactory? { - return when (factory) { - is ConnectionStorageFactory -> factory.getDataSourceFactory(key, options) - else -> return null - } - } -} - -typealias DataSourceFactory = () -> DataSource - -private class ConnectionStorageFactory : PolymorphicOptionsFactory<Storage> { - override val optionsClass = DataConnectionOptions::class - override val supportedKeys: List<String> = listOf("postgresql", "mariadb") - - fun getDataSourceFactory(key: String, options: Any): DataSourceFactory { - val hikariConfig = getHikariConfig(key, options as DataConnectionOptions) - return { HikariDataSource(hikariConfig) } - } - - override fun newInstance(key: String, options: Any, vararg extra: Any?): Storage { - return BackedStorage(ExposedBacking(getDataSourceFactory(key, options), (options as DataConnectionOptions).poolSize)) - } -} - -data class DataConnectionOptions(val address: String = "localhost", - val database: String = "parcels", - val username: String = "root", - val password: String = "", - val poolSize: Int = 4) { - - fun splitAddressAndPort(defaultPort: Int = 3306): Pair<String, Int>? { - val idx = address.indexOf(":").takeUnless { it == -1 } ?: return Pair(address, defaultPort) - - val addressName = address.substring(0, idx).takeUnless { it.isBlank() } ?: return null.also { - logger.error("(Invalidly) blank address in data storage options") - } - - val port = address.substring(idx + 1).toIntOrNull() ?: return null.also { - logger.error("Invalid port number in data storage options: $it, using $defaultPort as default") - } - - return Pair(addressName, port) - } - +package io.dico.parcels2.options
+
+import com.zaxxer.hikari.HikariDataSource
+import io.dico.parcels2.logger
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.storage.BackedStorage
+import io.dico.parcels2.storage.exposed.ExposedBacking
+import io.dico.parcels2.storage.getHikariConfig
+import javax.sql.DataSource
+
+object StorageOptionsFactories : PolymorphicOptionsFactories<Storage>("dialect", StorageOptions::class, ConnectionStorageFactory())
+
+class StorageOptions(dialect: String = "mariadb", options: Any = DataConnectionOptions()) : SimplePolymorphicOptions<Storage>(dialect, options, StorageOptionsFactories) {
+
+ fun getDataSourceFactory(): DataSourceFactory? {
+ return when (factory) {
+ is ConnectionStorageFactory -> factory.getDataSourceFactory(key, options)
+ else -> return null
+ }
+ }
+}
+
+typealias DataSourceFactory = () -> DataSource
+
+private class ConnectionStorageFactory : PolymorphicOptionsFactory<Storage> {
+ override val optionsClass = DataConnectionOptions::class
+ override val supportedKeys: List<String> = listOf("postgresql", "mariadb")
+
+ fun getDataSourceFactory(key: String, options: Any): DataSourceFactory {
+ val hikariConfig = getHikariConfig(key, options as DataConnectionOptions)
+ return { HikariDataSource(hikariConfig) }
+ }
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): Storage {
+ return BackedStorage(ExposedBacking(getDataSourceFactory(key, options), (options as DataConnectionOptions).poolSize))
+ }
+}
+
+data class DataConnectionOptions(val address: String = "localhost",
+ val database: String = "parcels",
+ val username: String = "root",
+ val password: String = "",
+ val poolSize: Int = 4) {
+
+ fun splitAddressAndPort(defaultPort: Int = 3306): Pair<String, Int>? {
+ val idx = address.indexOf(":").takeUnless { it == -1 } ?: return Pair(address, defaultPort)
+
+ val addressName = address.substring(0, idx).takeUnless { it.isBlank() } ?: return null.also {
+ logger.error("(Invalidly) blank address in data storage options")
+ }
+
+ val port = address.substring(idx + 1).toIntOrNull() ?: return null.also {
+ logger.error("Invalid port number in data storage options: $it, using $defaultPort as default")
+ }
+
+ return Pair(addressName, port)
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt index 12d5bc7..63a5db6 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt @@ -1,69 +1,69 @@ -package io.dico.parcels2.storage - -import io.dico.parcels2.* -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Job -import kotlinx.coroutines.channels.ReceiveChannel -import kotlinx.coroutines.channels.SendChannel -import org.joda.time.DateTime -import java.util.UUID -import kotlin.coroutines.CoroutineContext - -interface Backing { - - val name: String - - val isConnected: Boolean - - val coroutineContext: CoroutineContext - - fun launchJob(job: Backing.() -> Unit): Job - - fun <T> launchFuture(future: Backing.() -> T): Deferred<T> - - fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T> - - fun <T> openChannelForWriting(future: Backing.(T) -> Unit): SendChannel<T> - - - fun init() - - fun shutdown() - - - fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? - - fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) - - fun getPlayerUuidForName(name: String): UUID? - - fun updatePlayerName(uuid: UUID, name: String) - - fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>) - - fun transmitAllParcelData(channel: SendChannel<DataPair>) - - fun readParcelData(parcel: ParcelId): ParcelDataHolder? - - fun getOwnedParcels(user: PlayerProfile): List<ParcelId> - - fun getNumParcels(user: PlayerProfile): Int = getOwnedParcels(user).size - - - fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) - - fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) - - fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean) - - fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) - - fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) - - - fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>) - - fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder? - - fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) -} +package io.dico.parcels2.storage
+
+import io.dico.parcels2.*
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.ReceiveChannel
+import kotlinx.coroutines.channels.SendChannel
+import org.joda.time.DateTime
+import java.util.UUID
+import kotlin.coroutines.CoroutineContext
+
+interface Backing {
+
+ val name: String
+
+ val isConnected: Boolean
+
+ val coroutineContext: CoroutineContext
+
+ fun launchJob(job: Backing.() -> Unit): Job
+
+ fun <T> launchFuture(future: Backing.() -> T): Deferred<T>
+
+ fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T>
+
+ fun <T> openChannelForWriting(future: Backing.(T) -> Unit): SendChannel<T>
+
+
+ fun init()
+
+ fun shutdown()
+
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): DateTime?
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime)
+
+ fun getPlayerUuidForName(name: String): UUID?
+
+ fun updatePlayerName(uuid: UUID, name: String)
+
+ fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
+
+ fun transmitAllParcelData(channel: SendChannel<DataPair>)
+
+ fun readParcelData(parcel: ParcelId): ParcelDataHolder?
+
+ fun getOwnedParcels(user: PlayerProfile): List<ParcelId>
+
+ fun getNumParcels(user: PlayerProfile): Int = getOwnedParcels(user).size
+
+
+ fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?)
+
+ fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
+
+ fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean)
+
+ fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege)
+
+ fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration)
+
+
+ fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>)
+
+ fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder?
+
+ fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt b/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt index 80f41b2..54f7677 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt @@ -1,38 +1,38 @@ -package io.dico.parcels2.storage - -import java.lang.IllegalArgumentException -import java.nio.ByteBuffer -import java.util.UUID - -/* For putting it into the database */ -fun UUID.toByteArray(): ByteArray = - ByteBuffer.allocate(16).apply { - putLong(mostSignificantBits) - putLong(leastSignificantBits) - }.array() - -/* For getting it out of the database */ -fun ByteArray.toUUID(): UUID = - ByteBuffer.wrap(this).run { - val mostSignificantBits = getLong() - val leastSignificantBits = getLong() - UUID(mostSignificantBits, leastSignificantBits) - } - -/* For putting it into the database */ -fun IntArray.toByteArray(): ByteArray = - ByteBuffer.allocate(size * Int.SIZE_BYTES).also { buf -> - buf.asIntBuffer().put(this) - }.array() - -/* For getting it out of the database */ -fun ByteArray.toIntArray(): IntArray { - if (this.size % Int.SIZE_BYTES != 0) - throw IllegalArgumentException("Size must be divisible by ${Int.SIZE_BYTES}") - - return ByteBuffer.wrap(this).run { - IntArray(remaining() / 4).also { array -> - asIntBuffer().get(array) - } - } +package io.dico.parcels2.storage
+
+import java.lang.IllegalArgumentException
+import java.nio.ByteBuffer
+import java.util.UUID
+
+/* For putting it into the database */
+fun UUID.toByteArray(): ByteArray =
+ ByteBuffer.allocate(16).apply {
+ putLong(mostSignificantBits)
+ putLong(leastSignificantBits)
+ }.array()
+
+/* For getting it out of the database */
+fun ByteArray.toUUID(): UUID =
+ ByteBuffer.wrap(this).run {
+ val mostSignificantBits = getLong()
+ val leastSignificantBits = getLong()
+ UUID(mostSignificantBits, leastSignificantBits)
+ }
+
+/* For putting it into the database */
+fun IntArray.toByteArray(): ByteArray =
+ ByteBuffer.allocate(size * Int.SIZE_BYTES).also { buf ->
+ buf.asIntBuffer().put(this)
+ }.array()
+
+/* For getting it out of the database */
+fun ByteArray.toIntArray(): IntArray {
+ if (this.size % Int.SIZE_BYTES != 0)
+ throw IllegalArgumentException("Size must be divisible by ${Int.SIZE_BYTES}")
+
+ return ByteBuffer.wrap(this).run {
+ IntArray(remaining() / 4).also { array ->
+ asIntBuffer().get(array)
+ }
+ }
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt index 480d533..f3030f6 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt @@ -1,73 +1,73 @@ -package io.dico.parcels2.storage - -import com.zaxxer.hikari.HikariConfig -import io.dico.parcels2.options.DataConnectionOptions - -fun getHikariConfig(dialectName: String, - dco: DataConnectionOptions): HikariConfig = HikariConfig().apply { - - val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}") - - when (dialectName) { - "postgresql" -> run { - dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource" - dataSourceProperties["serverName"] = address - dataSourceProperties["portNumber"] = port.toString() - dataSourceProperties["databaseName"] = dco.database - } - - "mariadb" -> run { - dataSourceClassName = "org.mariadb.jdbc.MariaDbDataSource" - dataSourceProperties["serverName"] = address - dataSourceProperties["port"] = port.toString() - dataSourceProperties["databaseName"] = dco.database - dataSourceProperties["properties"] = "useUnicode=true;characterEncoding=utf8" - } - - else -> throw IllegalArgumentException("Unsupported dialect: $dialectName") - } - - poolName = "parcels" - maximumPoolSize = dco.poolSize - username = dco.username - password = dco.password - connectionTimeout = 15000 - leakDetectionThreshold = 10000 - connectionTestQuery = "SELECT 1" - - - /* - - addDataSourceProperty("serverName", address) - addDataSourceProperty("port", port.toString()) - addDataSourceProperty("databaseName", dco.database) - - // copied from github.com/lucko/LuckPerms - if (dialectName.toLowerCase() == "mariadb") { - addDataSourceProperty("properties", "useUnicode=true;characterEncoding=utf8") - } else if (dialectName.toLowerCase() == "h2") { - dataSourceProperties.remove("serverName") - dataSourceProperties.remove("port") - dataSourceProperties.remove("databaseName") - addDataSourceProperty("url", "jdbc:h2:${if (address.isBlank()) "" else "tcp://$address/"}~/${dco.database}") - } else if (dialectName.toLowerCase() == "mysql") { - // doesn't exist on the MariaDB driver - addDataSourceProperty("cachePrepStmts", "true") - addDataSourceProperty("alwaysSendSetIsolation", "false") - addDataSourceProperty("cacheServerConfiguration", "true") - addDataSourceProperty("elideSetAutoCommits", "true") - addDataSourceProperty("useLocalSessionState", "true") - - // already set as default on mariadb - addDataSourceProperty("useServerPrepStmts", "true") - addDataSourceProperty("prepStmtCacheSize", "250") - addDataSourceProperty("prepStmtCacheSqlLimit", "2048") - addDataSourceProperty("cacheCallableStmts", "true") - - // make sure unicode characters can be used. - addDataSourceProperty("characterEncoding", "utf8") - addDataSourceProperty("useUnicode", "true") - } else { - - }*/ -} +package io.dico.parcels2.storage
+
+import com.zaxxer.hikari.HikariConfig
+import io.dico.parcels2.options.DataConnectionOptions
+
+fun getHikariConfig(dialectName: String,
+ dco: DataConnectionOptions): HikariConfig = HikariConfig().apply {
+
+ val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}")
+
+ when (dialectName) {
+ "postgresql" -> run {
+ dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
+ dataSourceProperties["serverName"] = address
+ dataSourceProperties["portNumber"] = port.toString()
+ dataSourceProperties["databaseName"] = dco.database
+ }
+
+ "mariadb" -> run {
+ dataSourceClassName = "org.mariadb.jdbc.MariaDbDataSource"
+ dataSourceProperties["serverName"] = address
+ dataSourceProperties["port"] = port.toString()
+ dataSourceProperties["databaseName"] = dco.database
+ dataSourceProperties["properties"] = "useUnicode=true;characterEncoding=utf8"
+ }
+
+ else -> throw IllegalArgumentException("Unsupported dialect: $dialectName")
+ }
+
+ poolName = "parcels"
+ maximumPoolSize = dco.poolSize
+ username = dco.username
+ password = dco.password
+ connectionTimeout = 15000
+ leakDetectionThreshold = 10000
+ connectionTestQuery = "SELECT 1"
+
+
+ /*
+
+ addDataSourceProperty("serverName", address)
+ addDataSourceProperty("port", port.toString())
+ addDataSourceProperty("databaseName", dco.database)
+
+ // copied from github.com/lucko/LuckPerms
+ if (dialectName.toLowerCase() == "mariadb") {
+ addDataSourceProperty("properties", "useUnicode=true;characterEncoding=utf8")
+ } else if (dialectName.toLowerCase() == "h2") {
+ dataSourceProperties.remove("serverName")
+ dataSourceProperties.remove("port")
+ dataSourceProperties.remove("databaseName")
+ addDataSourceProperty("url", "jdbc:h2:${if (address.isBlank()) "" else "tcp://$address/"}~/${dco.database}")
+ } else if (dialectName.toLowerCase() == "mysql") {
+ // doesn't exist on the MariaDB driver
+ addDataSourceProperty("cachePrepStmts", "true")
+ addDataSourceProperty("alwaysSendSetIsolation", "false")
+ addDataSourceProperty("cacheServerConfiguration", "true")
+ addDataSourceProperty("elideSetAutoCommits", "true")
+ addDataSourceProperty("useLocalSessionState", "true")
+
+ // already set as default on mariadb
+ addDataSourceProperty("useServerPrepStmts", "true")
+ addDataSourceProperty("prepStmtCacheSize", "250")
+ addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
+ addDataSourceProperty("cacheCallableStmts", "true")
+
+ // make sure unicode characters can be used.
+ addDataSourceProperty("characterEncoding", "utf8")
+ addDataSourceProperty("useUnicode", "true")
+ } else {
+
+ }*/
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt index d718f20..f76aad6 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt @@ -1,114 +1,114 @@ -@file:Suppress("NOTHING_TO_INLINE") - -package io.dico.parcels2.storage - -import io.dico.parcels2.* -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Job -import kotlinx.coroutines.channels.ReceiveChannel -import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.launch -import org.joda.time.DateTime -import java.util.UUID -import kotlin.coroutines.CoroutineContext - -typealias DataPair = Pair<ParcelId, ParcelDataHolder?> -typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder> - -interface Storage { - val name: String - val isConnected: Boolean - - fun init(): Job - - fun shutdown(): Job - - - fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> - - fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job - - fun getPlayerUuidForName(name: String): Deferred<UUID?> - - fun updatePlayerName(uuid: UUID, name: String): Job - - fun readParcelData(parcel: ParcelId): Deferred<ParcelDataHolder?> - - fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair> - - fun transmitAllParcelData(): ReceiveChannel<DataPair> - - fun getOwnedParcels(user: PlayerProfile): Deferred<List<ParcelId>> - - fun getNumParcels(user: PlayerProfile): Deferred<Int> - - - fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?): Job - - fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job - - fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job - - fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege): Job - - fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job - - - fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>> - - fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?> - - fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job - - - fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>> -} - -class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope { - override val name get() = b.name - override val isConnected get() = b.isConnected - override val coroutineContext: CoroutineContext get() = b.coroutineContext - - override fun init() = launch { b.init() } - - override fun shutdown() = launch { b.shutdown() } - - - override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) } - - override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job = b.launchJob { b.setWorldCreationTime(worldId, time) } - - override fun getPlayerUuidForName(name: String): Deferred<UUID?> = b.launchFuture { b.getPlayerUuidForName(name) } - - override fun updatePlayerName(uuid: UUID, name: String): Job = b.launchJob { b.updatePlayerName(uuid, name) } - - override fun readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) } - - override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) } - - override fun transmitAllParcelData() = b.openChannel<DataPair> { b.transmitAllParcelData(it) } - - override fun getOwnedParcels(user: PlayerProfile) = b.launchFuture { b.getOwnedParcels(user) } - - override fun getNumParcels(user: PlayerProfile) = b.launchFuture { b.getNumParcels(user) } - - override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) = b.launchJob { b.setParcelData(parcel, data) } - - override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) = b.launchJob { b.setParcelOwner(parcel, owner) } - - override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) } - - override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setLocalPrivilege(parcel, player, privilege) } - - override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) } - - - override fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalPrivileges(it) } - - override fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?> = b.launchFuture { b.readGlobalPrivileges(owner) } - - override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setGlobalPrivilege(owner, player, privilege) } - - override fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) } -} +@file:Suppress("NOTHING_TO_INLINE")
+
+package io.dico.parcels2.storage
+
+import io.dico.parcels2.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.ReceiveChannel
+import kotlinx.coroutines.channels.SendChannel
+import kotlinx.coroutines.launch
+import org.joda.time.DateTime
+import java.util.UUID
+import kotlin.coroutines.CoroutineContext
+
+typealias DataPair = Pair<ParcelId, ParcelDataHolder?>
+typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder>
+
+interface Storage {
+ val name: String
+ val isConnected: Boolean
+
+ fun init(): Job
+
+ fun shutdown(): Job
+
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?>
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job
+
+ fun getPlayerUuidForName(name: String): Deferred<UUID?>
+
+ fun updatePlayerName(uuid: UUID, name: String): Job
+
+ fun readParcelData(parcel: ParcelId): Deferred<ParcelDataHolder?>
+
+ fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
+
+ fun transmitAllParcelData(): ReceiveChannel<DataPair>
+
+ fun getOwnedParcels(user: PlayerProfile): Deferred<List<ParcelId>>
+
+ fun getNumParcels(user: PlayerProfile): Deferred<Int>
+
+
+ fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?): Job
+
+ fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
+
+ fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job
+
+ fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege): Job
+
+ fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job
+
+
+ fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>>
+
+ fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?>
+
+ fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job
+
+
+ fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>>
+}
+
+class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope {
+ override val name get() = b.name
+ override val isConnected get() = b.isConnected
+ override val coroutineContext: CoroutineContext get() = b.coroutineContext
+
+ override fun init() = launch { b.init() }
+
+ override fun shutdown() = launch { b.shutdown() }
+
+
+ override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) }
+
+ override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job = b.launchJob { b.setWorldCreationTime(worldId, time) }
+
+ override fun getPlayerUuidForName(name: String): Deferred<UUID?> = b.launchFuture { b.getPlayerUuidForName(name) }
+
+ override fun updatePlayerName(uuid: UUID, name: String): Job = b.launchJob { b.updatePlayerName(uuid, name) }
+
+ override fun readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) }
+
+ override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) }
+
+ override fun transmitAllParcelData() = b.openChannel<DataPair> { b.transmitAllParcelData(it) }
+
+ override fun getOwnedParcels(user: PlayerProfile) = b.launchFuture { b.getOwnedParcels(user) }
+
+ override fun getNumParcels(user: PlayerProfile) = b.launchFuture { b.getNumParcels(user) }
+
+ override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) = b.launchJob { b.setParcelData(parcel, data) }
+
+ override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) = b.launchJob { b.setParcelOwner(parcel, owner) }
+
+ override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) }
+
+ override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setLocalPrivilege(parcel, player, privilege) }
+
+ override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) }
+
+
+ override fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalPrivileges(it) }
+
+ override fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?> = b.launchFuture { b.readGlobalPrivileges(owner) }
+
+ override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setGlobalPrivilege(owner, player, privilege) }
+
+ override fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt index e49be79..d9e3071 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt @@ -72,9 +72,11 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi override fun init() { synchronized { if (isShutdown || isConnected) throw IllegalStateException() - dataSource = dataSourceFactory() - database = Database.connect(dataSource!!) - transaction(database!!) { + val dataSource = dataSourceFactory() + this.dataSource = dataSource + val database = Database.connect(dataSource) + this.database = database + transaction(database) { create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, PrivilegesLocalT, PrivilegesGlobalT) } } @@ -84,7 +86,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi synchronized { if (isShutdown) throw IllegalStateException() isShutdown = true - coroutineContext[Job]!!.cancel(CancellationException("ExposedBacking shutdown")) + coroutineContext.cancel(CancellationException("ExposedBacking shutdown")) dataSource?.let { (it as? HikariDataSource)?.close() } diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt index 0245625..7640c0c 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt @@ -1,74 +1,74 @@ -package io.dico.parcels2.storage.exposed - -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.Function -import org.jetbrains.exposed.sql.statements.InsertStatement -import org.jetbrains.exposed.sql.transactions.TransactionManager - -class UpsertStatement<Key : Any>(table: Table, conflictColumn: Column<*>? = null, conflictIndex: Index? = null) : InsertStatement<Key>(table, false) { - val indexName: String - val indexColumns: List<Column<*>> - - init { - when { - conflictIndex != null -> { - indexName = conflictIndex.indexName - indexColumns = conflictIndex.columns - } - conflictColumn != null -> { - indexName = conflictColumn.name - indexColumns = listOf(conflictColumn) - } - else -> throw IllegalArgumentException() - } - } - - override fun prepareSQL(transaction: Transaction) = buildString { - append(super.prepareSQL(transaction)) - - val dialect = transaction.db.vendor - if (dialect == "postgresql") { - - append(" ON CONFLICT(") - append(indexName) - append(") DO UPDATE SET ") - - values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=EXCLUDED.${transaction.identity(it)}" } - - } else { - - append(" ON DUPLICATE KEY UPDATE ") - values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=VALUES(${transaction.identity(it)})" } - - } - } - -} - -inline fun <T : Table> T.upsert(conflictColumn: Column<*>? = null, conflictIndex: Index? = null, body: T.(UpsertStatement<Number>) -> Unit) = - UpsertStatement<Number>(this, conflictColumn, conflictIndex).apply { - body(this) - execute(TransactionManager.current()) - } - -fun Table.indexR(customIndexName: String? = null, isUnique: Boolean = false, vararg columns: Column<*>): Index { - val index = Index(columns.toList(), isUnique, customIndexName) - indices.add(index) - return index -} - -fun Table.uniqueIndexR(customIndexName: String? = null, vararg columns: Column<*>): Index = indexR(customIndexName, true, *columns) - -fun <T : Int?> ExpressionWithColumnType<T>.abs(): Function<T> = Abs(this) - -class Abs<T : Int?>(val expr: Expression<T>) : Function<T>(IntegerColumnType()) { - override fun toSQL(queryBuilder: QueryBuilder): String = "ABS(${expr.toSQL(queryBuilder)})" -} - -fun <T : Comparable<T>> greaterOf(col1: ExpressionWithColumnType<T>, col2: ExpressionWithColumnType<T>): Expression<T> = - with(SqlExpressionBuilder) { - case(col1) - .When(col1.greater(col2), col1) - .Else(col2) - } - +package io.dico.parcels2.storage.exposed
+
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.Function
+import org.jetbrains.exposed.sql.statements.InsertStatement
+import org.jetbrains.exposed.sql.transactions.TransactionManager
+
+class UpsertStatement<Key : Any>(table: Table, conflictColumn: Column<*>? = null, conflictIndex: Index? = null) : InsertStatement<Key>(table, false) {
+ val indexName: String
+ val indexColumns: List<Column<*>>
+
+ init {
+ when {
+ conflictIndex != null -> {
+ indexName = conflictIndex.indexName
+ indexColumns = conflictIndex.columns
+ }
+ conflictColumn != null -> {
+ indexName = conflictColumn.name
+ indexColumns = listOf(conflictColumn)
+ }
+ else -> throw IllegalArgumentException()
+ }
+ }
+
+ override fun prepareSQL(transaction: Transaction) = buildString {
+ append(super.prepareSQL(transaction))
+
+ val dialect = transaction.db.vendor
+ if (dialect == "postgresql") {
+
+ append(" ON CONFLICT(")
+ append(indexName)
+ append(") DO UPDATE SET ")
+
+ values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=EXCLUDED.${transaction.identity(it)}" }
+
+ } else {
+
+ append(" ON DUPLICATE KEY UPDATE ")
+ values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=VALUES(${transaction.identity(it)})" }
+
+ }
+ }
+
+}
+
+inline fun <T : Table> T.upsert(conflictColumn: Column<*>? = null, conflictIndex: Index? = null, body: T.(UpsertStatement<Number>) -> Unit) =
+ UpsertStatement<Number>(this, conflictColumn, conflictIndex).apply {
+ body(this)
+ execute(TransactionManager.current())
+ }
+
+fun Table.indexR(customIndexName: String? = null, isUnique: Boolean = false, vararg columns: Column<*>): Index {
+ val index = Index(columns.toList(), isUnique, customIndexName)
+ indices.add(index)
+ return index
+}
+
+fun Table.uniqueIndexR(customIndexName: String? = null, vararg columns: Column<*>): Index = indexR(customIndexName, true, *columns)
+
+fun <T : Int?> ExpressionWithColumnType<T>.abs(): Function<T> = Abs(this)
+
+class Abs<T : Int?>(val expr: Expression<T>) : Function<T>(IntegerColumnType()) {
+ override fun toSQL(queryBuilder: QueryBuilder): String = "ABS(${expr.toSQL(queryBuilder)})"
+}
+
+fun <T : Comparable<T>> greaterOf(col1: ExpressionWithColumnType<T>, col2: ExpressionWithColumnType<T>): Expression<T> =
+ with(SqlExpressionBuilder) {
+ case(col1)
+ .When(col1.greater(col2), col1)
+ .Else(col2)
+ }
+
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt index 6f6ad6b..8428b3a 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt @@ -1,165 +1,165 @@ -@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "unused", "MemberVisibilityCanBePrivate") - -package io.dico.parcels2.storage.exposed - -import io.dico.parcels2.ParcelId -import io.dico.parcels2.ParcelWorldId -import io.dico.parcels2.PlayerProfile -import io.dico.parcels2.storage.toByteArray -import io.dico.parcels2.storage.toUUID -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.statements.UpdateBuilder -import org.joda.time.DateTime -import java.util.UUID - -abstract class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String) - : Table(tableName) { - val id = integer(columnName).autoIncrement().primaryKey() - - @Suppress("UNCHECKED_CAST") - inline val table: TableT - get() = this as TableT - - internal inline fun getId(where: SqlExpressionBuilder.(TableT) -> Op<Boolean>): Int? { - return select { where(table) }.firstOrNull()?.let { it[id] } - } - - internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int { - return getId() ?: table.insertIgnore(body)[id] ?: getId() - ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its number") - } - - abstract fun getId(obj: QueryObj): Int? - abstract fun getOrInitId(obj: QueryObj): Int - fun getItem(id: Int): QueryObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getItem(it) } - abstract fun getItem(row: ResultRow): QueryObj? - - fun getId(obj: QueryObj, init: Boolean): Int? = if (init) getOrInitId(obj) else getId(obj) -} - -object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcels_worlds", "world_id") { - val name = varchar("name", 50) - val uid = binary("uid", 16).nullable() - val creation_time = datetime("creation_time").nullable() - val index_name = uniqueIndexR("index_name", name) - val index_uid = uniqueIndexR("index_uid", uid) - - internal inline fun getId(worldName: String, binaryUid: ByteArray?): Int? = getId { (name eq worldName).let { if (binaryUid == null) it else it or (uid eq binaryUid) } } - internal inline fun getId(worldName: String, uid: UUID?): Int? = getId(worldName, uid?.toByteArray()) - internal inline fun getOrInitId(worldName: String, worldUid: UUID?): Int = worldUid?.toByteArray().let { binaryUid -> - return getOrInitId( - { getId(worldName, binaryUid) }, - { it[name] = worldName; it[uid] = binaryUid }, - { "world named $worldName" }) - } - - override fun getId(world: ParcelWorldId): Int? = getId(world.name, world.uid) - override fun getOrInitId(world: ParcelWorldId): Int = getOrInitId(world.name, world.uid) - - override fun getItem(row: ResultRow): ParcelWorldId { - return ParcelWorldId(row[name], row[uid]?.toUUID()) - } - - fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? { - val id = getId(worldId) ?: return null - return select { WorldsT.id eq id }.firstOrNull()?.let { it[WorldsT.creation_time] } - } - - fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) { - val id = getOrInitId(worldId) - update({ WorldsT.id eq id }) { - it[WorldsT.creation_time] = time - } - } -} - -object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id") { - val world_id = integer("world_id").references(WorldsT.id) - val px = integer("px") - val pz = integer("pz") - val owner_id = integer("owner_id").references(ProfilesT.id).nullable() - val sign_oudated = bool("sign_outdated").default(false) - val claim_time = datetime("claim_time").nullable() - val index_location = uniqueIndexR("index_location", world_id, px, pz) - - private inline fun getId(worldId: Int, parcelX: Int, parcelZ: Int): Int? = getId { world_id.eq(worldId) and px.eq(parcelX) and pz.eq(parcelZ) } - private inline fun getId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int? = WorldsT.getId(worldName, worldUid)?.let { getId(it, parcelX, parcelZ) } - private inline fun getOrInitId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int { - val worldId = WorldsT.getOrInitId(worldName, worldUid) - return getOrInitId( - { getId(worldId, parcelX, parcelZ) }, - { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ }, - { "parcel at $worldName($parcelX, $parcelZ)" }) - } - - override fun getId(parcel: ParcelId): Int? = getId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z) - override fun getOrInitId(parcel: ParcelId): Int = getOrInitId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z) - - private inline fun getRow(id: Int): ResultRow? = select { ParcelsT.id eq id }.firstOrNull() - fun getRow(parcel: ParcelId): ResultRow? = getId(parcel)?.let { getRow(it) } - - override fun getItem(row: ResultRow): ParcelId? { - val worldId = row[world_id] - val world = WorldsT.getItem(worldId) ?: return null - return ParcelId(world, row[px], row[pz]) - } -} - -object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcels_profiles", "owner_id") { - val uuid = binary("uuid", 16).nullable() - val name = varchar("name", 32).nullable() - - // MySQL dialect MUST permit multiple null values for this to work. Server SQL does not allow this. That dialect is shit anyway. - val uuid_constraint = uniqueIndexR("uuid_constraint", uuid) - val index_pair = uniqueIndexR("index_pair", uuid, name) - - - private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid } - private inline fun getId(uuid: UUID) = getId(uuid.toByteArray()) - private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) } - private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) } - - private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid -> - getOrInitId( - { getId(binaryUuid) }, - { it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name }, - { "profile(uuid = $uuid, name = $name)" }) - } - - private inline fun getOrInitId(name: String) = getOrInitId( - { getId(name) }, - { it[ProfilesT.name] = name }, - { "owner(name = $name)" }) - - - override fun getId(profile: PlayerProfile): Int? = when (profile) { - is PlayerProfile.Real -> getId(profile.uuid) - is PlayerProfile.Fake -> getId(profile.name) - is PlayerProfile.Unresolved -> getRealId(profile.name) - else -> throw IllegalArgumentException() - } - - override fun getOrInitId(profile: PlayerProfile): Int = when (profile) { - is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.nameOrBukkitName) - is PlayerProfile.Fake -> getOrInitId(profile.name) - else -> throw IllegalArgumentException() // Unresolved profiles cannot be added to the database - } - - override fun getItem(row: ResultRow): PlayerProfile { - return PlayerProfile(row[uuid]?.toUUID(), row[name]) - } - - fun getRealItem(id: Int): PlayerProfile.Real? { - return getItem(id) as? PlayerProfile.Real - } - - /* - fun updatePlayerProfile(profile: PlayerProfile.Real) { - update({ uuid eq profile.uuid.toByteArray() }) { - it[name] = profile.nameOrBukkitName - } - }*/ - -} - +@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "unused", "MemberVisibilityCanBePrivate")
+
+package io.dico.parcels2.storage.exposed
+
+import io.dico.parcels2.ParcelId
+import io.dico.parcels2.ParcelWorldId
+import io.dico.parcels2.PlayerProfile
+import io.dico.parcels2.storage.toByteArray
+import io.dico.parcels2.storage.toUUID
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.statements.UpdateBuilder
+import org.joda.time.DateTime
+import java.util.UUID
+
+abstract class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String)
+ : Table(tableName) {
+ val id = integer(columnName).autoIncrement().primaryKey()
+
+ @Suppress("UNCHECKED_CAST")
+ inline val table: TableT
+ get() = this as TableT
+
+ internal inline fun getId(where: SqlExpressionBuilder.(TableT) -> Op<Boolean>): Int? {
+ return select { where(table) }.firstOrNull()?.let { it[id] }
+ }
+
+ internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int {
+ return getId() ?: table.insertIgnore(body)[id] ?: getId()
+ ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its number")
+ }
+
+ abstract fun getId(obj: QueryObj): Int?
+ abstract fun getOrInitId(obj: QueryObj): Int
+ fun getItem(id: Int): QueryObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getItem(it) }
+ abstract fun getItem(row: ResultRow): QueryObj?
+
+ fun getId(obj: QueryObj, init: Boolean): Int? = if (init) getOrInitId(obj) else getId(obj)
+}
+
+object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcels_worlds", "world_id") {
+ val name = varchar("name", 50)
+ val uid = binary("uid", 16).nullable()
+ val creation_time = datetime("creation_time").nullable()
+ val index_name = uniqueIndexR("index_name", name)
+ val index_uid = uniqueIndexR("index_uid", uid)
+
+ internal inline fun getId(worldName: String, binaryUid: ByteArray?): Int? = getId { (name eq worldName).let { if (binaryUid == null) it else it or (uid eq binaryUid) } }
+ internal inline fun getId(worldName: String, uid: UUID?): Int? = getId(worldName, uid?.toByteArray())
+ internal inline fun getOrInitId(worldName: String, worldUid: UUID?): Int = worldUid?.toByteArray().let { binaryUid ->
+ return getOrInitId(
+ { getId(worldName, binaryUid) },
+ { it[name] = worldName; it[uid] = binaryUid },
+ { "world named $worldName" })
+ }
+
+ override fun getId(world: ParcelWorldId): Int? = getId(world.name, world.uid)
+ override fun getOrInitId(world: ParcelWorldId): Int = getOrInitId(world.name, world.uid)
+
+ override fun getItem(row: ResultRow): ParcelWorldId {
+ return ParcelWorldId(row[name], row[uid]?.toUUID())
+ }
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
+ val id = getId(worldId) ?: return null
+ return select { WorldsT.id eq id }.firstOrNull()?.let { it[WorldsT.creation_time] }
+ }
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
+ val id = getOrInitId(worldId)
+ update({ WorldsT.id eq id }) {
+ it[WorldsT.creation_time] = time
+ }
+ }
+}
+
+object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id") {
+ val world_id = integer("world_id").references(WorldsT.id)
+ val px = integer("px")
+ val pz = integer("pz")
+ val owner_id = integer("owner_id").references(ProfilesT.id).nullable()
+ val sign_oudated = bool("sign_outdated").default(false)
+ val claim_time = datetime("claim_time").nullable()
+ val index_location = uniqueIndexR("index_location", world_id, px, pz)
+
+ private inline fun getId(worldId: Int, parcelX: Int, parcelZ: Int): Int? = getId { world_id.eq(worldId) and px.eq(parcelX) and pz.eq(parcelZ) }
+ private inline fun getId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int? = WorldsT.getId(worldName, worldUid)?.let { getId(it, parcelX, parcelZ) }
+ private inline fun getOrInitId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int {
+ val worldId = WorldsT.getOrInitId(worldName, worldUid)
+ return getOrInitId(
+ { getId(worldId, parcelX, parcelZ) },
+ { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ },
+ { "parcel at $worldName($parcelX, $parcelZ)" })
+ }
+
+ override fun getId(parcel: ParcelId): Int? = getId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
+ override fun getOrInitId(parcel: ParcelId): Int = getOrInitId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
+
+ private inline fun getRow(id: Int): ResultRow? = select { ParcelsT.id eq id }.firstOrNull()
+ fun getRow(parcel: ParcelId): ResultRow? = getId(parcel)?.let { getRow(it) }
+
+ override fun getItem(row: ResultRow): ParcelId? {
+ val worldId = row[world_id]
+ val world = WorldsT.getItem(worldId) ?: return null
+ return ParcelId(world, row[px], row[pz])
+ }
+}
+
+object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcels_profiles", "owner_id") {
+ val uuid = binary("uuid", 16).nullable()
+ val name = varchar("name", 32).nullable()
+
+ // MySQL dialect MUST permit multiple null values for this to work. Server SQL does not allow this. That dialect is shit anyway.
+ val uuid_constraint = uniqueIndexR("uuid_constraint", uuid)
+ val index_pair = uniqueIndexR("index_pair", uuid, name)
+
+
+ private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
+ private inline fun getId(uuid: UUID) = getId(uuid.toByteArray())
+ private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
+ private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
+
+ private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid ->
+ getOrInitId(
+ { getId(binaryUuid) },
+ { it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
+ { "profile(uuid = $uuid, name = $name)" })
+ }
+
+ private inline fun getOrInitId(name: String) = getOrInitId(
+ { getId(name) },
+ { it[ProfilesT.name] = name },
+ { "owner(name = $name)" })
+
+
+ override fun getId(profile: PlayerProfile): Int? = when (profile) {
+ is PlayerProfile.Real -> getId(profile.uuid)
+ is PlayerProfile.Fake -> getId(profile.name)
+ is PlayerProfile.Unresolved -> getRealId(profile.name)
+ else -> throw IllegalArgumentException()
+ }
+
+ override fun getOrInitId(profile: PlayerProfile): Int = when (profile) {
+ is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.nameOrBukkitName)
+ is PlayerProfile.Fake -> getOrInitId(profile.name)
+ else -> throw IllegalArgumentException() // Unresolved profiles cannot be added to the database
+ }
+
+ override fun getItem(row: ResultRow): PlayerProfile {
+ return PlayerProfile(row[uuid]?.toUUID(), row[name])
+ }
+
+ fun getRealItem(id: Int): PlayerProfile.Real? {
+ return getItem(id) as? PlayerProfile.Real
+ }
+
+ /*
+ fun updatePlayerProfile(profile: PlayerProfile.Real) {
+ update({ uuid eq profile.uuid.toByteArray() }) {
+ it[name] = profile.nameOrBukkitName
+ }
+ }*/
+
+}
+
// val ParcelsWithOptionsT = ParcelsT.join(ParcelOptionsT, JoinType.INNER, onColumn = ParcelsT.id, otherColumn = ParcelOptionsT.parcel_id)
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt index b9d16fc..26cfc7a 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt @@ -1,111 +1,111 @@ -@file:Suppress("PropertyName", "LocalVariableName", "NOTHING_TO_INLINE") - -package io.dico.parcels2.storage.exposed - -import io.dico.parcels2.* -import io.dico.parcels2.Privilege.DEFAULT -import io.dico.parcels2.util.ext.alsoIfTrue -import kotlinx.coroutines.channels.SendChannel -import org.jetbrains.exposed.sql.* - -object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_privilege_local", ParcelsT) -object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_privilege_global", ProfilesT) - -object ParcelOptionsT : Table("parcels_options") { - val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE) - val interact_bitmask = binary("interact_bitmask", 4) -} - -typealias PrivilegesSendChannel<AttachT> = SendChannel<Pair<AttachT, PrivilegesHolder>> - -sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) { - val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE) - val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE) - val privilege = integer("privilege") - val index_pair = uniqueIndexR("index_pair", attach_id, profile_id) - - fun setPrivilege(attachedOn: AttachT, player: PlayerProfile.Real, privilege: Privilege) { - privilege.requireNonTransient() - - if (privilege == DEFAULT) { - val player_id = ProfilesT.getId(player) ?: return - idTable.getId(attachedOn)?.let { holder -> - deleteWhere { (attach_id eq holder) and (profile_id eq player_id) } - } - return - } - - val holder = idTable.getOrInitId(attachedOn) - val player_id = ProfilesT.getOrInitId(player) - upsert(conflictIndex = index_pair) { - it[attach_id] = holder - it[profile_id] = player_id - it[this.privilege] = privilege.number - } - } - - fun readPrivileges(id: Int): PrivilegesHolder? { - val list = slice(profile_id, privilege).select { attach_id eq id } - val result = PrivilegesHolder() - for (row in list) { - val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue - result.setRawStoredPrivilege(profile, Privilege.getByNumber(row[privilege]) ?: continue) - } - return result - } - - fun sendAllPrivilegesH(channel: PrivilegesSendChannel<AttachT>) { - val iterator = selectAll().orderBy(attach_id).iterator() - - if (iterator.hasNext()) { - var row = iterator.next() - var id: Int = row[attach_id] - var attach: AttachT? = null - var map: PrivilegesHolder? = null - - fun initAttachAndMap() { - attach = idTable.getItem(id) - map = attach?.let { PrivilegesHolder() } - } - - fun sendIfPresent() { - if (attach != null && map != null) { - channel.offer(attach!! to map!!) - } - attach = null - map = null - } - - initAttachAndMap() - - do { - val rowId = row[attach_id] - if (rowId != id) { - sendIfPresent() - id = rowId - initAttachAndMap() - } - - if (attach == null) { - continue // owner not found for this owner id - } - - val profile = ProfilesT.getRealItem(row[profile_id]) - if (profile == null) { - logger.error("Privilege from database is null, id ${row[profile_id]}") - continue - } - val privilege = Privilege.getByNumber(row[privilege]) - if (privilege == null) { - logger.error("Privilege from database is null, number ${row[this.privilege]}") - continue - } - map!!.setRawStoredPrivilege(profile, privilege) - - } while (iterator.hasNext().alsoIfTrue { row = iterator.next() }) - - sendIfPresent() - } - } - -} +@file:Suppress("PropertyName", "LocalVariableName", "NOTHING_TO_INLINE")
+
+package io.dico.parcels2.storage.exposed
+
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.DEFAULT
+import io.dico.parcels2.util.ext.alsoIfTrue
+import kotlinx.coroutines.channels.SendChannel
+import org.jetbrains.exposed.sql.*
+
+object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_privilege_local", ParcelsT)
+object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_privilege_global", ProfilesT)
+
+object ParcelOptionsT : Table("parcels_options") {
+ val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
+ val interact_bitmask = binary("interact_bitmask", 4)
+}
+
+typealias PrivilegesSendChannel<AttachT> = SendChannel<Pair<AttachT, PrivilegesHolder>>
+
+sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) {
+ val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE)
+ val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE)
+ val privilege = integer("privilege")
+ val index_pair = uniqueIndexR("index_pair", attach_id, profile_id)
+
+ fun setPrivilege(attachedOn: AttachT, player: PlayerProfile.Real, privilege: Privilege) {
+ privilege.requireNonTransient()
+
+ if (privilege == DEFAULT) {
+ val player_id = ProfilesT.getId(player) ?: return
+ idTable.getId(attachedOn)?.let { holder ->
+ deleteWhere { (attach_id eq holder) and (profile_id eq player_id) }
+ }
+ return
+ }
+
+ val holder = idTable.getOrInitId(attachedOn)
+ val player_id = ProfilesT.getOrInitId(player)
+ upsert(conflictIndex = index_pair) {
+ it[attach_id] = holder
+ it[profile_id] = player_id
+ it[this.privilege] = privilege.number
+ }
+ }
+
+ fun readPrivileges(id: Int): PrivilegesHolder? {
+ val list = slice(profile_id, privilege).select { attach_id eq id }
+ val result = PrivilegesHolder()
+ for (row in list) {
+ val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
+ result.setRawStoredPrivilege(profile, Privilege.getByNumber(row[privilege]) ?: continue)
+ }
+ return result
+ }
+
+ fun sendAllPrivilegesH(channel: PrivilegesSendChannel<AttachT>) {
+ val iterator = selectAll().orderBy(attach_id).iterator()
+
+ if (iterator.hasNext()) {
+ var row = iterator.next()
+ var id: Int = row[attach_id]
+ var attach: AttachT? = null
+ var map: PrivilegesHolder? = null
+
+ fun initAttachAndMap() {
+ attach = idTable.getItem(id)
+ map = attach?.let { PrivilegesHolder() }
+ }
+
+ fun sendIfPresent() {
+ if (attach != null && map != null) {
+ channel.offer(attach!! to map!!)
+ }
+ attach = null
+ map = null
+ }
+
+ initAttachAndMap()
+
+ do {
+ val rowId = row[attach_id]
+ if (rowId != id) {
+ sendIfPresent()
+ id = rowId
+ initAttachAndMap()
+ }
+
+ if (attach == null) {
+ continue // owner not found for this owner id
+ }
+
+ val profile = ProfilesT.getRealItem(row[profile_id])
+ if (profile == null) {
+ logger.error("Privilege from database is null, id ${row[profile_id]}")
+ continue
+ }
+ val privilege = Privilege.getByNumber(row[privilege])
+ if (privilege == null) {
+ logger.error("Privilege from database is null, number ${row[this.privilege]}")
+ continue
+ }
+ map!!.setRawStoredPrivilege(profile, privilege)
+
+ } while (iterator.hasNext().alsoIfTrue { row = iterator.next() })
+
+ sendIfPresent()
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt index acc7c5e..a512f2a 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt @@ -1,9 +1,9 @@ -package io.dico.parcels2.storage.migration - -import io.dico.parcels2.storage.Storage -import kotlinx.coroutines.Job - -interface Migration { - fun migrateTo(storage: Storage): Job -} - +package io.dico.parcels2.storage.migration
+
+import io.dico.parcels2.storage.Storage
+import kotlinx.coroutines.Job
+
+interface Migration {
+ fun migrateTo(storage: Storage): Job
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt index 831fe42..954da5d 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt @@ -1,118 +1,118 @@ -@file:Suppress("RedundantSuspendModifier", "DEPRECATION") - -package io.dico.parcels2.storage.migration.plotme - -import com.zaxxer.hikari.HikariDataSource -import io.dico.parcels2.* -import io.dico.parcels2.options.PlotmeMigrationOptions -import io.dico.parcels2.storage.Storage -import io.dico.parcels2.storage.exposed.abs -import io.dico.parcels2.storage.exposed.greaterOf -import io.dico.parcels2.storage.migration.Migration -import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmePlotPlayerMap -import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmeTable -import io.dico.parcels2.storage.toUUID -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.newFixedThreadPoolContext -import org.jetbrains.exposed.sql.* -import org.slf4j.LoggerFactory -import java.sql.Blob -import java.util.UUID -import javax.sql.DataSource - -class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration { - private var dataSource: DataSource? = null - private var database: Database? = null - private var isShutdown: Boolean = false - private val mlogger = LoggerFactory.getLogger("PlotMe Migrator") - private val tables = PlotmeTables(options.tableNamesUppercase) - val dispatcher = newFixedThreadPoolContext(1, "PlotMe Migration Thread") - - private fun <T> transaction(statement: Transaction.() -> T) = org.jetbrains.exposed.sql.transactions.transaction(database!!, statement) - - override fun migrateTo(storage: Storage): Job { - return launch(dispatcher) { - init() - doWork(storage) - shutdown() - } - } - - fun init() { - if (isShutdown || database != null) throw IllegalStateException() - dataSource = options.storage.getDataSourceFactory()!!() - database = Database.connect(dataSource!!) - } - - fun shutdown() { - if (isShutdown) throw IllegalStateException() - dataSource?.let { - (it as? HikariDataSource)?.close() - } - database = null - isShutdown = true - } - - suspend fun doWork(target: Storage) = with (tables) { - val exit = transaction { - (!PlotmePlots.exists()).also { - if (it) mlogger.warn("Plotme tables don't appear to exist. Exiting.") - } - } - if (exit) return - - val worldCache = options.worldsFromTo.mapValues { ParcelWorldId(it.value) } - - fun getParcelId(table: PlotmeTable, row: ResultRow): ParcelId? { - val world = worldCache[row[table.world_name]] ?: return null - return ParcelId(world, row[table.px], row[table.pz]) - } - - fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: Privilege) { - selectAll().forEach { row -> - val parcel = getParcelId(this, row) ?: return@forEach - val profile = PrivilegeKey.safe(row[player_uuid]?.toUUID(), row[player_name]) ?: return@forEach - target.setLocalPrivilege(parcel, profile, kind) - } - } - - mlogger.info("Transmitting data from plotmeplots table") - var count = 0 - transaction { - - PlotmePlots.selectAll() - .orderBy(PlotmePlots.world_name) - .orderBy(greaterOf(PlotmePlots.px.abs(), PlotmePlots.pz.abs())) - .forEach { row -> - val parcel = getParcelId(PlotmePlots, row) ?: return@forEach - val owner = PlayerProfile.safe(row[PlotmePlots.owner_uuid]?.toUUID(), row[PlotmePlots.owner_name]) - target.setParcelOwner(parcel, owner) - target.setParcelOwnerSignOutdated(parcel, true) - ++count - } - } - - mlogger.info("Transmitting data from plotmeallowed table") - transaction { - PlotmeAllowed.transmitPlotmeAddedTable(Privilege.CAN_BUILD) - } - - mlogger.info("Transmitting data from plotmedenied table") - transaction { - PlotmeDenied.transmitPlotmeAddedTable(Privilege.BANNED) - } - - mlogger.warn("Data has been **transmitted**. $count plots were migrated to the parcels database.") - mlogger.warn("Loading parcel data might take a while as enqueued transactions from this migration are completed.") - } - - private fun Blob.toUUID(): UUID? { - val ba = ByteArray(16) - val count = binaryStream.read(ba, 0, 16) - if (count < 16) return null - return ba.toUUID() - } - - +@file:Suppress("RedundantSuspendModifier", "DEPRECATION")
+
+package io.dico.parcels2.storage.migration.plotme
+
+import com.zaxxer.hikari.HikariDataSource
+import io.dico.parcels2.*
+import io.dico.parcels2.options.PlotmeMigrationOptions
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.storage.exposed.abs
+import io.dico.parcels2.storage.exposed.greaterOf
+import io.dico.parcels2.storage.migration.Migration
+import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmePlotPlayerMap
+import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmeTable
+import io.dico.parcels2.storage.toUUID
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.newFixedThreadPoolContext
+import org.jetbrains.exposed.sql.*
+import org.slf4j.LoggerFactory
+import java.sql.Blob
+import java.util.UUID
+import javax.sql.DataSource
+
+class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
+ private var dataSource: DataSource? = null
+ private var database: Database? = null
+ private var isShutdown: Boolean = false
+ private val mlogger = LoggerFactory.getLogger("PlotMe Migrator")
+ private val tables = PlotmeTables(options.tableNamesUppercase)
+ val dispatcher = newFixedThreadPoolContext(1, "PlotMe Migration Thread")
+
+ private fun <T> transaction(statement: Transaction.() -> T) = org.jetbrains.exposed.sql.transactions.transaction(database!!, statement)
+
+ override fun migrateTo(storage: Storage): Job {
+ return launch(dispatcher) {
+ init()
+ doWork(storage)
+ shutdown()
+ }
+ }
+
+ fun init() {
+ if (isShutdown || database != null) throw IllegalStateException()
+ dataSource = options.storage.getDataSourceFactory()!!()
+ database = Database.connect(dataSource!!)
+ }
+
+ fun shutdown() {
+ if (isShutdown) throw IllegalStateException()
+ dataSource?.let {
+ (it as? HikariDataSource)?.close()
+ }
+ database = null
+ isShutdown = true
+ }
+
+ suspend fun doWork(target: Storage) = with (tables) {
+ val exit = transaction {
+ (!PlotmePlots.exists()).also {
+ if (it) mlogger.warn("Plotme tables don't appear to exist. Exiting.")
+ }
+ }
+ if (exit) return
+
+ val worldCache = options.worldsFromTo.mapValues { ParcelWorldId(it.value) }
+
+ fun getParcelId(table: PlotmeTable, row: ResultRow): ParcelId? {
+ val world = worldCache[row[table.world_name]] ?: return null
+ return ParcelId(world, row[table.px], row[table.pz])
+ }
+
+ fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: Privilege) {
+ selectAll().forEach { row ->
+ val parcel = getParcelId(this, row) ?: return@forEach
+ val profile = PrivilegeKey.safe(row[player_uuid]?.toUUID(), row[player_name]) ?: return@forEach
+ target.setLocalPrivilege(parcel, profile, kind)
+ }
+ }
+
+ mlogger.info("Transmitting data from plotmeplots table")
+ var count = 0
+ transaction {
+
+ PlotmePlots.selectAll()
+ .orderBy(PlotmePlots.world_name)
+ .orderBy(greaterOf(PlotmePlots.px.abs(), PlotmePlots.pz.abs()))
+ .forEach { row ->
+ val parcel = getParcelId(PlotmePlots, row) ?: return@forEach
+ val owner = PlayerProfile.safe(row[PlotmePlots.owner_uuid]?.toUUID(), row[PlotmePlots.owner_name])
+ target.setParcelOwner(parcel, owner)
+ target.setParcelOwnerSignOutdated(parcel, true)
+ ++count
+ }
+ }
+
+ mlogger.info("Transmitting data from plotmeallowed table")
+ transaction {
+ PlotmeAllowed.transmitPlotmeAddedTable(Privilege.CAN_BUILD)
+ }
+
+ mlogger.info("Transmitting data from plotmedenied table")
+ transaction {
+ PlotmeDenied.transmitPlotmeAddedTable(Privilege.BANNED)
+ }
+
+ mlogger.warn("Data has been **transmitted**. $count plots were migrated to the parcels database.")
+ mlogger.warn("Loading parcel data might take a while as enqueued transactions from this migration are completed.")
+ }
+
+ private fun Blob.toUUID(): UUID? {
+ val ba = ByteArray(16)
+ val count = binaryStream.read(ba, 0, 16)
+ if (count < 16) return null
+ return ba.toUUID()
+ }
+
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt index dc788c8..b276e11 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt @@ -1,31 +1,31 @@ -package io.dico.parcels2.storage.migration.plotme - -import org.jetbrains.exposed.sql.Table - -class PlotmeTables(val uppercase: Boolean) { - fun String.toCorrectCase() = if (uppercase) this else toLowerCase() - - val PlotmePlots = PlotmePlotsT() - val PlotmeAllowed = PlotmeAllowedT() - val PlotmeDenied = PlotmeDeniedT() - - inner abstract class PlotmeTable(name: String) : Table(name) { - val px = integer("idX").primaryKey() - val pz = integer("idZ").primaryKey() - val world_name = varchar("world", 32).primaryKey() - } - - inner abstract class PlotmePlotPlayerMap(name: String) : PlotmeTable(name) { - val player_name = varchar("player", 32) - val player_uuid = blob("playerid").nullable() - } - - inner class PlotmePlotsT : PlotmeTable("plotmePlots".toCorrectCase()) { - val owner_name = varchar("owner", 32) - val owner_uuid = blob("ownerid").nullable() - } - - inner class PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase()) - inner class PlotmeDeniedT : PlotmePlotPlayerMap("plotmeDenied".toCorrectCase()) -} - +package io.dico.parcels2.storage.migration.plotme
+
+import org.jetbrains.exposed.sql.Table
+
+class PlotmeTables(val uppercase: Boolean) {
+ fun String.toCorrectCase() = if (uppercase) this else toLowerCase()
+
+ val PlotmePlots = PlotmePlotsT()
+ val PlotmeAllowed = PlotmeAllowedT()
+ val PlotmeDenied = PlotmeDeniedT()
+
+ inner abstract class PlotmeTable(name: String) : Table(name) {
+ val px = integer("idX").primaryKey()
+ val pz = integer("idZ").primaryKey()
+ val world_name = varchar("world", 32).primaryKey()
+ }
+
+ inner abstract class PlotmePlotPlayerMap(name: String) : PlotmeTable(name) {
+ val player_name = varchar("player", 32)
+ val player_uuid = blob("playerid").nullable()
+ }
+
+ inner class PlotmePlotsT : PlotmeTable("plotmePlots".toCorrectCase()) {
+ val owner_name = varchar("owner", 32)
+ val owner_uuid = blob("ownerid").nullable()
+ }
+
+ inner class PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase())
+ inner class PlotmeDeniedT : PlotmePlotPlayerMap("plotmeDenied".toCorrectCase())
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt b/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt index 618eaed..c7d813b 100644 --- a/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt +++ b/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt @@ -3,6 +3,7 @@ package io.dico.parcels2.util import io.dico.parcels2.util.ext.isValid import org.bukkit.Bukkit import org.bukkit.OfflinePlayer +import java.lang.IllegalArgumentException import java.util.UUID fun getPlayerName(uuid: UUID): String? = getOfflinePlayer(uuid)?.name @@ -12,3 +13,11 @@ fun getOfflinePlayer(uuid: UUID): OfflinePlayer? = Bukkit.getOfflinePlayer(uuid) fun getOfflinePlayer(name: String): OfflinePlayer? = Bukkit.getOfflinePlayer(name).takeIf { it.isValid } fun isServerThread(): Boolean = Thread.currentThread().name == "Server thread" + +fun isPlayerNameValid(name: String): Boolean = + name.length in 3..16 + && name.find { it !in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" } == null + +fun checkPlayerNameValid(name: String) { + if (!isPlayerNameValid(name)) throw IllegalArgumentException("Invalid player name: $name") +} diff --git a/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt b/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt index 3eb2e81..3904026 100644 --- a/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt +++ b/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt @@ -1,43 +1,43 @@ -package io.dico.parcels2.util - -import kotlinx.coroutines.CancellableContinuation -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Delay -import kotlinx.coroutines.Runnable -import kotlinx.coroutines.timeunit.TimeUnit -import org.bukkit.plugin.Plugin -import kotlin.coroutines.CoroutineContext - -abstract class MainThreadDispatcher : CoroutineDispatcher(), Delay { - abstract val mainThread: Thread - abstract fun runOnMainThread(task: Runnable) -} - -@Suppress("FunctionName") -fun MainThreadDispatcher(plugin: Plugin): MainThreadDispatcher { - return object : MainThreadDispatcher() { - override val mainThread: Thread = Thread.currentThread() - - override fun dispatch(context: CoroutineContext, block: Runnable) { - doDispatch(block) - } - - override fun runOnMainThread(task: Runnable) { - doDispatch(task) - } - - private fun doDispatch(task: Runnable) { - if (Thread.currentThread() === mainThread) task.run() - else plugin.server.scheduler.runTaskLater(plugin, task, 0) - } - - override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) { - val task = Runnable { - with (continuation) { resumeUndispatched(Unit) } - } - - val millis = unit.toMillis(time) - plugin.server.scheduler.runTaskLater(plugin, task, (millis + 25) / 50 - 1) - } - } +package io.dico.parcels2.util
+
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Delay
+import kotlinx.coroutines.Runnable
+import kotlinx.coroutines.timeunit.TimeUnit
+import org.bukkit.plugin.Plugin
+import kotlin.coroutines.CoroutineContext
+
+abstract class MainThreadDispatcher : CoroutineDispatcher(), Delay {
+ abstract val mainThread: Thread
+ abstract fun runOnMainThread(task: Runnable)
+}
+
+@Suppress("FunctionName")
+fun MainThreadDispatcher(plugin: Plugin): MainThreadDispatcher {
+ return object : MainThreadDispatcher() {
+ override val mainThread: Thread = Thread.currentThread()
+
+ override fun dispatch(context: CoroutineContext, block: Runnable) {
+ doDispatch(block)
+ }
+
+ override fun runOnMainThread(task: Runnable) {
+ doDispatch(task)
+ }
+
+ private fun doDispatch(task: Runnable) {
+ if (Thread.currentThread() === mainThread) task.run()
+ else plugin.server.scheduler.runTaskLater(plugin, task, 0)
+ }
+
+ override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
+ val task = Runnable {
+ with (continuation) { resumeUndispatched(Unit) }
+ }
+
+ val millis = unit.toMillis(time)
+ plugin.server.scheduler.runTaskLater(plugin, task, (millis + 25) / 50 - 1)
+ }
+ }
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/PluginAware.kt b/src/main/kotlin/io/dico/parcels2/util/PluginAware.kt new file mode 100644 index 0000000..b55f991 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/util/PluginAware.kt @@ -0,0 +1,19 @@ +@file:Suppress("RedundantLambdaArrow") + +package io.dico.parcels2.util + +import org.bukkit.plugin.Plugin +import org.bukkit.scheduler.BukkitTask + +interface PluginAware { + val plugin: Plugin +} + +inline fun PluginAware.schedule(delay: Int = 0, crossinline task: () -> Unit): BukkitTask { + return plugin.server.scheduler.runTaskLater(plugin, { -> task() }, delay.toLong()) +} + +inline fun PluginAware.scheduleRepeating(interval: Int, delay: Int = 0, crossinline task: () -> Unit): BukkitTask { + return plugin.server.scheduler.runTaskTimer(plugin, { -> task() }, delay.toLong(), interval.toLong()) +} + diff --git a/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt b/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt deleted file mode 100644 index f29ba2b..0000000 --- a/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.dico.parcels2.util - -import org.bukkit.plugin.Plugin -import org.bukkit.scheduler.BukkitTask - -interface PluginScheduler { - val plugin: Plugin - - fun schedule(delay: Int, task: () -> Unit): BukkitTask { - return plugin.server.scheduler.runTaskLater(plugin, task, delay.toLong()) - } - - fun scheduleRepeating(delay: Int, interval: Int, task: () -> Unit): BukkitTask { - return plugin.server.scheduler.runTaskTimer(plugin, task, delay.toLong(), interval.toLong()) - } -} - -@Suppress("NOTHING_TO_INLINE") -inline fun PluginScheduler.schedule(noinline task: () -> Unit) = schedule(0, task) - diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt index e160e55..1351b5d 100644 --- a/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt +++ b/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt @@ -1,108 +1,108 @@ -package io.dico.parcels2.util.ext - -import org.bukkit.Material -import org.bukkit.Material.* - -/* -colors: -WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$, -wood: -OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$, - */ - -val Material.isBed - get() = when (this) { - WHITE_BED, - ORANGE_BED, - MAGENTA_BED, - LIGHT_BLUE_BED, - YELLOW_BED, - LIME_BED, - PINK_BED, - GRAY_BED, - LIGHT_GRAY_BED, - CYAN_BED, - PURPLE_BED, - BLUE_BED, - BROWN_BED, - GREEN_BED, - RED_BED, - BLACK_BED -> true - else -> false - } - -val Material.isWoodDoor - get() = when (this) { - OAK_DOOR, - BIRCH_DOOR, - SPRUCE_DOOR, - JUNGLE_DOOR, - ACACIA_DOOR, - DARK_OAK_DOOR -> true - else -> false - } - -val Material.isWoodTrapdoor - get() = when (this) { - OAK_TRAPDOOR, - BIRCH_TRAPDOOR, - SPRUCE_TRAPDOOR, - JUNGLE_TRAPDOOR, - ACACIA_TRAPDOOR, - DARK_OAK_TRAPDOOR -> true - else -> false - } - -val Material.isWoodFenceGate - get() = when (this) { - OAK_FENCE_GATE, - BIRCH_FENCE_GATE, - SPRUCE_FENCE_GATE, - JUNGLE_FENCE_GATE, - ACACIA_FENCE_GATE, - DARK_OAK_FENCE_GATE -> true - else -> false - } - -val Material.isWoodButton - get() = when (this) { - OAK_BUTTON, - BIRCH_BUTTON, - SPRUCE_BUTTON, - JUNGLE_BUTTON, - ACACIA_BUTTON, - DARK_OAK_BUTTON -> true - else -> false - } - -private fun getMaterialPrefixed(prefix: String, name: String): Material { - return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist") -} - -fun getMaterialsWithWoodTypePrefix(name: String) = arrayOf( - getMaterialPrefixed("OAK", name), - getMaterialPrefixed("BIRCH", name), - getMaterialPrefixed("SPRUCE", name), - getMaterialPrefixed("JUNGLE", name), - getMaterialPrefixed("ACACIA", name), - getMaterialPrefixed("DARK_OAK", name) -) - -fun getMaterialsWithWoolColorPrefix(name: String) = arrayOf( - getMaterialPrefixed("WHITE", name), - getMaterialPrefixed("ORANGE", name), - getMaterialPrefixed("MAGENTA", name), - getMaterialPrefixed("LIGHT_BLUE", name), - getMaterialPrefixed("YELLOW", name), - getMaterialPrefixed("LIME", name), - getMaterialPrefixed("PINK", name), - getMaterialPrefixed("GRAY", name), - getMaterialPrefixed("LIGHT_GRAY", name), - getMaterialPrefixed("CYAN", name), - getMaterialPrefixed("PURPLE", name), - getMaterialPrefixed("BLUE", name), - getMaterialPrefixed("BROWN", name), - getMaterialPrefixed("GREEN", name), - getMaterialPrefixed("RED", name), - getMaterialPrefixed("BLACK", name) +package io.dico.parcels2.util.ext
+
+import org.bukkit.Material
+import org.bukkit.Material.*
+
+/*
+colors:
+WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$,
+wood:
+OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$,
+ */
+
+val Material.isBed
+ get() = when (this) {
+ WHITE_BED,
+ ORANGE_BED,
+ MAGENTA_BED,
+ LIGHT_BLUE_BED,
+ YELLOW_BED,
+ LIME_BED,
+ PINK_BED,
+ GRAY_BED,
+ LIGHT_GRAY_BED,
+ CYAN_BED,
+ PURPLE_BED,
+ BLUE_BED,
+ BROWN_BED,
+ GREEN_BED,
+ RED_BED,
+ BLACK_BED -> true
+ else -> false
+ }
+
+val Material.isWoodDoor
+ get() = when (this) {
+ OAK_DOOR,
+ BIRCH_DOOR,
+ SPRUCE_DOOR,
+ JUNGLE_DOOR,
+ ACACIA_DOOR,
+ DARK_OAK_DOOR -> true
+ else -> false
+ }
+
+val Material.isWoodTrapdoor
+ get() = when (this) {
+ OAK_TRAPDOOR,
+ BIRCH_TRAPDOOR,
+ SPRUCE_TRAPDOOR,
+ JUNGLE_TRAPDOOR,
+ ACACIA_TRAPDOOR,
+ DARK_OAK_TRAPDOOR -> true
+ else -> false
+ }
+
+val Material.isWoodFenceGate
+ get() = when (this) {
+ OAK_FENCE_GATE,
+ BIRCH_FENCE_GATE,
+ SPRUCE_FENCE_GATE,
+ JUNGLE_FENCE_GATE,
+ ACACIA_FENCE_GATE,
+ DARK_OAK_FENCE_GATE -> true
+ else -> false
+ }
+
+val Material.isWoodButton
+ get() = when (this) {
+ OAK_BUTTON,
+ BIRCH_BUTTON,
+ SPRUCE_BUTTON,
+ JUNGLE_BUTTON,
+ ACACIA_BUTTON,
+ DARK_OAK_BUTTON -> true
+ else -> false
+ }
+
+private fun getMaterialPrefixed(prefix: String, name: String): Material {
+ return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist")
+}
+
+fun getMaterialsWithWoodTypePrefix(name: String) = arrayOf(
+ getMaterialPrefixed("OAK", name),
+ getMaterialPrefixed("BIRCH", name),
+ getMaterialPrefixed("SPRUCE", name),
+ getMaterialPrefixed("JUNGLE", name),
+ getMaterialPrefixed("ACACIA", name),
+ getMaterialPrefixed("DARK_OAK", name)
+)
+
+fun getMaterialsWithWoolColorPrefix(name: String) = arrayOf(
+ getMaterialPrefixed("WHITE", name),
+ getMaterialPrefixed("ORANGE", name),
+ getMaterialPrefixed("MAGENTA", name),
+ getMaterialPrefixed("LIGHT_BLUE", name),
+ getMaterialPrefixed("YELLOW", name),
+ getMaterialPrefixed("LIME", name),
+ getMaterialPrefixed("PINK", name),
+ getMaterialPrefixed("GRAY", name),
+ getMaterialPrefixed("LIGHT_GRAY", name),
+ getMaterialPrefixed("CYAN", name),
+ getMaterialPrefixed("PURPLE", name),
+ getMaterialPrefixed("BLUE", name),
+ getMaterialPrefixed("BROWN", name),
+ getMaterialPrefixed("GREEN", name),
+ getMaterialPrefixed("RED", name),
+ getMaterialPrefixed("BLACK", name)
)
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt index 75aba35..e5e8aa9 100644 --- a/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt +++ b/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt @@ -1,81 +1,81 @@ -package io.dico.parcels2.util.ext - -import io.dico.dicore.Formatting -import io.dico.parcels2.logger -import java.io.File - -fun File.tryCreate(): Boolean { - if (exists()) { - return !isDirectory - } - val parent = parentFile - if (parent == null || !(parent.exists() || parent.mkdirs()) || !createNewFile()) { - logger.warn("Failed to create file $canonicalPath") - return false - } - return true -} - -inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() } -inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() } - -inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block) - -//inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition() -//inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition() -inline fun <T> T?.ifNullRun(block: () -> Unit): T? { - if (this == null) block() - return this -} - -inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) { - return EditLoopScope(this).doEditLoop(block) -} - -inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.() -> Unit) { - return EditLoopScope(this).doEditLoop(block) -} - -class EditLoopScope<T, U>(val _map: MutableMap<T, U>) { - private var iterator: MutableIterator<MutableMap.MutableEntry<T, U>>? = null - lateinit var _entry: MutableMap.MutableEntry<T, U> - - inline val key get() = _entry.key - inline var value - get() = _entry.value - set(target) = run { _entry.setValue(target) } - - inline fun doEditLoop(block: EditLoopScope<T, U>.() -> Unit) { - val it = _initIterator() - while (it.hasNext()) { - _entry = it.next() - block() - } - } - - inline fun doEditLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) { - val it = _initIterator() - while (it.hasNext()) { - val entry = it.next().also { _entry = it } - block(entry.key, entry.value) - } - } - - fun remove() { - iterator!!.remove() - } - - fun _initIterator(): MutableIterator<MutableMap.MutableEntry<T, U>> { - iterator?.let { throw IllegalStateException() } - return _map.entries.iterator().also { iterator = it } - } - -} - -operator fun Formatting.plus(other: Formatting) = toString() + other -operator fun Formatting.plus(other: String) = toString() + other - -inline fun <T> Pair<T, T>.forEach(block: (T) -> Unit) { - block(first) - block(second) -} +package io.dico.parcels2.util.ext
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.logger
+import java.io.File
+
+fun File.tryCreate(): Boolean {
+ if (exists()) {
+ return !isDirectory
+ }
+ val parent = parentFile
+ if (parent == null || !(parent.exists() || parent.mkdirs()) || !createNewFile()) {
+ logger.warn("Failed to create file $canonicalPath")
+ return false
+ }
+ return true
+}
+
+inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() }
+inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() }
+
+inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
+
+//inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
+//inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
+inline fun <T> T?.ifNullRun(block: () -> Unit): T? {
+ if (this == null) block()
+ return this
+}
+
+inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
+ return EditLoopScope(this).doEditLoop(block)
+}
+
+inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.() -> Unit) {
+ return EditLoopScope(this).doEditLoop(block)
+}
+
+class EditLoopScope<T, U>(val _map: MutableMap<T, U>) {
+ private var iterator: MutableIterator<MutableMap.MutableEntry<T, U>>? = null
+ lateinit var _entry: MutableMap.MutableEntry<T, U>
+
+ inline val key get() = _entry.key
+ inline var value
+ get() = _entry.value
+ set(target) = run { _entry.setValue(target) }
+
+ inline fun doEditLoop(block: EditLoopScope<T, U>.() -> Unit) {
+ val it = _initIterator()
+ while (it.hasNext()) {
+ _entry = it.next()
+ block()
+ }
+ }
+
+ inline fun doEditLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
+ val it = _initIterator()
+ while (it.hasNext()) {
+ val entry = it.next().also { _entry = it }
+ block(entry.key, entry.value)
+ }
+ }
+
+ fun remove() {
+ iterator!!.remove()
+ }
+
+ fun _initIterator(): MutableIterator<MutableMap.MutableEntry<T, U>> {
+ iterator?.let { throw IllegalStateException() }
+ return _map.entries.iterator().also { iterator = it }
+ }
+
+}
+
+operator fun Formatting.plus(other: Formatting) = toString() + other
+operator fun Formatting.plus(other: String) = toString() + other
+
+inline fun <T> Pair<T, T>.forEach(block: (T) -> Unit) {
+ block(first)
+ block(second)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt index a7f21c5..4c502a0 100644 --- a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt +++ b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt @@ -1,57 +1,57 @@ -package io.dico.parcels2.util.ext - -import io.dico.dicore.Formatting -import io.dico.parcels2.ParcelsPlugin -import io.dico.parcels2.logger -import org.bukkit.OfflinePlayer -import org.bukkit.entity.Player -import org.bukkit.permissions.Permissible -import org.bukkit.plugin.java.JavaPlugin - -inline val OfflinePlayer.uuid get() = uniqueId - -@Suppress("UsePropertyAccessSyntax") -inline val OfflinePlayer.isValid - get() = isOnline() || hasPlayedBefore() - -const val PERM_BAN_BYPASS = "parcels.admin.bypass.ban" -const val PERM_BUILD_ANYWHERE = "parcels.admin.bypass.build" -const val PERM_ADMIN_MANAGE = "parcels.admin.manage" - -inline val Permissible.hasPermBanBypass get() = hasPermission(PERM_BAN_BYPASS) -inline val Permissible.hasPermGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode") -inline val Permissible.hasPermBuildAnywhere get() = hasPermission(PERM_BUILD_ANYWHERE) -inline val Permissible.hasPermAdminManage get() = hasPermission(PERM_ADMIN_MANAGE) -inline val Permissible.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others") -inline val Permissible.hasPermRandomSpecific get() = hasPermission("parcels.command.random.specific") -val Player.parcelLimit: Int - get() { - for (info in effectivePermissions) { - val perm = info.permission - if (perm.startsWith("parcels.limit.")) { - val limitString = perm.substring("parcels.limit.".length) - if (limitString == "*") { - return Int.MAX_VALUE - } - return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also { - logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).") - } - } - } - return DEFAULT_LIMIT - } - -private const val DEFAULT_LIMIT = 1 -private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a") - -fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) { - if (except) { - sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message)) - } else if (nopermit) { - sendMessage(prefix + Formatting.RED + Formatting.translateChars('&', message)) - } else { - sendMessage(prefix + Formatting.translateChars('&', message)) - } -} - -const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:" +package io.dico.parcels2.util.ext
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.logger
+import org.bukkit.OfflinePlayer
+import org.bukkit.entity.Player
+import org.bukkit.permissions.Permissible
+import org.bukkit.plugin.java.JavaPlugin
+
+inline val OfflinePlayer.uuid get() = uniqueId
+
+@Suppress("UsePropertyAccessSyntax")
+inline val OfflinePlayer.isValid
+ get() = isOnline() || hasPlayedBefore()
+
+const val PERM_BAN_BYPASS = "parcels.admin.bypass.ban"
+const val PERM_BUILD_ANYWHERE = "parcels.admin.bypass.build"
+const val PERM_ADMIN_MANAGE = "parcels.admin.manage"
+
+inline val Permissible.hasPermBanBypass get() = hasPermission(PERM_BAN_BYPASS)
+inline val Permissible.hasPermGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode")
+inline val Permissible.hasPermBuildAnywhere get() = hasPermission(PERM_BUILD_ANYWHERE)
+inline val Permissible.hasPermAdminManage get() = hasPermission(PERM_ADMIN_MANAGE)
+inline val Permissible.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others")
+inline val Permissible.hasPermRandomSpecific get() = hasPermission("parcels.command.random.specific")
+val Player.parcelLimit: Int
+ get() {
+ for (info in effectivePermissions) {
+ val perm = info.permission
+ if (perm.startsWith("parcels.limit.")) {
+ val limitString = perm.substring("parcels.limit.".length)
+ if (limitString == "*") {
+ return Int.MAX_VALUE
+ }
+ return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also {
+ logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).")
+ }
+ }
+ }
+ return DEFAULT_LIMIT
+ }
+
+private const val DEFAULT_LIMIT = 1
+private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a")
+
+fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) {
+ if (except) {
+ sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message))
+ } else if (nopermit) {
+ sendMessage(prefix + Formatting.RED + Formatting.translateChars('&', message))
+ } else {
+ sendMessage(prefix + Formatting.translateChars('&', message))
+ }
+}
+
+const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:"
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt b/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt index cf67148..5b16860 100644 --- a/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt +++ b/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt @@ -1,19 +1,19 @@ -package io.dico.parcels2.util.math - -enum class Dimension { - X, - Y, - Z; - - val otherDimensions - get() = when (this) { - X -> Y to Z - Y -> X to Z - Z -> X to Y - } - - companion object { - private val values = values() - operator fun get(ordinal: Int) = values[ordinal] - } +package io.dico.parcels2.util.math
+
+enum class Dimension {
+ X,
+ Y,
+ Z;
+
+ val otherDimensions
+ get() = when (this) {
+ X -> Y to Z
+ Y -> X to Z
+ Z -> X to Y
+ }
+
+ companion object {
+ private val values = values()
+ operator fun get(ordinal: Int) = values[ordinal]
+ }
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Math.kt b/src/main/kotlin/io/dico/parcels2/util/math/Math.kt index 12c3e9f..5f8deef 100644 --- a/src/main/kotlin/io/dico/parcels2/util/math/Math.kt +++ b/src/main/kotlin/io/dico/parcels2/util/math/Math.kt @@ -1,42 +1,42 @@ -package io.dico.parcels2.util.math - -fun Double.floor(): Int { - val down = toInt() - if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) { - return down - 1 - } - return down -} - -infix fun Int.umod(divisor: Int): Int { - val out = this % divisor - if (out < 0) { - return out + divisor - } - return out -} - -val Int.even: Boolean get() = and(1) == 0 - -fun IntRange.clamp(min: Int, max: Int): IntRange { - if (first < min) { - if (last > max) { - return IntRange(min, max) - } - return IntRange(min, last) - } - if (last > max) { - return IntRange(first, max) - } - return this -} - -// the name coerceAtMost is bad -fun Int.clampMax(max: Int) = coerceAtMost(max) -fun Double.clampMin(min: Double) = coerceAtLeast(min) -fun Double.clampMax(max: Double) = coerceAtMost(max) - -// Why does this not exist? -infix fun Int.ceilDiv(divisor: Int): Int { - return -Math.floorDiv(-this, divisor) +package io.dico.parcels2.util.math
+
+fun Double.floor(): Int {
+ val down = toInt()
+ if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) {
+ return down - 1
+ }
+ return down
+}
+
+infix fun Int.umod(divisor: Int): Int {
+ val out = this % divisor
+ if (out < 0) {
+ return out + divisor
+ }
+ return out
+}
+
+val Int.even: Boolean get() = and(1) == 0
+
+fun IntRange.clamp(min: Int, max: Int): IntRange {
+ if (first < min) {
+ if (last > max) {
+ return IntRange(min, max)
+ }
+ return IntRange(min, last)
+ }
+ if (last > max) {
+ return IntRange(first, max)
+ }
+ return this
+}
+
+// the name coerceAtMost is bad
+fun Int.clampMax(max: Int) = coerceAtMost(max)
+fun Double.clampMin(min: Double) = coerceAtLeast(min)
+fun Double.clampMax(max: Double) = coerceAtMost(max)
+
+// Why does this not exist?
+infix fun Int.ceilDiv(divisor: Int): Int {
+ return -Math.floorDiv(-this, divisor)
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Region.kt b/src/main/kotlin/io/dico/parcels2/util/math/Region.kt index cdbd497..cdcbe0e 100644 --- a/src/main/kotlin/io/dico/parcels2/util/math/Region.kt +++ b/src/main/kotlin/io/dico/parcels2/util/math/Region.kt @@ -1,37 +1,37 @@ -package io.dico.parcels2.util.math - -data class Region(val origin: Vec3i, val size: Vec3i) { - val blockCount: Int get() = size.x * size.y * size.z - - val center: Vec3d - get() { - val x = (origin.x + size.x) / 2.0 - val y = (origin.y + size.y) / 2.0 - val z = (origin.z + size.z) / 2.0 - return Vec3d(x, y, z) - } - - val end: Vec3i - get() = origin + size - - val max: Vec3i - get() = Vec3i(origin.x + size.x - 1, origin.y + size.y - 1, origin.z + size.z - 1) - - fun withSize(size: Vec3i): Region { - if (size == this.size) return this - return Region(origin, size) - } - - operator fun contains(loc: Vec3i): Boolean = getFirstUncontainedDimensionOf(loc) == null - - fun getFirstUncontainedDimensionOf(loc: Vec3i): Dimension? { - val max = max - return when { - loc.x !in origin.x..max.x -> Dimension.X - loc.z !in origin.z..max.z -> Dimension.Z - loc.y !in origin.y..max.y -> Dimension.Y - else -> null - } - } - +package io.dico.parcels2.util.math
+
+data class Region(val origin: Vec3i, val size: Vec3i) {
+ val blockCount: Int get() = size.x * size.y * size.z
+
+ val center: Vec3d
+ get() {
+ val x = (origin.x + size.x) / 2.0
+ val y = (origin.y + size.y) / 2.0
+ val z = (origin.z + size.z) / 2.0
+ return Vec3d(x, y, z)
+ }
+
+ val end: Vec3i
+ get() = origin + size
+
+ val max: Vec3i
+ get() = Vec3i(origin.x + size.x - 1, origin.y + size.y - 1, origin.z + size.z - 1)
+
+ fun withSize(size: Vec3i): Region {
+ if (size == this.size) return this
+ return Region(origin, size)
+ }
+
+ operator fun contains(loc: Vec3i): Boolean = getFirstUncontainedDimensionOf(loc) == null
+
+ fun getFirstUncontainedDimensionOf(loc: Vec3i): Dimension? {
+ val max = max
+ return when {
+ loc.x !in origin.x..max.x -> Dimension.X
+ loc.z !in origin.z..max.z -> Dimension.Z
+ loc.y !in origin.y..max.y -> Dimension.Y
+ else -> null
+ }
+ }
+
}
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt index 5945120..3b25526 100644 --- a/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt +++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt @@ -1,9 +1,9 @@ -package io.dico.parcels2.util.math - -data class Vec2i( - val x: Int, - val z: Int -) { - fun add(ox: Int, oz: Int) = Vec2i(x + ox, z + oz) - fun toChunk() = Vec2i(x shr 4, z shr 4) -} +package io.dico.parcels2.util.math
+
+data class Vec2i(
+ val x: Int,
+ val z: Int
+) {
+ fun add(ox: Int, oz: Int) = Vec2i(x + ox, z + oz)
+ fun toChunk() = Vec2i(x shr 4, z shr 4)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt index 787f46c..2c3512f 100644 --- a/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt +++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt @@ -11,6 +11,8 @@ data class Vec3d( constructor(loc: Location) : this(loc.x, loc.y, loc.z) operator fun plus(o: Vec3d) = Vec3d(x + o.x, y + o.y, z + o.z) + operator fun plus(o: Vec3i) = Vec3d(x + o.x, y + o.y, z + o.z) + operator fun minus(o: Vec3d) = Vec3d(x - o.x, y - o.y, z - o.z) operator fun minus(o: Vec3i) = Vec3d(x - o.x, y - o.y, z - o.z) infix fun addX(o: Double) = Vec3d(x + o, y, z) infix fun addY(o: Double) = Vec3d(x, y + o, z) @@ -50,4 +52,10 @@ data class Vec3d( Dimension.Y -> addY(value) Dimension.Z -> addZ(value) } + + fun copyInto(loc: Location) { + loc.x = x + loc.y = y + loc.z = z + } }
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt index b25764e..b3ba169 100644 --- a/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt +++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt @@ -15,7 +15,9 @@ data class Vec3i( fun toVec2i() = Vec2i(x, z) operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z) + operator fun plus(o: Vec3d) = Vec3d(x + o.x, y + o.y, z + o.z) operator fun minus(o: Vec3i) = Vec3i(x - o.x, y - o.y, z - o.z) + operator fun minus(o: Vec3d) = Vec3d(x - o.x, y - o.y, z - o.z) infix fun addX(o: Int) = Vec3i(x + o, y, z) infix fun addY(o: Int) = Vec3i(x, y + o, z) infix fun addZ(o: Int) = Vec3i(x, y, z + o) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index eff524c..c2e272e 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,17 +1,17 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg\n</pattern>--> - <pattern>%magenta(%-16.-16(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern> - </encoder> - </appender> - - <root level="debug"> - <appender-ref ref="STDOUT" /> - </root> - - <logger name="com.zaxxer.hikari.pool.HikariPool" level="info"/> - <logger name="com.zaxxer.hikari.pool.PoolBase" level="info"/> - <logger name="com.zaxxer.hikari.HikariConfig" level="info"/> - <logger name="Exposed" level="info"/> +<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg\n</pattern>-->
+ <pattern>%magenta(%-16.-16(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern>
+ </encoder>
+ </appender>
+
+ <root level="debug">
+ <appender-ref ref="STDOUT" />
+ </root>
+
+ <logger name="com.zaxxer.hikari.pool.HikariPool" level="info"/>
+ <logger name="com.zaxxer.hikari.pool.PoolBase" level="info"/>
+ <logger name="com.zaxxer.hikari.HikariConfig" level="info"/>
+ <logger name="Exposed" level="info"/>
</configuration>
\ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 19c68da..8830377 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ -name: Parcels -author: Dico -main: io.dico.parcels2.ParcelsPlugin -version: 0.1 -api-version: 1.13 +name: Parcels
+author: Dico
+main: io.dico.parcels2.ParcelsPlugin
+version: 0.1
+api-version: 1.13
load: STARTUP
\ No newline at end of file @@ -71,8 +71,8 @@ Database - Find and patch ways to add new useless entries (for regular players at least) -Prevent invalid player names from being saved to the database. -Here, invalid player names mean names that contain invalid characters. +~~Prevent invalid player names from being saved to the database. +Here, invalid player names mean names that contain invalid characters.~~ Use an atomic GET OR INSERT query so that parallel execution doesn't cause problems (as is currently the case when migrating). @@ -87,17 +87,17 @@ Implement a container that doesn't require loading all parcel data on startup (C After testing on Redstoner - -Clear (and swap) entities on /p clear etc -Fix command lag -Chorus fruit can grow outside plots -Vines can grow outside plots -Ghasts, bats, phantoms and magma cubes can be spawned with eggs -ParcelTarget doesn't report a world that wasn't found correctly -Jumping on turtle eggs is considered as interacting with pressure plates +~~Clear (and swap) entities on /p clear etc~~ +~~Fix command lag~~ +Chorus fruit can grow outside plots -- not detectable? +~~Vines can grow outside plots~~ +~~Ghasts, bats, phantoms and magma cubes can be spawned with eggs~~ +ParcelTarget doesn't report a world that wasn't found correctly -- ?? +~~Jumping on turtle eggs is considered as interacting with pressure plates~~ Setbiome internal error when progress reporting is attached -Unclaim doesn't clear the plot. It probably should. -Players can shoot boats and minecarts. -You can use disabled items by rightclicking air. -Tab complete isn't working correctly. +~~Unclaim doesn't clear the plot. It probably should.~~ removed +Players can shoot boats and minecarts. -- ?? +~~You can use disabled items by rightclicking air.~~ +Tab complete isn't working correctly. -- disabled much of it now ~~Bed use in nether and end might not have to be blocked.~~ |