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 {
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<JavaPlugin>()
@@ -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")
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("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")

View File

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

View File

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

View File

@@ -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<Any?>): 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<Any?>
): 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<Any?> { 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?>): Any? {
return suspendCoroutineOrReturn { cont ->
invoke(instance, *args, cont)
}
}
@Throws(CommandException::class)
private fun Deferred<Any?>.getResult(): String? {
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"
include("dicore3:core")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<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 {
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<Vec3i>.(Region) -> Unit) = object : RegionTraverser() {
override suspend fun SequenceBuilder<Vec3i>.build(region: Region) {
inline fun create(crossinline builder: suspend SequenceScope<Vec3i>.(Region) -> Unit) = object : RegionTraverser() {
override suspend fun SequenceScope<Vec3i>.build(region: Region) {
builder(region)
}
}
private suspend fun SequenceBuilder<Vec3i>.traverseDownward(region: Region) {
private suspend fun SequenceScope<Vec3i>.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<Vec3i>.traverseUpward(region: Region) {
private suspend fun SequenceScope<Vec3i>.traverseUpward(region: Region) {
val origin = region.origin
val size = region.size
repeat(size.y) { y ->

View File

@@ -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<Worker>
val workers: List<Worker>
/**
* 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<Worker> = _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) }
}
}
}
/*

View File

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

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

View File

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

View File

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

View File

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

View File

@@ -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<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) {
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, Int>(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, 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 {
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> {
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 {

View File

@@ -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<Array<Parcel>>
@@ -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<Parcel> = 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<Parcel> = buildSequence {
fun allParcels(): Sequence<Parcel> = sequence {
for (array in parcels) {
yieldAll(array.iterator())
}

View File

@@ -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 <T> generate(chunkX: Int,
chunkZ: Int,
floor: T, wall:
T, pathMain: T,
pathAlt: T,
fill: T,
setter: (Int, Int, Int, T) -> Unit) {
private inline fun <T> 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<ParcelLocator, ParcelBlockManager> {
return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, worktimeLimiter)
override fun makeParcelLocatorAndBlockManager(
worldId: ParcelWorldId,
container: ParcelContainer,
coroutineScope: CoroutineScope,
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? {
@@ -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()

View File

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

View File

@@ -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(',')

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -153,6 +153,13 @@ object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("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)

View File

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

View File

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

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

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

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

View File

@@ -1,4 +1,4 @@
package io.dico.parcels2.util
package io.dico.parcels2.util.ext
fun Double.floor(): Int {
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 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.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.logger
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player

14
todo.md
View File

@@ -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.~~