Improve async command approach - use coroutines correctly
This commit is contained in:
@@ -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" }
|
||||
}
|
||||
}
|
||||
|
||||
stdout.flush()
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.dico.dicore.command;
|
||||
|
||||
public interface ICommandSuspendReceiver extends ICommandReceiver {
|
||||
|
||||
int getTimeout();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Object result;
|
||||
return callAsCoroutine(context, args);
|
||||
}
|
||||
|
||||
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 (ex instanceof CommandException) {
|
||||
throw (CommandException) 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();
|
||||
}
|
||||
return null;
|
||||
private String callAsCoroutine(ExecutionContext context, Object[] args) {
|
||||
return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandReceiver.Factory) instance, context, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
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()
|
||||
val player = context.sender as Player
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
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}")
|
||||
|
||||
awaitSynchronousTask {
|
||||
val limit = player.parcelLimit
|
||||
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!"
|
||||
}
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user