summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico200 <dico.karssiens@gmail.com>2018-07-26 17:21:26 +0100
committerDico200 <dico.karssiens@gmail.com>2018-07-26 17:21:26 +0100
commitbf1da033703f1343cfc54c61f8ace1fea7dbac25 (patch)
tree4ce40ade58bece02a6ef84f41b97d8940f22e471
parentc0e4ab728edcfc7415039ec5806c984521a65338 (diff)
Improve async command approach - use coroutines correctly
-rw-r--r--build.gradle.kts95
-rw-r--r--dicore3/command/build.gradle.kts2
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ICommandReceiver.java17
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ICommandSuspendReceiver.java7
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java92
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java27
-rw-r--r--dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt72
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelWorld.kt7
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt70
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt82
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt6
-rw-r--r--src/main/resources/logback.xml2
12 files changed, 346 insertions, 133 deletions
diff --git a/build.gradle.kts b/build.gradle.kts
index ba041c6..f0cd548 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,23 +1,28 @@
@file:Suppress("UNUSED_VARIABLE")
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-import org.jetbrains.kotlin.gradle.dsl.Coroutines
+import org.jetbrains.kotlin.gradle.dsl.Coroutines.ENABLE
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin
import java.io.PrintWriter
-plugins {
- kotlin("jvm") version "1.2.51"
- id("com.github.johnrengelman.plugin-shadow") version "2.0.3"
-}
+val stdout = PrintWriter(File("$rootDir/gradle-output.txt"))
-kotlin.experimental.coroutines = Coroutines.ENABLE
+buildscript {
+ dependencies {
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.51")
+ }
+}
group = "io.dico"
version = "0.1"
+inline fun <reified T : Plugin<out Project>> Project.apply() =
+ (this as PluginAware).apply<T>()
+
allprojects {
- apply {
- plugin(JavaPlugin::class.java)
- }
+ apply<JavaPlugin>()
+
repositories {
mavenCentral()
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots")
@@ -34,12 +39,28 @@ allprojects {
}
project(":dicore3:dicore3-command") {
+ apply<KotlinPlatformJvmPlugin>()
+
+ kotlin.experimental.coroutines = ENABLE
+
dependencies {
+ // why the fuck does it need reflect explicitly?
+ compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4")
+ compile(kotlin("reflect", version = "1.2.50"))
+ compile(kotlin("stdlib-jdk8", version = "1.2.51"))
compile(project(":dicore3:dicore3-core"))
compile("com.thoughtworks.paranamer:paranamer:2.8")
}
}
+
+plugins {
+ kotlin("jvm") version "1.2.51"
+ id("com.github.johnrengelman.plugin-shadow") version "2.0.3"
+}
+
+kotlin.experimental.coroutines = ENABLE
+
repositories {
maven("https://dl.bintray.com/kotlin/exposed")
}
@@ -65,14 +86,22 @@ dependencies {
}
tasks {
+ val compileKotlin by getting(KotlinCompile::class) {
+ //this.setupPlugins()
+
+ //serializedCompilerArguments.add("-java-parameters")
+ }
+
fun Jar.packageDependencies(vararg names: String) {
- from(*project.configurations.compile.resolvedConfiguration.firstLevelModuleDependencies
- .filter { it.moduleName in names }
- .flatMap { it.allModuleArtifacts }
- .map { it.file }
- .map(::zipTree)
- .toTypedArray()
- )
+ //afterEvaluate {
+ from(*project.configurations.compile.resolvedConfiguration.firstLevelModuleDependencies
+ .filter { it.moduleName in names }
+ .flatMap { it.allModuleArtifacts }
+ .map { it.file }
+ .map(::zipTree)
+ .toTypedArray()
+ )
+ //}
}
fun Jar.packageDependency(name: String, configure: ModuleDependency.() -> Unit) {
@@ -92,18 +121,18 @@ tasks {
}
fun Jar.packageArtifacts(vararg names: String) {
- val stream = PrintWriter(File("$rootDir/gradle-output.txt"))
- from(*project.configurations.compile.resolvedConfiguration.resolvedArtifacts
- .filter {
- val id = it.moduleVersion.id
- (id.name in names).also {
- if (!it) stream.println("Not including artifact: ${id.group}:${id.name}")
+ //afterEvaluate {
+ from(*project.configurations.compile.resolvedConfiguration.resolvedArtifacts
+ .filter {
+ val id = it.moduleVersion.id
+ (id.name in names).also {
+ if (!it) stdout.println("Not including artifact: ${id.group}:${id.name}")
+ }
}
- }
- .map { it.file }
- .map(::zipTree)
- .toTypedArray())
- stream.flush()
+ .map { it.file }
+ .map(::zipTree)
+ .toTypedArray())
+ //}
}
val serverDir = "$rootDir/debug"
@@ -151,7 +180,13 @@ tasks {
"trove4j",
"joda-time",
- "annotations"
+
+ "annotations",
+ "kotlin-stdlib-common",
+ "kotlin-stdlib",
+ "kotlin-stdlib-jdk7",
+ "kotlin-stdlib-jdk8",
+ "kotlin-reflect"
)
relocate("org.yaml.snakeyaml", "io.dico.parcels2.util.snakeyaml")
@@ -164,4 +199,6 @@ tasks {
allprojects {
tasks.filter { it is Jar }.forEach { it.group = "artifacts" }
-} \ No newline at end of file
+}
+
+stdout.flush() \ No newline at end of file
diff --git a/dicore3/command/build.gradle.kts b/dicore3/command/build.gradle.kts
index 6077c91..b4e64bd 100644
--- a/dicore3/command/build.gradle.kts
+++ b/dicore3/command/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.dsl.Coroutines
+import org.jetbrains.kotlin.js.translate.context.Namer.kotlin
group = "io.dico.dicore3"
//name = "dicore3-command"
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandReceiver.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandReceiver.java
new file mode 100644
index 0000000..88b0d50
--- /dev/null
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandReceiver.java
@@ -0,0 +1,17 @@
+package io.dico.dicore.command;
+
+import org.bukkit.plugin.Plugin;
+
+import java.lang.reflect.Method;
+
+public interface ICommandReceiver {
+
+ interface Factory {
+
+ ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName);
+
+ Plugin getPlugin();
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandSuspendReceiver.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandSuspendReceiver.java
new file mode 100644
index 0000000..3f952d3
--- /dev/null
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandSuspendReceiver.java
@@ -0,0 +1,7 @@
+package io.dico.dicore.command;
+
+public interface ICommandSuspendReceiver extends ICommandReceiver {
+
+ int getTimeout();
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
index f033d9e..591d824 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
@@ -10,7 +10,8 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-final class ReflectiveCommand extends Command {
+public final class ReflectiveCommand extends Command {
+ private final Cmd cmdAnnotation;
private final Method method;
private final Object instance;
private String[] parameterOrder;
@@ -20,6 +21,7 @@ final class ReflectiveCommand extends Command {
if (!method.isAnnotationPresent(Cmd.class)) {
throw new CommandParseException("No @Cmd present for the method " + method.toGenericString());
}
+ cmdAnnotation = method.getAnnotation(Cmd.class);
java.lang.reflect.Parameter[] parameters = method.getParameters();
@@ -46,6 +48,14 @@ final class ReflectiveCommand extends Command {
this.flags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters);
}
+ public Method getMethod() {
+ return method;
+ }
+
+ public Object getInstance() {
+ return instance;
+ }
+
void setParameterOrder(String[] parameterOrder) {
this.parameterOrder = parameterOrder;
}
@@ -54,7 +64,7 @@ final class ReflectiveCommand extends Command {
ChildCommandAddress result = new ChildCommandAddress();
result.setCommand(this);
- Cmd cmd = method.getAnnotation(Cmd.class);
+ Cmd cmd = cmdAnnotation;
result.getNames().add(cmd.value());
for (String alias : cmd.aliases()) {
result.getNames().add(alias);
@@ -71,54 +81,86 @@ final class ReflectiveCommand extends Command {
@Override
public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
- //System.out.println("In ReflectiveCommand.execute()");
-
String[] parameterOrder = this.parameterOrder;
int start = Integer.bitCount(flags);
- //System.out.println("start = " + start);
Object[] args = new Object[parameterOrder.length + start];
int i = 0;
if ((flags & 1) != 0) {
- args[i++] = sender;
+ try {
+ args[i++] = ((ICommandReceiver.Factory) instance).getReceiver(context, method, cmdAnnotation.value());
+ } catch (Exception ex) {
+ handleException(ex);
+ return null; // unreachable
+ }
}
if ((flags & 2) != 0) {
+ args[i++] = sender;
+ }
+ if ((flags & 4) != 0) {
args[i++] = context;
}
- //System.out.println("i = " + i);
- //System.out.println("parameterOrder = " + Arrays.toString(parameterOrder));
for (int n = args.length; i < n; i++) {
- //System.out.println("n = " + n);
args[i] = context.get(parameterOrder[i - start]);
- //System.out.println("context.get(parameterOrder[i - start]) = " + context.get(parameterOrder[i - start]));
- //System.out.println("context.get(parameterOrder[i - start]).getClass() = " + context.get(parameterOrder[i - start]).getClass());
}
- //System.out.println("args = " + Arrays.toString(args));
+ if (!isSuspendFunction()) {
+ return callSynchronously(args);
+ }
+
+ return callAsCoroutine(context, args);
+ }
- Object result;
+ private boolean isSuspendFunction() {
try {
- result = method.invoke(instance, args);
- } catch (InvocationTargetException ex) {
+ return KotlinReflectiveRegistrationKt.isSuspendFunction(method);
+ } catch (Throwable ex) {
+ return false;
+ }
+ }
+
+ public String callSynchronously(Object[] args) throws CommandException {
+ try {
+ return getResult(method.invoke(instance, args), null);
+ } catch (Exception ex) {
+ return getResult(null, ex);
+ }
+ }
+
+ public static String getResult(Object returned, Exception ex) throws CommandException {
+ if (ex != null) {
+ handleException(ex);
+ return null; // unreachable
+ }
+
+ if (returned instanceof String) {
+ return (String) returned;
+ }
+ if (returned instanceof CommandResult) {
+ return ((CommandResult) returned).getMessage();
+ }
+ return null;
+ }
+
+ public static void handleException(Exception ex) throws CommandException {
+ if (ex instanceof InvocationTargetException) {
if (ex.getCause() instanceof CommandException) {
throw (CommandException) ex.getCause();
}
ex.printStackTrace();
throw new CommandException("An internal error occurred while executing this command.", ex);
- } catch (Exception ex) {
- ex.printStackTrace();
- throw new CommandException("An internal error occurred while executing this command.", ex);
- }
-
- if (result instanceof String) {
- return (String) result;
}
- if (result instanceof CommandResult) {
- return ((CommandResult) result).getMessage();
+ if (ex instanceof CommandException) {
+ throw (CommandException) ex;
}
- return null;
+ ex.printStackTrace();
+ throw new CommandException("An internal error occurred while executing this command.", ex);
+ }
+
+ private String callAsCoroutine(ExecutionContext context, Object[] args) {
+ return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandReceiver.Factory) instance, context, args);
}
}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
index 84bb10b..0003c1f 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
@@ -197,10 +197,20 @@ public class ReflectiveRegistration {
static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException {
ParameterList list = command.getParameterList();
+ boolean hasReceiverParameter = false;
boolean hasSenderParameter = false;
int start = 0;
Class<?> firstParameterType = null;
- if (parameters.length > start && CommandSender.class.isAssignableFrom(firstParameterType = parameters[0].getType())) {
+ Class<?> senderParameterType = null;
+
+ if (parameters.length > start
+ && command.getInstance() instanceof ICommandReceiver.Factory
+ && ICommandReceiver.class.isAssignableFrom(firstParameterType = parameters[start].getType())) {
+ hasReceiverParameter = true;
+ start++;
+ }
+
+ if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) {
hasSenderParameter = true;
start++;
}
@@ -212,12 +222,17 @@ public class ReflectiveRegistration {
}
String[] parameterNames = lookupParameterNames(method, parameters, start);
- command.setParameterOrder(parameterNames);
-
for (int i = start, n = parameters.length; i < n; i++) {
+ if (parameters[i].getType().getName().equals("kotlin.coroutines.experimental.Continuation")) {
+ List<String> temp = new ArrayList<>(Arrays.asList(parameterNames));
+ temp.remove(i - start);
+ parameterNames = temp.toArray(new String[0]);
+ continue;
+ }
Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]);
list.addParameter(parameter);
}
+ command.setParameterOrder(parameterNames);
RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class);
if (cmdPermissions != null) {
@@ -257,9 +272,9 @@ public class ReflectiveRegistration {
command.setDescription();
}
- if (hasSenderParameter && Player.class.isAssignableFrom(firstParameterType)) {
+ if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) {
command.addContextFilter(IContextFilter.PLAYER_ONLY);
- } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(firstParameterType)) {
+ } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) {
command.addContextFilter(IContextFilter.CONSOLE_ONLY);
} else if (method.isAnnotationPresent(RequirePlayer.class)) {
command.addContextFilter(IContextFilter.PLAYER_ONLY);
@@ -269,7 +284,7 @@ public class ReflectiveRegistration {
list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs());
list.setFinalParameterMayBeFlag(true);
- return (hasSenderParameter ? 1 : 0) | (hasContextParameter ? 2 : 0);
+ return (hasSenderParameter ? 2 : 0) | (hasContextParameter ? 4 : 0) | (hasReceiverParameter ? 1 : 0);
}
public static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command) throws CommandParseException {
diff --git a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt
new file mode 100644
index 0000000..b31d091
--- /dev/null
+++ b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt
@@ -0,0 +1,72 @@
+package io.dico.dicore.command.registration.reflect
+
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.EMessageType
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.ICommandReceiver
+import kotlinx.coroutines.experimental.CoroutineStart.UNDISPATCHED
+import kotlinx.coroutines.experimental.Deferred
+import kotlinx.coroutines.experimental.asCoroutineDispatcher
+import kotlinx.coroutines.experimental.async
+import java.lang.reflect.Method
+import java.util.*
+import java.util.concurrent.CancellationException
+import java.util.concurrent.Executor
+import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
+import kotlin.reflect.jvm.kotlinFunction
+
+fun isSuspendFunction(method: Method): Boolean {
+ val func = method.kotlinFunction ?: return false
+ return func.isSuspend
+}
+
+fun callAsCoroutine(command: ReflectiveCommand,
+ factory: ICommandReceiver.Factory,
+ context: ExecutionContext,
+ args: Array<Any?>): String? {
+ val dispatcher = Executor { task -> factory.plugin.server.scheduler.runTask(factory.plugin, task) }.asCoroutineDispatcher()
+
+ // UNDISPATCHED causes the handler to run until the first suspension point on the current thread
+ val job = async(context = dispatcher, start = UNDISPATCHED) { command.method.invokeSuspend(command.instance, args) }
+
+ if (job.isCompleted) {
+ return job.getResult()
+ }
+
+ job.invokeOnCompletion {
+ val cc = context.address.chatController
+ try {
+ val result = job.getResult()
+ cc.sendMessage(context.sender, EMessageType.RESULT, result)
+ } catch (ex: Throwable) {
+ cc.handleException(context.sender, context, ex)
+ }
+ }
+
+ return null
+}
+
+private suspend fun Method.invokeSuspend(instance: Any?, args: Array<Any?>): Any? {
+ return suspendCoroutineOrReturn { cont ->
+ println()
+ println("Calling command method suspendedly")
+ println(toGenericString())
+ println(Arrays.toString(arrayOf(instance, *args, cont)))
+ println()
+ invoke(instance, *args, cont)
+ }
+}
+
+@Throws(CommandException::class)
+private fun Deferred<Any?>.getResult(): String? {
+ getCompletionExceptionOrNull()?.let { ex ->
+ if (ex is CancellationException) {
+ System.err.println("An asynchronously dispatched command was cancelled unexpectedly")
+ ex.printStackTrace()
+ throw CommandException("The command was cancelled unexpectedly (see console)")
+ }
+ if (ex is Exception) return ReflectiveCommand.getResult(null, ex)
+ throw ex
+ }
+ return ReflectiveCommand.getResult(getCompleted(), null)
+} \ 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 c346a04..2868556 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
@@ -16,6 +16,8 @@ import org.bukkit.entity.Entity
import org.bukkit.entity.Player
import java.util.*
import kotlin.coroutines.experimental.buildSequence
+import kotlin.reflect.jvm.javaMethod
+import kotlin.reflect.jvm.kotlinFunction
class Worlds(private val plugin: ParcelsPlugin) {
val worlds: Map<String, ParcelWorld> get() = _worlds
@@ -39,6 +41,11 @@ class Worlds(private val plugin: ParcelsPlugin) {
}
}
+ init {
+ val function = ::loadWorlds
+ function.javaMethod!!.kotlinFunction
+ }
+
operator fun SerializableParcel.invoke(): Parcel? {
return world()?.parcelByID(pos)
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt b/src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt
index b4eea83..a98ddc1 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt
@@ -1,46 +1,57 @@
-@file:Suppress("NOTHING_TO_INLINE")
-
package io.dico.parcels2.command
-import io.dico.dicore.command.CommandException
-import io.dico.dicore.command.Validate
+import io.dico.dicore.command.*
import io.dico.parcels2.Parcel
import io.dico.parcels2.ParcelWorld
import io.dico.parcels2.Worlds
+import io.dico.parcels2.logger
import io.dico.parcels2.util.hasAdminManage
import io.dico.parcels2.util.uuid
import org.bukkit.entity.Player
+import java.lang.reflect.Method
+import kotlin.reflect.full.extensionReceiverParameter
+import kotlin.reflect.full.findAnnotation
+import kotlin.reflect.jvm.javaType
+import kotlin.reflect.jvm.jvmErasure
+import kotlin.reflect.jvm.kotlinFunction
-/*
- * Scope types for extension lambdas
- */
-sealed class BaseScope
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class ParcelRequire(val admin: Boolean = false, val owner: Boolean = false)
-class WorldOnlyScope(val world: ParcelWorld) : BaseScope()
+sealed class BaseScope(private var _timeout: Int = 0) : ICommandSuspendReceiver {
+ override fun getTimeout() = _timeout
+ fun setTimeout(timeout: Int) {
+ _timeout = timeout
+ }
+}
+
+class SuspendOnlyScope : BaseScope()
class ParcelScope(val world: ParcelWorld, val parcel: Parcel) : BaseScope()
+class WorldOnlyScope(val world: ParcelWorld) : BaseScope()
-/*
- * Interface to implicitly access worlds object by creating extension functions for it
- */
-interface HasWorlds {
- val worlds: Worlds
-}
+fun getParcelCommandReceiver(worlds: Worlds, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver {
+ val function = method.kotlinFunction!!
+ val receiverType = function.extensionReceiverParameter!!.type
+ logger.info("Receiver type: ${receiverType.javaType.typeName}")
-/*
- * Functions to be used by command implementations
- */
-inline fun <T> HasWorlds.requireInWorld(player: Player,
- admin: Boolean = false,
- block: WorldOnlyScope.() -> T): T {
- return WorldOnlyScope(worlds.getWorldRequired(player, admin = admin)).block()
-}
+ val require = function.findAnnotation<ParcelRequire>()
+ val admin = require?.admin == true
+ val owner = require?.owner == true
+
+ val player = context.sender as Player
-inline fun <T> HasWorlds.requireInParcel(player: Player,
- admin: Boolean = false,
- own: Boolean = false,
- block: ParcelScope.() -> T): T {
- val parcel = worlds.getParcelRequired(player, admin = admin, own = own)
- return ParcelScope(parcel.world, parcel).block()
+ return when (receiverType.jvmErasure) {
+ ParcelScope::class -> worlds.getParcelRequired(player, admin = admin, own = owner).let {
+ ParcelScope(it.world, it)
+ }
+ WorldOnlyScope::class -> worlds.getWorldRequired(player, admin = admin).let {
+ WorldOnlyScope(it)
+ }
+ SuspendOnlyScope::class -> SuspendOnlyScope()
+ else -> throw InternalError("Invalid command receiver type")
+
+ }
}
/*
@@ -60,4 +71,3 @@ fun Worlds.getParcelRequired(player: Player, admin: Boolean = false, own: Boolea
return parcel
}
-
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt
index b10c23e..0917cf9 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt
@@ -2,20 +2,29 @@ package io.dico.parcels2.command
import io.dico.dicore.command.CommandException
import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.ICommandReceiver
import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.Desc
import io.dico.dicore.command.annotation.RequireParameters
import io.dico.parcels2.ParcelOwner
import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.logger
import io.dico.parcels2.storage.getParcelBySerializedValue
import io.dico.parcels2.util.hasParcelHomeOthers
import io.dico.parcels2.util.parcelLimit
import io.dico.parcels2.util.uuid
import org.bukkit.entity.Player
+import org.bukkit.plugin.Plugin
+import java.lang.reflect.Method
-@Suppress("unused")
-class ParcelCommands(override val plugin: ParcelsPlugin) : HasWorlds, HasPlugin {
- override val worlds = plugin.worlds
+//@Suppress("unused")
+class ParcelCommands(val plugin: ParcelsPlugin) : ICommandReceiver.Factory {
+ private inline val worlds get() = plugin.worlds
+
+ override fun getPlugin(): Plugin = plugin
+ override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver {
+ return getParcelCommandReceiver(plugin.worlds, context, target, cmdName)
+ }
private fun error(message: String): Nothing {
throw CommandException(message)
@@ -25,31 +34,29 @@ class ParcelCommands(override val plugin: ParcelsPlugin) : HasWorlds, HasPlugin
@Desc("Finds the unclaimed parcel nearest to origin,",
"and gives it to you",
shortVersion = "sets you up with a fresh, unclaimed parcel")
- fun cmdAuto(player: Player, context: ExecutionContext) = requireInWorld(player) {
- delegateCommandAsync(context) {
- val numOwnedParcels = plugin.storage.getNumParcels(ParcelOwner(uuid = player.uuid)).await()
-
- awaitSynchronousTask {
- val limit = player.parcelLimit
-
- if (numOwnedParcels >= limit) {
- error("You have enough plots for now")
- }
-
- val parcel = world.nextEmptyParcel()
- ?: error("This world is full, please ask an admin to upsize it")
- parcel.owner = ParcelOwner(uuid = player.uuid)
- player.teleport(parcel.homeLocation)
- "Enjoy your new parcel!"
- }
+ suspend fun WorldOnlyScope.cmdAuto(player: Player): Any? {
+ logger.info("cmdAuto thread before await: ${Thread.currentThread().name}")
+ val numOwnedParcels = plugin.storage.getNumParcels(ParcelOwner(uuid = player.uuid)).await()
+ logger.info("cmdAuto thread before await: ${Thread.currentThread().name}")
+
+ val limit = player.parcelLimit
+
+ if (numOwnedParcels >= limit) {
+ error("You have enough plots for now")
}
+
+ val parcel = world.nextEmptyParcel()
+ ?: error("This world is full, please ask an admin to upsize it")
+ parcel.owner = ParcelOwner(uuid = player.uuid)
+ player.teleport(parcel.homeLocation)
+ return "Enjoy your new parcel!"
}
@Cmd("info", aliases = ["i"])
@Desc("Displays general information",
"about the parcel you're on",
shortVersion = "displays information about this parcel")
- fun cmdInfo(player: Player) = requireInParcel(player) { parcel.infoString }
+ fun ParcelScope.cmdInfo(player: Player) = parcel.infoString
@Cmd("home", aliases = ["h"])
@Desc("Teleports you to your parcels,",
@@ -58,36 +65,33 @@ class ParcelCommands(override val plugin: ParcelsPlugin) : HasWorlds, HasPlugin
"more than one parcel",
shortVersion = "teleports you to parcels")
@RequireParameters(0)
- fun cmdHome(player: Player, context: ExecutionContext, target: NamedParcelTarget) {
+ suspend fun SuspendOnlyScope.cmdHome(player: Player, context: ExecutionContext,
+ @NamedParcelDefault(NamedParcelDefaultValue.FIRST_OWNED) target: NamedParcelTarget): Any? {
if (player !== target.player && !player.hasParcelHomeOthers) {
error("You do not have permission to teleport to other people's parcels")
}
- return delegateCommandAsync(context) {
- val ownedParcelsResult = plugin.storage.getOwnedParcels(ParcelOwner(uuid = target.player.uuid)).await()
- awaitSynchronousTask {
- val uuid = target.player.uuid
- val ownedParcels = ownedParcelsResult
- .map { worlds.getParcelBySerializedValue(it) }
- .filter { it != null && it.world == target.world && it.owner?.uuid == uuid }
+ logger.info("cmdHome thread before await: ${Thread.currentThread().name}")
+ val ownedParcelsResult = plugin.storage.getOwnedParcels(ParcelOwner(uuid = target.player.uuid)).await()
+ logger.info("cmdHome thread after await: ${Thread.currentThread().name}")
- val targetMatch = ownedParcels.getOrNull(target.index)
- ?: error("The specified parcel could not be matched")
+ val uuid = target.player.uuid
+ val ownedParcels = ownedParcelsResult
+ .map { worlds.getParcelBySerializedValue(it) }
+ .filter { it != null && it.world == target.world && it.owner?.uuid == uuid }
- player.teleport(targetMatch.homeLocation)
- ""
- }
- }
+ val targetMatch = ownedParcels.getOrNull(target.index)
+ ?: error("The specified parcel could not be matched")
+
+ player.teleport(targetMatch.homeLocation)
+ return ""
}
@Cmd("claim")
@Desc("If this parcel is unowned, makes you the owner",
shortVersion = "claims this parcel")
- fun cmdClaim(player: Player) {
+ fun ParcelScope.cmdClaim(player: Player) {
}
-
-
-
} \ 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
index b0140f1..38c5f6d 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt
@@ -7,9 +7,9 @@ interface StorageFactory {
companion object StorageFactories {
private val map: MutableMap<String, StorageFactory> = HashMap()
- fun registerFactory(method: String, generator: StorageFactory): Boolean = map.putIfAbsent(method.toLowerCase(), generator) == null
+ fun registerFactory(dialect: String, generator: StorageFactory): Boolean = map.putIfAbsent(dialect.toLowerCase(), generator) == null
- fun getFactory(method: String): StorageFactory? = map[method.toLowerCase()]
+ fun getFactory(dialect: String): StorageFactory? = map[dialect.toLowerCase()]
init {
// have to write the code like this in kotlin.
@@ -20,7 +20,7 @@ interface StorageFactory {
val optionsClass: KClass<out Any>
- fun newStorageInstance(method: String, options: Any): Storage
+ fun newStorageInstance(dialect: String, options: Any): Storage
}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 6617645..58426c6 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -1,4 +1,4 @@
-<configuration>
+<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%32.-32logger{32}) - %msg</pattern>