Archived
0

Work down some todo items, update to kotlin 1.3-rc

This commit is contained in:
Dico
2018-09-23 06:34:03 +01:00
parent f499555f8b
commit 038e698a14
46 changed files with 553 additions and 264 deletions

View File

@@ -14,12 +14,10 @@ version = "0.2"
plugins { plugins {
java 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" id("com.github.johnrengelman.plugin-shadow") version "2.0.3"
} }
kotlin.experimental.coroutines = ENABLE
allprojects { allprojects {
apply<JavaPlugin>() apply<JavaPlugin>()
@@ -28,6 +26,8 @@ allprojects {
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots")
maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-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/exposed")
maven("https://dl.bintray.com/kotlin/kotlin-eap")
maven("https://dl.bintray.com/kotlin/kotlinx/")
} }
dependencies { dependencies {
@@ -51,7 +51,7 @@ project(":dicore3:dicore3-command") {
dependencies { dependencies {
c.kotlinStd(kotlin("stdlib-jdk8")) c.kotlinStd(kotlin("stdlib-jdk8"))
c.kotlinStd(kotlin("reflect")) 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(project(":dicore3:dicore3-core"))
compile("com.thoughtworks.paranamer:paranamer:2.8") compile("com.thoughtworks.paranamer:paranamer:2.8")
@@ -59,17 +59,19 @@ project(":dicore3:dicore3-command") {
} }
} }
dependencies { dependencies {
compile(project(":dicore3:dicore3-core")) compile(project(":dicore3:dicore3-core"))
compile(project(":dicore3:dicore3-command")) compile(project(":dicore3:dicore3-command"))
c.kotlinStd(kotlin("stdlib-jdk8")) c.kotlinStd(kotlin("stdlib-jdk8"))
c.kotlinStd(kotlin("reflect")) c.kotlinStd(kotlin("reflect"))
c.kotlinStd(kotlinx("coroutines-core:0.23.4")) c.kotlinStd(kotlinx("coroutines-core:0.26.1-eap13"))
c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.0") c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.7-rc-conf")
compile("org.jetbrains.exposed:exposed:0.10.3") { isTransitive = false } // 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.5") { isTransitive = false }
compile("joda-time:joda-time:2.10") compile("joda-time:joda-time:2.10")
compile("com.zaxxer:HikariCP:3.2.0") compile("com.zaxxer:HikariCP:3.2.0")
compile("ch.qos.logback:logback-classic:1.2.3") { isTransitive = false } compile("ch.qos.logback:logback-classic:1.2.3") { isTransitive = false }
@@ -131,6 +133,7 @@ tasks {
} }
val createDebugServer by creating { val createDebugServer by creating {
// todo
val jarUrl = URL("https://yivesmirror.com/files/spigot/spigot-latest.jar") val jarUrl = URL("https://yivesmirror.com/files/spigot/spigot-latest.jar")
val serverJarFile = file("$serverDir/lib/spigot.jar") val serverJarFile = file("$serverDir/lib/spigot.jar")

View File

@@ -200,6 +200,15 @@ public class ExecutionContext {
return originalBuffer.getArrayFromIndex(cursorStart); 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) { public Formatting getFormat(EMessageType type) {
return address.getChatController().getChatFormatForType(type); return address.getChatController().getChatFormatForType(type);
} }

View File

@@ -12,6 +12,11 @@ public interface ICommandReceiver {
Plugin getPlugin(); Plugin getPlugin();
// type is CoroutineContext, but we avoid referring to Kotlin runtime here
default Object getCoroutineContext() {
return null;
}
} }
} }

View File

@@ -4,15 +4,15 @@ import io.dico.dicore.command.CommandException
import io.dico.dicore.command.EMessageType import io.dico.dicore.command.EMessageType
import io.dico.dicore.command.ExecutionContext import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.ICommandReceiver import io.dico.dicore.command.ICommandReceiver
import kotlinx.coroutines.experimental.CoroutineStart.UNDISPATCHED import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
import kotlinx.coroutines.experimental.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.experimental.asCoroutineDispatcher import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.experimental.async import kotlinx.coroutines.async
import java.lang.reflect.Method import java.lang.reflect.Method
import java.util.*
import java.util.concurrent.CancellationException import java.util.concurrent.CancellationException
import java.util.concurrent.Executor import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn import kotlin.coroutines.intrinsics.intercepted
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
import kotlin.reflect.jvm.kotlinFunction import kotlin.reflect.jvm.kotlinFunction
fun isSuspendFunction(method: Method): Boolean { fun isSuspendFunction(method: Method): Boolean {
@@ -20,16 +20,21 @@ fun isSuspendFunction(method: Method): Boolean {
return func.isSuspend return func.isSuspend
} }
fun callAsCoroutine(command: ReflectiveCommand, fun callAsCoroutine(
command: ReflectiveCommand,
factory: ICommandReceiver.Factory, factory: ICommandReceiver.Factory,
context: ExecutionContext, context: ExecutionContext,
args: Array<Any?>): String? { args: Array<Any?>
val dispatcher = Executor { task -> factory.plugin.server.scheduler.runTask(factory.plugin, task) }.asCoroutineDispatcher() ): String? {
// UNDISPATCHED causes the handler to run until the first suspension point on the current thread, // 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. // 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. // 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<Any?> { cont ->
command.method.invoke(command.instance, *args, cont.intercepted())
}
}
if (job.isCompleted) { if (job.isCompleted) {
return job.getResult() return job.getResult()
@@ -48,12 +53,6 @@ fun callAsCoroutine(command: ReflectiveCommand,
return null return null
} }
private suspend fun Method.invokeSuspend(instance: Any?, args: Array<Any?>): Any? {
return suspendCoroutineOrReturn { cont ->
invoke(instance, *args, cont)
}
}
@Throws(CommandException::class) @Throws(CommandException::class)
private fun Deferred<Any?>.getResult(): String? { private fun Deferred<Any?>.getResult(): String? {
getCompletionExceptionOrNull()?.let { ex -> getCompletionExceptionOrNull()?.let { ex ->

1
gradle.properties Normal file
View File

@@ -0,0 +1 @@
kotlin.code.style=official

View File

@@ -1,3 +1,9 @@
pluginManagement.repositories {
maven("http://dl.bintray.com/kotlin/kotlin-eap")
mavenCentral()
maven("https://plugins.gradle.org/m2/")
}
rootProject.name = "parcels2" rootProject.name = "parcels2"
include("dicore3:core") include("dicore3:core")

View File

@@ -1,6 +1,6 @@
package io.dico.parcels2 package io.dico.parcels2
import io.dico.parcels2.util.findWoodKindPrefixedMaterials import io.dico.parcels2.util.ext.findWoodKindPrefixedMaterials
import org.bukkit.Material import org.bukkit.Material
import java.util.EnumMap import java.util.EnumMap
@@ -28,7 +28,8 @@ private constructor(val id: Int,
arrayOf( arrayOf(
Interactables(id++, "button", true, Interactables(id++, "button", true,
Material.STONE_BUTTON, Material.STONE_BUTTON,
*findWoodKindPrefixedMaterials("BUTTON")), *findWoodKindPrefixedMaterials("BUTTON")
),
Interactables(id++, "lever", true, Interactables(id++, "lever", true,
Material.LEVER), Material.LEVER),

View File

@@ -1,7 +1,7 @@
package io.dico.parcels2 package io.dico.parcels2
import io.dico.parcels2.util.Vec2i 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.Location
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player import org.bukkit.entity.Player
@@ -32,6 +32,8 @@ interface Parcel : ParcelData {
fun dispose() fun dispose()
suspend fun withBlockVisitorPermit(block: suspend () -> Unit)
val homeLocation: Location get() = world.blockManager.getHomeLocation(id) val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
} }

View File

@@ -7,6 +7,7 @@ import io.dico.parcels2.blockvisitor.WorktimeLimiter
import io.dico.parcels2.util.Region import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.get import io.dico.parcels2.util.get
import kotlinx.coroutines.CoroutineScope
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.World import org.bukkit.World
@@ -38,6 +39,7 @@ abstract class ParcelGenerator : ChunkGenerator() {
abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId, abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId,
container: ParcelContainer, container: ParcelContainer,
coroutineScope: CoroutineScope,
worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager> worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager>
} }

View File

@@ -22,6 +22,8 @@ interface ParcelWorldId {
} }
} }
fun ParcelWorldId.toStringExt() = "ParcelWorld($name)"
/** /**
* Used by storage backing options to encompass the location of a parcel * Used by storage backing options to encompass the location of a parcel
* Does NOT support equality operator. * Does NOT support equality operator.
@@ -31,6 +33,7 @@ interface ParcelId {
val x: Int val x: Int
val z: Int val z: Int
val pos: Vec2i get() = Vec2i(x, z) val pos: Vec2i get() = Vec2i(x, z)
val idString get() = "$x,$z"
fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId) fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId)
companion object { companion object {
@@ -41,9 +44,15 @@ interface ParcelId {
} }
} }
fun ParcelId.toStringExt() = "Parcel(${worldId.name},$idString)"
private class ParcelWorldIdImpl(override val name: String, 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, private class ParcelIdImpl(override val worldId: ParcelWorldId,
override val x: Int, override val x: Int,
override val z: Int) : ParcelId override val z: Int) : ParcelId {
override fun toString() = toStringExt()
}

View File

@@ -3,8 +3,7 @@ package io.dico.parcels2
import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.options.RuntimeWorldOptions
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.floor import io.dico.parcels2.util.ext.floor
import org.bukkit.Chunk
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.World import org.bukkit.World
import org.bukkit.block.Block import org.bukkit.block.Block
@@ -70,6 +69,8 @@ interface ParcelContainer {
fun getParcelById(id: Vec2i): Parcel? = getParcelById(id.x, id.z) fun getParcelById(id: Vec2i): Parcel? = getParcelById(id.x, id.z)
fun getParcelById(id: ParcelId): Parcel? = getParcelById(id.x, id.z)
fun nextEmptyParcel(): Parcel? fun nextEmptyParcel(): Parcel?
} }

View File

@@ -10,23 +10,27 @@ import io.dico.parcels2.defaultimpl.GlobalAddedDataManagerImpl
import io.dico.parcels2.defaultimpl.ParcelProviderImpl import io.dico.parcels2.defaultimpl.ParcelProviderImpl
import io.dico.parcels2.listener.ParcelEntityTracker import io.dico.parcels2.listener.ParcelEntityTracker
import io.dico.parcels2.listener.ParcelListeners import io.dico.parcels2.listener.ParcelListeners
import io.dico.parcels2.listener.WorldEditListener
import io.dico.parcels2.options.Options import io.dico.parcels2.options.Options
import io.dico.parcels2.options.optionsMapper import io.dico.parcels2.options.optionsMapper
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.FunctionHelper import io.dico.parcels2.util.MainThreadDispatcher
import io.dico.parcels2.util.tryCreate import io.dico.parcels2.util.PluginScheduler
import io.dico.parcels2.util.ext.tryCreate
import kotlinx.coroutines.CoroutineScope
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.generator.ChunkGenerator import org.bukkit.generator.ChunkGenerator
import org.bukkit.plugin.Plugin
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
import java.util.Random import kotlin.coroutines.CoroutineContext
val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin") val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin")
private inline val plogger get() = logger private inline val plogger get() = logger
class ParcelsPlugin : JavaPlugin() { class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
lateinit var optionsFile: File; private set lateinit var optionsFile: File; private set
lateinit var options: Options; private set lateinit var options: Options; private set
lateinit var parcelProvider: ParcelProvider; private set lateinit var parcelProvider: ParcelProvider; private set
@@ -38,7 +42,8 @@ class ParcelsPlugin : JavaPlugin() {
private var listeners: ParcelListeners? = null private var listeners: ParcelListeners? = null
private var cmdDispatcher: ICommandDispatcher? = 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) } val worktimeLimiter: WorktimeLimiter by lazy { TickWorktimeLimiter(this, options.tickWorktime) }
override fun onEnable() { override fun onEnable() {
@@ -135,9 +140,14 @@ class ParcelsPlugin : JavaPlugin() {
if (listeners == null) { if (listeners == null) {
listeners = ParcelListeners(parcelProvider, entityTracker, storage) listeners = ParcelListeners(parcelProvider, entityTracker, storage)
registrator.registerListeners(listeners!!) 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)
} }
} }

View File

@@ -3,12 +3,13 @@
package io.dico.parcels2 package io.dico.parcels2
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.getPlayerNameOrDefault import io.dico.parcels2.util.PLAYER_NAME_PLACEHOLDER
import io.dico.parcels2.util.isValid import io.dico.parcels2.util.getPlayerName
import io.dico.parcels2.util.uuid import io.dico.parcels2.util.ext.isValid
import kotlinx.coroutines.experimental.Deferred import io.dico.parcels2.util.ext.uuid
import kotlinx.coroutines.experimental.Unconfined import kotlinx.coroutines.Deferred
import kotlinx.coroutines.experimental.async import kotlinx.coroutines.Unconfined
import kotlinx.coroutines.async
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import java.util.UUID import java.util.UUID
@@ -18,7 +19,7 @@ interface PlayerProfile {
val name: String? val name: String?
val nameOrBukkitName: String? val nameOrBukkitName: String?
val notNullName: String val notNullName: String
val isStar: Boolean get() = false val isStar: Boolean get() = this is Star
val exists: Boolean get() = this is RealImpl val exists: Boolean get() = this is RealImpl
fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean
@@ -78,9 +79,9 @@ interface PlayerProfile {
override val uuid: UUID override val uuid: UUID
override val nameOrBukkitName: String? override val nameOrBukkitName: String?
// If a player is online, their name is prioritized to get name changes right immediately // 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 override val notNullName: String
get() = name ?: getPlayerNameOrDefault(uuid) get() = nameOrBukkitName ?: PLAYER_NAME_PLACEHOLDER
val player: OfflinePlayer? get() = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid } val player: OfflinePlayer? get() = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }
val playerUnchecked: OfflinePlayer get() = Bukkit.getOfflinePlayer(uuid) val playerUnchecked: OfflinePlayer get() = Bukkit.getOfflinePlayer(uuid)
@@ -115,8 +116,8 @@ interface PlayerProfile {
object Star : BaseImpl(), Real { object Star : BaseImpl(), Real {
override val name: String = "*" override val name: String = "*"
// hopefully nobody will have this random UUID :)
override val uuid: UUID = UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1") 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 { override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
return true return true
@@ -148,7 +149,7 @@ interface PlayerProfile {
} }
suspend fun tryResolveSuspendedly(storage: Storage): Real? { 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 { fun resolve(uuid: UUID): Real {

View File

@@ -2,13 +2,11 @@ package io.dico.parcels2.blockvisitor
import io.dico.parcels2.util.Region import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec3i import io.dico.parcels2.util.Vec3i
import kotlin.coroutines.experimental.SequenceBuilder
import kotlin.coroutines.experimental.buildIterator
abstract class RegionTraverser { abstract class RegionTraverser {
fun traverseRegion(region: Region): Iterable<Vec3i> = Iterable { buildIterator { build(region) } } fun traverseRegion(region: Region): Iterable<Vec3i> = Iterable { iterator<Vec3i> { build(region) } }
protected abstract suspend fun SequenceBuilder<Vec3i>.build(region: Region) protected abstract suspend fun SequenceScope<Vec3i>.build(region: Region)
companion object { companion object {
val upward = create { traverseUpward(it) } val upward = create { traverseUpward(it) }
@@ -16,13 +14,13 @@ abstract class RegionTraverser {
val forClearing get() = downward val forClearing get() = downward
val forFilling get() = upward val forFilling get() = upward
inline fun create(crossinline builder: suspend SequenceBuilder<Vec3i>.(Region) -> Unit) = object : RegionTraverser() { inline fun create(crossinline builder: suspend SequenceScope<Vec3i>.(Region) -> Unit) = object : RegionTraverser() {
override suspend fun SequenceBuilder<Vec3i>.build(region: Region) { override suspend fun SequenceScope<Vec3i>.build(region: Region) {
builder(region) builder(region)
} }
} }
private suspend fun SequenceBuilder<Vec3i>.traverseDownward(region: Region) { private suspend fun SequenceScope<Vec3i>.traverseDownward(region: Region) {
val origin = region.origin val origin = region.origin
val size = region.size val size = region.size
repeat(size.y) { y -> repeat(size.y) { y ->
@@ -34,7 +32,7 @@ abstract class RegionTraverser {
} }
} }
private suspend fun SequenceBuilder<Vec3i>.traverseUpward(region: Region) { private suspend fun SequenceScope<Vec3i>.traverseUpward(region: Region) {
val origin = region.origin val origin = region.origin
val size = region.size val size = region.size
repeat(size.y) { y -> repeat(size.y) { y ->

View File

@@ -1,38 +1,42 @@
package io.dico.parcels2.blockvisitor package io.dico.parcels2.blockvisitor
import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.util.FunctionHelper import io.dico.parcels2.logger
import kotlinx.coroutines.experimental.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart.LAZY
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import java.lang.System.currentTimeMillis import java.lang.System.currentTimeMillis
import java.util.LinkedList import java.util.LinkedList
import java.util.logging.Level import kotlin.coroutines.Continuation
import kotlin.coroutines.experimental.Continuation import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
import kotlin.coroutines.experimental.intrinsics.suspendCoroutineUninterceptedOrReturn import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
typealias TimeLimitedTask = suspend WorkerScope.() -> Unit typealias TimeLimitedTask = suspend WorkerScope.() -> Unit
typealias WorkerUpdateLister = Worker.(Double, Long) -> Unit typealias WorkerUpdateLister = Worker.(Double, Long) -> Unit
data class TickWorktimeOptions(var workTime: Int, var tickInterval: Int) 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 * Submit a [task] that should be run synchronously, but limited such that it does not stall the server
* a bunch * a bunch
*/ */
abstract fun submit(task: TimeLimitedTask): Worker fun submit(task: TimeLimitedTask): Worker
/** /**
* Get a list of all workers * Get a list of all workers
*/ */
abstract val workers: List<Worker> val workers: List<Worker>
/** /**
* Attempts to complete any remaining tasks immediately, without suspension. * Attempts to complete any remaining tasks immediately, without suspension.
*/ */
abstract fun completeAllTasks() fun completeAllTasks()
} }
interface Timed { interface Timed {
@@ -77,9 +81,14 @@ interface Worker : Timed {
/** /**
* Calls the given [block] when this worker completes, with the progress value 1.0. * 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 fun onCompleted(block: WorkerUpdateLister): Worker
/**
* Await completion of this worker
*/
suspend fun awaitCompletion()
} }
interface WorkerScope : Timed { 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 * 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 * 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 // The currently registered bukkit scheduler task
private var bukkitTask: BukkitTask? = null private var bukkitTask: BukkitTask? = null
// The workers. // The workers.
@@ -121,12 +130,12 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo
override val workers: List<Worker> = _workers override val workers: List<Worker> = _workers
override fun submit(task: TimeLimitedTask): Worker { override fun submit(task: TimeLimitedTask): Worker {
val worker: WorkerContinuation = WorkerImpl(plugin.functionHelper, task) val worker: WorkerContinuation = WorkerImpl(plugin, task)
if (bukkitTask == null) { if (bukkitTask == null) {
val completed = worker.resume(options.workTime.toLong()) val completed = worker.resume(options.workTime.toLong())
if (completed) return worker if (completed) return worker
bukkitTask = plugin.functionHelper.scheduleRepeating(0, options.tickInterval) { tickJobs() } bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickJobs() }
} }
_workers.addFirst(worker) _workers.addFirst(worker)
@@ -171,8 +180,10 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo
} }
private class WorkerImpl(val functionHelper: FunctionHelper, private class WorkerImpl(
val task: TimeLimitedTask) : WorkerContinuation { val scope: CoroutineScope,
val task: TimeLimitedTask
) : WorkerContinuation, CoroutineScope by scope {
override var job: Job? = null; private set override var job: Job? = null; private set
override val elapsedTime override val elapsedTime
@@ -204,27 +215,44 @@ private class WorkerImpl(val functionHelper: FunctionHelper,
// report any error that occurred // report any error that occurred
completionException = exception?.also { completionException = exception?.also {
if (it !is CancellationException) 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 // convert to elapsed time here
startTimeOrElapsedTime = System.currentTimeMillis() - startTimeOrElapsedTime startTimeOrElapsedTime = System.currentTimeMillis() - startTimeOrElapsedTime
onCompleted?.let { it(1.0, elapsedTime) } onCompleted?.let { it(1.0, elapsedTime) }
onCompleted = null
onProgressUpdate = { prog, el -> }
} }
} }
override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: WorkerUpdateLister): Worker { override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: WorkerUpdateLister): Worker {
onProgressUpdate?.let { throw IllegalStateException() } onProgressUpdate?.let { throw IllegalStateException() }
if (asCompletionListener) onCompleted(block)
if (isComplete) return this
onProgressUpdate = block onProgressUpdate = block
progressUpdateInterval = minInterval progressUpdateInterval = minInterval
lastUpdateTime = System.currentTimeMillis() + minDelay - minInterval lastUpdateTime = System.currentTimeMillis() + minDelay - minInterval
if (asCompletionListener) onCompleted(block)
return this return this
} }
override fun onCompleted(block: WorkerUpdateLister): Worker { override fun onCompleted(block: WorkerUpdateLister): Worker {
onCompleted?.let { throw IllegalStateException() } if (isComplete) {
onCompleted = block 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 return this
} }
@@ -265,7 +293,7 @@ private class WorkerImpl(val functionHelper: FunctionHelper,
} }
try { try {
val job = functionHelper.launchLazilyOnMainThread { task() } val job = launch(start = LAZY) { task() }
initJob(job = job) initJob(job = job)
job.start() job.start()
} catch (t: Throwable) { } catch (t: Throwable) {
@@ -275,6 +303,18 @@ private class WorkerImpl(val functionHelper: FunctionHelper,
return continuation == null 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) }
}
}
} }
/* /*

View File

@@ -1,26 +1,25 @@
package io.dico.parcels2.command package io.dico.parcels2.command
import io.dico.dicore.command.CommandException import io.dico.dicore.command.CommandException
import io.dico.dicore.command.EMessageType
import io.dico.dicore.command.ExecutionContext import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.ICommandReceiver import io.dico.dicore.command.ICommandReceiver
import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.ParcelWorld import io.dico.parcels2.ParcelWorld
import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.util.hasAdminManage import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.util.parcelLimit import io.dico.parcels2.util.ext.hasAdminManage
import io.dico.parcels2.util.ext.parcelLimit
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin import org.bukkit.plugin.Plugin
import java.lang.reflect.Method import java.lang.reflect.Method
abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandReceiver.Factory { abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandReceiver.Factory {
override fun getPlugin(): Plugin = plugin override fun getPlugin(): Plugin = plugin
override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver { override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver {
return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName) return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName)
} }
protected inline val worlds get() = plugin.parcelProvider
protected fun error(message: String): Nothing { protected fun error(message: String): Nothing {
throw CommandException(message) 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
} }

View File

@@ -4,7 +4,7 @@ import io.dico.dicore.command.Validate
import io.dico.dicore.command.annotation.Cmd import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.Desc import io.dico.dicore.command.annotation.Desc
import io.dico.parcels2.ParcelsPlugin 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.OfflinePlayer
import org.bukkit.entity.Player import org.bukkit.entity.Player

View File

@@ -1,7 +1,43 @@
package io.dico.parcels2.command 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.ParcelsPlugin
import io.dico.parcels2.PlayerProfile
class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { 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()
}
} }

View File

@@ -1,16 +1,18 @@
package io.dico.parcels2.command package io.dico.parcels2.command
import io.dico.dicore.command.EMessageType
import io.dico.dicore.command.ExecutionContext 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.Cmd
import io.dico.dicore.command.annotation.Desc import io.dico.dicore.command.annotation.Desc
import io.dico.dicore.command.annotation.Flag import io.dico.dicore.command.annotation.Flag
import io.dico.dicore.command.annotation.RequireParameters import io.dico.dicore.command.annotation.RequireParameters
import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.util.hasAdminManage import io.dico.parcels2.command.ParcelTarget.Kind
import io.dico.parcels2.util.hasParcelHomeOthers import io.dico.parcels2.util.ext.hasAdminManage
import io.dico.parcels2.util.uuid import io.dico.parcels2.util.ext.hasParcelHomeOthers
import io.dico.parcels2.util.ext.uuid
import org.bukkit.block.Biome
import org.bukkit.entity.Player import org.bukkit.entity.Player
class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@@ -43,17 +45,20 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
"more than one parcel", "more than one parcel",
shortVersion = "teleports you to parcels") shortVersion = "teleports you to parcels")
@RequireParameters(0) @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) return cmdGoto(player, target)
} }
@Cmd("tp", aliases = ["teleport"]) @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) return cmdGoto(player, target)
} }
@Cmd("goto") @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) { if (target is ParcelTarget.ByOwner) {
target.resolveOwner(plugin.storage) target.resolveOwner(plugin.storage)
if (!target.owner.matches(player) && !player.hasParcelHomeOthers) { if (!target.owner.matches(player) && !player.hasParcelHomeOthers) {
@@ -64,11 +69,12 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
val match = target.getParcelSuspend(plugin.storage) val match = target.getParcelSuspend(plugin.storage)
?: error("The specified parcel could not be matched") ?: error("The specified parcel could not be matched")
player.teleport(match.homeLocation) player.teleport(match.homeLocation)
return "" return null
} }
@Cmd("goto_fake") @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) return cmdGoto(player, target)
} }
@@ -97,23 +103,18 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("clear") @Cmd("clear")
@ParcelRequire(owner = true) @ParcelRequire(owner = true)
fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? { fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? {
if (!sure) return "Are you sure? You cannot undo this action!\n" + if (!sure) return areYouSureMessage(context)
"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))
}
clearWithProgressUpdates(context, "Clear")
return null return null
} }
@Cmd("swap") @Cmd("setbiome")
fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? { @ParcelRequire(owner = true)
TODO() 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"
} }
} }

View File

@@ -7,8 +7,8 @@ import io.dico.dicore.command.Validate
import io.dico.parcels2.Parcel import io.dico.parcels2.Parcel
import io.dico.parcels2.ParcelProvider import io.dico.parcels2.ParcelProvider
import io.dico.parcels2.ParcelWorld import io.dico.parcels2.ParcelWorld
import io.dico.parcels2.util.hasAdminManage import io.dico.parcels2.util.ext.hasAdminManage
import io.dico.parcels2.util.uuid import io.dico.parcels2.util.ext.uuid
import org.bukkit.entity.Player import org.bukkit.entity.Player
import java.lang.reflect.Method import java.lang.reflect.Method
import kotlin.reflect.full.extensionReceiverParameter import kotlin.reflect.full.extensionReceiverParameter

View File

@@ -7,8 +7,10 @@ import io.dico.dicore.command.parameter.type.ParameterType
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.floor import io.dico.parcels2.util.ext.floor
import kotlinx.coroutines.experimental.Deferred import kotlinx.coroutines.CoroutineStart.*
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player 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? abstract suspend fun getParcelSuspend(storage: Storage): Parcel?
fun ParcelsPlugin.getParcelDeferred(): Deferred<Parcel?> = functionHelper.deferUndispatchedOnMainThread { getParcelSuspend(storage) } fun ParcelsPlugin.getParcelDeferred(): Deferred<Parcel?> = async(start = UNDISPATCHED) { getParcelSuspend(storage) }
class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) { class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) {
override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel() 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 {
companion object Config : ParameterConfig<Kind, Int>(Kind::class.java) {
override fun toParameterInfo(annotation: Kind): Int {
return annotation.kind
}
const val ID = 1 // ID const val ID = 1 // ID
const val OWNER_REAL = 2 // an owner backed by a UUID const val OWNER_REAL = 2 // an owner backed by a UUID
const val OWNER_FAKE = 4 // an owner not 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 // instead of parcel that the player is in
} }
class PType(val parcelProvider: ParcelProvider) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, ParcelTarget.Config) { annotation class Kind(val kind: Int)
private object Config : ParameterConfig<Kind, Int>(Kind::class.java) {
override fun toParameterInfo(annotation: Kind): Int {
return annotation.kind
}
}
class PType(val parcelProvider: ParcelProvider) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, Config) {
override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget { override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget {
var input = buffer.next() 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<PlayerProfile, Int> { private fun getHomeIndex(parameter: Parameter<*, *>, kind: Int, sender: CommandSender, input: String): Pair<PlayerProfile, Int> {
val splitIdx = input.indexOf(':') val splitIdx = input.indexOf(':')
val ownerString: String val ownerString: String
val indexString: String val index: Int?
if (splitIdx == -1) { if (splitIdx == -1) {
// just the index. // just the index.
ownerString = "" index = input.toIntOrNull()
indexString = input ownerString = if (index == null) input else ""
} else { } else {
ownerString = input.substring(0, splitIdx) 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()) val owner = if (ownerString.isEmpty())
@@ -133,10 +139,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
else else
PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0) PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0)
val index = if (indexString.isEmpty()) 0 else indexString.toIntOrNull() return owner to (index ?: 0)
?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer")
return owner to index
} }
private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>, objName: String): Player { private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>, objName: String): Player {

View File

@@ -2,9 +2,8 @@ package io.dico.parcels2.defaultimpl
import io.dico.parcels2.Parcel import io.dico.parcels2.Parcel
import io.dico.parcels2.ParcelContainer import io.dico.parcels2.ParcelContainer
import io.dico.parcels2.ParcelId
import io.dico.parcels2.ParcelWorld import io.dico.parcels2.ParcelWorld
import kotlin.coroutines.experimental.buildIterator
import kotlin.coroutines.experimental.buildSequence
class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer { class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer {
private var parcels: Array<Array<Parcel>> private var parcels: Array<Array<Parcel>>
@@ -38,12 +37,20 @@ class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer {
return parcels.getOrNull(x + world.options.axisLimit)?.getOrNull(z + world.options.axisLimit) 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? { override fun nextEmptyParcel(): Parcel? {
return walkInCircle().find { it.owner == null } return walkInCircle().find { it.owner == null }
} }
private fun walkInCircle(): Iterable<Parcel> = Iterable { private fun walkInCircle(): Iterable<Parcel> = Iterable {
buildIterator { iterator {
val center = world.options.axisLimit val center = world.options.axisLimit
for (radius in 0..center) { for (radius in 0..center) {
var x = center - radius; var x = center - radius;
@@ -56,7 +63,7 @@ class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer {
} }
} }
fun allParcels(): Sequence<Parcel> = buildSequence { fun allParcels(): Sequence<Parcel> = sequence {
for (array in parcels) { for (array in parcels) {
yieldAll(array.iterator()) yieldAll(array.iterator())
} }

View File

@@ -2,10 +2,19 @@ package io.dico.parcels2.defaultimpl
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.blockvisitor.RegionTraverser import io.dico.parcels2.blockvisitor.RegionTraverser
import io.dico.parcels2.blockvisitor.TimeLimitedTask
import io.dico.parcels2.blockvisitor.Worker import io.dico.parcels2.blockvisitor.Worker
import io.dico.parcels2.blockvisitor.WorktimeLimiter import io.dico.parcels2.blockvisitor.WorktimeLimiter
import io.dico.parcels2.options.DefaultGeneratorOptions 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.*
import org.bukkit.block.Biome import org.bukkit.block.Biome
import org.bukkit.block.BlockFace 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.BlockData
import org.bukkit.block.data.type.Sign import org.bukkit.block.data.type.Sign
import org.bukkit.block.data.type.Slab import org.bukkit.block.data.type.Slab
import java.lang.IllegalArgumentException
import java.util.Random import java.util.Random
private val airType = Bukkit.createBlockData(Material.AIR) private val airType = Bukkit.createBlockData(Material.AIR)
private const val chunkSize = 16 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 private var _world: World? = null
override val world: World override val world: World
get() { get() {
@@ -36,13 +49,15 @@ class DefaultParcelGenerator(override val worldName: String, private val o: Defa
val makePathMain = o.pathSize > 2 val makePathMain = o.pathSize > 2
val makePathAlt = o.pathSize > 4 val makePathAlt = o.pathSize > 4
private inline fun <T> generate(chunkX: Int, private inline fun <T> generate(
chunkX: Int,
chunkZ: Int, chunkZ: Int,
floor: T, wall: floor: T, wall:
T, pathMain: T, T, pathMain: T,
pathAlt: T, pathAlt: T,
fill: T, fill: T,
setter: (Int, Int, Int, T) -> Unit) { setter: (Int, Int, Int, T) -> Unit
) {
val floorHeight = o.floorHeight val floorHeight = o.floorHeight
val parcelSize = o.parcelSize 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) return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix)
} }
override fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId, override fun makeParcelLocatorAndBlockManager(
worldId: ParcelWorldId,
container: ParcelContainer, container: ParcelContainer,
worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager> { coroutineScope: CoroutineScope,
return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, worktimeLimiter) worktimeLimiter: WorktimeLimiter
): Pair<ParcelLocator, ParcelBlockManager> {
return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, worktimeLimiter)
} }
private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? { private inline fun <T> 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 return null
} }
private inner class ParcelLocatorImpl(val worldId: ParcelWorldId, private inner class ParcelLocatorImpl(
val container: ParcelContainer) : ParcelLocator { val worldId: ParcelWorldId,
val container: ParcelContainer
) : ParcelLocator {
override val world: World = this@DefaultParcelGenerator.world override val world: World = this@DefaultParcelGenerator.world
override fun getParcelAt(x: Int, z: Int): Parcel? { override fun getParcelAt(x: Int, z: Int): Parcel? {
@@ -137,8 +157,11 @@ class DefaultParcelGenerator(override val worldName: String, private val o: Defa
} }
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private inner class ParcelBlockManagerImpl(val worldId: ParcelWorldId, private inner class ParcelBlockManagerImpl(
override val worktimeLimiter: WorktimeLimiter) : ParcelBlockManagerBase() { val worldId: ParcelWorldId,
coroutineScope: CoroutineScope,
override val worktimeLimiter: WorktimeLimiter
) : ParcelBlockManagerBase(), CoroutineScope by coroutineScope {
override val world: World = this@DefaultParcelGenerator.world override val world: World = this@DefaultParcelGenerator.world
override val parcelTraverser: RegionTraverser = RegionTraverser.upToAndDownUntil(o.floorHeight) 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 world = world
val b = getBottomBlock(parcel) val b = getBottomBlock(parcel)
val parcelSize = o.parcelSize 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 region = getRegion(parcel)
val blocks = parcelTraverser.traverseRegion(region) val blocks = parcelTraverser.traverseRegion(region)
val blockCount = region.blockCount.toDouble() val blockCount = region.blockCount.toDouble()

View File

@@ -3,7 +3,7 @@
package io.dico.parcels2.defaultimpl package io.dico.parcels2.defaultimpl
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.util.alsoIfTrue import io.dico.parcels2.util.ext.alsoIfTrue
import java.util.Collections import java.util.Collections
class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataManager { class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataManager {

View File

@@ -3,11 +3,11 @@ package io.dico.parcels2.defaultimpl
import io.dico.dicore.Formatting import io.dico.dicore.Formatting
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.util.Vec2i 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.Material
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import org.joda.time.DateTime import org.joda.time.DateTime
import kotlin.reflect.KProperty import java.util.concurrent.atomic.AtomicInteger
class ParcelImpl(override val world: ParcelWorld, class ParcelImpl(override val world: ParcelWorld,
override val x: Int, override val x: Int,
@@ -15,8 +15,8 @@ class ParcelImpl(override val world: ParcelWorld,
override val id: ParcelId = this override val id: ParcelId = this
override val pos get() = Vec2i(x, z) override val pos get() = Vec2i(x, z)
override var data: ParcelDataHolder = ParcelDataHolder(); private set override var data: ParcelDataHolder = ParcelDataHolder(); private set
override val infoString by ParcelInfoStringComputer override val infoString get() = ParcelInfoStringComputer.getInfoString(this)
override var hasBlockVisitors: Boolean = false; private set override val hasBlockVisitors get() = blockVisitors.get() > 0
override val worldId: ParcelWorldId get() = world.id override val worldId: ParcelWorldId get() = world.id
override fun copyDataIgnoringDatabase(data: ParcelData) { override fun copyDataIgnoringDatabase(data: ParcelData) {
@@ -115,6 +115,18 @@ class ParcelImpl(override val world: ParcelWorld,
// TODO update storage // 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 { 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") { appendField("ID") {
append(parcel.x) append(parcel.x)
append(',') append(',')

View File

@@ -1,8 +1,11 @@
package io.dico.parcels2.defaultimpl package io.dico.parcels2.defaultimpl
import io.dico.parcels2.* import io.dico.parcels2.*
import kotlinx.coroutines.experimental.Unconfined import io.dico.parcels2.util.schedule
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.CoroutineStart.*
import kotlinx.coroutines.Unconfined
import kotlinx.coroutines.launch
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.WorldCreator import org.bukkit.WorldCreator
import org.joda.time.DateTime import org.joda.time.DateTime
@@ -42,7 +45,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
private fun loadWorlds0() { private fun loadWorlds0() {
if (Bukkit.getWorlds().isEmpty()) { 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") plugin.logger.warning("Scheduling to load worlds in the next tick, \nbecause no bukkit worlds are loaded yet")
return return
} }
@@ -58,7 +61,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") } else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") }
parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage, parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage,
plugin.globalAddedData, ::DefaultParcelContainer, plugin.worktimeLimiter) plugin.globalAddedData, ::DefaultParcelContainer, plugin, plugin.worktimeLimiter)
if (!worldExists) { if (!worldExists) {
val time = DateTime.now() val time = DateTime.now()
@@ -77,7 +80,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
} }
private fun loadStoredData() { private fun loadStoredData() {
plugin.functionHelper.launchLazilyOnMainThread { plugin.launch {
val migration = plugin.options.migration val migration = plugin.options.migration
if (migration.enabled) { if (migration.enabled) {
migration.instance?.newInstance()?.apply { migration.instance?.newInstance()?.apply {
@@ -102,7 +105,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
logger.info("Loading data completed") logger.info("Loading data completed")
_dataIsLoaded = true _dataIsLoaded = true
}.start() }
} }
/* /*

View File

@@ -6,6 +6,7 @@ import io.dico.parcels2.*
import io.dico.parcels2.blockvisitor.WorktimeLimiter import io.dico.parcels2.blockvisitor.WorktimeLimiter
import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.options.RuntimeWorldOptions
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import kotlinx.coroutines.CoroutineScope
import org.bukkit.World import org.bukkit.World
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.UUID import java.util.UUID
@@ -16,6 +17,7 @@ class ParcelWorldImpl(override val world: World,
override val storage: Storage, override val storage: Storage,
override val globalAddedData: GlobalAddedDataManager, override val globalAddedData: GlobalAddedDataManager,
containerFactory: ParcelContainerFactory, containerFactory: ParcelContainerFactory,
coroutineScope: CoroutineScope,
worktimeLimiter: WorktimeLimiter) worktimeLimiter: WorktimeLimiter)
: ParcelWorld, : ParcelWorld,
ParcelWorldId, ParcelWorldId,
@@ -37,7 +39,7 @@ class ParcelWorldImpl(override val world: World,
override val blockManager: ParcelBlockManager override val blockManager: ParcelBlockManager
init { init {
val pair = generator.makeParcelLocatorAndBlockManager(id, container, worktimeLimiter) val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, worktimeLimiter)
locator = pair.first locator = pair.first
blockManager = pair.second blockManager = pair.second
@@ -84,5 +86,5 @@ class ParcelWorldImpl(override val world: World,
return container.nextEmptyParcel() return container.nextEmptyParcel()
} }
override fun toString() = toStringExt()
} }

View File

@@ -2,8 +2,8 @@ package io.dico.parcels2.listener
import io.dico.parcels2.Parcel import io.dico.parcels2.Parcel
import io.dico.parcels2.ParcelProvider import io.dico.parcels2.ParcelProvider
import io.dico.parcels2.util.editLoop import io.dico.parcels2.util.ext.editLoop
import io.dico.parcels2.util.isPresentAnd import io.dico.parcels2.util.ext.isPresentAnd
import org.bukkit.entity.Entity import org.bukkit.entity.Entity
class ParcelEntityTracker(val parcelProvider: ParcelProvider) { class ParcelEntityTracker(val parcelProvider: ParcelProvider) {

View File

@@ -8,7 +8,7 @@ import io.dico.parcels2.ParcelProvider
import io.dico.parcels2.ParcelWorld import io.dico.parcels2.ParcelWorld
import io.dico.parcels2.statusKey import io.dico.parcels2.statusKey
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.* import io.dico.parcels2.util.ext.*
import org.bukkit.Material.* import org.bukkit.Material.*
import org.bukkit.World import org.bukkit.World
import org.bukkit.block.Biome import org.bukkit.block.Biome

View File

@@ -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)
}
}
}

View File

@@ -1,11 +1,11 @@
package io.dico.parcels2.storage package io.dico.parcels2.storage
import io.dico.parcels2.* import io.dico.parcels2.*
import kotlinx.coroutines.experimental.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.experimental.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.experimental.channels.ReceiveChannel import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.experimental.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.UUID import java.util.UUID

View File

@@ -3,11 +3,11 @@
package io.dico.parcels2.storage package io.dico.parcels2.storage
import io.dico.parcels2.* import io.dico.parcels2.*
import kotlinx.coroutines.experimental.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.experimental.channels.ReceiveChannel import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.experimental.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.launch
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.UUID import java.util.UUID

View File

@@ -8,14 +8,14 @@ import io.dico.parcels2.PlayerProfile.Star.name
import io.dico.parcels2.storage.AddedDataPair import io.dico.parcels2.storage.AddedDataPair
import io.dico.parcels2.storage.Backing import io.dico.parcels2.storage.Backing
import io.dico.parcels2.storage.DataPair 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.toByteArray
import io.dico.parcels2.util.toUUID import io.dico.parcels2.util.toUUID
import kotlinx.coroutines.experimental.* import kotlinx.coroutines.*
import kotlinx.coroutines.experimental.channels.ArrayChannel import kotlinx.coroutines.channels.ArrayChannel
import kotlinx.coroutines.experimental.channels.LinkedListChannel import kotlinx.coroutines.channels.LinkedListChannel
import kotlinx.coroutines.experimental.channels.ReceiveChannel import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.experimental.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SchemaUtils.create import org.jetbrains.exposed.sql.SchemaUtils.create
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction

View File

@@ -153,6 +153,13 @@ object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcels_profil
return getItem(id) as? PlayerProfile.Real return getItem(id) as? PlayerProfile.Real
} }
/*
fun updatePlayerProfile(profile: PlayerProfile.Real) {
update({ uuid eq profile.uuid.toByteArray() }) {
it[name] = profile.nameOrBukkitName
}
}*/
} }
// val ParcelsWithOptionsT = ParcelsT.join(ParcelOptionsT, JoinType.INNER, onColumn = ParcelsT.id, otherColumn = ParcelOptionsT.parcel_id) // val ParcelsWithOptionsT = ParcelsT.join(ParcelOptionsT, JoinType.INNER, onColumn = ParcelsT.id, otherColumn = ParcelOptionsT.parcel_id)

View File

@@ -3,7 +3,7 @@
package io.dico.parcels2.storage.exposed package io.dico.parcels2.storage.exposed
import io.dico.parcels2.* import io.dico.parcels2.*
import kotlinx.coroutines.experimental.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import java.util.UUID import java.util.UUID

View File

@@ -1,7 +1,7 @@
package io.dico.parcels2.storage.migration package io.dico.parcels2.storage.migration
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.Job
interface Migration { interface Migration {
fun migrateTo(storage: Storage): Job fun migrateTo(storage: Storage): Job

View File

@@ -10,9 +10,9 @@ import io.dico.parcels2.storage.exposed.abs
import io.dico.parcels2.storage.exposed.greater import io.dico.parcels2.storage.exposed.greater
import io.dico.parcels2.storage.migration.Migration import io.dico.parcels2.storage.migration.Migration
import io.dico.parcels2.util.toUUID import io.dico.parcels2.util.toUUID
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.experimental.newFixedThreadPoolContext import kotlinx.coroutines.newFixedThreadPoolContext
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.sql.Blob import java.sql.Blob

View File

@@ -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 <T> deferLazilyOnMainThread(block: suspend CoroutineScope.() -> T): Deferred<T> {
return async(context = mainThreadDispatcher, start = CoroutineStart.LAZY, block = block)
}
fun <T> deferUndispatchedOnMainThread(block: suspend CoroutineScope.() -> T): Deferred<T> {
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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)

View File

@@ -1,16 +1,11 @@
package io.dico.parcels2.util package io.dico.parcels2.util
import io.dico.parcels2.util.ext.isValid
import org.bukkit.Bukkit import org.bukkit.Bukkit
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.UUID import java.util.UUID
@Suppress("UsePropertyAccessSyntax") const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:"
fun getPlayerNameOrDefault(uuid: UUID?, ifUnknown: String? = null): String {
return uuid
?.let { getPlayerName(it) }
?: ifUnknown
?: ":unknown_name:"
}
fun getPlayerName(uuid: UUID): String? { fun getPlayerName(uuid: UUID): String? {
return Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isValid }?.name return Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isValid }?.name

View File

@@ -1,4 +1,4 @@
package io.dico.parcels2.util package io.dico.parcels2.util.ext
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.Material.* import org.bukkit.Material.*

View File

@@ -1,4 +1,4 @@
package io.dico.parcels2.util package io.dico.parcels2.util.ext
fun Double.floor(): Int { fun Double.floor(): Int {
val down = toInt() val down = toInt()

View File

@@ -1,4 +1,4 @@
package io.dico.parcels2.util package io.dico.parcels2.util.ext
import io.dico.parcels2.logger import io.dico.parcels2.logger
import java.io.File import java.io.File

View File

@@ -1,8 +1,7 @@
package io.dico.parcels2.util package io.dico.parcels2.util.ext
import io.dico.dicore.Formatting import io.dico.dicore.Formatting
import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.logger import io.dico.parcels2.logger
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player import org.bukkit.entity.Player

14
todo.md
View File

@@ -3,18 +3,18 @@
Commands Commands
- -
Basically all admin commands. Basically all admin commands.
* setowner * ~~setowner~~
* dispose * ~~dispose~~
* reset * ~~reset~~
* swap * swap
* New admin commands that I can't think of right now. * New admin commands that I can't think of right now.
Also Also
* setbiome * ~~setbiome~~
* random * random
Modify home command: 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 * Listen for command events that use plotme-style argument, and transform the command
~~Add permissions to commands (replace or fix `IContextFilter` from command lib ~~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, Scan through blocks that were added since original Parcels implementation,
that might introduce things that need to be checked or listened for. 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. 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). 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.~~