From 038e698a1421e95d1dc96117cd9a2ae0cfdddf6a Mon Sep 17 00:00:00 2001 From: Dico Date: Sun, 23 Sep 2018 06:34:03 +0100 Subject: Work down some todo items, update to kotlin 1.3-rc --- build.gradle.kts | 19 ++-- .../io/dico/dicore/command/ExecutionContext.java | 9 ++ .../io/dico/dicore/command/ICommandReceiver.java | 5 + .../reflect/KotlinReflectiveRegistration.kt | 37 ++++--- gradle.properties | 1 + settings.gradle.kts | 6 ++ src/main/kotlin/io/dico/parcels2/Interactable.kt | 5 +- src/main/kotlin/io/dico/parcels2/Parcel.kt | 4 +- .../kotlin/io/dico/parcels2/ParcelGenerator.kt | 2 + src/main/kotlin/io/dico/parcels2/ParcelId.kt | 13 ++- src/main/kotlin/io/dico/parcels2/ParcelWorld.kt | 5 +- src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt | 22 +++-- src/main/kotlin/io/dico/parcels2/PlayerProfile.kt | 23 ++--- .../dico/parcels2/blockvisitor/RegionTraverser.kt | 14 ++- .../dico/parcels2/blockvisitor/WorktimeLimiter.kt | 84 +++++++++++----- .../parcels2/command/AbstractParcelCommands.kt | 27 ++++-- .../parcels2/command/CommandsAddedStatusLocal.kt | 2 +- .../io/dico/parcels2/command/CommandsAdmin.kt | 36 +++++++ .../io/dico/parcels2/command/CommandsGeneral.kt | 45 ++++----- .../parcels2/command/ParcelCommandReceivers.kt | 4 +- .../io/dico/parcels2/command/ParcelTarget.kt | 41 ++++---- .../parcels2/defaultimpl/DefaultParcelContainer.kt | 15 ++- .../parcels2/defaultimpl/DefaultParcelGenerator.kt | 82 ++++++++++++---- .../defaultimpl/GlobalAddedDataManagerImpl.kt | 2 +- .../io/dico/parcels2/defaultimpl/ParcelImpl.kt | 22 ++++- .../parcels2/defaultimpl/ParcelProviderImpl.kt | 15 +-- .../dico/parcels2/defaultimpl/ParcelWorldImpl.kt | 6 +- .../dico/parcels2/listener/ParcelEntityTracker.kt | 4 +- .../io/dico/parcels2/listener/ParcelListeners.kt | 2 +- .../io/dico/parcels2/listener/WorldEditListener.kt | 80 +++++++++++++++ .../kotlin/io/dico/parcels2/storage/Backing.kt | 10 +- .../kotlin/io/dico/parcels2/storage/Storage.kt | 10 +- .../parcels2/storage/exposed/ExposedBacking.kt | 12 +-- .../io/dico/parcels2/storage/exposed/IdTables.kt | 7 ++ .../io/dico/parcels2/storage/exposed/ListTables.kt | 2 +- .../dico/parcels2/storage/migration/Migration.kt | 2 +- .../storage/migration/plotme/PlotmeMigration.kt | 6 +- .../kotlin/io/dico/parcels2/util/FunctionHelper.kt | 53 ---------- .../io/dico/parcels2/util/MainThreadDispatcher.kt | 31 ++++++ .../io/dico/parcels2/util/MaterialExtensions.kt | 108 --------------------- .../kotlin/io/dico/parcels2/util/MiscExtensions.kt | 72 -------------- .../io/dico/parcels2/util/NumberExtensions.kt | 32 ------ .../io/dico/parcels2/util/PlayerExtensions.kt | 51 ---------- .../io/dico/parcels2/util/PluginScheduler.kt | 20 ++++ src/main/kotlin/io/dico/parcels2/util/UUIDUtil.kt | 9 +- .../kotlin/io/dico/parcels2/util/ext/Material.kt | 108 +++++++++++++++++++++ src/main/kotlin/io/dico/parcels2/util/ext/Math.kt | 32 ++++++ src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt | 72 ++++++++++++++ .../kotlin/io/dico/parcels2/util/ext/Player.kt | 50 ++++++++++ todo.md | 14 +-- 50 files changed, 811 insertions(+), 522 deletions(-) create mode 100644 gradle.properties create mode 100644 src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt delete mode 100644 src/main/kotlin/io/dico/parcels2/util/FunctionHelper.kt create mode 100644 src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt delete mode 100644 src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt delete mode 100644 src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt delete mode 100644 src/main/kotlin/io/dico/parcels2/util/NumberExtensions.kt delete mode 100644 src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt create mode 100644 src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt create mode 100644 src/main/kotlin/io/dico/parcels2/util/ext/Material.kt create mode 100644 src/main/kotlin/io/dico/parcels2/util/ext/Math.kt create mode 100644 src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt create mode 100644 src/main/kotlin/io/dico/parcels2/util/ext/Player.kt diff --git a/build.gradle.kts b/build.gradle.kts index fa4eb09..34a9737 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,12 +14,10 @@ version = "0.2" plugins { java - kotlin("jvm") version "1.2.51" + kotlin("jvm") version "1.3.0-rc-57" id("com.github.johnrengelman.plugin-shadow") version "2.0.3" } -kotlin.experimental.coroutines = ENABLE - allprojects { apply() @@ -28,6 +26,8 @@ allprojects { maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots") maven("https://dl.bintray.com/kotlin/exposed") + maven("https://dl.bintray.com/kotlin/kotlin-eap") + maven("https://dl.bintray.com/kotlin/kotlinx/") } dependencies { @@ -51,7 +51,7 @@ project(":dicore3:dicore3-command") { dependencies { c.kotlinStd(kotlin("stdlib-jdk8")) c.kotlinStd(kotlin("reflect")) - c.kotlinStd(kotlinx("coroutines-core:0.24.0")) + c.kotlinStd(kotlinx("coroutines-core:0.26.1-eap13")) compile(project(":dicore3:dicore3-core")) compile("com.thoughtworks.paranamer:paranamer:2.8") @@ -59,17 +59,19 @@ project(":dicore3:dicore3-command") { } } - dependencies { compile(project(":dicore3:dicore3-core")) compile(project(":dicore3:dicore3-command")) c.kotlinStd(kotlin("stdlib-jdk8")) c.kotlinStd(kotlin("reflect")) - c.kotlinStd(kotlinx("coroutines-core:0.23.4")) - c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.0") + c.kotlinStd(kotlinx("coroutines-core:0.26.1-eap13")) + c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.7-rc-conf") + + // not on sk89q maven repo yet + compileClasspath(files("$rootDir/debug/plugins/worldedit-bukkit-7.0.0-beta-01.jar")) - compile("org.jetbrains.exposed:exposed:0.10.3") { isTransitive = false } + compile("org.jetbrains.exposed:exposed:0.10.5") { isTransitive = false } compile("joda-time:joda-time:2.10") compile("com.zaxxer:HikariCP:3.2.0") compile("ch.qos.logback:logback-classic:1.2.3") { isTransitive = false } @@ -131,6 +133,7 @@ tasks { } val createDebugServer by creating { + // todo val jarUrl = URL("https://yivesmirror.com/files/spigot/spigot-latest.jar") val serverJarFile = file("$serverDir/lib/spigot.jar") diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java index 4c014fb..4450a92 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java @@ -200,6 +200,15 @@ public class ExecutionContext { return originalBuffer.getArrayFromIndex(cursorStart); } + /** + * The path used to access this address. + * + * @return the path used to access this address. + */ + public String[] getRoute() { + return Arrays.copyOf(originalBuffer.toArray(), address.getDepth()); + } + public Formatting getFormat(EMessageType type) { return address.getChatController().getChatFormatForType(type); } 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 index 88b0d50..6660bf8 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandReceiver.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandReceiver.java @@ -12,6 +12,11 @@ public interface ICommandReceiver { Plugin getPlugin(); + // type is CoroutineContext, but we avoid referring to Kotlin runtime here + default Object getCoroutineContext() { + return null; + } + } } diff --git a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt index 9d23ee6..c09088e 100644 --- a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt +++ b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt @@ -4,15 +4,15 @@ 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 kotlinx.coroutines.CoroutineStart.UNDISPATCHED +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.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.coroutines.CoroutineContext +import kotlin.coroutines.intrinsics.intercepted +import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn import kotlin.reflect.jvm.kotlinFunction fun isSuspendFunction(method: Method): Boolean { @@ -20,16 +20,21 @@ fun isSuspendFunction(method: Method): Boolean { return func.isSuspend } -fun callAsCoroutine(command: ReflectiveCommand, - factory: ICommandReceiver.Factory, - context: ExecutionContext, - args: Array): String? { - val dispatcher = Executor { task -> factory.plugin.server.scheduler.runTask(factory.plugin, task) }.asCoroutineDispatcher() +fun callAsCoroutine( + command: ReflectiveCommand, + factory: ICommandReceiver.Factory, + context: ExecutionContext, + args: Array +): String? { // UNDISPATCHED causes the handler to run until the first suspension point on the current thread, // meaning command handlers that don't have suspension points will run completely synchronously. // Tasks that take time to compute should suspend the coroutine and resume on another thread. - val job = async(context = dispatcher, start = UNDISPATCHED) { command.method.invokeSuspend(command.instance, args) } + val job = GlobalScope.async(context = factory.coroutineContext as CoroutineContext, start = UNDISPATCHED) { + suspendCoroutineUninterceptedOrReturn { cont -> + command.method.invoke(command.instance, *args, cont.intercepted()) + } + } if (job.isCompleted) { return job.getResult() @@ -48,12 +53,6 @@ fun callAsCoroutine(command: ReflectiveCommand, return null } -private suspend fun Method.invokeSuspend(instance: Any?, args: Array): Any? { - return suspendCoroutineOrReturn { cont -> - invoke(instance, *args, cont) - } -} - @Throws(CommandException::class) private fun Deferred.getResult(): String? { getCompletionExceptionOrNull()?.let { ex -> diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..29e08e8 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 020ed4e..2977cab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,9 @@ +pluginManagement.repositories { + maven("http://dl.bintray.com/kotlin/kotlin-eap") + mavenCentral() + maven("https://plugins.gradle.org/m2/") +} + rootProject.name = "parcels2" include("dicore3:core") diff --git a/src/main/kotlin/io/dico/parcels2/Interactable.kt b/src/main/kotlin/io/dico/parcels2/Interactable.kt index 36a8bdd..f1beac5 100644 --- a/src/main/kotlin/io/dico/parcels2/Interactable.kt +++ b/src/main/kotlin/io/dico/parcels2/Interactable.kt @@ -1,6 +1,6 @@ package io.dico.parcels2 -import io.dico.parcels2.util.findWoodKindPrefixedMaterials +import io.dico.parcels2.util.ext.findWoodKindPrefixedMaterials import org.bukkit.Material import java.util.EnumMap @@ -28,7 +28,8 @@ private constructor(val id: Int, arrayOf( Interactables(id++, "button", true, Material.STONE_BUTTON, - *findWoodKindPrefixedMaterials("BUTTON")), + *findWoodKindPrefixedMaterials("BUTTON") + ), Interactables(id++, "lever", true, Material.LEVER), diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt index 6d8a1ca..bdf4ff9 100644 --- a/src/main/kotlin/io/dico/parcels2/Parcel.kt +++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt @@ -1,7 +1,7 @@ package io.dico.parcels2 import io.dico.parcels2.util.Vec2i -import io.dico.parcels2.util.hasBuildAnywhere +import io.dico.parcels2.util.ext.hasBuildAnywhere import org.bukkit.Location import org.bukkit.OfflinePlayer import org.bukkit.entity.Player @@ -32,6 +32,8 @@ interface Parcel : ParcelData { fun dispose() + suspend fun withBlockVisitorPermit(block: suspend () -> Unit) + val homeLocation: Location get() = world.blockManager.getHomeLocation(id) } diff --git a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt index f8e4fd0..8e7a5df 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt @@ -7,6 +7,7 @@ import io.dico.parcels2.blockvisitor.WorktimeLimiter import io.dico.parcels2.util.Region import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.get +import kotlinx.coroutines.CoroutineScope import org.bukkit.Chunk import org.bukkit.Location import org.bukkit.World @@ -38,6 +39,7 @@ abstract class ParcelGenerator : ChunkGenerator() { abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId, container: ParcelContainer, + coroutineScope: CoroutineScope, worktimeLimiter: WorktimeLimiter): Pair } diff --git a/src/main/kotlin/io/dico/parcels2/ParcelId.kt b/src/main/kotlin/io/dico/parcels2/ParcelId.kt index 20d4cc8..72eceb9 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelId.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelId.kt @@ -22,6 +22,8 @@ interface ParcelWorldId { } } +fun ParcelWorldId.toStringExt() = "ParcelWorld($name)" + /** * Used by storage backing options to encompass the location of a parcel * Does NOT support equality operator. @@ -31,6 +33,7 @@ interface ParcelId { val x: Int val z: Int val pos: Vec2i get() = Vec2i(x, z) + val idString get() = "$x,$z" fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId) companion object { @@ -41,9 +44,15 @@ interface ParcelId { } } +fun ParcelId.toStringExt() = "Parcel(${worldId.name},$idString)" + private class ParcelWorldIdImpl(override val name: String, - override val uid: UUID?) : ParcelWorldId + override val uid: UUID?) : ParcelWorldId { + override fun toString() = toStringExt() +} private class ParcelIdImpl(override val worldId: ParcelWorldId, override val x: Int, - override val z: Int) : ParcelId + override val z: Int) : ParcelId { + override fun toString() = toStringExt() +} diff --git a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt index d307947..d70368f 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt @@ -3,8 +3,7 @@ package io.dico.parcels2 import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.storage.Storage import io.dico.parcels2.util.Vec2i -import io.dico.parcels2.util.floor -import org.bukkit.Chunk +import io.dico.parcels2.util.ext.floor import org.bukkit.Location import org.bukkit.World import org.bukkit.block.Block @@ -70,6 +69,8 @@ interface ParcelContainer { fun getParcelById(id: Vec2i): Parcel? = getParcelById(id.x, id.z) + fun getParcelById(id: ParcelId): Parcel? = getParcelById(id.x, id.z) + fun nextEmptyParcel(): Parcel? } diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt index 01ee857..c863716 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt @@ -10,23 +10,27 @@ import io.dico.parcels2.defaultimpl.GlobalAddedDataManagerImpl import io.dico.parcels2.defaultimpl.ParcelProviderImpl import io.dico.parcels2.listener.ParcelEntityTracker import io.dico.parcels2.listener.ParcelListeners +import io.dico.parcels2.listener.WorldEditListener import io.dico.parcels2.options.Options import io.dico.parcels2.options.optionsMapper import io.dico.parcels2.storage.Storage -import io.dico.parcels2.util.FunctionHelper -import io.dico.parcels2.util.tryCreate +import io.dico.parcels2.util.MainThreadDispatcher +import io.dico.parcels2.util.PluginScheduler +import io.dico.parcels2.util.ext.tryCreate +import kotlinx.coroutines.CoroutineScope import org.bukkit.Bukkit import org.bukkit.generator.ChunkGenerator +import org.bukkit.plugin.Plugin import org.bukkit.plugin.java.JavaPlugin import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.File -import java.util.Random +import kotlin.coroutines.CoroutineContext val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin") private inline val plogger get() = logger -class ParcelsPlugin : JavaPlugin() { +class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler { lateinit var optionsFile: File; private set lateinit var options: Options; private set lateinit var parcelProvider: ParcelProvider; private set @@ -38,7 +42,8 @@ class ParcelsPlugin : JavaPlugin() { private var listeners: ParcelListeners? = null private var cmdDispatcher: ICommandDispatcher? = null - val functionHelper: FunctionHelper = FunctionHelper(this) + override val coroutineContext: CoroutineContext = MainThreadDispatcher(this) + override val plugin: Plugin get() = this val worktimeLimiter: WorktimeLimiter by lazy { TickWorktimeLimiter(this, options.tickWorktime) } override fun onEnable() { @@ -135,9 +140,14 @@ class ParcelsPlugin : JavaPlugin() { if (listeners == null) { listeners = ParcelListeners(parcelProvider, entityTracker, storage) registrator.registerListeners(listeners!!) + + val worldEditPlugin = server.pluginManager.getPlugin("WorldEdit") + if (worldEditPlugin != null) { + WorldEditListener.register(this, worldEditPlugin) + } } - functionHelper.scheduleRepeating(100, 5, entityTracker::tick) + scheduleRepeating(100, 5, entityTracker::tick) } } \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt index c735c68..0ef9f9d 100644 --- a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt +++ b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt @@ -3,12 +3,13 @@ package io.dico.parcels2 import io.dico.parcels2.storage.Storage -import io.dico.parcels2.util.getPlayerNameOrDefault -import io.dico.parcels2.util.isValid -import io.dico.parcels2.util.uuid -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.Unconfined -import kotlinx.coroutines.experimental.async +import io.dico.parcels2.util.PLAYER_NAME_PLACEHOLDER +import io.dico.parcels2.util.getPlayerName +import io.dico.parcels2.util.ext.isValid +import io.dico.parcels2.util.ext.uuid +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Unconfined +import kotlinx.coroutines.async import org.bukkit.Bukkit import org.bukkit.OfflinePlayer import java.util.UUID @@ -18,7 +19,7 @@ interface PlayerProfile { val name: String? val nameOrBukkitName: String? val notNullName: String - val isStar: Boolean get() = false + val isStar: Boolean get() = this is Star val exists: Boolean get() = this is RealImpl fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean @@ -78,9 +79,9 @@ interface PlayerProfile { override val uuid: UUID override val nameOrBukkitName: String? // If a player is online, their name is prioritized to get name changes right immediately - get() = Bukkit.getPlayer(uuid)?.name ?: name ?: Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }?.name + get() = Bukkit.getPlayer(uuid)?.name ?: name ?: getPlayerName(uuid) override val notNullName: String - get() = name ?: getPlayerNameOrDefault(uuid) + get() = nameOrBukkitName ?: PLAYER_NAME_PLACEHOLDER val player: OfflinePlayer? get() = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid } val playerUnchecked: OfflinePlayer get() = Bukkit.getOfflinePlayer(uuid) @@ -115,8 +116,8 @@ interface PlayerProfile { object Star : BaseImpl(), Real { override val name: String = "*" + // hopefully nobody will have this random UUID :) override val uuid: UUID = UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1") - override val isStar: Boolean get() = true override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean { return true @@ -148,7 +149,7 @@ interface PlayerProfile { } suspend fun tryResolveSuspendedly(storage: Storage): Real? { - return storage.getPlayerUuidForName(name).await()?.let { RealImpl(it, name) } + return storage.getPlayerUuidForName(name).await()?.let { resolve(it) } } fun resolve(uuid: UUID): Real { diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt index 1cac5f9..3899db9 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt @@ -2,13 +2,11 @@ package io.dico.parcels2.blockvisitor import io.dico.parcels2.util.Region import io.dico.parcels2.util.Vec3i -import kotlin.coroutines.experimental.SequenceBuilder -import kotlin.coroutines.experimental.buildIterator abstract class RegionTraverser { - fun traverseRegion(region: Region): Iterable = Iterable { buildIterator { build(region) } } + fun traverseRegion(region: Region): Iterable = Iterable { iterator { build(region) } } - protected abstract suspend fun SequenceBuilder.build(region: Region) + protected abstract suspend fun SequenceScope.build(region: Region) companion object { val upward = create { traverseUpward(it) } @@ -16,13 +14,13 @@ abstract class RegionTraverser { val forClearing get() = downward val forFilling get() = upward - inline fun create(crossinline builder: suspend SequenceBuilder.(Region) -> Unit) = object : RegionTraverser() { - override suspend fun SequenceBuilder.build(region: Region) { + inline fun create(crossinline builder: suspend SequenceScope.(Region) -> Unit) = object : RegionTraverser() { + override suspend fun SequenceScope.build(region: Region) { builder(region) } } - private suspend fun SequenceBuilder.traverseDownward(region: Region) { + private suspend fun SequenceScope.traverseDownward(region: Region) { val origin = region.origin val size = region.size repeat(size.y) { y -> @@ -34,7 +32,7 @@ abstract class RegionTraverser { } } - private suspend fun SequenceBuilder.traverseUpward(region: Region) { + private suspend fun SequenceScope.traverseUpward(region: Region) { val origin = region.origin val size = region.size repeat(size.y) { y -> diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt index ea4db62..f735903 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt @@ -1,38 +1,42 @@ package io.dico.parcels2.blockvisitor import io.dico.parcels2.ParcelsPlugin -import io.dico.parcels2.util.FunctionHelper -import kotlinx.coroutines.experimental.CancellationException -import kotlinx.coroutines.experimental.Job +import io.dico.parcels2.logger +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart.LAZY +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch import org.bukkit.scheduler.BukkitTask import java.lang.System.currentTimeMillis import java.util.LinkedList -import java.util.logging.Level -import kotlin.coroutines.experimental.Continuation -import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED -import kotlin.coroutines.experimental.intrinsics.suspendCoroutineUninterceptedOrReturn +import kotlin.coroutines.Continuation +import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED +import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine typealias TimeLimitedTask = suspend WorkerScope.() -> Unit typealias WorkerUpdateLister = Worker.(Double, Long) -> Unit data class TickWorktimeOptions(var workTime: Int, var tickInterval: Int) -sealed class WorktimeLimiter { +interface WorktimeLimiter { /** * Submit a [task] that should be run synchronously, but limited such that it does not stall the server * a bunch */ - abstract fun submit(task: TimeLimitedTask): Worker + fun submit(task: TimeLimitedTask): Worker /** * Get a list of all workers */ - abstract val workers: List + val workers: List /** * Attempts to complete any remaining tasks immediately, without suspension. */ - abstract fun completeAllTasks() + fun completeAllTasks() } interface Timed { @@ -77,9 +81,14 @@ interface Worker : Timed { /** * Calls the given [block] when this worker completes, with the progress value 1.0. - * Repeated invocations of this method result in an [IllegalStateException] + * Multiple listeners may be registered to this function. */ fun onCompleted(block: WorkerUpdateLister): Worker + + /** + * Await completion of this worker + */ + suspend fun awaitCompletion() } interface WorkerScope : Timed { @@ -113,7 +122,7 @@ private interface WorkerContinuation : Worker, WorkerScope { * There is a configurable maxiumum amount of milliseconds that can be allocated to all workers together in each server tick * This object attempts to split that maximum amount of milliseconds equally between all jobs */ -class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWorktimeOptions) : WorktimeLimiter() { +class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWorktimeOptions) : WorktimeLimiter { // The currently registered bukkit scheduler task private var bukkitTask: BukkitTask? = null // The workers. @@ -121,12 +130,12 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo override val workers: List = _workers override fun submit(task: TimeLimitedTask): Worker { - val worker: WorkerContinuation = WorkerImpl(plugin.functionHelper, task) + val worker: WorkerContinuation = WorkerImpl(plugin, task) if (bukkitTask == null) { val completed = worker.resume(options.workTime.toLong()) if (completed) return worker - bukkitTask = plugin.functionHelper.scheduleRepeating(0, options.tickInterval) { tickJobs() } + bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickJobs() } } _workers.addFirst(worker) @@ -171,8 +180,10 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo } -private class WorkerImpl(val functionHelper: FunctionHelper, - val task: TimeLimitedTask) : WorkerContinuation { +private class WorkerImpl( + val scope: CoroutineScope, + val task: TimeLimitedTask +) : WorkerContinuation, CoroutineScope by scope { override var job: Job? = null; private set override val elapsedTime @@ -204,27 +215,44 @@ private class WorkerImpl(val functionHelper: FunctionHelper, // report any error that occurred completionException = exception?.also { if (it !is CancellationException) - functionHelper.plugin.logger.log(Level.SEVERE, "TimeLimitedTask for plugin ${functionHelper.plugin.name} generated an exception", it) + logger.error("TimeLimitedTask generated an exception", it) } // convert to elapsed time here startTimeOrElapsedTime = System.currentTimeMillis() - startTimeOrElapsedTime onCompleted?.let { it(1.0, elapsedTime) } + + onCompleted = null + onProgressUpdate = { prog, el -> } } } override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: WorkerUpdateLister): Worker { onProgressUpdate?.let { throw IllegalStateException() } + if (asCompletionListener) onCompleted(block) + if (isComplete) return this onProgressUpdate = block progressUpdateInterval = minInterval lastUpdateTime = System.currentTimeMillis() + minDelay - minInterval - if (asCompletionListener) onCompleted(block) + return this } override fun onCompleted(block: WorkerUpdateLister): Worker { - onCompleted?.let { throw IllegalStateException() } - onCompleted = block + if (isComplete) { + block(1.0, startTimeOrElapsedTime) + return this + } + + val cur = onCompleted + onCompleted = if (cur == null) { + block + } else { + fun Worker.(prog: Double, el: Long) { + cur(prog, el) + block(prog, el) + } + } return this } @@ -265,7 +293,7 @@ private class WorkerImpl(val functionHelper: FunctionHelper, } try { - val job = functionHelper.launchLazilyOnMainThread { task() } + val job = launch(start = LAZY) { task() } initJob(job = job) job.start() } catch (t: Throwable) { @@ -275,6 +303,18 @@ private class WorkerImpl(val functionHelper: FunctionHelper, return continuation == null } + override suspend fun awaitCompletion() { + if (isComplete) return + + // easy path - if the job was initialized already + job?.apply { join(); return } + + // other way. + return suspendCoroutine { cont -> + onCompleted { prog, el -> cont.resume(Unit) } + } + } + } /* diff --git a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt index 31a551a..bd27fe7 100644 --- a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt +++ b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt @@ -1,26 +1,25 @@ package io.dico.parcels2.command import io.dico.dicore.command.CommandException +import io.dico.dicore.command.EMessageType import io.dico.dicore.command.ExecutionContext import io.dico.dicore.command.ICommandReceiver -import io.dico.parcels2.PlayerProfile import io.dico.parcels2.ParcelWorld import io.dico.parcels2.ParcelsPlugin -import io.dico.parcels2.util.hasAdminManage -import io.dico.parcels2.util.parcelLimit +import io.dico.parcels2.PlayerProfile +import io.dico.parcels2.util.ext.hasAdminManage +import io.dico.parcels2.util.ext.parcelLimit import org.bukkit.entity.Player import org.bukkit.plugin.Plugin import java.lang.reflect.Method abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandReceiver.Factory { - override fun getPlugin(): Plugin = plugin + override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver { return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName) } - protected inline val worlds get() = plugin.parcelProvider - protected fun error(message: String): Nothing { throw CommandException(message) } @@ -40,5 +39,21 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei } } + protected fun areYouSureMessage(context: ExecutionContext) = "Are you sure? You cannot undo this action!\n" + + "Run \"/${context.route.joinToString(" ")} -sure\" if you want to go through with this." + + protected fun ParcelScope.clearWithProgressUpdates(context: ExecutionContext, action: String) { + world.blockManager.clearParcel(parcel.id) + .onProgressUpdate(1000, 1000) { progress, elapsedTime -> + val alt = context.getFormat(EMessageType.NUMBER) + val main = context.getFormat(EMessageType.INFORMATIVE) + context.sendMessage( + EMessageType.INFORMATIVE, false, "$action progress: $alt%.02f$main%%, $alt%.2f${main}s elapsed" + .format(progress * 100, elapsedTime / 1000.0) + ) + } + } + + override fun getCoroutineContext() = plugin.coroutineContext } diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusLocal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusLocal.kt index 69da341..223e504 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusLocal.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusLocal.kt @@ -4,7 +4,7 @@ import io.dico.dicore.command.Validate import io.dico.dicore.command.annotation.Cmd import io.dico.dicore.command.annotation.Desc import io.dico.parcels2.ParcelsPlugin -import io.dico.parcels2.util.hasAdminManage +import io.dico.parcels2.util.ext.hasAdminManage import org.bukkit.OfflinePlayer import org.bukkit.entity.Player diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt index 2fe18ed..35ede71 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt @@ -1,7 +1,43 @@ package io.dico.parcels2.command +import io.dico.dicore.command.ExecutionContext +import io.dico.dicore.command.annotation.Cmd +import io.dico.dicore.command.annotation.Flag import io.dico.parcels2.ParcelsPlugin +import io.dico.parcels2.PlayerProfile class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { + @Cmd("setowner") + @ParcelRequire(admin = true) + fun ParcelScope.cmdSetowner(target: PlayerProfile): Any? { + parcel.owner = target + + val fakeString = if (target.isFake) " (fake)" else "" + return "${target.notNullName}$fakeString is the new owner of (${parcel.id.idString})" + } + + @Cmd("dispose") + @ParcelRequire(admin = true) + fun ParcelScope.cmdDispose(): Any? { + parcel.dispose() + return "Data of (${parcel.id.idString}) has been disposed" + } + + @Cmd("reset") + @ParcelRequire(admin = true) + fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? { + if (!sure) return areYouSureMessage(context) + parcel.dispose() + clearWithProgressUpdates(context, "Reset") + return null + } + + @Cmd("swap") + fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? { + if (!sure) return areYouSureMessage(context) + TODO() + } + + } \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt index d02c974..95083b6 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt @@ -1,16 +1,18 @@ package io.dico.parcels2.command -import io.dico.dicore.command.EMessageType import io.dico.dicore.command.ExecutionContext +import io.dico.dicore.command.Validate import io.dico.dicore.command.annotation.Cmd import io.dico.dicore.command.annotation.Desc import io.dico.dicore.command.annotation.Flag import io.dico.dicore.command.annotation.RequireParameters import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.PlayerProfile -import io.dico.parcels2.util.hasAdminManage -import io.dico.parcels2.util.hasParcelHomeOthers -import io.dico.parcels2.util.uuid +import io.dico.parcels2.command.ParcelTarget.Kind +import io.dico.parcels2.util.ext.hasAdminManage +import io.dico.parcels2.util.ext.hasParcelHomeOthers +import io.dico.parcels2.util.ext.uuid +import org.bukkit.block.Biome import org.bukkit.entity.Player class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { @@ -43,17 +45,20 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { "more than one parcel", shortVersion = "teleports you to parcels") @RequireParameters(0) - suspend fun cmdHome(player: Player, @ParcelTarget.Kind(ParcelTarget.OWNER_REAL) target: ParcelTarget): Any? { + suspend fun cmdHome(player: Player, + @Kind(ParcelTarget.OWNER_REAL) target: ParcelTarget): Any? { return cmdGoto(player, target) } @Cmd("tp", aliases = ["teleport"]) - suspend fun cmdTp(player: Player, @ParcelTarget.Kind(ParcelTarget.ID) target: ParcelTarget): Any? { + suspend fun cmdTp(player: Player, + @Kind(ParcelTarget.ID) target: ParcelTarget): Any? { return cmdGoto(player, target) } @Cmd("goto") - suspend fun cmdGoto(player: Player, @ParcelTarget.Kind(ParcelTarget.ANY) target: ParcelTarget): Any? { + suspend fun cmdGoto(player: Player, + @Kind(ParcelTarget.ANY) target: ParcelTarget): Any? { if (target is ParcelTarget.ByOwner) { target.resolveOwner(plugin.storage) if (!target.owner.matches(player) && !player.hasParcelHomeOthers) { @@ -64,11 +69,12 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { val match = target.getParcelSuspend(plugin.storage) ?: error("The specified parcel could not be matched") player.teleport(match.homeLocation) - return "" + return null } @Cmd("goto_fake") - suspend fun cmdGotoFake(player: Player, @ParcelTarget.Kind(ParcelTarget.OWNER_FAKE) target: ParcelTarget): Any? { + suspend fun cmdGotoFake(player: Player, + @Kind(ParcelTarget.OWNER_FAKE) target: ParcelTarget): Any? { return cmdGoto(player, target) } @@ -97,23 +103,18 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { @Cmd("clear") @ParcelRequire(owner = true) fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? { - if (!sure) return "Are you sure? You cannot undo this action!\n" + - "Run \"/${context.rawInput} -sure\" if you want to go through with this." - - world.blockManager.clearParcel(parcel.id) - .onProgressUpdate(1000, 1000) { progress, elapsedTime -> - val alt = context.getFormat(EMessageType.NUMBER) - val main = context.getFormat(EMessageType.INFORMATIVE) - context.sendMessage(EMessageType.INFORMATIVE, false, "Clear progress: $alt%.02f$main%%, $alt%.2f${main}s elapsed" - .format(progress * 100, elapsedTime / 1000.0)) - } + if (!sure) return areYouSureMessage(context) + clearWithProgressUpdates(context, "Clear") return null } - @Cmd("swap") - fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? { - TODO() + @Cmd("setbiome") + @ParcelRequire(owner = true) + fun ParcelScope.cmdSetbiome(context: ExecutionContext, biome: Biome): Any? { + Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel") + world.blockManager.setBiome(parcel.id, biome) + return "Biome has been set to $biome" } } \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt index eab02c4..488148d 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt @@ -7,8 +7,8 @@ import io.dico.dicore.command.Validate import io.dico.parcels2.Parcel import io.dico.parcels2.ParcelProvider import io.dico.parcels2.ParcelWorld -import io.dico.parcels2.util.hasAdminManage -import io.dico.parcels2.util.uuid +import io.dico.parcels2.util.ext.hasAdminManage +import io.dico.parcels2.util.ext.uuid import org.bukkit.entity.Player import java.lang.reflect.Method import kotlin.reflect.full.extensionReceiverParameter diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt index bbdcdb8..abf7d40 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt @@ -7,8 +7,10 @@ import io.dico.dicore.command.parameter.type.ParameterType import io.dico.parcels2.* import io.dico.parcels2.storage.Storage import io.dico.parcels2.util.Vec2i -import io.dico.parcels2.util.floor -import kotlinx.coroutines.experimental.Deferred +import io.dico.parcels2.util.ext.floor +import kotlinx.coroutines.CoroutineStart.* +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async import org.bukkit.command.CommandSender import org.bukkit.entity.Player @@ -16,7 +18,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef abstract suspend fun getParcelSuspend(storage: Storage): Parcel? - fun ParcelsPlugin.getParcelDeferred(): Deferred = functionHelper.deferUndispatchedOnMainThread { getParcelSuspend(storage) } + fun ParcelsPlugin.getParcelDeferred(): Deferred = async(start = UNDISPATCHED) { getParcelSuspend(storage) } class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) { override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel() @@ -57,13 +59,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef } } - annotation class Kind(val kind: Int) - - companion object Config : ParameterConfig(Kind::class.java) { - override fun toParameterInfo(annotation: Kind): Int { - return annotation.kind - } - + companion object { const val ID = 1 // ID const val OWNER_REAL = 2 // an owner backed by a UUID const val OWNER_FAKE = 4 // an owner not backed by a UUID @@ -78,7 +74,14 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef // instead of parcel that the player is in } - class PType(val parcelProvider: ParcelProvider) : ParameterType(ParcelTarget::class.java, ParcelTarget.Config) { + annotation class Kind(val kind: Int) + private object Config : ParameterConfig(Kind::class.java) { + override fun toParameterInfo(annotation: Kind): Int { + return annotation.kind + } + } + + class PType(val parcelProvider: ParcelProvider) : ParameterType(ParcelTarget::class.java, Config) { override fun parse(parameter: Parameter, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget { var input = buffer.next() @@ -117,15 +120,18 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef private fun getHomeIndex(parameter: Parameter<*, *>, kind: Int, sender: CommandSender, input: String): Pair { val splitIdx = input.indexOf(':') val ownerString: String - val indexString: String + val index: Int? if (splitIdx == -1) { // just the index. - ownerString = "" - indexString = input + index = input.toIntOrNull() + ownerString = if (index == null) input else "" } else { ownerString = input.substring(0, splitIdx) - indexString = input.substring(splitIdx + 1) + + val indexString = input.substring(splitIdx + 1) + index = indexString.toIntOrNull() + ?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer") } val owner = if (ownerString.isEmpty()) @@ -133,10 +139,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef else PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0) - val index = if (indexString.isEmpty()) 0 else indexString.toIntOrNull() - ?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer") - - return owner to index + return owner to (index ?: 0) } private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>, objName: String): Player { diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt index 0597a9f..e6d29f4 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt @@ -2,9 +2,8 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.Parcel import io.dico.parcels2.ParcelContainer +import io.dico.parcels2.ParcelId import io.dico.parcels2.ParcelWorld -import kotlin.coroutines.experimental.buildIterator -import kotlin.coroutines.experimental.buildSequence class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer { private var parcels: Array> @@ -38,12 +37,20 @@ class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer { return parcels.getOrNull(x + world.options.axisLimit)?.getOrNull(z + world.options.axisLimit) } + override fun getParcelById(id: ParcelId): Parcel? { + if (!world.id.equals(id.worldId)) throw IllegalArgumentException() + return when (id) { + is Parcel -> id + else -> getParcelById(id.x, id.z) + } + } + override fun nextEmptyParcel(): Parcel? { return walkInCircle().find { it.owner == null } } private fun walkInCircle(): Iterable = Iterable { - buildIterator { + iterator { val center = world.options.axisLimit for (radius in 0..center) { var x = center - radius; @@ -56,7 +63,7 @@ class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer { } } - fun allParcels(): Sequence = buildSequence { + fun allParcels(): Sequence = sequence { for (array in parcels) { yieldAll(array.iterator()) } diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt index 244d38c..44ecc6c 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt @@ -2,10 +2,19 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.* import io.dico.parcels2.blockvisitor.RegionTraverser +import io.dico.parcels2.blockvisitor.TimeLimitedTask import io.dico.parcels2.blockvisitor.Worker import io.dico.parcels2.blockvisitor.WorktimeLimiter import io.dico.parcels2.options.DefaultGeneratorOptions -import io.dico.parcels2.util.* +import io.dico.parcels2.util.Region +import io.dico.parcels2.util.Vec2i +import io.dico.parcels2.util.Vec3i +import io.dico.parcels2.util.ext.even +import io.dico.parcels2.util.ext.umod +import io.dico.parcels2.util.get +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart.UNDISPATCHED +import kotlinx.coroutines.launch import org.bukkit.* import org.bukkit.block.Biome import org.bukkit.block.BlockFace @@ -13,13 +22,17 @@ 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 java.lang.IllegalArgumentException import java.util.Random private val airType = Bukkit.createBlockData(Material.AIR) private const val chunkSize = 16 -class DefaultParcelGenerator(override val worldName: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() { +class DefaultParcelGenerator( + override val worldName: String, + private val o: DefaultGeneratorOptions +) : ParcelGenerator() { private var _world: World? = null override val world: World get() { @@ -36,13 +49,15 @@ class DefaultParcelGenerator(override val worldName: String, private val o: Defa 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) { + 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 @@ -104,10 +119,13 @@ class DefaultParcelGenerator(override val worldName: String, private val o: Defa return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix) } - override fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId, - container: ParcelContainer, - worktimeLimiter: WorktimeLimiter): Pair { - return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, worktimeLimiter) + override fun makeParcelLocatorAndBlockManager( + worldId: ParcelWorldId, + container: ParcelContainer, + coroutineScope: CoroutineScope, + worktimeLimiter: WorktimeLimiter + ): Pair { + return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, worktimeLimiter) } private inline fun convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? { @@ -123,8 +141,10 @@ class DefaultParcelGenerator(override val worldName: String, private val o: Defa return null } - private inner class ParcelLocatorImpl(val worldId: ParcelWorldId, - val container: ParcelContainer) : ParcelLocator { + private inner class ParcelLocatorImpl( + val worldId: ParcelWorldId, + val container: ParcelContainer + ) : ParcelLocator { override val world: World = this@DefaultParcelGenerator.world override fun getParcelAt(x: Int, z: Int): Parcel? { @@ -137,8 +157,11 @@ class DefaultParcelGenerator(override val worldName: String, private val o: Defa } @Suppress("DEPRECATION") - private inner class ParcelBlockManagerImpl(val worldId: ParcelWorldId, - override val worktimeLimiter: WorktimeLimiter) : ParcelBlockManagerBase() { + private inner class ParcelBlockManagerImpl( + val worldId: ParcelWorldId, + coroutineScope: CoroutineScope, + override val worktimeLimiter: WorktimeLimiter + ) : ParcelBlockManagerBase(), CoroutineScope by coroutineScope { override val world: World = this@DefaultParcelGenerator.world override val parcelTraverser: RegionTraverser = RegionTraverser.upToAndDownUntil(o.floorHeight) @@ -198,7 +221,28 @@ class DefaultParcelGenerator(override val worldName: String, private val o: Defa } } - override fun setBiome(parcel: ParcelId, biome: Biome): Worker = worktimeLimiter.submit { + private fun getParcel(parcelId: ParcelId): Parcel? { + // todo dont rely on this cast + val world = worldId as? ParcelWorld ?: return null + return world.getParcelById(parcelId) + } + + private fun submitBlockVisitor(parcelId: ParcelId, task: TimeLimitedTask): Worker { + val parcel = getParcel(parcelId) ?: return worktimeLimiter.submit(task) + if (parcel.hasBlockVisitors) throw IllegalArgumentException("This parcel already has a block visitor") + + val worker = worktimeLimiter.submit(task) + + launch(start = UNDISPATCHED) { + parcel.withBlockVisitorPermit { + worker.awaitCompletion() + } + } + + return worker + } + + override fun setBiome(parcel: ParcelId, biome: Biome): Worker = submitBlockVisitor(parcel) { val world = world val b = getBottomBlock(parcel) val parcelSize = o.parcelSize @@ -210,7 +254,7 @@ class DefaultParcelGenerator(override val worldName: String, private val o: Defa } } - override fun clearParcel(parcel: ParcelId): Worker = worktimeLimiter.submit { + override fun clearParcel(parcel: ParcelId): Worker = submitBlockVisitor(parcel) { val region = getRegion(parcel) val blocks = parcelTraverser.traverseRegion(region) val blockCount = region.blockCount.toDouble() diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt index ba54375..49ab71f 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt @@ -3,7 +3,7 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.* -import io.dico.parcels2.util.alsoIfTrue +import io.dico.parcels2.util.ext.alsoIfTrue import java.util.Collections class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataManager { diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt index dd3c121..59e84f4 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt @@ -3,11 +3,11 @@ package io.dico.parcels2.defaultimpl import io.dico.dicore.Formatting import io.dico.parcels2.* import io.dico.parcels2.util.Vec2i -import io.dico.parcels2.util.alsoIfTrue +import io.dico.parcels2.util.ext.alsoIfTrue import org.bukkit.Material import org.bukkit.OfflinePlayer import org.joda.time.DateTime -import kotlin.reflect.KProperty +import java.util.concurrent.atomic.AtomicInteger class ParcelImpl(override val world: ParcelWorld, override val x: Int, @@ -15,8 +15,8 @@ class ParcelImpl(override val world: ParcelWorld, override val id: ParcelId = this override val pos get() = Vec2i(x, z) override var data: ParcelDataHolder = ParcelDataHolder(); private set - override val infoString by ParcelInfoStringComputer - override var hasBlockVisitors: Boolean = false; private set + override val infoString get() = ParcelInfoStringComputer.getInfoString(this) + override val hasBlockVisitors get() = blockVisitors.get() > 0 override val worldId: ParcelWorldId get() = world.id override fun copyDataIgnoringDatabase(data: ParcelData) { @@ -115,6 +115,18 @@ class ParcelImpl(override val world: ParcelWorld, // TODO update storage } + private var blockVisitors = AtomicInteger(0) + + override suspend fun withBlockVisitorPermit(block: suspend () -> Unit) { + try { + blockVisitors.getAndIncrement() + block() + } finally { + blockVisitors.getAndDecrement() + } + } + + override fun toString() = toStringExt() } private object ParcelInfoStringComputer { @@ -159,7 +171,7 @@ private object ParcelInfoStringComputer { } } - operator fun getValue(parcel: Parcel, property: KProperty<*>): String = buildString { + fun getInfoString(parcel: Parcel): String = buildString { appendField("ID") { append(parcel.x) append(',') diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt index c9f4613..8920e2e 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt @@ -1,8 +1,11 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.* -import kotlinx.coroutines.experimental.Unconfined -import kotlinx.coroutines.experimental.launch +import io.dico.parcels2.util.schedule +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.CoroutineStart.* +import kotlinx.coroutines.Unconfined +import kotlinx.coroutines.launch import org.bukkit.Bukkit import org.bukkit.WorldCreator import org.joda.time.DateTime @@ -42,7 +45,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { private fun loadWorlds0() { if (Bukkit.getWorlds().isEmpty()) { - plugin.functionHelper.schedule(::loadWorlds0) + plugin.schedule(::loadWorlds0) plugin.logger.warning("Scheduling to load worlds in the next tick, \nbecause no bukkit worlds are loaded yet") return } @@ -58,7 +61,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") } parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage, - plugin.globalAddedData, ::DefaultParcelContainer, plugin.worktimeLimiter) + plugin.globalAddedData, ::DefaultParcelContainer, plugin, plugin.worktimeLimiter) if (!worldExists) { val time = DateTime.now() @@ -77,7 +80,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { } private fun loadStoredData() { - plugin.functionHelper.launchLazilyOnMainThread { + plugin.launch { val migration = plugin.options.migration if (migration.enabled) { migration.instance?.newInstance()?.apply { @@ -102,7 +105,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { logger.info("Loading data completed") _dataIsLoaded = true - }.start() + } } /* diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt index a54e519..9f96a8c 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt @@ -6,6 +6,7 @@ import io.dico.parcels2.* import io.dico.parcels2.blockvisitor.WorktimeLimiter import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.storage.Storage +import kotlinx.coroutines.CoroutineScope import org.bukkit.World import org.joda.time.DateTime import java.util.UUID @@ -16,6 +17,7 @@ class ParcelWorldImpl(override val world: World, override val storage: Storage, override val globalAddedData: GlobalAddedDataManager, containerFactory: ParcelContainerFactory, + coroutineScope: CoroutineScope, worktimeLimiter: WorktimeLimiter) : ParcelWorld, ParcelWorldId, @@ -37,7 +39,7 @@ class ParcelWorldImpl(override val world: World, override val blockManager: ParcelBlockManager init { - val pair = generator.makeParcelLocatorAndBlockManager(id, container, worktimeLimiter) + val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, worktimeLimiter) locator = pair.first blockManager = pair.second @@ -84,5 +86,5 @@ class ParcelWorldImpl(override val world: World, return container.nextEmptyParcel() } - + override fun toString() = toStringExt() } diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt index edb48b5..eaacf93 100644 --- a/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt +++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt @@ -2,8 +2,8 @@ package io.dico.parcels2.listener import io.dico.parcels2.Parcel import io.dico.parcels2.ParcelProvider -import io.dico.parcels2.util.editLoop -import io.dico.parcels2.util.isPresentAnd +import io.dico.parcels2.util.ext.editLoop +import io.dico.parcels2.util.ext.isPresentAnd import org.bukkit.entity.Entity class ParcelEntityTracker(val parcelProvider: ParcelProvider) { diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt index eedc416..6d89ee6 100644 --- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt +++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt @@ -8,7 +8,7 @@ import io.dico.parcels2.ParcelProvider import io.dico.parcels2.ParcelWorld import io.dico.parcels2.statusKey import io.dico.parcels2.storage.Storage -import io.dico.parcels2.util.* +import io.dico.parcels2.util.ext.* import org.bukkit.Material.* import org.bukkit.World import org.bukkit.block.Biome diff --git a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt new file mode 100644 index 0000000..b68f7c2 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt @@ -0,0 +1,80 @@ +package io.dico.parcels2.listener + +import com.sk89q.worldedit.EditSession.Stage.BEFORE_REORDER +import com.sk89q.worldedit.Vector +import com.sk89q.worldedit.Vector2D +import com.sk89q.worldedit.WorldEdit +import com.sk89q.worldedit.WorldEditException +import com.sk89q.worldedit.bukkit.WorldEditPlugin +import com.sk89q.worldedit.event.extent.EditSessionEvent +import com.sk89q.worldedit.extent.AbstractDelegateExtent +import com.sk89q.worldedit.extent.Extent +import com.sk89q.worldedit.util.eventbus.EventHandler.Priority.VERY_EARLY +import com.sk89q.worldedit.util.eventbus.Subscribe +import com.sk89q.worldedit.world.biome.BaseBiome +import com.sk89q.worldedit.world.block.BaseBlock +import com.sk89q.worldedit.world.block.BlockStateHolder +import io.dico.parcels2.ParcelWorld +import io.dico.parcels2.ParcelsPlugin +import io.dico.parcels2.util.ext.hasBuildAnywhere +import io.dico.parcels2.util.ext.sendParcelMessage +import org.bukkit.entity.Player +import org.bukkit.plugin.Plugin + +class WorldEditListener(val parcels: ParcelsPlugin, val worldEdit: WorldEdit) { + + @Subscribe(priority = VERY_EARLY) + fun onEditSession(event: EditSessionEvent) { + val worldName = event.world?.name ?: return + val world = parcels.parcelProvider.getWorld(worldName) ?: return + if (event.stage == BEFORE_REORDER) return + + val actor = event.actor + if (actor == null || !actor.isPlayer) return + + val player = parcels.server.getPlayer(actor.uniqueId) + if (player.hasBuildAnywhere) return + + event.extent = ParcelsExtent(event.extent, world, player) + } + + private class ParcelsExtent(extent: Extent, + val world: ParcelWorld, + val player: Player) : AbstractDelegateExtent(extent) { + private var messageSent = false + + private fun canBuild(x: Int, z: Int): Boolean { + world.getParcelAt(x, z)?.let { parcel -> + if (parcel.canBuild(player, checkAdmin = false)) { + return true + } + } + + if (!messageSent) { + messageSent = true + player.sendParcelMessage(except = true, message = "You can't use WorldEdit there") + } + + return false + } + + override fun setBlock(location: Vector, block: BlockStateHolder<*>): Boolean { + return canBuild(location.blockX, location.blockZ) && super.setBlock(location, block) + } + + override fun setBiome(coord: Vector2D, biome: BaseBiome): Boolean { + return canBuild(coord.blockX, coord.blockZ) && super.setBiome(coord, biome) + } + + } + + companion object { + fun register(parcels: ParcelsPlugin, worldEditPlugin: Plugin) { + if (worldEditPlugin !is WorldEditPlugin) return + val worldEdit = worldEditPlugin.worldEdit + val listener = WorldEditListener(parcels, worldEdit) + worldEdit.eventBus.register(listener) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt index 6bef483..3875467 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt @@ -1,11 +1,11 @@ package io.dico.parcels2.storage import io.dico.parcels2.* -import kotlinx.coroutines.experimental.CoroutineDispatcher -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.Job -import kotlinx.coroutines.experimental.channels.ReceiveChannel -import kotlinx.coroutines.experimental.channels.SendChannel +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.SendChannel import org.joda.time.DateTime import java.util.UUID diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt index a0e94e0..7b04bce 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt @@ -3,11 +3,11 @@ package io.dico.parcels2.storage import io.dico.parcels2.* -import kotlinx.coroutines.experimental.Deferred -import kotlinx.coroutines.experimental.Job -import kotlinx.coroutines.experimental.channels.ReceiveChannel -import kotlinx.coroutines.experimental.channels.SendChannel -import kotlinx.coroutines.experimental.launch +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.launch import org.joda.time.DateTime import java.util.UUID diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt index 8cd2804..39a32e0 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt @@ -8,14 +8,14 @@ import io.dico.parcels2.PlayerProfile.Star.name import io.dico.parcels2.storage.AddedDataPair import io.dico.parcels2.storage.Backing import io.dico.parcels2.storage.DataPair -import io.dico.parcels2.util.synchronized +import io.dico.parcels2.util.ext.synchronized import io.dico.parcels2.util.toByteArray import io.dico.parcels2.util.toUUID -import kotlinx.coroutines.experimental.* -import kotlinx.coroutines.experimental.channels.ArrayChannel -import kotlinx.coroutines.experimental.channels.LinkedListChannel -import kotlinx.coroutines.experimental.channels.ReceiveChannel -import kotlinx.coroutines.experimental.channels.SendChannel +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.ArrayChannel +import kotlinx.coroutines.channels.LinkedListChannel +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.SendChannel import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SchemaUtils.create import org.jetbrains.exposed.sql.transactions.transaction diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt index d8315fd..afbaa6e 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt @@ -153,6 +153,13 @@ object ProfilesT : IdTransactionsTable("parcels_profil return getItem(id) as? PlayerProfile.Real } + /* + fun updatePlayerProfile(profile: PlayerProfile.Real) { + update({ uuid eq profile.uuid.toByteArray() }) { + it[name] = profile.nameOrBukkitName + } + }*/ + } // val ParcelsWithOptionsT = ParcelsT.join(ParcelOptionsT, JoinType.INNER, onColumn = ParcelsT.id, otherColumn = ParcelOptionsT.parcel_id) \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt index bbf6872..c976f69 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt @@ -3,7 +3,7 @@ package io.dico.parcels2.storage.exposed import io.dico.parcels2.* -import kotlinx.coroutines.experimental.channels.SendChannel +import kotlinx.coroutines.channels.SendChannel import org.jetbrains.exposed.sql.* import java.util.UUID diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt index 0db669a..acc7c5e 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt @@ -1,7 +1,7 @@ package io.dico.parcels2.storage.migration import io.dico.parcels2.storage.Storage -import kotlinx.coroutines.experimental.Job +import kotlinx.coroutines.Job interface Migration { fun migrateTo(storage: Storage): Job diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt index 9921268..3fae57f 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt @@ -10,9 +10,9 @@ import io.dico.parcels2.storage.exposed.abs import io.dico.parcels2.storage.exposed.greater import io.dico.parcels2.storage.migration.Migration import io.dico.parcels2.util.toUUID -import kotlinx.coroutines.experimental.Job -import kotlinx.coroutines.experimental.launch -import kotlinx.coroutines.experimental.newFixedThreadPoolContext +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.newFixedThreadPoolContext import org.jetbrains.exposed.sql.* import org.slf4j.LoggerFactory import java.sql.Blob diff --git a/src/main/kotlin/io/dico/parcels2/util/FunctionHelper.kt b/src/main/kotlin/io/dico/parcels2/util/FunctionHelper.kt deleted file mode 100644 index ea16652..0000000 --- a/src/main/kotlin/io/dico/parcels2/util/FunctionHelper.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.dico.parcels2.util - -import io.dico.parcels2.ParcelsPlugin -import kotlinx.coroutines.experimental.* -import org.bukkit.scheduler.BukkitTask -import kotlin.coroutines.experimental.CoroutineContext - -@Suppress("NOTHING_TO_INLINE") -class FunctionHelper(val plugin: ParcelsPlugin) { - val mainThreadDispatcher: MainThreadDispatcher = MainThreadDispatcherImpl() - - fun deferLazilyOnMainThread(block: suspend CoroutineScope.() -> T): Deferred { - return async(context = mainThreadDispatcher, start = CoroutineStart.LAZY, block = block) - } - - fun deferUndispatchedOnMainThread(block: suspend CoroutineScope.() -> T): Deferred { - return async(context = mainThreadDispatcher, start = CoroutineStart.UNDISPATCHED, block = block) - } - - fun launchLazilyOnMainThread(block: suspend CoroutineScope.() -> Unit): Job { - return launch(context = mainThreadDispatcher, start = CoroutineStart.LAZY, block = block) - } - - inline fun schedule(noinline task: () -> Unit) = schedule(0, task) - - fun schedule(delay: Int, task: () -> Unit): BukkitTask { - return plugin.server.scheduler.runTaskLater(plugin, task, delay.toLong()) - } - - fun scheduleRepeating(delay: Int, interval: Int, task: () -> Unit): BukkitTask { - return plugin.server.scheduler.runTaskTimer(plugin, task, delay.toLong(), interval.toLong()) - } - - abstract class MainThreadDispatcher : CoroutineDispatcher() { - abstract val mainThread: Thread - abstract fun runOnMainThread(task: Runnable) - } - - private inner class MainThreadDispatcherImpl : MainThreadDispatcher() { - override val mainThread: Thread = Thread.currentThread() - - override fun dispatch(context: CoroutineContext, block: Runnable) { - runOnMainThread(block) - } - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun runOnMainThread(task: Runnable) { - if (Thread.currentThread() === mainThread) task.run() - else plugin.server.scheduler.runTaskLater(plugin, task, 0) - } - } - -} diff --git a/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt b/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt new file mode 100644 index 0000000..b1d18ab --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt @@ -0,0 +1,31 @@ +package io.dico.parcels2.util + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Runnable +import org.bukkit.plugin.Plugin +import kotlin.coroutines.CoroutineContext + +abstract class MainThreadDispatcher : CoroutineDispatcher() { + abstract val mainThread: Thread + abstract fun runOnMainThread(task: Runnable) +} + +@Suppress("FunctionName") +fun MainThreadDispatcher(plugin: Plugin): MainThreadDispatcher { + return object : MainThreadDispatcher() { + override val mainThread: Thread = Thread.currentThread() + + override fun dispatch(context: CoroutineContext, block: Runnable) { + doDispatch(block) + } + + override fun runOnMainThread(task: Runnable) { + doDispatch(task) + } + + private fun doDispatch(task: Runnable) { + if (Thread.currentThread() === mainThread) task.run() + else plugin.server.scheduler.runTaskLater(plugin, task, 0) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt deleted file mode 100644 index fadd722..0000000 --- a/src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt +++ /dev/null @@ -1,108 +0,0 @@ -package io.dico.parcels2.util - -import org.bukkit.Material -import org.bukkit.Material.* - -/* -colors: -WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$, -wood: -OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$, - */ - -val Material.isBed - get() = when (this) { - WHITE_BED, - ORANGE_BED, - MAGENTA_BED, - LIGHT_BLUE_BED, - YELLOW_BED, - LIME_BED, - PINK_BED, - GRAY_BED, - LIGHT_GRAY_BED, - CYAN_BED, - PURPLE_BED, - BLUE_BED, - BROWN_BED, - GREEN_BED, - RED_BED, - BLACK_BED -> true - else -> false - } - -val Material.isWoodDoor - get() = when (this) { - OAK_DOOR, - BIRCH_DOOR, - SPRUCE_DOOR, - JUNGLE_DOOR, - ACACIA_DOOR, - DARK_OAK_DOOR -> true - else -> false - } - -val Material.isWoodTrapdoor - get() = when (this) { - OAK_TRAPDOOR, - BIRCH_TRAPDOOR, - SPRUCE_TRAPDOOR, - JUNGLE_TRAPDOOR, - ACACIA_TRAPDOOR, - DARK_OAK_TRAPDOOR -> true - else -> false - } - -val Material.isWoodFenceGate - get() = when (this) { - OAK_FENCE_GATE, - BIRCH_FENCE_GATE, - SPRUCE_FENCE_GATE, - JUNGLE_FENCE_GATE, - ACACIA_FENCE_GATE, - DARK_OAK_FENCE_GATE -> true - else -> false - } - -val Material.isWoodButton - get() = when (this) { - OAK_BUTTON, - BIRCH_BUTTON, - SPRUCE_BUTTON, - JUNGLE_BUTTON, - ACACIA_BUTTON, - DARK_OAK_BUTTON -> true - else -> false - } - -private fun getMaterialPrefixed(prefix: String, name: String): Material { - return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist") -} - -fun findWoodKindPrefixedMaterials(name: String) = arrayOf( - getMaterialPrefixed("OAK", name), - getMaterialPrefixed("BIRCH", name), - getMaterialPrefixed("SPRUCE", name), - getMaterialPrefixed("JUNGLE", name), - getMaterialPrefixed("ACACIA", name), - getMaterialPrefixed("DARK_OAK", name) -) - -fun findColorPrefixedMaterials(name: String) = arrayOf( - getMaterialPrefixed("WHITE", name), - getMaterialPrefixed("ORANGE", name), - getMaterialPrefixed("MAGENTA", name), - getMaterialPrefixed("LIGHT_BLUE", name), - getMaterialPrefixed("YELLOW", name), - getMaterialPrefixed("LIME", name), - getMaterialPrefixed("PINK", name), - getMaterialPrefixed("GRAY", name), - getMaterialPrefixed("LIGHT_GRAY", name), - getMaterialPrefixed("CYAN", name), - getMaterialPrefixed("PURPLE", name), - getMaterialPrefixed("BLUE", name), - getMaterialPrefixed("BROWN", name), - getMaterialPrefixed("GREEN", name), - getMaterialPrefixed("RED", name), - getMaterialPrefixed("BLACK", name) -) \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt deleted file mode 100644 index 24f9401..0000000 --- a/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt +++ /dev/null @@ -1,72 +0,0 @@ -package io.dico.parcels2.util - -import io.dico.parcels2.logger -import java.io.File - -fun File.tryCreate(): Boolean { - if (exists()) { - return !isDirectory - } - val parent = parentFile - if (parent == null || !(parent.exists() || parent.mkdirs()) || !createNewFile()) { - logger.warn("Failed to create file $canonicalPath") - return false - } - return true -} - -inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() } -inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() } - -inline fun Any.synchronized(block: () -> R): R = synchronized(this, block) - -inline fun T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition() -inline fun T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition() -inline fun T?.ifNullRun(block: () -> Unit): T? { - if (this == null) block() - return this -} - -inline fun MutableMap.editLoop(block: EditLoopScope.(T, U) -> Unit) { - return EditLoopScope(this).doEditLoop(block) -} - -inline fun MutableMap.editLoop(block: EditLoopScope.() -> Unit) { - return EditLoopScope(this).doEditLoop(block) -} - -class EditLoopScope(val _map: MutableMap) { - private var iterator: MutableIterator>? = null - lateinit var _entry: MutableMap.MutableEntry - - inline val key get() = _entry.key - inline var value - get() = _entry.value - set(target) = run { _entry.setValue(target) } - - inline fun doEditLoop(block: EditLoopScope.() -> Unit) { - val it = _initIterator() - while (it.hasNext()) { - _entry = it.next() - block() - } - } - - inline fun doEditLoop(block: EditLoopScope.(T, U) -> Unit) { - val it = _initIterator() - while (it.hasNext()) { - val entry = it.next().also { _entry = it } - block(entry.key, entry.value) - } - } - - fun remove() { - iterator!!.remove() - } - - fun _initIterator(): MutableIterator> { - iterator?.let { throw IllegalStateException() } - return _map.entries.iterator().also { iterator = it } - } - -} diff --git a/src/main/kotlin/io/dico/parcels2/util/NumberExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/NumberExtensions.kt deleted file mode 100644 index 214a797..0000000 --- a/src/main/kotlin/io/dico/parcels2/util/NumberExtensions.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.dico.parcels2.util - -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/util/PlayerExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt deleted file mode 100644 index 9604365..0000000 --- a/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.dico.parcels2.util - -import io.dico.dicore.Formatting -import io.dico.parcels2.ParcelsPlugin -import io.dico.parcels2.PlayerProfile -import io.dico.parcels2.logger -import org.bukkit.OfflinePlayer -import org.bukkit.entity.Player -import org.bukkit.plugin.java.JavaPlugin - -inline val OfflinePlayer.uuid get() = uniqueId - -@Suppress("UsePropertyAccessSyntax") -inline val OfflinePlayer.isValid - get() = isOnline() || hasPlayedBefore() - -inline val Player.hasBanBypass get() = hasPermission("parcels.admin.bypass.ban") -inline val Player.hasGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode") -inline val Player.hasBuildAnywhere get() = hasPermission("parcels.admin.bypass.build") -inline val Player.hasAdminManage get() = hasPermission("parcels.admin.manage") -inline val Player.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others") -inline val Player.hasRandomSpecific get() = hasPermission("parcels.command.random.specific") -val Player.parcelLimit: Int - get() { - for (info in effectivePermissions) { - val perm = info.permission - if (perm.startsWith("parcels.limit.")) { - val limitString = perm.substring("parcels.limit.".length) - if (limitString == "*") { - return Int.MAX_VALUE - } - return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also { - logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).") - } - } - } - return DEFAULT_LIMIT - } - -private const val DEFAULT_LIMIT = 1 -private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a") - -fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) { - if (except) { - sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message)) - } else if (nopermit) { - sendMessage(prefix + Formatting.RED + Formatting.translateChars('&', message)) - } else { - sendMessage(prefix + Formatting.translateChars('&', message)) - } -} \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt b/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt new file mode 100644 index 0000000..f29ba2b --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt @@ -0,0 +1,20 @@ +package io.dico.parcels2.util + +import org.bukkit.plugin.Plugin +import org.bukkit.scheduler.BukkitTask + +interface PluginScheduler { + val plugin: Plugin + + fun schedule(delay: Int, task: () -> Unit): BukkitTask { + return plugin.server.scheduler.runTaskLater(plugin, task, delay.toLong()) + } + + fun scheduleRepeating(delay: Int, interval: Int, task: () -> Unit): BukkitTask { + return plugin.server.scheduler.runTaskTimer(plugin, task, delay.toLong(), interval.toLong()) + } +} + +@Suppress("NOTHING_TO_INLINE") +inline fun PluginScheduler.schedule(noinline task: () -> Unit) = schedule(0, task) + diff --git a/src/main/kotlin/io/dico/parcels2/util/UUIDUtil.kt b/src/main/kotlin/io/dico/parcels2/util/UUIDUtil.kt index bca2428..268c1b9 100644 --- a/src/main/kotlin/io/dico/parcels2/util/UUIDUtil.kt +++ b/src/main/kotlin/io/dico/parcels2/util/UUIDUtil.kt @@ -1,16 +1,11 @@ package io.dico.parcels2.util +import io.dico.parcels2.util.ext.isValid import org.bukkit.Bukkit import java.nio.ByteBuffer import java.util.UUID -@Suppress("UsePropertyAccessSyntax") -fun getPlayerNameOrDefault(uuid: UUID?, ifUnknown: String? = null): String { - return uuid - ?.let { getPlayerName(it) } - ?: ifUnknown - ?: ":unknown_name:" -} +const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:" fun getPlayerName(uuid: UUID): String? { return Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isValid }?.name diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt new file mode 100644 index 0000000..c375cb2 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt @@ -0,0 +1,108 @@ +package io.dico.parcels2.util.ext + +import org.bukkit.Material +import org.bukkit.Material.* + +/* +colors: +WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$, +wood: +OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$, + */ + +val Material.isBed + get() = when (this) { + WHITE_BED, + ORANGE_BED, + MAGENTA_BED, + LIGHT_BLUE_BED, + YELLOW_BED, + LIME_BED, + PINK_BED, + GRAY_BED, + LIGHT_GRAY_BED, + CYAN_BED, + PURPLE_BED, + BLUE_BED, + BROWN_BED, + GREEN_BED, + RED_BED, + BLACK_BED -> true + else -> false + } + +val Material.isWoodDoor + get() = when (this) { + OAK_DOOR, + BIRCH_DOOR, + SPRUCE_DOOR, + JUNGLE_DOOR, + ACACIA_DOOR, + DARK_OAK_DOOR -> true + else -> false + } + +val Material.isWoodTrapdoor + get() = when (this) { + OAK_TRAPDOOR, + BIRCH_TRAPDOOR, + SPRUCE_TRAPDOOR, + JUNGLE_TRAPDOOR, + ACACIA_TRAPDOOR, + DARK_OAK_TRAPDOOR -> true + else -> false + } + +val Material.isWoodFenceGate + get() = when (this) { + OAK_FENCE_GATE, + BIRCH_FENCE_GATE, + SPRUCE_FENCE_GATE, + JUNGLE_FENCE_GATE, + ACACIA_FENCE_GATE, + DARK_OAK_FENCE_GATE -> true + else -> false + } + +val Material.isWoodButton + get() = when (this) { + OAK_BUTTON, + BIRCH_BUTTON, + SPRUCE_BUTTON, + JUNGLE_BUTTON, + ACACIA_BUTTON, + DARK_OAK_BUTTON -> true + else -> false + } + +private fun getMaterialPrefixed(prefix: String, name: String): Material { + return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist") +} + +fun findWoodKindPrefixedMaterials(name: String) = arrayOf( + getMaterialPrefixed("OAK", name), + getMaterialPrefixed("BIRCH", name), + getMaterialPrefixed("SPRUCE", name), + getMaterialPrefixed("JUNGLE", name), + getMaterialPrefixed("ACACIA", name), + getMaterialPrefixed("DARK_OAK", name) +) + +fun findColorPrefixedMaterials(name: String) = arrayOf( + getMaterialPrefixed("WHITE", name), + getMaterialPrefixed("ORANGE", name), + getMaterialPrefixed("MAGENTA", name), + getMaterialPrefixed("LIGHT_BLUE", name), + getMaterialPrefixed("YELLOW", name), + getMaterialPrefixed("LIME", name), + getMaterialPrefixed("PINK", name), + getMaterialPrefixed("GRAY", name), + getMaterialPrefixed("LIGHT_GRAY", name), + getMaterialPrefixed("CYAN", name), + getMaterialPrefixed("PURPLE", name), + getMaterialPrefixed("BLUE", name), + getMaterialPrefixed("BROWN", name), + getMaterialPrefixed("GREEN", name), + getMaterialPrefixed("RED", name), + getMaterialPrefixed("BLACK", name) +) \ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Math.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Math.kt new file mode 100644 index 0000000..32540fd --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/util/ext/Math.kt @@ -0,0 +1,32 @@ +package io.dico.parcels2.util.ext + +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/util/ext/Misc.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt new file mode 100644 index 0000000..d7feabf --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt @@ -0,0 +1,72 @@ +package io.dico.parcels2.util.ext + +import io.dico.parcels2.logger +import java.io.File + +fun File.tryCreate(): Boolean { + if (exists()) { + return !isDirectory + } + val parent = parentFile + if (parent == null || !(parent.exists() || parent.mkdirs()) || !createNewFile()) { + logger.warn("Failed to create file $canonicalPath") + return false + } + return true +} + +inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() } +inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() } + +inline fun Any.synchronized(block: () -> R): R = synchronized(this, block) + +inline fun T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition() +inline fun T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition() +inline fun T?.ifNullRun(block: () -> Unit): T? { + if (this == null) block() + return this +} + +inline fun MutableMap.editLoop(block: EditLoopScope.(T, U) -> Unit) { + return EditLoopScope(this).doEditLoop(block) +} + +inline fun MutableMap.editLoop(block: EditLoopScope.() -> Unit) { + return EditLoopScope(this).doEditLoop(block) +} + +class EditLoopScope(val _map: MutableMap) { + private var iterator: MutableIterator>? = null + lateinit var _entry: MutableMap.MutableEntry + + inline val key get() = _entry.key + inline var value + get() = _entry.value + set(target) = run { _entry.setValue(target) } + + inline fun doEditLoop(block: EditLoopScope.() -> Unit) { + val it = _initIterator() + while (it.hasNext()) { + _entry = it.next() + block() + } + } + + inline fun doEditLoop(block: EditLoopScope.(T, U) -> Unit) { + val it = _initIterator() + while (it.hasNext()) { + val entry = it.next().also { _entry = it } + block(entry.key, entry.value) + } + } + + fun remove() { + iterator!!.remove() + } + + fun _initIterator(): MutableIterator> { + iterator?.let { throw IllegalStateException() } + return _map.entries.iterator().also { iterator = it } + } + +} diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt new file mode 100644 index 0000000..38402f0 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt @@ -0,0 +1,50 @@ +package io.dico.parcels2.util.ext + +import io.dico.dicore.Formatting +import io.dico.parcels2.ParcelsPlugin +import io.dico.parcels2.logger +import org.bukkit.OfflinePlayer +import org.bukkit.entity.Player +import org.bukkit.plugin.java.JavaPlugin + +inline val OfflinePlayer.uuid get() = uniqueId + +@Suppress("UsePropertyAccessSyntax") +inline val OfflinePlayer.isValid + get() = isOnline() || hasPlayedBefore() + +inline val Player.hasBanBypass get() = hasPermission("parcels.admin.bypass.ban") +inline val Player.hasGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode") +inline val Player.hasBuildAnywhere get() = hasPermission("parcels.admin.bypass.build") +inline val Player.hasAdminManage get() = hasPermission("parcels.admin.manage") +inline val Player.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others") +inline val Player.hasRandomSpecific get() = hasPermission("parcels.command.random.specific") +val Player.parcelLimit: Int + get() { + for (info in effectivePermissions) { + val perm = info.permission + if (perm.startsWith("parcels.limit.")) { + val limitString = perm.substring("parcels.limit.".length) + if (limitString == "*") { + return Int.MAX_VALUE + } + return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also { + logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).") + } + } + } + return DEFAULT_LIMIT + } + +private const val DEFAULT_LIMIT = 1 +private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a") + +fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) { + if (except) { + sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message)) + } else if (nopermit) { + sendMessage(prefix + Formatting.RED + Formatting.translateChars('&', message)) + } else { + sendMessage(prefix + Formatting.translateChars('&', message)) + } +} \ No newline at end of file diff --git a/todo.md b/todo.md index 541aecf..c04937a 100644 --- a/todo.md +++ b/todo.md @@ -3,18 +3,18 @@ Commands - Basically all admin commands. -* setowner -* dispose -* reset +* ~~setowner~~ +* ~~dispose~~ +* ~~reset~~ * swap * New admin commands that I can't think of right now. Also -* setbiome +* ~~setbiome~~ * random Modify home command: -* Make `:` not be required if prior component cannot be parsed to an int +* ~~Make `:` not be required if prior component cannot be parsed to an int~~ * Listen for command events that use plotme-style argument, and transform the command ~~Add permissions to commands (replace or fix `IContextFilter` from command lib @@ -61,7 +61,7 @@ Prevent block spreading subject to conditions. Scan through blocks that were added since original Parcels implementation, that might introduce things that need to be checked or listened for. -WorldEdit Listener. +~~WorldEdit Listener.~~ Limit number of beacons in a parcel and/or avoid potion effects being applied outside the parcel. @@ -77,6 +77,6 @@ Use an atomic GET OR INSERT query so that parallel execution doesn't cause probl Implement a container that doesn't require loading all parcel data on startup (Complex). -Update player profiles in the database on join to account for name changes. +~~Update player profiles in the database on join to account for name changes.~~ -- cgit v1.2.3