From 9f81a74bd9e89541448bf79d73d0b9bc297e72ee Mon Sep 17 00:00:00 2001 From: Dico200 Date: Sun, 22 Jul 2018 07:12:53 +0200 Subject: port RedstonerPlots a bit --- build.gradle.kts | 59 +- src/main/java/io/dico/dicore/Reflection.java | 780 ---------------- src/main/java/io/dico/dicore/Registrator.java | 987 --------------------- .../dico/dicore/exceptions/ExceptionHandler.java | 203 ----- .../checkedfunctions/CheckedBiConsumer.java | 69 -- .../checkedfunctions/CheckedBiFunction.java | 76 -- .../checkedfunctions/CheckedConsumer.java | 67 -- .../checkedfunctions/CheckedFunction.java | 74 -- .../checkedfunctions/CheckedFunctionalObject.java | 85 -- .../checkedfunctions/CheckedRunnable.java | 62 -- .../checkedfunctions/CheckedSupplier.java | 70 -- src/main/kotlin/io/dico/parcels2/Options.kt | 105 +++ src/main/kotlin/io/dico/parcels2/Parcel.kt | 31 +- src/main/kotlin/io/dico/parcels2/ParcelWorld.kt | 172 +++- src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt | 9 +- src/main/kotlin/io/dico/parcels2/WorldGenerator.kt | 263 ++++++ .../io/dico/parcels2/math/NumberExtensions.kt | 32 + .../kotlin/io/dico/parcels2/storage/Backing.kt | 37 + .../kotlin/io/dico/parcels2/storage/Exposed.kt | 85 ++ src/main/kotlin/io/dico/parcels2/storage/Hikari.kt | 51 ++ .../kotlin/io/dico/parcels2/storage/Jackson.kt | 95 ++ .../io/dico/parcels2/storage/SerializableTypes.kt | 28 + .../kotlin/io/dico/parcels2/storage/Storage.kt | 62 +- .../io/dico/parcels2/storage/StorageFactory.kt | 46 + .../io/dico/parcels2/storage/backing/Exposed.kt | 41 - .../io/dico/parcels2/util/PlayerExtensions.kt | 22 +- src/main/resources/logback.xml | 11 + src/main/resources/plugin.yml | 5 + 28 files changed, 1052 insertions(+), 2575 deletions(-) delete mode 100644 src/main/java/io/dico/dicore/Reflection.java delete mode 100644 src/main/java/io/dico/dicore/Registrator.java delete mode 100644 src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java delete mode 100644 src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java delete mode 100644 src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java delete mode 100644 src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java delete mode 100644 src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java delete mode 100644 src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java delete mode 100644 src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java delete mode 100644 src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java create mode 100644 src/main/kotlin/io/dico/parcels2/Options.kt create mode 100644 src/main/kotlin/io/dico/parcels2/WorldGenerator.kt create mode 100644 src/main/kotlin/io/dico/parcels2/math/NumberExtensions.kt create mode 100644 src/main/kotlin/io/dico/parcels2/storage/Backing.kt create mode 100644 src/main/kotlin/io/dico/parcels2/storage/Exposed.kt create mode 100644 src/main/kotlin/io/dico/parcels2/storage/Hikari.kt create mode 100644 src/main/kotlin/io/dico/parcels2/storage/Jackson.kt create mode 100644 src/main/kotlin/io/dico/parcels2/storage/SerializableTypes.kt create mode 100644 src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt delete mode 100644 src/main/kotlin/io/dico/parcels2/storage/backing/Exposed.kt create mode 100644 src/main/resources/logback.xml create mode 100644 src/main/resources/plugin.yml diff --git a/build.gradle.kts b/build.gradle.kts index ccb3622..fe71506 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,14 @@ +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import org.jetbrains.kotlin.gradle.dsl.Coroutines +import shadow.org.apache.tools.zip.ZipOutputStream plugins { kotlin("jvm") version "1.2.51" + id("com.github.johnrengelman.plugin-shadow") version "2.0.3" } group = "io.dico" @@ -9,24 +16,58 @@ version = "0.1" repositories { mavenCentral() + mavenLocal() maven("https://dl.bintray.com/kotlin/exposed") } kotlin.experimental.coroutines = Coroutines.ENABLE dependencies { - compile(files("../res/spigot-1.13-pre7.jar")) compile(kotlin("stdlib-jdk8")) + + shadow(files("../res/spigot-1.13-pre7.jar")) + + compile("io.dico.dicore:dicore3-core:1.2-mc-1.13") + compile("io.dico.dicore:dicore3-command:1.2-mc-1.13") + compile("org.jetbrains.exposed:exposed:0.10.3") compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4") + compile("com.zaxxer:HikariCP:3.2.0") + + val jacksonVersion = "2.9.6" + compile("com.fasterxml.jackson.core:jackson-core:$jacksonVersion") + compile("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + compile("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jacksonVersion") + compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion") + + compile("org.slf4j:slf4j-api:1.7.25") + compile("ch.qos.logback:logback-classic:1.2.3") + testCompile("junit:junit:4.12") } -val jar by tasks.getting(Jar::class) -val fatJar by tasks.creating(Jar::class) { - baseName = "parcels2-all" - manifest.attributes["Main-Class"] = "" - destinationDir = file("debug/plugins") - from(*configurations.compile.map(::zipTree).toTypedArray()) - with(jar) -} +tasks { + val jar by getting(Jar::class) + + val fatJar by creating(Jar::class) { + destinationDir = file("$rootDir/debug/plugins") + baseName = "parcels2-all" + from(*configurations.compile.map(::zipTree).toTypedArray()) + with(jar) + } + + val shadowJar by getting(ShadowJar::class) { + destinationDir = file("$rootDir/debug/plugins") + baseName = "parcels2-shaded" + + dependencies { + exclude(dependency(files("../res/spigot-1.13-pre7.jar"))) + } + + relocate("", "io.dico.parcels2.util.") { + exclude("META-INF/*") + exclude("logback*") + exclude("plugin*") + } + } +} \ No newline at end of file diff --git a/src/main/java/io/dico/dicore/Reflection.java b/src/main/java/io/dico/dicore/Reflection.java deleted file mode 100644 index b7b1ed4..0000000 --- a/src/main/java/io/dico/dicore/Reflection.java +++ /dev/null @@ -1,780 +0,0 @@ -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 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 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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). - *

- * 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 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 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). - *

- * 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 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 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. - *

- * 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 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 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 - *

- * 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 The expected/known field type - * @return The field value - */ - public static 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). - *

- * 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 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 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. - *

- * 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 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 getStaticFieldValue(int modifiers, Class clazz, String fieldName) { - return getStaticFieldValue(searchField(modifiers, clazz, fieldName)); - } - - /** - * Gets static field value - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - *

- * Equivalent to the call {@code getFieldValue(field, (Object) null)} - * - * @param field the field - * @param The expected/known field type - * @return The field value - * @see #getFieldValue(Field, Object) - */ - public static 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). - *

- * 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). - *

- * 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. - *

- * 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 - *

- * 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). - *

- * 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. - *

- * 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 - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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. - *

- * 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()} - *

- * 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...)} - *

- * 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 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 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} - *

- * 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...)} - *

- * 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 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 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. - *

- * 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...)} - *

- * 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 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 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. - *

- * 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...)} - *

- * 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 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 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 - *

- * 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 The expected/known method return type - * @return The result of calling the method - */ - public static 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} - *

- * 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...)} - *

- * 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 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 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. - *

- * 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...)} - *

- * 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 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 invokeStaticMethod(int modifiers, Class clazz, String methodName, Object... args) { - return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args); - } - - /** - * Invokes the static method with the given arguments - *

- * 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 The expected/known method return type - * @return The result of calling the method - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeStaticMethod(Method method, Object... args) { - return invokeMethod(method, (Object) null, args); - } - -} diff --git a/src/main/java/io/dico/dicore/Registrator.java b/src/main/java/io/dico/dicore/Registrator.java deleted file mode 100644 index 9977418..0000000 --- a/src/main/java/io/dico/dicore/Registrator.java +++ /dev/null @@ -1,987 +0,0 @@ -package io.dico.dicore; - -import org.bukkit.Server; -import org.bukkit.event.*; -import org.bukkit.event.player.PlayerEvent; -import org.bukkit.event.player.PlayerKickEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.server.PluginDisableEvent; -import org.bukkit.event.server.PluginEnableEvent; -import org.bukkit.plugin.EventExecutor; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.RegisteredListener; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.*; -import java.util.*; -import java.util.function.Consumer; - -/** - * This class acts as a utility to register event listeners in a functional manner. - * Listeners passed are always {@code Consumer} objects. - *

- * Registrations are made using its - * * {@link #registerListener(Class, Consumer)} - * * {@link #registerListener(Class, EventPriority, Consumer)} - * * {@link #registerListener(Class, boolean, Consumer)} - * * {@link #registerListener(Class, EventPriority, boolean, Consumer)} - * * {@link #registerListeners(Class)} - * * {@link #registerListeners(Object)} - * * {@link #registerListeners(Class, Object)} - * * {@link #registerPlayerQuitListener(Consumer)} - * methods. - *

- * Listeners registered in this way are generally a bit faster than when registered through {@link org.bukkit.plugin.PluginManager#registerEvents(Listener, Plugin)} - * Because it does not use reflection to call the event handlers. - * - * @implNote This class uses only one {@link Listener listener object} across all its instances, by fooling spigot into - * thinking they're all distinct ones (by violating the {@link Object#equals(Object)} contract). - *

- * Standard Registrator instances also use a fake plugin identity to register its listeners. - * You can use the {{@link #Registrator(Plugin)}} constructor to use real plugin identities. - */ -@SuppressWarnings("DanglingJavadoc") -public final class Registrator { - - // ############################################ - // # Public static methods - // ############################################ - - public static Registrator getInstance() { - return instance; - } - - // ############################################ - // # Static fields and initializer - // ############################################ - - private static final Registrator instance; - private static final Listener universalListenerObject; - private static final Plugin defaultFakePlugin; - private static final Map, HandlerListInfo> handlerListCache; - - static { - handlerListCache = new IdentityHashMap<>(); - defaultFakePlugin = new RegistratorPlugin(); - instance = new Registrator(); - universalListenerObject = new Listener() { - - //@formatter:off - /** return false here to fool the HandlerList into believing each registration is from another Listener. - * as a result, no exceptions will be thrown when registering multiple listeners for the same event and priority. - * - * Another option is to have this for each instance: - * - * - *

{@code
-                private Listener getListenerFor(HandlerList list, EventPriority priority) {
-                    int needed = (int) (listeners.get(list).stream().filter(listener -> listener.getPriority() == priority).count() + 1);
-                    while (needed > myListeners.size()) {
-                        myListeners.add(new Listener() {});
-                    }
-                    return myListeners.get(needed - 1);
-                }
-             * }
- * - * - * Where {@code myListeners} is a List - * - * - */ - //@formatter:on - @Override - public boolean equals(Object obj) { - - return false; - } - }; - } - - // ############################################ - // # Instance fields and constructors - // ############################################ - - private final List registrations; - private Plugin plugin; - private Registration pluginEnableListener; - private Registration pluginDisableListener; - private boolean enabled; - - /** - * Constructs a new instance using the {@link #defaultFakePlugin universal plugin object} - */ - public Registrator() { - this(false); - } - - /** - * Constructs a new instance using an artificial plugin. - * - * @param distinctPlugin true if the artificial plugin should be distinct from the {@link #defaultFakePlugin universal plugin object} - */ - public Registrator(boolean distinctPlugin) { - this(distinctPlugin ? new RegistratorPlugin() : defaultFakePlugin); - } - - /** - * Constructs a new instance using the given plugin - * - * @param plugin The plugin to register the listeners with - * @throws NullPointerException if plugin is null - */ - public Registrator(Plugin plugin) { - this.registrations = new ArrayList<>(); - setPlugin(plugin); - } - - // ############################################ - // # Internal static methods - // ############################################ - - /** - * static {@link EventExecutor} instantiator to make sure executors don't reference any objects unnecessarily. - */ - @SuppressWarnings("unchecked") - private static EventExecutor newEventExecutor(Class eventClass, Consumer handler) { - if (getHandlerListInfoOf(eventClass).requiresFilter) { - return (ignored, event) -> { - if (eventClass.isInstance(event)) { - handler.accept((T) event); - } - }; - } - return (ignored, event) -> handler.accept((T) event); - } - - /** - * Reflectively acquire the HandlerList for the given event type. - * - * @param eventClass the Event type - * @return its HandlerList, or null if one can't be found - */ - private static HandlerList getHandlerListOf(Class eventClass) { - try { - return getHandlerListInfoOf(eventClass).handlerList; - } catch (RuntimeException e) { - return null; - } - } - - private static HandlerListInfo getHandlerListInfoOf(Class eventClass) { - return handlerListCache.computeIfAbsent(eventClass, clz -> { - Method method = Reflection.deepSearchMethod(clz, "getHandlerList"); - boolean requiresFilter = clz != method.getDeclaringClass(); - return new HandlerListInfo(Reflection.invokeStaticMethod(method), requiresFilter); - }); - } - - // ############################################ - // # Public instance methods - // ############################################ - - /** - * Change the plugin used by the listeners of this registrator. - * - * @param plugin the plugin to use - * @throws NullPointerException if plugin is null - * @throws IllegalStateException if this registrator was returned by {@link #getInstance()} - */ - public void setPlugin(Plugin plugin) { - Objects.requireNonNull(plugin); - if (this.plugin == plugin) { - return; - } - - if (this.plugin != null) { - if (this == instance) { - throw new IllegalStateException("You may not modify the plugin used by the universal Registrator instance"); - } - - setEnabled(false); - setPluginListenerRegisteredStates(false, false); - setListenersPluginTo(plugin); - } - - this.plugin = plugin; - initPluginListeners(); - updatePluginListeners(plugin.isEnabled()); - if (plugin.isEnabled()) { - setEnabled(true); - } - } - - /** - * @return The plugin object used when registering the listeners - */ - public Plugin getRegistrationPlugin() { - return plugin; - } - - /** - * @return True if the plugin used was artificial / not an actual plugin on the server / fooled the bukkit api - */ - public boolean hasFakePlugin() { - return plugin instanceof RegistratorPlugin; - } - - /** - * @return An unmodifiable view of the registrations made by this {@link Registrator} - */ - public List getListeners() { - return Collections.unmodifiableList(registrations); - } - - /** - * Make a new listener handle for the given event type. - * The returned listener handle is not managed by this {@link Registrator}, and you must register it yourself. - * the event priority is set to {@link EventPriority#HIGHEST} - * the ignore cancelled flag is set to {@code true} - * - * @param eventClass The event type - * @param handler the listener - * @param the event type - * @return this - * / - public ListenerHandle makeListenerHandle(Class eventClass, Consumer handler) { - return makeListenerHandle(eventClass, EventPriority.HIGHEST, handler); - }/* */ - - /** - * Make a new listener handle for the given event type. - * The returned listener handle is not managed by this {@link Registrator}, and you must register it yourself. - * The ignoreCancelled flag is set to false if {@code priority} is {@link EventPriority#LOW} or {@link EventPriority#LOWEST} - * otherwise, it is set to true. - * - * @param eventClass The event type - * @param priority the event priority - * @param handler the listener - * @param the event type - * @return this - * / - public ListenerHandle makeListenerHandle(Class eventClass, EventPriority priority, Consumer handler) { - boolean ignoreCancelled = Cancellable.class.isAssignableFrom(eventClass) && priority.getSlot() > EventPriority.LOW.getSlot(); - return makeListenerHandle(eventClass, priority, ignoreCancelled, handler); - }/* */ - - /** - * Make a new listener handle for the given event type. - * The returned listener handle is not managed by this {@link Registrator}, and you must register it yourself. - * If {@code ignoreCancelled} is true, the event priority is set to {@link EventPriority#HIGHEST} - * Otherwise, it is set to {@link EventPriority#LOW} - * - * @param eventClass The event type - * @param ignoreCancelled the ignoreCancelled flag of the listener - * @param handler The listener - * @param The event type - * @return this - * / - public ListenerHandle makeListenerHandle(Class eventClass, boolean ignoreCancelled, Consumer handler) { - return makeListenerHandle(eventClass, ignoreCancelled ? EventPriority.HIGHEST : EventPriority.LOW, ignoreCancelled, handler); - }/* */ - - /** - * Make a new listener handle for the given event type. - * The returned listener handle is not managed by this {@link Registrator}, and you must register it yourself. - * - * @param eventClass The event type - * @param priority the event priority - * @param ignoreCancelled the ignoreCancelled flag of the listener - * @param handler the listener - * @param the event type - * @return this - * / - public ListenerHandle makeListenerHandle(Class eventClass, EventPriority priority, boolean ignoreCancelled, Consumer handler) { - return (ListenerHandle) createRegistration(true, priority, ignoreCancelled, eventClass, handler); - }/* */ - - /** - * Register a listener for the given event type. - * the event priority is set to {@link EventPriority#HIGHEST} - * the ignore cancelled flag is set to {@code true} - * - * @param eventClass The event type - * @param handler the listener - * @param the event type - * @return this - */ - public Registrator registerListener(Class eventClass, Consumer handler) { - return registerListener(eventClass, EventPriority.HIGHEST, handler); - } - - /** - * Register a listener for the given event type. - * The ignoreCancelled flag is set to false if {@code priority} is {@link EventPriority#LOW} or {@link EventPriority#LOWEST} - * otherwise, it is set to true. - * - * @param eventClass The event type - * @param priority the event priority - * @param handler the listener - * @param the event type - * @return this - */ - public Registrator registerListener(Class eventClass, EventPriority priority, Consumer handler) { - boolean ignoreCancelled = Cancellable.class.isAssignableFrom(eventClass) && priority.getSlot() > EventPriority.LOW.getSlot(); - return registerListener(eventClass, priority, ignoreCancelled, handler); - } - - /** - * Register a listener for the given event type. - * If {@code ignoreCancelled} is true, the event priority is set to {@link EventPriority#HIGHEST} - * Otherwise, it is set to {@link EventPriority#LOW} - * - * @param eventClass The event type - * @param ignoreCancelled the ignoreCancelled flag of the listener - * @param handler The listener - * @param The event type - * @return this - */ - public Registrator registerListener(Class eventClass, boolean ignoreCancelled, Consumer handler) { - return registerListener(eventClass, ignoreCancelled ? EventPriority.HIGHEST : EventPriority.LOW, ignoreCancelled, handler); - } - - /** - * Register a listener for the given event type. - * - * @param eventClass The event type - * @param priority the event priority - * @param ignoreCancelled the ignoreCancelled flag of the listener - * @param handler the listener - * @param the event type - * @return this - */ - public Registrator registerListener(Class eventClass, EventPriority priority, boolean ignoreCancelled, Consumer handler) { - registerListener(createRegistration(false, priority, ignoreCancelled, eventClass, handler)); - return this; - } - - public Registrator registerListeners(Class clazz, Object instance) { - for (ListenerFieldInfo fieldInfo : getListenerFields(clazz, instance)) { - registerListener(fieldInfo.eventClass, fieldInfo.anno.priority(), fieldInfo.anno.ignoreCancelled(), fieldInfo.lambda); - } - return this; - } - - public Registrator registerListeners(Class clazz) { - return registerListeners(clazz, null); - } - - public Registrator registerListeners(Object instance) { - return registerListeners(instance.getClass(), instance); - } - - /* * / - public ChainedListenerHandle makeChainedListenerHandle(Class clazz, Object instance) { - ChainedListenerHandle rv = ChainedListenerHandles.empty(); - for (ListenerFieldInfo fieldInfo : getListenerFields(clazz, instance)) { - rv = rv.withElement(makeListenerHandle(fieldInfo.eventClass, fieldInfo.anno.priority(), fieldInfo.anno.ignoreCancelled(), fieldInfo.lambda)); - } - return rv; - } - - public ChainedListenerHandle makeChainedListenerHandle(Class clazz) { - return makeChainedListenerHandle(clazz, null); - } - - public ChainedListenerHandle makeChainedListenerHandle(Object instance) { - return makeChainedListenerHandle(instance.getClass(), instance); - } - - public ListenerHandle makePlayerQuitListenerHandle(Consumer handler) { - ListenerHandle first = makeListenerHandle(PlayerQuitEvent.class, EventPriority.NORMAL, handler); - ListenerHandle second = makeListenerHandle(PlayerKickEvent.class, EventPriority.NORMAL, handler); - return ChainedListenerHandles.singleton(first).withElement(second); - } - /* */ - - public Registrator registerPlayerQuitListener(Consumer handler) { - registerListener(PlayerQuitEvent.class, EventPriority.NORMAL, handler); - return registerListener(PlayerKickEvent.class, EventPriority.NORMAL, handler); - } - - @Override - public String toString() { - return "Registrator{" + - "plugin: " + plugin + - ", enabled: " + enabled + - ", registrations: " + registrations.size() + - '}'; - } - - public String toStringWithAllRegistrations() { - StringBuilder sb = new StringBuilder("Registrator {"); - sb.append("\n plugin: ").append(plugin); - sb.append("\n enabled: ").append(enabled); - sb.append("\n registrations: ["); - - Iterator iterator = registrations.iterator(); - if (iterator.hasNext()) { - sb.append("\n ").append(iterator.next().toString()); - } - while (iterator.hasNext()) { - sb.append(',').append("\n ").append(iterator.next().toString()); - } - if (!registrations.isEmpty()) { - sb.append("\n "); - } - sb.append("]\n}"); - return sb.toString(); - } - - // ############################################ - // # Public types - // ############################################ - - public interface IEventListener extends Consumer { - @Override - void accept(T event); - } - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface ListenerInfo { - - String[] events() default {}; - - EventPriority priority() default EventPriority.HIGHEST; - - boolean ignoreCancelled() default true; - } - - public static class Registration extends RegisteredListener { - - private final EventExecutor executor; - private final Class eventClass; - private final StackTraceElement caller; - private boolean registered; - - Registration(Class eventClass, StackTraceElement caller, EventExecutor executor, EventPriority priority, Plugin plugin, boolean ignoreCancelled) { - super(universalListenerObject, executor, priority, plugin, ignoreCancelled); - this.executor = executor; - this.eventClass = eventClass; - this.caller = caller; - } - - Registration setPlugin(Plugin plugin) { - if (getPlugin() == plugin) { - return this; - } - boolean registered = this.registered; - unregister(); - Registration out = new Registration(eventClass, caller, executor, getPriority(), plugin, isIgnoringCancelled()); - if (registered) { - out.register(); - } - return out; - } - - public Class getEventClass() { - return eventClass; - } - - public StackTraceElement getCaller() { - return caller; - } - - public boolean isRegistered() { - return registered; - } - - void register() { - if (!registered) { - registered = true; - getHandlerListOf(eventClass).register(this); - } - } - - void unregister() { - if (registered) { - registered = false; - getHandlerListOf(eventClass).unregister(this); - } - } - - @Override - public String toString() { - return "Listener for " + eventClass.getSimpleName() + (caller == null ? "" : " registered at " + caller.toString()); - } - - } - - // ############################################ - // # Internal instance methods - // ############################################ - - @SuppressWarnings("UnusedReturnValue") - private boolean setEnabled(boolean enabled) { - if (this.enabled != enabled) { - this.enabled = enabled; - if (enabled) { - registerAllListeners(); - } else { - unregisterAllListeners(); - } - return true; - } - return false; - } - - private void initPluginListeners() { - if (hasFakePlugin()) { - pluginEnableListener = pluginDisableListener = null; - } else { - if (pluginEnableListener != null) { - pluginEnableListener = pluginEnableListener.setPlugin(plugin); - } else { - pluginEnableListener = createRegistration(null, false, EventPriority.NORMAL, false, PluginEnableEvent.class, this::onPluginEnable); - } - if (pluginDisableListener != null) { - pluginDisableListener = pluginDisableListener.setPlugin(plugin); - } else { - pluginDisableListener = createRegistration(null, false, EventPriority.NORMAL, false, PluginDisableEvent.class, this::onPluginDisable); - } - } - } - - private void updatePluginListeners(boolean pluginEnabled) { - if (hasFakePlugin()) { - setPluginListenerRegisteredStates(false, false); - } else { - setPluginListenerRegisteredStates(!pluginEnabled, pluginEnabled); - } - } - - private void setPluginListenerRegisteredStates(boolean enableListenerRegistered, boolean disableListenerRegistered) { - if (pluginEnableListener != null) { - if (enableListenerRegistered) { - PluginEnableEvent.getHandlerList().register(pluginEnableListener); - } else { - PluginEnableEvent.getHandlerList().unregister(pluginEnableListener); - } - } - if (pluginDisableListener != null) { - if (disableListenerRegistered) { - PluginDisableEvent.getHandlerList().register(pluginDisableListener); - } else { - PluginDisableEvent.getHandlerList().unregister(pluginDisableListener); - } - } - } - - private void onPluginEnable(PluginEnableEvent event) { - if (event.getPlugin() == plugin) { - setEnabled(true); - updatePluginListeners(true); - } - } - - private void onPluginDisable(PluginDisableEvent event) { - if (event.getPlugin() == plugin) { - setEnabled(false); - updatePluginListeners(false); - } - } - - private void setListenersPluginTo(Plugin plugin) { - List registrations = this.registrations; - for (int n = registrations.size(), i = 0; i < n; i++) { - registrations.set(i, registrations.get(i).setPlugin(plugin)); - } - } - - private void registerListener(Registration registration) { - registrations.add(registration); - if (enabled) { - registration.register(); - } - } - - private Registration createRegistration(boolean asHandle, - EventPriority priority, - boolean ignoreCancelled, - Class eventClass, - Consumer handler) { - StackTraceElement caller = null; - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - if (stackTrace.length > 0) { - String className = Registrator.class.getName(); - for (StackTraceElement element : stackTrace) { - if (!element.getClassName().equals(className) && !element.getClassName().startsWith("java.lang")) { - caller = element; - break; - } - } - } - - return createRegistration(caller, asHandle, priority, ignoreCancelled, eventClass, handler); - } - - private Registration createRegistration(StackTraceElement caller, - boolean asHandle, - EventPriority priority, - boolean ignoreCancelled, - Class eventClass, - Consumer handler) { - EventExecutor executor = newEventExecutor(eventClass, handler); - /* * / - if (asHandle) { - return new RegistrationWithHandle(eventClass, caller, executor, priority, plugin, ignoreCancelled); - } - /* */ - return new Registration(eventClass, caller, executor, priority, plugin, ignoreCancelled); - } - - private void registerAllListeners() { - for (Registration registration : registrations) { - registration.register(); - } - } - - private void unregisterAllListeners() { - for (Registration registration : registrations) { - registration.unregister(); - } - } - - @SuppressWarnings("unchecked") - private Collection getListenerFields(Class clazz, Object instance) { - Collection rv = new ArrayList<>(); - - Field[] fields = clazz.getDeclaredFields(); - boolean isStatic = instance == null; - if (!isStatic && !clazz.isInstance(instance)) { - throw new IllegalArgumentException("Instance must be an instance of the given class"); - } - - fieldLoop: - for (Field f : fields) { - if (isStatic != Modifier.isStatic(f.getModifiers()) - || !f.isAnnotationPresent(ListenerInfo.class)) { - continue; - } - - if (!IEventListener.class.isAssignableFrom(f.getType())) { - handleListenerFieldError(new ListenerFieldError(f, "Field type cannot be assigned to IEventListener: " + f.getGenericType().getTypeName())); - continue; - } - - Type eventType = null; - if (f.getType() == IEventListener.class) { - - Type[] typeArgs; - if (!(f.getGenericType() instanceof ParameterizedType) - || (typeArgs = ((ParameterizedType) f.getGenericType()).getActualTypeArguments()).length != 1) { - // TODO: if its a TypeVariable, in some cases it might be possible to get the type. - // Log a warning or throw an exception - handleListenerFieldError(new ListenerFieldError(f, "Failed to recognize event class from field type: " + f.getGenericType().getTypeName())); - continue; - } - eventType = typeArgs[0]; - - } else { - // field type is subtype of IEventListener. - // TODO: link type arguments from field declaration (f.getGenericType()) to matching TypeVariables - Type[] interfaces = f.getType().getGenericInterfaces(); - for (Type itf : interfaces) { - Class itfClass; - Type[] arguments = null; - if (itf instanceof ParameterizedType) { - if (!(((ParameterizedType) itf).getRawType() instanceof Class)) { - // Should not happen: throw error - throw new InternalError("rawType of ParameterizedType expected to be a Class"); - } - itfClass = (Class) ((ParameterizedType) itf).getRawType(); - arguments = ((ParameterizedType) itf).getActualTypeArguments(); - } else if (itf instanceof Class) { - itfClass = (Class) itf; - } else { - // TypeVariable? Not sure - // Ignore - continue; - } - - if (itfClass == IEventListener.class) { - if (arguments == null || arguments.length != 1) { - // Log a warning or throw an exception - handleListenerFieldError(new ListenerFieldError(f, "")); - continue fieldLoop; - } - - eventType = arguments[0]; - break; - } - } - - if (eventType == null) { - // Log a warning or throw an exception - handleListenerFieldError(new ListenerFieldError(f, "Failed to recognize event class from field type: " + f.getGenericType().getTypeName())); - continue; - } - } - - if (!(eventType instanceof Class)) { - if (eventType instanceof ParameterizedType) { - Type rawType = ((ParameterizedType) eventType).getRawType(); - if (!(rawType instanceof Class)) { - // Log a warning or throw an exception - handleListenerFieldError(new ListenerFieldError(f, "Failed to recognize event class from a Type: " + eventType)); - continue; - } - eventType = rawType; - } else { - // Log a warning or throw an exception - handleListenerFieldError(new ListenerFieldError(f, "Failed to recognize event class from a Type: " + eventType)); - continue; - } - } - - Consumer lambda; - try { - f.setAccessible(true); - lambda = (Consumer) f.get(instance); - } catch (IllegalArgumentException | IllegalAccessException | ClassCastException e) { - // Log a warning or throw an exception - handleListenerFieldError(new ListenerFieldError(f, e)); - continue; - } - - Class baseEventClass = (Class) eventType; - - ListenerInfo anno = f.getAnnotation(ListenerInfo.class); - String[] eventClassNames = anno.events(); - if (eventClassNames.length > 0) { - - // The same field might get added here multiple times, to register it with multiple events. - // This list is used to prevent adding its listener to the same handler list multiple times. - // Allocation of a table is not necessary at this scale. - List handlerLists = new ArrayList<>(); - - for (String eventClassName : eventClassNames) { - Class eventClass = getEventClassByName(eventClassName); - if (eventClass != null && baseEventClass.isAssignableFrom(eventClass)) { - HandlerList handlerList = getHandlerListOf(eventClass); - if (handlerList == null) { - // multiple warnings could be raised here for the same field - handleListenerFieldError(new ListenerFieldError(f, "There is no HandlerList available for the event " + eventClass.getName())); - continue; - } - - if (handlerLists.contains(handlerList)) { - // Ignore: it will work as intended - continue; - } - - handlerLists.add(handlerList); - rv.add(new ListenerFieldInfo(eventClass, lambda, anno)); - } else { - // Error: event class string is not recognized or cannot be assigned to the event type - // Log a warning or throw an exception - String msg = String.format("Event class '%s', resolved to '%s', is unresolved or cannot be assigned to '%s'", - eventClassName, eventClass == null ? null : eventClass.getName(), baseEventClass.getName()); - handleListenerFieldError(new ListenerFieldError(f, msg)); - // Don't add the field to the result list - } - } - - } else { - rv.add(new ListenerFieldInfo(baseEventClass, lambda, anno)); - } - } - return rv; - } - - private static void handleListenerFieldError(ListenerFieldError error) { - // Log a warning or throw an exception. Behaviour can be changed. - throw error; - } - - private static Class getEventClassByName(String name) { - try { - //noinspection unchecked - return (Class) Class.forName("org.bukkit.event." + name); - } catch (ClassNotFoundException | ClassCastException e) { - return null; - } - } - - // ############################################ - // # Internal types - // ############################################ - - /* * / - private static final class RegistrationWithHandle extends Registration implements ListenerHandle { - RegistrationWithHandle(Class eventClass, StackTraceElement caller, EventExecutor executor, EventPriority priority, Plugin plugin, boolean ignoreCancelled) { - super(eventClass, caller, executor, priority, plugin, ignoreCancelled); - } - - @Override - public void register() { - super.register(); - } - - @Override - public void unregister() { - super.unregister(); - } - } - /* */ - - private static final class HandlerListInfo { - final HandlerList handlerList; - // true if and only if the handler list resides in a super class of the event for which it was requested. - // the filter is needed to filter out event instances not of the requested class. - // See newEventExecutor(eventClass, handler) - final boolean requiresFilter; - - HandlerListInfo(HandlerList handlerList, boolean requiresFilter) { - this.handlerList = handlerList; - this.requiresFilter = requiresFilter; - } - } - - private static final class ListenerFieldInfo { - final Class eventClass; - final Consumer lambda; - final ListenerInfo anno; - - ListenerFieldInfo(Class eventClass, Consumer lambda, ListenerInfo anno) { - this.eventClass = eventClass; - this.lambda = lambda; - this.anno = anno; - } - } - - /** - * Error class to report fields that are intended to be listeners with illegal properties - */ - static final class ListenerFieldError extends Error { - private Field field; - - public ListenerFieldError(Field field, String message) { - super(message); - this.field = field; - } - - public ListenerFieldError(Field field, Throwable cause) { - super(cause); - this.field = field; - } - - public Field getField() { - return field; - } - } - - private static class RegistratorPlugin implements Plugin { - @Override - public java.io.File getDataFolder() { - return null; - } - - @Override - public org.bukkit.plugin.PluginDescriptionFile getDescription() { - return null; - } - - @Override - public org.bukkit.configuration.file.FileConfiguration getConfig() { - return null; - } - - @Override - public java.io.InputStream getResource(String s) { - return null; - } - - @Override - public void saveConfig() { - } - - @Override - public void saveDefaultConfig() { - } - - @Override - public void saveResource(String s, boolean b) { - } - - @Override - public void reloadConfig() { - } - - @Override - public org.bukkit.plugin.PluginLoader getPluginLoader() { - return null; - } - - @Override - public Server getServer() { - return null; - } - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public void onDisable() { - } - - @Override - public void onLoad() { - } - - @Override - public void onEnable() { - } - - @Override - public boolean isNaggable() { - return false; - } - - @Override - public void setNaggable(boolean b) { - } - - /* * / - @Override - public com.avaje.ebean.EbeanServer getDatabase() { - return null; - } - /* */ - @Override - public org.bukkit.generator.ChunkGenerator getDefaultWorldGenerator(String s, String s1) { - return null; - } - - @Override - public java.util.logging.Logger getLogger() { - return null; - } - - @Override - public String getName() { - return null; - } - - @Override - public boolean onCommand(org.bukkit.command.CommandSender commandSender, org.bukkit.command.Command command, String s, String[] strings) { - return false; - } - - @Override - public List onTabComplete(org.bukkit.command.CommandSender commandSender, org.bukkit.command.Command command, String s, String[] strings) { - return null; - } - - @Override - public boolean equals(Object obj) { - return this == obj; - } - } - -} \ No newline at end of file diff --git a/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java b/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java deleted file mode 100644 index 002eaa0..0000000 --- a/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java +++ /dev/null @@ -1,203 +0,0 @@ -package io.dico.dicore.exceptions; - -import io.dico.dicore.exceptions.checkedfunctions.CheckedFunctionalObject; -import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable; -import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier; - -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 never 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. - *

- * 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 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. - *

- * Any exceptions thrown by this handler are delegated to the caller. - * - * @param action The supplier whose result to compute - * @param 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 supplySafe(CheckedSupplier 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 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 Supplier safeSupplier(CheckedSupplier supplier) { - return () -> supplySafe(supplier); - } - - /** - * Logs the given exception as an error to {@code out} - *

- * 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 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 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/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java b/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java deleted file mode 100644 index badf05b..0000000 --- a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.BiConsumer; - -/** - * checked mimic of {@link BiConsumer} - * - * @param - * @param - * @param - */ -@FunctionalInterface -public interface CheckedBiConsumer - extends CheckedFunctionalObject, BiConsumer { - - /** - * 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 handleExceptionsWith(ExceptionHandler handler) { - return new CheckedBiConsumer() { - @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 handleExceptionsWith(ExceptionHandler handler) { - return CheckedBiConsumer.this.handleExceptionsWith(handler); - } - }; - } - -} diff --git a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java b/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java deleted file mode 100644 index 9b39325..0000000 --- a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.BiFunction; - -/** - * checked mimic of {@link BiFunction} - * - * @param - * @param - * @param - * @param - */ -@FunctionalInterface -public interface CheckedBiFunction - extends CheckedFunctionalObject, BiFunction { - - /** - * 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 handleExceptionsWith(ExceptionHandler handler) { - return new CheckedBiFunction() { - @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 handleExceptionsWith(ExceptionHandler handler) { - return CheckedBiFunction.this.handleExceptionsWith(handler); - } - }; - } -} diff --git a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java b/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java deleted file mode 100644 index fa48ae8..0000000 --- a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Consumer; - -/** - * checked mimic of {@link Consumer} - * - * @param - * @param - */ -@FunctionalInterface -public interface CheckedConsumer - extends CheckedFunctionalObject, Consumer { - - /** - * 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 handleExceptionsWith(ExceptionHandler handler) { - return new CheckedConsumer() { - @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 handleExceptionsWith(ExceptionHandler handler) { - return CheckedConsumer.this.handleExceptionsWith(handler); - } - }; - } - -} diff --git a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java b/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java deleted file mode 100644 index 9ff806a..0000000 --- a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Function; - -/** - * checked mimic of {@link Function} - * - * @param - * @param - * @param - */ -@FunctionalInterface -public interface CheckedFunction - extends CheckedFunctionalObject, Function { - - /** - * 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 handleExceptionsWith(ExceptionHandler handler) { - return new CheckedFunction() { - @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 handleExceptionsWith(ExceptionHandler handler) { - return CheckedFunction.this.handleExceptionsWith(handler); - } - }; - } - -} diff --git a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java b/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java deleted file mode 100644 index e84b33d..0000000 --- a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java +++ /dev/null @@ -1,85 +0,0 @@ -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} - *

- * Checked functional interfaces are functions with throws declarations. - * The name comes from the fact that they can throw checked exceptions. - *

- * They extend their non-checked counterparts, whose methods are implemented by - * returning the result of {@link #resultOnError(TException, Object...)} when an exception is thrown - *

- * 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 The return type of this functional interface's method - * @param The type of exception that might be thrown - */ -public interface CheckedFunctionalObject extends ExceptionHandler { - - /** - * {@inheritDoc} - * - * @param ex The exception to be handled - */ - @Override - default void handle(Throwable ex) { - handleGenericException(ex); - } - - /** - * {@inheritDoc} - *

- * 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. - *

- * 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 handleExceptionsWith(ExceptionHandler handler); - -} diff --git a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java b/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java deleted file mode 100644 index 55de6f8..0000000 --- a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -/** - * checked mimic of {@link Runnable} - * - * @param - */ -@FunctionalInterface -public interface CheckedRunnable - extends CheckedFunctionalObject, 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 handleExceptionsWith(ExceptionHandler handler) { - return new CheckedRunnable() { - @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 handleExceptionsWith(ExceptionHandler handler) { - return CheckedRunnable.this.handleExceptionsWith(handler); - } - }; - } - -} diff --git a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java b/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java deleted file mode 100644 index dec2e7e..0000000 --- a/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Supplier; - -/** - * checked mimic of {@link Supplier} - * - * @param - * @param - */ -@FunctionalInterface -public interface CheckedSupplier - extends CheckedFunctionalObject, Supplier { - - /** - * 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 handleExceptionsWith(ExceptionHandler handler) { - return new CheckedSupplier() { - @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 handleExceptionsWith(ExceptionHandler handler) { - return CheckedSupplier.this.handleExceptionsWith(handler); - } - }; - } -} diff --git a/src/main/kotlin/io/dico/parcels2/Options.kt b/src/main/kotlin/io/dico/parcels2/Options.kt new file mode 100644 index 0000000..4324df3 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/Options.kt @@ -0,0 +1,105 @@ +package io.dico.parcels2 + + +import com.fasterxml.jackson.annotation.JsonIgnore +import io.dico.parcels2.storage.Storage +import io.dico.parcels2.storage.StorageFactory +import io.dico.parcels2.storage.yamlObjectMapper +import org.bukkit.Bukkit +import org.bukkit.Bukkit.createBlockData +import org.bukkit.GameMode +import org.bukkit.Material +import org.bukkit.block.Biome +import org.bukkit.block.data.BlockData +import java.io.Reader +import java.io.Writer +import java.util.* + +class Options { + var worlds: Map = HashMap() + private set + var storage: StorageOptions = StorageOptions("mysql", DataConnectionOptions()) + + fun addWorld(name: String, options: WorldOptions) = (worlds as MutableMap).put(name, options) + + fun addDefaultWorld() = addWorld("plotworld", WorldOptions()) + + fun writeTo(writer: Writer) = yamlObjectMapper.writeValue(writer, this) + + fun mergeFrom(reader: Reader) = yamlObjectMapper.readerForUpdating(this).readValue(reader) + + override fun toString(): String = yamlObjectMapper.writeValueAsString(this) + +} + +data class WorldOptions(var gameMode: GameMode? = GameMode.CREATIVE, + var dayTime: Boolean = true, + var noWeather: Boolean = true, + var dropEntityItems: Boolean = true, + var doTileDrops: Boolean = false, + var disableExplosions: Boolean = true, + var blockPortalCreation: Boolean = true, + var blockMobSpawning: Boolean = true, + var blockedItems: Set = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL), + var axisLimit: Int = 10, + var generator: GeneratorOptions = DefaultGeneratorOptions()) { + +} + +abstract class GeneratorOptions { + + abstract fun generatorFactory(): GeneratorFactory + + fun getGenerator(worldName: String) = generatorFactory().newParcelGenerator(worldName, this) + +} + +data class DefaultGeneratorOptions(var defaultBiome: Biome = Biome.JUNGLE, + var wallType: BlockData = createBlockData(Material.STONE_SLAB), + var floorType: BlockData = createBlockData(Material.QUARTZ_BLOCK), + var fillType: BlockData = createBlockData(Material.QUARTZ_BLOCK), + var pathMainType: BlockData = createBlockData(Material.SANDSTONE), + var pathAltType: BlockData = createBlockData(Material.REDSTONE_BLOCK), + var parcelSize: Int = 101, + var pathSize: Int = 9, + var floorHeight: Int = 64, + var offsetX: Int = 0, + var offsetZ: Int = 0) : GeneratorOptions() { + + override fun generatorFactory(): GeneratorFactory = DefaultParcelGenerator.Factory + +} + +class StorageOptions(val dialect: String, + val options: Any) { + + @get:JsonIgnore + val factory = StorageFactory.getFactory(dialect) ?: throw IllegalArgumentException("Invalid storage dialect: $dialect") + + fun newStorageInstance(): Storage = factory.newStorageInstance(dialect, options) + +} + +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? { + 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).toIntOrNull() ?: return null.also { + logger.error("Invalid port number in data storage options: $it, using $defaultPort as default") + } + + return Pair(addressName, port) + } + +} + +data class DataFileOptions(val location: String = "/flatfile-storage/") \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt index 6fd8cf3..0ec2c0b 100644 --- a/src/main/kotlin/io/dico/parcels2/Parcel.kt +++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt @@ -2,6 +2,7 @@ package io.dico.parcels2 import io.dico.parcels2.math.Vec2i import io.dico.parcels2.util.getPlayerName +import io.dico.parcels2.util.hasBuildAnywhere import org.bukkit.Bukkit import org.bukkit.entity.Player import java.util.* @@ -10,13 +11,29 @@ class Parcel(val world: ParcelWorld, val pos: Vec2i, var data: ParcelData = ParcelData()) { - + val id get() = "${pos.x}:${pos.z}" } class ParcelData { - val owner: ParcelOwner? = null - val added = mutableMapOf() + private val added = mutableMapOf() + var owner: ParcelOwner? = null + + fun setAddedStatus(uuid: UUID): AddedStatus = added.getOrDefault(uuid, AddedStatus.DEFAULT) + fun setAddedStatus(uuid: UUID, state: AddedStatus) = state.takeIf { it != AddedStatus.DEFAULT }?.let { added[uuid] = it } + ?: added.remove(uuid) + + fun isBanned(uuid: UUID) = setAddedStatus(uuid) == AddedStatus.BANNED + fun isAllowed(uuid: UUID) = setAddedStatus(uuid) == AddedStatus.ALLOWED + fun canBuild(player: Player) = isAllowed(player.uniqueId) + || owner?.matches(player, allowNameMatch = false) ?: false + || player.hasBuildAnywhere +} + +enum class AddedStatus { + DEFAULT, + ALLOWED, + BANNED } data class ParcelOwner(val uuid: UUID? = null, @@ -29,11 +46,13 @@ data class ParcelOwner(val uuid: UUID? = null, val playerName get() = getPlayerName(uuid, name) @Suppress("DEPRECATION") - val offlinePlayer get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name)) - ?.takeIf { it.isOnline() || it.hasPlayedBefore() } + val offlinePlayer + get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name)) + ?.takeIf { it.isOnline() || it.hasPlayedBefore() } fun matches(player: Player, allowNameMatch: Boolean = false): Boolean { - return player.uniqueId == uuid || (allowNameMatch && player.name == name) + return uuid?.let { it == player.uniqueId } ?: false + || (allowNameMatch && name?.let { it == player.name } ?: false) } } \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt index 9832ce8..97e3942 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt @@ -1,4 +1,174 @@ package io.dico.parcels2 -class ParcelWorld { +import io.dico.parcels2.math.Vec2i +import io.dico.parcels2.math.floor +import kotlinx.coroutines.experimental.launch +import org.bukkit.Bukkit +import org.bukkit.Location +import org.bukkit.World +import org.bukkit.WorldCreator +import org.bukkit.block.Block +import org.bukkit.entity.Entity +import org.bukkit.entity.Player +import java.util.* +import kotlin.coroutines.experimental.buildSequence + +val worlds: Map get() = _worlds +private val _worlds: MutableMap = HashMap() + +fun getWorld(name: String): ParcelWorld? = _worlds.get(name) + +fun getWorld(world: World): ParcelWorld? = getWorld(world.name) + +fun getParcelAt(block: Block): Parcel? = getParcelAt(block.world, block.x, block.z) + +fun getParcelAt(player: Player): Parcel? = getParcelAt(player.location) + +fun getParcelAt(location: Location): Parcel? = getParcelAt(location.world, location.x.floor(), location.z.floor()) + +fun getParcelAt(world: World, x: Int, z: Int): Parcel? = getParcelAt(world.name, x, z) + +fun getParcelAt(world: String, x: Int, z: Int): Parcel? { + with (getWorld(world) ?: return null) { + return generator.parcelAt(x, z) + } +} + +fun loadWorlds(options: Options) { + for ((worldName, worldOptions) in options.worlds.entries) { + val world: ParcelWorld + try { + world = ParcelWorld(worldName, worldOptions, worldOptions.generator.getGenerator(worldName)) + } catch (ex: Exception) { + ex.printStackTrace() + continue + } + + _worlds.put(worldName, world) + + if (Bukkit.getWorld(worldName) == null) { + val bworld = WorldCreator(worldName).generator(world.generator).createWorld() + val spawn = world.generator.getFixedSpawnLocation(bworld, null) + bworld.setSpawnLocation(spawn.x.floor(), spawn.y.floor(), spawn.z.floor()) + } + + } + +} + +interface ParcelProvider { + + fun parcelAt(x: Int, z: Int): Parcel? + + fun parcelAt(vec: Vec2i): Parcel? = parcelAt(vec.x, vec.z) + + fun parcelAt(loc: Location): Parcel? = parcelAt(loc.x.floor(), loc.z.floor()) + + fun parcelAt(entity: Entity): Parcel? = parcelAt(entity.location) + + fun parcelAt(block: Block): Parcel? = parcelAt(block.x, block.z) +} + +class ParcelWorld(val name: String, + val options: WorldOptions, + val generator: ParcelGenerator) : ParcelProvider by generator { + val world: World by lazy { + val tmp = Bukkit.getWorld(name) + if (tmp == null) { + throw NullPointerException("World $name does not appear to be loaded") + } + tmp + } + + val container: ParcelContainer = DefaultParcelContainer(this) + + fun parcelByID(x: Int, z: Int): Parcel? { + TODO("not implemented") + } + + fun parcelByID(id: Vec2i): Parcel? = parcelByID(id.x, id.z) + + fun enforceOptionsIfApplicable() { + val world = world + val options = options + if (options.dayTime) { + world.setGameRuleValue("doDaylightCycle", "false") + world.setTime(6000) + } + + if (options.noWeather) { + world.setStorm(false) + world.setThundering(false) + world.weatherDuration = Integer.MAX_VALUE + } + + world.setGameRuleValue("doTileDrops", "${options.doTileDrops}") + } + +} + +abstract class ParcelContainer { + + abstract fun ployByID(x: Int, z: Int): Parcel? + + abstract fun nextEmptyParcel(): Parcel? + +} + +class DefaultParcelContainer(private val world: ParcelWorld) : ParcelContainer() { + private var parcels: Array> + + init { + parcels = initArray(world.options.axisLimit, world) + } + + fun resizeIfSizeChanged() { + if (parcels.size / 2 != world.options.axisLimit) { + resize(world.options.axisLimit) + } + } + + fun resize(axisLimit: Int) { + parcels = initArray(axisLimit, world, this) + } + + fun initArray(axisLimit: Int, world: ParcelWorld, cur: DefaultParcelContainer? = null): Array> { + val arraySize = 2 * axisLimit + 1 + return Array(arraySize, { + val x = it - axisLimit + Array(arraySize, { + val z = it - axisLimit + cur?.ployByID(x, z) ?: Parcel(world, Vec2i(x, z)) + }) + }) + } + + override fun ployByID(x: Int, z: Int): Parcel? { + return parcels[x][z] + } + + override fun nextEmptyParcel(): Parcel? { + TODO() + } + + fun allParcels(): Sequence = buildSequence { + for (array in parcels) { + yieldAll(array.iterator()) + } + } + + fun loadAllData() { + /* + val channel = Main.instance.storage.readParcelData(allParcels(), 100).channel + launch(Main.instance.storage.asyncDispatcher) { + for ((parcel, data) in channel) { + if (data != null) { + parcel.data = data + } + } + } + */ + TODO() + } + } \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt index 6c1d6e9..5ffce7c 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt @@ -1,12 +1,15 @@ package io.dico.parcels2 import org.bukkit.plugin.java.JavaPlugin +import org.slf4j.LoggerFactory -class ParcelsPlugin : JavaPlugin() { - - +val logger = LoggerFactory.getLogger("ParcelsPlugin") +class ParcelsPlugin : JavaPlugin() { + override fun onEnable() { + super.onEnable() + } } \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt b/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt new file mode 100644 index 0000000..8f6a3aa --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt @@ -0,0 +1,263 @@ +package io.dico.parcels2 + +import io.dico.parcels2.math.Vec2i +import io.dico.parcels2.math.clamp +import io.dico.parcels2.math.even +import io.dico.parcels2.math.umod +import org.bukkit.* +import org.bukkit.Bukkit.createBlockData +import org.bukkit.block.Biome +import org.bukkit.block.Block +import org.bukkit.block.BlockFace +import org.bukkit.block.Skull +import org.bukkit.block.data.BlockData +import org.bukkit.block.data.type.Sign +import org.bukkit.block.data.type.Slab +import org.bukkit.entity.Entity +import org.bukkit.generator.BlockPopulator +import org.bukkit.generator.ChunkGenerator +import java.util.* +import kotlin.coroutines.experimental.buildIterator +import kotlin.reflect.KClass + +abstract class ParcelGenerator : ChunkGenerator(), ParcelProvider { + abstract val world: ParcelWorld + + abstract val factory: GeneratorFactory + + abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData + + abstract fun populate(world: World?, random: Random?, chunk: Chunk?) + + abstract override fun getFixedSpawnLocation(world: World?, random: Random?): Location + + override fun getDefaultPopulators(world: World?): MutableList { + return Collections.singletonList(object : BlockPopulator() { + override fun populate(world: World?, random: Random?, chunk: Chunk?) { + this@ParcelGenerator.populate(world, random, chunk) + } + }) + } + + abstract fun updateOwner(parcel: Parcel) + + abstract fun getBottomCoord(parcel: Parcel): Vec2i + + abstract fun getHomeLocation(parcel: Parcel): Location + + abstract fun setBiome(parcel: Parcel, biome: Biome) + + abstract fun getEntities(parcel: Parcel): Collection + + abstract fun getBlocks(parcel: Parcel, yRange: IntRange = 0..255): Iterator + +} + +interface GeneratorFactory { + companion object GeneratorFactories { + private val map: MutableMap = HashMap() + + fun registerFactory(generator: GeneratorFactory): Boolean = map.putIfAbsent(generator.name, generator) == null + + fun getFactory(name: String): GeneratorFactory? = map.get(name) + + init { + registerFactory(DefaultParcelGenerator.Factory) + } + + } + + val name: String + + val optionsClass: KClass + + fun newParcelGenerator(worldName: String, options: GeneratorOptions): ParcelGenerator + +} + +class DefaultParcelGenerator(name: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() { + override val world: ParcelWorld by lazy { TODO() } + override val factory = Factory + + companion object Factory : GeneratorFactory { + override val name get() = "default" + override val optionsClass get() = DefaultGeneratorOptions::class + override fun newParcelGenerator(worldName: String, options: GeneratorOptions): ParcelGenerator { + return DefaultParcelGenerator(worldName, options as DefaultGeneratorOptions) + } + } + + val sectionSize = o.parcelSize + o.pathSize + val pathOffset = (if (o.pathSize % 2 == 0) o.pathSize + 2 else o.pathSize + 1) / 2 + val makePathMain = o.pathSize > 2 + val makePathAlt = o.pathSize > 4 + + private inline fun generate(chunkX: Int, + chunkZ: Int, + floor: T, wall: + T, pathMain: T, + pathAlt: T, + fill: T, + setter: (Int, Int, Int, T) -> Unit) { + + val floorHeight = o.floorHeight + val parcelSize = o.parcelSize + val sectionSize = sectionSize + val pathOffset = pathOffset + val makePathMain = makePathMain + val makePathAlt = makePathAlt + + // parcel bottom x and z + // umod is unsigned %: the result is always >= 0 + val pbx = ((chunkX shl 4) - o.offsetX) umod sectionSize + val pbz = ((chunkZ shl 4) - o.offsetZ) umod sectionSize + + var curHeight: Int + var x: Int + var z: Int + for (cx in 0..15) { + for (cz in 0..15) { + x = (pbx + cx) % sectionSize - pathOffset + z = (pbz + cz) % sectionSize - pathOffset + curHeight = floorHeight + + val type = when { + (x in 0 until parcelSize && z in 0 until parcelSize) -> floor + (x in -1..parcelSize && z in -1..parcelSize) -> { + curHeight++ + wall + } + (makePathAlt && x in -2 until parcelSize + 2 && z in -2 until parcelSize + 2) -> pathAlt + (makePathMain) -> pathMain + else -> { + curHeight++ + wall + } + } + + for (y in 0 until curHeight) { + setter(cx, y, cz, fill) + } + setter(cx, curHeight, cz, type) + } + } + } + + override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData { + val out = Bukkit.createChunkData(world) + generate(chunkX, chunkZ, o.floorType, o.wallType, o.pathMainType, o.pathAltType, o.fillType) { x, y, z, type -> + out.setBlock(x, y, z, type) + } + return out + } + + + override fun populate(world: World?, random: Random?, chunk: Chunk?) { + /* + generate(chunk!!.x, chunk.z, o.floorType.data, o.wallType.data, o.pathMainType.data, o.pathAltType.data, o.fillType.data) { x, y, z, type -> + if (type == 0.toByte()) chunk.getBlock(x, y, z).setData(type, false) + } + */ + } + + override fun getFixedSpawnLocation(world: World?, random: Random?): Location { + val fix = if (o.parcelSize.even) 0.5 else 0.0 + return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix) + } + + override fun parcelAt(x: Int, z: Int): Parcel? { + val sectionSize = sectionSize + val parcelSize = o.parcelSize + val absX = x - o.offsetX - pathOffset + val absZ = z - o.offsetZ - pathOffset + val modX = absX umod sectionSize + val modZ = absZ umod sectionSize + if (0 <= modX && modX < parcelSize && 0 <= modZ && modZ < parcelSize) { + return world.parcelByID((absX - modX) / sectionSize, (absZ - modZ) / sectionSize) + } + return null + } + + override fun getBottomCoord(parcel: Parcel): Vec2i = Vec2i(sectionSize * parcel.pos.x + pathOffset + o.offsetX, + sectionSize * parcel.pos.z + pathOffset + o.offsetZ) + + override fun getHomeLocation(parcel: Parcel): Location { + val bottom = getBottomCoord(parcel) + return Location(world.world, bottom.x.toDouble(), o.floorHeight + 1.0, bottom.z + (o.parcelSize - 1) / 2.0, -90F, 0F) + } + + override fun updateOwner(parcel: Parcel) { + val world = this.world.world + val b = getBottomCoord(parcel) + + val wallBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 1) + val signBlock = world.getBlockAt(b.x - 2, o.floorHeight + 1, b.z - 1) + val skullBlock = world.getBlockAt(b.x - 1, o.floorHeight + 2, b.z - 1) + + val owner = parcel.data?.owner + if (owner == null) { + wallBlock.blockData = o.wallType + signBlock.type = Material.AIR + skullBlock.type = Material.AIR + } else { + + val wallBlockType: BlockData = if (o.wallType is Slab) + (o.wallType.clone() as Slab).apply { type = Slab.Type.DOUBLE } + else + o.wallType + + wallBlock.blockData = wallBlockType + + signBlock.blockData = (createBlockData(Material.WALL_SIGN) as Sign).apply { rotation = BlockFace.NORTH } + + val sign = signBlock.state as org.bukkit.block.Sign + sign.setLine(0, parcel.id) + sign.setLine(2, owner.playerName) + sign.update() + + skullBlock.type = Material.PLAYER_HEAD + val skull = skullBlock.state as Skull + if (owner.uuid != null) { + skull.owningPlayer = owner.offlinePlayer + } else { + skull.owner = owner.name + } + skull.rotation = BlockFace.WEST + skull.update() + } + } + + override fun setBiome(parcel: Parcel, biome: Biome) { + val world = this.world.world + val b = getBottomCoord(parcel) + val parcelSize = o.parcelSize + for (x in b.x until b.x + parcelSize) { + for (z in b.z until b.z + parcelSize) { + world.setBiome(x, z, biome) + } + } + } + + override fun getEntities(parcel: Parcel): Collection { + val world = this.world.world + val b = getBottomCoord(parcel) + val parcelSize = o.parcelSize + val center = Location(world, (b.x + parcelSize) / 2.0, 128.0, (b.z + parcelSize) / 2.0) + return world.getNearbyEntities(center, parcelSize / 2.0 + 0.2, 128.0, parcelSize / 2.0 + 0.2) + } + + override fun getBlocks(parcel: Parcel, yRange: IntRange): Iterator = buildIterator { + val range = yRange.clamp(0, 255) + val world = this@DefaultParcelGenerator.world.world + val b = getBottomCoord(parcel) + val parcelSize = o.parcelSize + for (x in b.x until b.x + parcelSize) { + for (z in b.z until b.z + parcelSize) { + for (y in range) { + yield(world.getBlockAt(x, y, z)) + } + } + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/math/NumberExtensions.kt b/src/main/kotlin/io/dico/parcels2/math/NumberExtensions.kt new file mode 100644 index 0000000..faf2939 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/math/NumberExtensions.kt @@ -0,0 +1,32 @@ +package io.dico.parcels2.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 +} \ 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 new file mode 100644 index 0000000..cd33b3d --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt @@ -0,0 +1,37 @@ +package io.dico.parcels2.storage + +import io.dico.parcels2.Parcel +import io.dico.parcels2.ParcelData +import io.dico.parcels2.ParcelOwner +import io.dico.parcels2.storage.SerializableParcel +import kotlinx.coroutines.experimental.channels.ProducerScope +import java.util.* + +interface Backing { + + val name: String + + suspend fun init() + + suspend fun shutdown() + + /** + * This producer function is capable of constantly reading plots from a potentially infinite sequence, + * and provide plotdata for it as read from the database. + */ + + suspend fun ProducerScope>.produceParcelData(parcels: Sequence) + + suspend fun readParcelData(plotFor: Parcel): ParcelData? + + suspend fun getOwnedParcels(user: ParcelOwner): List + + suspend fun setParcelOwner(plotFor: Parcel, owner: ParcelOwner?) + + suspend fun setParcelPlayerState(plotFor: Parcel, player: UUID, state: Boolean?) + + suspend fun setParcelAllowsInteractInventory(plot: Parcel, value: Boolean) + + suspend fun setParcelAllowsInteractInputs(plot: Parcel, value: Boolean) + +} \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/Exposed.kt b/src/main/kotlin/io/dico/parcels2/storage/Exposed.kt new file mode 100644 index 0000000..ea02b8b --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/storage/Exposed.kt @@ -0,0 +1,85 @@ +package io.dico.parcels2.storage + +import com.zaxxer.hikari.HikariDataSource +import io.dico.parcels2.Parcel +import io.dico.parcels2.ParcelData +import io.dico.parcels2.ParcelOwner +import kotlinx.coroutines.experimental.channels.ProducerScope +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.sql.Table +import java.util.* +import javax.sql.DataSource + +object ParcelsTable : Table() { + val id = integer("id").autoIncrement().primaryKey() + val px = integer("px") + val pz = integer("pz") + val world_uuid = binary("world_uuid", 16).also { uniqueIndex("location", it, px, pz) } + val world = varchar("world", 32).nullable() + val owner_uuid = binary("owner_uuid", 16).nullable() + val owner = varchar("owner", 16).nullable() +} + +object ParcelsAddedTable : Table() { + val id = integer("id").references(ParcelsTable.id, ReferenceOption.CASCADE) + val player_uuid = binary("player_uuid", 16).also { uniqueIndex("pair", id, it) } + val allowed_flag = bool("allowed_flag") +} + +object PlayerAddedTable : Table() { + val owner_uuid = binary("owner_uuid", 16) + val player_uuid = binary("player_uuid", 16).also { uniqueIndex("pair", owner_uuid, it) } + val allowed_flag = bool("allowed_flag") +} + +class ExposedBacking(val dataSource: DataSource) : Backing { + override val name get() = "Exposed" + lateinit var database: Database + + override suspend fun init() { + database = Database.connect(dataSource) + } + + override suspend fun shutdown() { + if (dataSource is HikariDataSource) { + dataSource.close() + } + } + + override suspend fun ProducerScope>.produceParcelData(parcels: Sequence) { + TODO() + } + + override suspend fun readParcelData(plotFor: Parcel): ParcelData? { + TODO() + } + + override suspend fun getOwnedParcels(user: ParcelOwner): List { + TODO() + } + + override suspend fun setParcelOwner(plotFor: Parcel, owner: ParcelOwner?) { + TODO() + } + + override suspend fun setParcelPlayerState(plotFor: Parcel, player: UUID, state: Boolean?) { + TODO() + } + + override suspend fun setParcelAllowsInteractInventory(plot: Parcel, value: Boolean) { + TODO() + } + + override suspend fun setParcelAllowsInteractInputs(plot: Parcel, value: Boolean) { + TODO() + } + +} + + + + + + + diff --git a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt new file mode 100644 index 0000000..7e4fb7f --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt @@ -0,0 +1,51 @@ +package io.dico.parcels2.storage + +import com.zaxxer.hikari.HikariConfig +import com.zaxxer.hikari.HikariDataSource +import io.dico.parcels2.DataConnectionOptions +import javax.sql.DataSource + +fun getHikariDataSource(dialectName: String, + driver: String, + dco: DataConnectionOptions): DataSource = with(HikariConfig()) { + + val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}") + + poolName = "redstonerplots" + maximumPoolSize = dco.poolSize + dataSourceClassName = driver + 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 { + // 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") + } + + HikariDataSource(this) + +} diff --git a/src/main/kotlin/io/dico/parcels2/storage/Jackson.kt b/src/main/kotlin/io/dico/parcels2/storage/Jackson.kt new file mode 100644 index 0000000..18ad31f --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/storage/Jackson.kt @@ -0,0 +1,95 @@ +package io.dico.parcels2.storage + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.* +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.KotlinModule +import io.dico.parcels2.* +import org.bukkit.Bukkit +import org.bukkit.block.data.BlockData +import kotlin.reflect.KClass +import kotlin.reflect.full.isSuperclassOf + +val yamlObjectMapper = 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<*> { + if (GeneratorOptions::class.isSuperclassOf(beanDesc?.beanClass?.kotlin as KClass<*>)) { + return GeneratorOptionsSerializer(serializer as JsonSerializer) + } + + return super.modifySerializer(config, beanDesc, serializer) + } + }) + + addSerializer(BlockDataSerializer()) + addDeserializer(BlockData::class.java, BlockDataDeserializer()) + + /* + addSerializer(StorageOptionsSerializer()) + addDeserializer(StorageOptions::class.java, StorageOptionsDeserializer()) + */ + + addDeserializer(GeneratorOptions::class.java, GeneratorOptionsDeserializer()) + } + + registerModule(kotlinModule) +} + +private class BlockDataSerializer : StdSerializer(BlockData::class.java) { + + override fun serialize(value: BlockData, gen: JsonGenerator, provider: SerializerProvider) { + gen.writeString(value.asString) + } + +} + +private class BlockDataDeserializer : StdDeserializer(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) + } + } + +} + +/* +class StorageOptionsDeserializer : JsonDeserializer() { + + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StorageOptions { + val node = p!!.readValueAsTree() + val dialect = node.get("dialect").asText() + val optionsNode = node.get("options") + val factory = StorageFactory.getFactory(dialect) ?: throw IllegalStateException("Unknown storage dialect: $dialect") + val options = p.codec.treeToValue(optionsNode, factory.optionsClass.java) + return StorageOptions(dialect, factory, options) + } + +} + +class StorageOptionsSerializer : StdSerializer(StorageOptions::class.java) { + + override fun serialize(value: StorageOptions?, gen: JsonGenerator?, serializers: SerializerProvider?) { + with(gen!!) { + writeStartObject() + writeStringField("dialect", value!!.dialect) + writeFieldName("options") + writeObject(value.options) + writeEndObject() + } + } + +} +*/ diff --git a/src/main/kotlin/io/dico/parcels2/storage/SerializableTypes.kt b/src/main/kotlin/io/dico/parcels2/storage/SerializableTypes.kt new file mode 100644 index 0000000..4e467b1 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/storage/SerializableTypes.kt @@ -0,0 +1,28 @@ +package io.dico.parcels2.storage + +import io.dico.parcels2.Parcel +import io.dico.parcels2.ParcelWorld +import io.dico.parcels2.math.Vec2i +import org.bukkit.Bukkit +import org.bukkit.World +import java.util.* + +data class SerializableWorld(val name: String? = null, + val uid: UUID? = null) { + + init { + uid ?: name ?: throw IllegalArgumentException("uuid and/or name must be present") + } + + val world: World? by lazy { uid?.let { Bukkit.getWorld(it) } ?: name?.let { Bukkit.getWorld(it) } } + val parcelWorld: ParcelWorld? by lazy { TODO() } +} + +/** + * Used by storage backing options to encompass the location of a parcel + */ +data class SerializableParcel(val world: SerializableWorld, + val coord: Vec2i) { + + val parcel: Parcel? by lazy { TODO() } +} \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt index 7414b88..36f5400 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt @@ -1,69 +1,65 @@ package io.dico.parcels2.storage -import kotlinx.coroutines.experimental.CoroutineDispatcher -import kotlinx.coroutines.experimental.CoroutineScope -import kotlinx.coroutines.experimental.CoroutineStart -import kotlinx.coroutines.experimental.asCoroutineDispatcher -import kotlinx.coroutines.experimental.channels.ProducerJob +import io.dico.parcels2.Parcel +import io.dico.parcels2.ParcelData +import io.dico.parcels2.ParcelOwner +import kotlinx.coroutines.experimental.* +import kotlinx.coroutines.experimental.channels.ReceiveChannel import kotlinx.coroutines.experimental.channels.produce import java.util.* -import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor import java.util.concurrent.Executors -/* interface Storage { - val name: String - val syncDispatcher: CoroutineDispatcher - val asyncDispatcher: CoroutineDispatcher - fun init(): CompletableFuture + fun init(): Deferred - fun shutdown(): CompletableFuture + fun shutdown(): Deferred - fun readPlotData(plotFor: Plot): CompletableFuture + fun readParcelData(parcelFor: Parcel): Deferred - fun readPlotData(plotsFor: Sequence, channelCapacity: Int): ProducerJob> + fun readParcelData(parcelsFor: Sequence, channelCapacity: Int): ReceiveChannel> - fun getOwnedPlots(user: PlotOwner): CompletableFuture> + fun getOwnedParcels(user: ParcelOwner): Deferred> - fun setPlotOwner(plotFor: Plot, owner: PlotOwner?): CompletableFuture + fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?): Deferred - fun setPlotPlayerState(plotFor: Plot, player: UUID, state: Boolean?): CompletableFuture + fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?): Deferred - fun setPlotAllowsInteractInventory(plot: Plot, value: Boolean): CompletableFuture + fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean): Deferred - fun setPlotAllowsInteractInputs(plot: Plot, value: Boolean): CompletableFuture + fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean): Deferred } -class StorageWithBacking internal constructor(val backing: Backing) : Storage { +class StorageWithCoroutineBacking internal constructor(val backing: Backing) : Storage { override val name get() = backing.name override val syncDispatcher = Executor { it.run() }.asCoroutineDispatcher() - override val asyncDispatcher = Executors.newFixedThreadPool(4) { Thread(it, "AbstractStorageThread") }.asCoroutineDispatcher() + val poolSize: Int get() = 4 + override val asyncDispatcher = Executors.newFixedThreadPool(poolSize) { Thread(it, "Parcels2_StorageThread") }.asCoroutineDispatcher() - private fun future(block: suspend CoroutineScope.() -> T) = kotlinx.coroutines.experimental.future.future(asyncDispatcher, CoroutineStart.ATOMIC, block) + private fun future(block: suspend CoroutineScope.() -> T) = async(context = asyncDispatcher, start = CoroutineStart.ATOMIC, block = block) - override fun init(): CompletableFuture = future { backing.init() } + override fun init() = future { backing.init() } - override fun shutdown(): CompletableFuture = future { backing.shutdown() } + override fun shutdown() = future { backing.shutdown() } - override fun readPlotData(plotFor: Plot) = future { backing.readPlotData(plotFor) } + override fun readParcelData(parcelFor: Parcel) = future { backing.readParcelData(parcelFor) } - override fun readPlotData(plotsFor: Sequence, channelCapacity: Int) = - produce>(asyncDispatcher, capacity = channelCapacity) { backing.producePlotData(this, plotsFor) } + override fun readParcelData(parcelsFor: Sequence, channelCapacity: Int) = produce(asyncDispatcher, capacity = channelCapacity) { + with(backing) { produceParcelData(parcelsFor) } + } - override fun getOwnedPlots(user: PlotOwner) = future { backing.getOwnedPlots(user) } + override fun getOwnedParcels(user: ParcelOwner) = future { backing.getOwnedParcels(user) } - override fun setPlotOwner(plotFor: Plot, owner: PlotOwner?) = future { backing.setPlotOwner(plotFor, owner) } + override fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?) = future { backing.setParcelOwner(parcelFor, owner) } - override fun setPlotPlayerState(plotFor: Plot, player: UUID, state: Boolean?) = future { backing.setPlotPlayerState(plotFor, player, state) } + override fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?) = future { backing.setParcelPlayerState(parcelFor, player, state) } - override fun setPlotAllowsInteractInventory(plot: Plot, value: Boolean) = future { backing.setPlotAllowsInteractInventory(plot, value) } + override fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean) = future { backing.setParcelAllowsInteractInventory(parcel, value) } - override fun setPlotAllowsInteractInputs(plot: Plot, value: Boolean) = future { backing.setPlotAllowsInteractInputs(plot, value) } + override fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean) = future { backing.setParcelAllowsInteractInputs(parcel, value) } } - */ \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt b/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt new file mode 100644 index 0000000..7429351 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt @@ -0,0 +1,46 @@ +package io.dico.parcels2.storage + +import io.dico.parcels2.DataConnectionOptions +import kotlin.reflect.KClass + +interface StorageFactory { + companion object StorageFactories { + private val map: MutableMap = HashMap() + + fun registerFactory(method: String, generator: StorageFactory): Boolean = map.putIfAbsent(method.toLowerCase(), generator) == null + + fun getFactory(method: String): StorageFactory? = map[method.toLowerCase()] + + init { + // have to write the code like this in kotlin. + // This code is absolutely disgusting + ConnectionStorageFactory().register(this) + } + } + + val optionsClass: KClass + + fun newStorageInstance(method: String, options: Any): Storage + +} + +class ConnectionStorageFactory : StorageFactory { + override val optionsClass = DataConnectionOptions::class + + private val types: Map = with(HashMap()) { + put("mysql", "com.mysql.jdbc.jdbc2.optional.MysqlDataSource") + this + } + + fun register(companion: StorageFactory.StorageFactories) { + types.keys.forEach { + companion.registerFactory(it, this) + } + } + + override fun newStorageInstance(dialect: String, options: Any): Storage { + val driverClass = types[dialect.toLowerCase()] ?: throw IllegalArgumentException("Storage dialect $dialect is not supported") + return StorageWithCoroutineBacking(ExposedBacking(getHikariDataSource(dialect, driverClass, options as DataConnectionOptions))) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/backing/Exposed.kt b/src/main/kotlin/io/dico/parcels2/storage/backing/Exposed.kt deleted file mode 100644 index 404e48c..0000000 --- a/src/main/kotlin/io/dico/parcels2/storage/backing/Exposed.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.dico.parcels2.storage.backing - -import org.jetbrains.exposed.sql.ReferenceOption -import org.jetbrains.exposed.sql.Table - -object ParcelsTable : Table() { - val id = integer("id").autoIncrement().primaryKey() - val px = integer("px") - val pz = integer("pz") - val world_uuid = binary("world_uuid", 16).also { uniqueIndex("location", it, px, pz) } - val world = varchar("world", 32).nullable() - val owner_uuid = binary("owner_uuid", 16).nullable() - val owner = varchar("owner", 16).nullable() -} - -object ParcelsAddedTable : Table() { - val id = integer("id").references(ParcelsTable.id, ReferenceOption.CASCADE) - val player_uuid = binary("player_uuid", 16).also { uniqueIndex("pair", id, it) } - val allowed_flag = bool("allowed_flag") -} - -object PlayerAddedTable : Table() { - val owner_uuid = binary("owner_uuid", 16) - val player_uuid = binary("player_uuid", 16).also { uniqueIndex("pair", owner_uuid, it) } - val allowed_flag = bool("allowed_flag") -} - -class AbstractParcelsDatabase { - - - - - -} - - - - - - - diff --git a/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt index 47bbf8c..3ef3c89 100644 --- a/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt +++ b/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt @@ -1,13 +1,17 @@ package io.dico.parcels2.util +import io.dico.dicore.Formatting +import io.dico.parcels2.ParcelsPlugin +import io.dico.parcels2.logger import org.bukkit.entity.Player +import org.bukkit.plugin.java.JavaPlugin -val Player.hasBanBypass get() = hasPermission("plots.admin.bypass.ban") -val Player.hasBuildAnywhere get() = hasPermission("plots.admin.bypass.build") -val Player.hasGamemodeBypass get() = hasPermission("plots.admin.bypass.gamemode") -val Player.hasAdminManage get() = hasPermission("plots.admin.manage") -val Player.hasPlotHomeOthers get() = hasPermission("plots.command.home.others") -val Player.hasRandomSpecific get() = hasPermission("plots.command.random.specific") +inline val Player.hasBanBypass get() = hasPermission("plots.admin.bypass.ban") +inline val Player.hasBuildAnywhere get() = hasPermission("plots.admin.bypass.build") +inline val Player.hasGamemodeBypass get() = hasPermission("plots.admin.bypass.gamemode") +inline val Player.hasAdminManage get() = hasPermission("plots.admin.manage") +inline val Player.hasPlotHomeOthers get() = hasPermission("plots.command.home.others") +inline val Player.hasRandomSpecific get() = hasPermission("plots.command.random.specific") val Player.plotLimit: Int get() { for (info in effectivePermissions) { @@ -18,15 +22,15 @@ val Player.plotLimit: Int return Int.MAX_VALUE } return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also { - Main.instance.logger.severe("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).") + logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).") } } } return DEFAULT_LIMIT } -val DEFAULT_LIMIT = 1 -internal val prefix = Formatting.translateChars('&', "&4[&c${Main.instance.name}&4] &a") +private const val DEFAULT_LIMIT = 1 +private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a") fun Player.sendPlotMessage(except: Boolean = false, nopermit: Boolean = false, message: String) { if (except) { diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..351aba8 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,11 @@ + + + + %d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%32.-32logger{32}) - %msg%n + + + + + + + \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..3d8cd01 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,5 @@ +name: Parcels +author: Dico +main: io.dico.parcels2.ParcelsPlugin +version: 0.1 +api-version: 1.13 \ No newline at end of file -- cgit v1.2.3