summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico Karssiens <dico.karssiens@gmail.com>2018-07-30 04:49:43 +0100
committerDico Karssiens <dico.karssiens@gmail.com>2018-07-30 04:49:43 +0100
commitdee994b992b1c6df15f6c24b49cd27c25e0657d6 (patch)
tree0cdc22b00b1823b13468864ce0fc7a30fd9d7f79
parent33bb19a54779547726165fa47d20f222cbe1e0a6 (diff)
Add WorktimeLimiter API, basic /parcel clear functionality
-rw-r--r--src/main/kotlin/io/dico/parcels2/Options.kt9
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelWorld.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt5
-rw-r--r--src/main/kotlin/io/dico/parcels2/WorldGenerator.kt36
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt67
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitor.kt5
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitorManager.kt52
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitorOptions.kt3
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraversal.kt30
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt45
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt207
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt11
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt25
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/Region.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/Vec3i.kt18
15 files changed, 415 insertions, 104 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/Options.kt b/src/main/kotlin/io/dico/parcels2/Options.kt
index 41b3568..05df718 100644
--- a/src/main/kotlin/io/dico/parcels2/Options.kt
+++ b/src/main/kotlin/io/dico/parcels2/Options.kt
@@ -2,7 +2,7 @@ package io.dico.parcels2
import com.fasterxml.jackson.annotation.JsonIgnore
-import io.dico.parcels2.blockvisitor.BlockVisitorOptions
+import io.dico.parcels2.blockvisitor.TickWorktimeOptions
import io.dico.parcels2.storage.Storage
import io.dico.parcels2.storage.StorageFactory
import io.dico.parcels2.storage.yamlObjectMapper
@@ -19,6 +19,7 @@ class Options {
var worlds: Map<String, WorldOptions> = HashMap()
private set
var storage: StorageOptions = StorageOptions("postgresql", DataConnectionOptions())
+ var tickWorktime: TickWorktimeOptions = TickWorktimeOptions(30, 1)
fun addWorld(name: String, options: WorldOptions) = (worlds as MutableMap).put(name, options)
@@ -40,8 +41,7 @@ data class WorldOptions(var gameMode: GameMode? = GameMode.CREATIVE,
var blockMobSpawning: Boolean = true,
var blockedItems: Set<Material> = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL),
var axisLimit: Int = 10,
- var generator: GeneratorOptions = DefaultGeneratorOptions(),
- var blockVisitor: BlockVisitorOptions = BlockVisitorOptions()) {
+ var generator: GeneratorOptions = DefaultGeneratorOptions()) {
}
@@ -73,7 +73,8 @@ class StorageOptions(val dialect: String,
val options: Any) {
@get:JsonIgnore
- val factory = StorageFactory.getFactory(dialect) ?: throw IllegalArgumentException("Invalid storage dialect: $dialect")
+ val factory = StorageFactory.getFactory(dialect)
+ ?: throw IllegalArgumentException("Invalid storage dialect: $dialect")
fun newStorageInstance(): Storage = factory.newStorageInstance(dialect, options)
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
index d4287af..9a50b31 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
@@ -20,7 +20,7 @@ import kotlin.coroutines.experimental.buildSequence
import kotlin.reflect.jvm.javaMethod
import kotlin.reflect.jvm.kotlinFunction
-class Worlds(private val plugin: ParcelsPlugin) {
+class Worlds(val plugin: ParcelsPlugin) {
val worlds: Map<String, ParcelWorld> get() = _worlds
private val _worlds: MutableMap<String, ParcelWorld> = HashMap()
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
index fd4270f..8f0e89e 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
@@ -3,6 +3,8 @@ package io.dico.parcels2
import io.dico.dicore.Registrator
import io.dico.dicore.command.EOverridePolicy
import io.dico.dicore.command.ICommandDispatcher
+import io.dico.parcels2.blockvisitor.TickWorktimeLimiter
+import io.dico.parcels2.blockvisitor.WorktimeLimiter
import io.dico.parcels2.command.getParcelCommands
import io.dico.parcels2.listener.ParcelEntityTracker
import io.dico.parcels2.listener.ParcelListeners
@@ -29,8 +31,7 @@ class ParcelsPlugin : JavaPlugin() {
lateinit var entityTracker: ParcelEntityTracker; private set
private var listeners: ParcelListeners? = null
private var cmdDispatcher: ICommandDispatcher? = null
-
- val mainThreadDispatcher = Executor { server.scheduler.runTask(this, it) }.asCoroutineDispatcher()
+ val worktimeLimiter: WorktimeLimiter by lazy { TickWorktimeLimiter(this, options) }
override fun onEnable() {
plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
diff --git a/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt b/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt
index 6bd214b..e61788c 100644
--- a/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt
@@ -1,9 +1,8 @@
package io.dico.parcels2
-import io.dico.parcels2.util.Vec2i
-import io.dico.parcels2.util.clamp
-import io.dico.parcels2.util.even
-import io.dico.parcels2.util.umod
+import io.dico.parcels2.blockvisitor.JobData
+import io.dico.parcels2.blockvisitor.RegionTraversal
+import io.dico.parcels2.util.*
import org.bukkit.*
import org.bukkit.Bukkit.createBlockData
import org.bukkit.block.Biome
@@ -51,6 +50,8 @@ abstract class ParcelGenerator : ChunkGenerator(), ParcelProvider {
abstract fun getBlocks(parcel: Parcel, yRange: IntRange = 0..255): Iterator<Block>
+ abstract fun clearParcel(parcel: Parcel): JobData
+
}
interface GeneratorFactory {
@@ -78,6 +79,9 @@ interface GeneratorFactory {
class DefaultParcelGenerator(val worlds: Worlds, val name: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() {
override val world: ParcelWorld by lazy { worlds.getWorld(name)!! }
override val factory = Factory
+ val worktimeLimiter = worlds.plugin.worktimeLimiter
+ val maxHeight by lazy { world.world.maxHeight }
+ val airType = worlds.plugin.server.createBlockData(Material.AIR)
companion object Factory : GeneratorFactory {
override val name get() = "default"
@@ -260,4 +264,28 @@ class DefaultParcelGenerator(val worlds: Worlds, val name: String, private val o
}
}
+ override fun clearParcel(parcel: Parcel) = worktimeLimiter.submit {
+ val bottom = getBottomCoord(parcel)
+ val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize))
+ val blocks = RegionTraversal.XZY.regionTraverser(region)
+ val blockCount = region.blockCount.toDouble()
+
+ val world = world.world
+ val floorHeight = o.floorHeight
+ val airType = airType; val floorType = o.floorType; val fillType = o.fillType
+
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ val y = vec.y
+ val blockType = when {
+ y > floorHeight -> airType
+ y == floorHeight -> floorType
+ else -> fillType
+ }
+ world[vec].blockData = blockType
+ setProgress((index + 1) / blockCount)
+ }
+
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
new file mode 100644
index 0000000..c046940
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
@@ -0,0 +1,67 @@
+package io.dico.parcels2.blockvisitor
+
+import org.bukkit.Material
+import org.bukkit.Material.*
+import java.util.*
+
+val attachables: Set<Material> = EnumSet.of(
+ ACACIA_DOOR,
+ ACTIVATOR_RAIL,
+ BIRCH_DOOR,
+ BROWN_MUSHROOM,
+ CACTUS,
+ CAKE,
+ WHITE_CARPET, ORANGE_CARPET, MAGENTA_CARPET, LIGHT_BLUE_CARPET, YELLOW_CARPET, LIME_CARPET, PINK_CARPET, GRAY_CARPET, LIGHT_GRAY_CARPET, CYAN_CARPET, PURPLE_CARPET, BLUE_CARPET, BROWN_CARPET, GREEN_CARPET, RED_CARPET, BLACK_CARPET,
+ CARROT,
+ COCOA,
+ WHEAT,
+ DARK_OAK_DOOR,
+ DEAD_BUSH,
+ DETECTOR_RAIL,
+ REPEATER,
+ TALL_GRASS, TALL_SEAGRASS,
+ DRAGON_EGG,
+ FIRE,
+ FLOWER_POT,
+ OAK_PRESSURE_PLATE, BIRCH_PRESSURE_PLATE, SPRUCE_PRESSURE_PLATE, JUNGLE_PRESSURE_PLATE, ACACIA_PRESSURE_PLATE, DARK_OAK_PRESSURE_PLATE,
+ STONE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE,
+ IRON_DOOR,
+ OAK_DOOR, BIRCH_DOOR, SPRUCE_DOOR, JUNGLE_DOOR, ACACIA_DOOR, DARK_OAK_DOOR,
+ OAK_BUTTON, BIRCH_BUTTON, SPRUCE_BUTTON, JUNGLE_BUTTON, ACACIA_BUTTON, DARK_OAK_BUTTON,
+ STONE_BUTTON,
+ OAK_TRAPDOOR, BIRCH_TRAPDOOR, SPRUCE_TRAPDOOR, JUNGLE_TRAPDOOR, ACACIA_TRAPDOOR, DARK_OAK_TRAPDOOR,
+ IRON_TRAPDOOR,
+ LADDER,
+ LEVER,
+ MELON_STEM,
+ NETHER_WART,
+ PISTON,
+ STICKY_PISTON,
+ NETHER_PORTAL,
+ POTATO,
+ POWERED_RAIL,
+ PUMPKIN_STEM,
+ RAIL,
+ COMPARATOR,
+ REDSTONE_TORCH,
+ REDSTONE_WIRE,
+ RED_MUSHROOM,
+ SUNFLOWER,
+ FLOWER_POT,
+ CHORUS_FLOWER,
+ OAK_SAPLING, BIRCH_SAPLING, SPRUCE_SAPLING, JUNGLE_SAPLING, ACACIA_SAPLING, DARK_OAK_SAPLING,
+ SIGN,
+ SNOW,
+ SPRUCE_DOOR,
+ STONE_BUTTON,
+ SUGAR_CANE,
+ TORCH,
+ TRIPWIRE,
+ TRIPWIRE_HOOK,
+ VINE,
+ WHITE_BANNER, ORANGE_BANNER, MAGENTA_BANNER, LIGHT_BLUE_BANNER, YELLOW_BANNER, LIME_BANNER, PINK_BANNER, GRAY_BANNER, LIGHT_GRAY_BANNER, CYAN_BANNER, PURPLE_BANNER, BLUE_BANNER, BROWN_BANNER, GREEN_BANNER, RED_BANNER, BLACK_BANNER,
+ WHITE_WALL_BANNER, ORANGE_WALL_BANNER, MAGENTA_WALL_BANNER, LIGHT_BLUE_WALL_BANNER, YELLOW_WALL_BANNER, LIME_WALL_BANNER, PINK_WALL_BANNER, GRAY_WALL_BANNER, LIGHT_GRAY_WALL_BANNER, CYAN_WALL_BANNER, PURPLE_WALL_BANNER, BLUE_WALL_BANNER, BROWN_WALL_BANNER, GREEN_WALL_BANNER, RED_WALL_BANNER, BLACK_WALL_BANNER,
+ WALL_SIGN,
+ LILY_PAD,
+ DANDELION
+); \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitor.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitor.kt
deleted file mode 100644
index 12433e1..0000000
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitor.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package io.dico.parcels2.blockvisitor
-
-import io.dico.dicore.task.IteratorTask
-
-abstract class BlockVisitor<T>(iterator: Iterator<T>?) : IteratorTask<T>(iterator)
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitorManager.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitorManager.kt
deleted file mode 100644
index a26328f..0000000
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitorManager.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package io.dico.parcels2.blockvisitor
-
-import io.dico.parcels2.util.MutableVec3i
-import io.dico.parcels2.util.Region
-import kotlinx.coroutines.experimental.Deferred
-import org.bukkit.block.Block
-import org.bukkit.plugin.Plugin
-import kotlin.coroutines.experimental.SequenceBuilder
-import kotlin.coroutines.experimental.buildIterator
-
-typealias BlockProcessor = (Block) -> Boolean
-
-class BlockVisitorManager(val plugin: Plugin, var options: BlockVisitorOptions) {
-
-
- fun doOperationSynchronously(region: Region, processor: BlockProcessor): Deferred<Unit> {
-
-
- }
-
-
-}
-
-class RegionOperation(val region: Region, val processor: BlockProcessor) {
-
- fun process(maxMillis: Int) {
-
- }
-
-
-}
-
-enum class RegionTraversal(private val builder: suspend SequenceBuilder<MutableVec3i>.(Region) -> Unit) {
- XZY({ region ->
- val origin = region.origin
- val result = MutableVec3i(origin.x, origin.y, origin.z)
-
- val size = region.size
-
- repeat(size.y) { y ->
- repeat()
-
- result.y++
- }
-
- })
-
- ;
-
- fun regionTraverser(region: Region) = Iterable { buildIterator { builder(region) } }
-
-}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitorOptions.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitorOptions.kt
deleted file mode 100644
index e4ebf48..0000000
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/BlockVisitorOptions.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package io.dico.parcels2.blockvisitor
-
-data class BlockVisitorOptions(var pauseTicks: Int = 1, var workMillis: Int = 30) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraversal.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraversal.kt
new file mode 100644
index 0000000..0e7d217
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraversal.kt
@@ -0,0 +1,30 @@
+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
+
+enum class RegionTraversal(private val builder: suspend SequenceBuilder<Vec3i>.(Region) -> Unit) {
+ XZY({ region ->
+ val origin = region.origin
+ val size = region.size
+
+ repeat(size.y) { y ->
+ repeat(size.z) { z ->
+ repeat(size.x) { x ->
+ yield(origin.add(x, y, z))
+ }
+ }
+ }
+
+ }),
+
+ ;
+
+ fun regionTraverser(region: Region) = Iterable { buildIterator { builder(region) } }
+
+}
+
+
+
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
index b48e4f1..1c01571 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
@@ -1,13 +1,52 @@
package io.dico.parcels2.blockvisitor
+import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec3i
+import io.dico.parcels2.util.get
+import org.bukkit.World
import org.bukkit.block.data.BlockData
-class Schematic(val origin: Vec3i, val size: Vec3i) {
- private var data: Array<BlockData>? = null
+class Schematic {
+ val size: Vec3i get() = _size!!
+ private var _size: Vec3i? = null
+ set(value) {
+ field?.let { throw IllegalStateException() }
+ field = value
+ }
+ private var _data: Array<BlockData?>? = null
+ //private var extra: Map<Vec3i, (Block) -> Unit>? = null
+ private var isLoaded = false; private set
+ fun getLoadTask(world: World, region: Region): TimeLimitedTask = {
+ val size = region.size.also { _size = it }
+ val data = arrayOfNulls<BlockData>(region.blockCount).also { _data = it }
+ //val extra = mutableMapOf<Vec3i, (Block) -> Unit>().also { extra = it }
+ val blocks = RegionTraversal.XZY.regionTraverser(region)
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ val block = world[vec]
+ if (block.y > 255) continue
+ val blockData = block.blockData
+ data[index] = blockData
+ }
+ isLoaded = true
+ }
-} \ No newline at end of file
+ fun getPasteTask(world: World, position: Vec3i): TimeLimitedTask = {
+ if (!isLoaded) throw IllegalStateException()
+ val region = Region(position, _size!!)
+ val blocks = RegionTraversal.XZY.regionTraverser(region)
+ val data = _data!!
+
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ val block = world[vec]
+ if (block.y > 255) continue
+ data[index]?.let { block.blockData = it }
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt
new file mode 100644
index 0000000..2394da0
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt
@@ -0,0 +1,207 @@
+package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.Options
+import kotlinx.coroutines.experimental.CoroutineStart
+import kotlinx.coroutines.experimental.Job
+import kotlinx.coroutines.experimental.asCoroutineDispatcher
+import kotlinx.coroutines.experimental.launch
+import org.bukkit.plugin.Plugin
+import org.bukkit.scheduler.BukkitTask
+import java.util.*
+import java.util.concurrent.Executor
+import kotlin.coroutines.experimental.Continuation
+import kotlin.coroutines.experimental.ContinuationInterceptor
+import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
+import kotlin.coroutines.experimental.intrinsics.suspendCoroutineUninterceptedOrReturn
+
+interface WorktimeLimiter {
+ /**
+ * Submit a task that should be run synchronously, but limited such that it does not stall the server
+ * a bunch
+ */
+ fun submit(job: TimeLimitedTask): JobData
+
+ /**
+ * A task should call this frequently during its execution, such that the timer can suspend it when necessary.
+ */
+ suspend fun markSuspensionPoint()
+
+ /**
+ * A task should call this method to indicate its progress
+ */
+ fun setProgress(progress: Double)
+}
+
+typealias TimeLimitedTask = suspend WorktimeLimiter.() -> Unit
+
+interface JobData {
+ val job: Job?
+ val isComplete: Boolean
+ val progress: Double?
+
+ /**
+ * Calls the given [block] whenever the progress is updated,
+ * if [minInterval] milliseconds expired since the last call.
+ *
+ * The first call occurs after at least [minDelay] milliseconds in a likewise manner.
+ * Repeated invocations of this method result in an [IllegalStateException]
+ */
+ fun onProgressUpdate(minDelay: Int, minInterval: Int, block: JobUpdateListener): JobData
+ val isUpdateBlockPresent: Boolean
+
+ /**
+ * Calls the given [block] when this job completes.
+ */
+ fun onCompleted(block: JobUpdateListener): JobData
+}
+
+typealias JobUpdateListener = JobData.(Double) -> Unit
+
+class JobDataImpl(val task: TimeLimitedTask) : JobData {
+ override var job: Job? = null
+ set(value) {
+ field?.let { throw IllegalStateException() }
+ field = value!!
+ value.invokeOnCompletion { onCompletedBlock?.invoke(this, 1.0) }
+ }
+
+ var next: Continuation<Unit>? = null
+
+ override var progress: Double? = null
+ set(value) {
+ field = value
+ doProgressUpdate()
+ }
+
+ private fun doProgressUpdate() {
+ val progressUpdate = progressUpdateBlock ?: return
+ val time = System.currentTimeMillis()
+ if (time > lastUpdateTime + progressUpdateInterval) {
+ progressUpdate(progress!!)
+ lastUpdateTime = time
+ }
+ }
+
+ override val isUpdateBlockPresent get() = progressUpdateBlock != null
+ private var progressUpdateBlock: JobUpdateListener? = null
+ private var progressUpdateInterval: Int = 0
+ private var lastUpdateTime: Long = 0L
+ override fun onProgressUpdate(minDelay: Int, minInterval: Int, block: JobUpdateListener): JobDataImpl {
+ progressUpdateBlock?.let { throw IllegalStateException() }
+ progressUpdateBlock = block
+ progressUpdateInterval = minInterval
+ lastUpdateTime = System.currentTimeMillis() + minDelay - minInterval
+ return this
+ }
+
+ override val isComplete get() = job?.isCompleted == true
+ private var onCompletedBlock: JobUpdateListener? = null
+ override fun onCompleted(block: JobUpdateListener): JobDataImpl {
+ onCompletedBlock?.let { throw IllegalStateException() }
+ onCompletedBlock = block
+ return this
+ }
+
+}
+
+/**
+ * An object that controls one or more jobs, ensuring that they don't stall the server too much.
+ * The amount of milliseconds that can accumulate each server tick is configurable
+ */
+class TickWorktimeLimiter(private val plugin: Plugin, private val optionsRoot: Options) : WorktimeLimiter {
+ // Coroutine dispatcher for jobs
+ private val dispatcher = Executor(Runnable::run).asCoroutineDispatcher()
+ // union of Continuation<Unit> and suspend WorktimeLimited.() -> Unit
+ private var jobs = LinkedList<JobDataImpl>()
+ // The currently registered bukkit scheduler task
+ private var task: BukkitTask? = null
+ // The data associated with the task that is currently being executed
+ private var curJobData: JobDataImpl? = null
+ // Used to keep track of when the current task should end
+ private var curJobEndTime = 0L
+ // Tick work time options
+ private inline val options get() = optionsRoot.tickWorktime
+
+ override fun submit(job: TimeLimitedTask): JobData {
+ val jobData = JobDataImpl(job)
+ jobs.addFirst(jobData)
+ if (task == null) task = plugin.server.scheduler.runTaskTimer(plugin, ::tickJobs, 0, options.tickInterval.toLong())
+ return jobData
+ }
+
+ override suspend fun markSuspensionPoint() {
+ if (System.currentTimeMillis() >= curJobEndTime)
+ suspendCoroutineUninterceptedOrReturn(::scheduleContinuation)
+ }
+
+ override fun setProgress(progress: Double) {
+ curJobData!!.progress = progress
+ }
+
+ private fun tickJobs() {
+ if (jobs.isEmpty()) return
+ val tickStartTime = System.currentTimeMillis()
+ val jobs = this.jobs; this.jobs = LinkedList()
+
+ var count = jobs.size
+
+ while (!jobs.isEmpty()) {
+ val job = jobs.poll()
+ val time = System.currentTimeMillis()
+ val timeElapsed = time - tickStartTime
+ val timeLeft = options.workTime - timeElapsed
+
+ if (timeLeft <= 0) {
+ this.jobs.addAll(0, jobs)
+ return
+ }
+
+ val timePerJob = (timeLeft + count - 1) / count
+ tickJob(job, time + timePerJob)
+ count--
+ }
+
+ if (jobs.isEmpty() && this.jobs.isEmpty()) {
+ task?.cancel()
+ task = null
+ }
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun tickJob(job: JobDataImpl, endTime: Long) {
+ curJobData = job
+ curJobEndTime = endTime
+ try {
+ val next = job.next
+ if (next == null) startJob(job)
+ else next.resume(Unit)
+ }
+ finally {
+ curJobData = null
+ curJobEndTime = 0L
+ }
+ }
+
+ private fun startJob(job: JobDataImpl) {
+ job.job = launch(context = dispatcher, start = CoroutineStart.UNDISPATCHED) { job.task(this@TickWorktimeLimiter) }
+ }
+
+ private fun scheduleContinuation(continuation: Continuation<Unit>): Any? {
+ curJobData!!.next = continuation
+ jobs.addLast(curJobData)
+ return COROUTINE_SUSPENDED
+ }
+
+}
+
+data class TickWorktimeOptions(var workTime: Int, var tickInterval: Int)
+
+
+/**
+ * While the implementation of [kotlin.coroutines.experimental.intrinsics.intercepted] is intrinsic, it should look something like this
+ * We don't care for intercepting the coroutine as we want it to resume immediately when we call resume().
+ * Thus, above, we use an unintercepted suspension. It's not necessary as the dispatcher (or interceptor) also calls it synchronously, but whatever.
+ */
+private fun <T> Continuation<T>.interceptedImpl(): Continuation<T> {
+ return context[ContinuationInterceptor]?.interceptContinuation(this) ?: this
+} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
index 7308a00..715e957 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
@@ -1,15 +1,18 @@
package io.dico.parcels2.command
+import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.Desc
import io.dico.dicore.command.annotation.RequireParameters
import io.dico.parcels2.ParcelOwner
import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.blockvisitor.JobUpdateListener
import io.dico.parcels2.command.NamedParcelDefaultValue.FIRST_OWNED
import io.dico.parcels2.storage.getParcelBySerializedValue
import io.dico.parcels2.util.hasAdminManage
import io.dico.parcels2.util.hasParcelHomeOthers
import io.dico.parcels2.util.uuid
+import kotlinx.coroutines.experimental.Job
import org.bukkit.entity.Player
//@Suppress("unused")
@@ -77,5 +80,13 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
return "Enjoy your new parcel!"
}
+ @Cmd("clear")
+ @ParcelRequire(owner = true)
+ fun ParcelScope.cmdClear(player: Player, context: ExecutionContext) {
+ val onProgressUpdate: JobUpdateListener = { progress -> context.sendMessage("[Clearing] Progress: %.06f%%".format(progress * 100)) }
+ world.generator.clearParcel(parcel)
+ .onProgressUpdate(1000, 1500, onProgressUpdate)
+ .onCompleted(onProgressUpdate)
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt
index 5cfcb47..f38f687 100644
--- a/src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/MaterialExtensions.kt
@@ -5,30 +5,9 @@ import org.bukkit.Material.*
/*
colors:
-WHITE_$,
-ORANGE_$,
-MAGENTA_$,
-LIGHT_BLUE_$,
-YELLOW_$,
-LIME_$,
-PINK_$,
-GRAY_$,
-LIGHT_GRAY_$,
-CYAN_$,
-PURPLE_$,
-BLUE_$,
-BROWN_$,
-GREEN_$,
-RED_$,
-BLACK_$,
-
+WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$,
wood:
-OAK_$,
-BIRCH_$,
-SPRUCE_$,
-JUNGLE_$,
-ACACIA_$,
-DARK_OAK_$,
+OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$,
*/
val Material.isBed get() = when(this) {
diff --git a/src/main/kotlin/io/dico/parcels2/util/Region.kt b/src/main/kotlin/io/dico/parcels2/util/Region.kt
index 8cc98df..5717906 100644
--- a/src/main/kotlin/io/dico/parcels2/util/Region.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/Region.kt
@@ -1,3 +1,5 @@
package io.dico.parcels2.util
-data class Region(val origin: Vec3i, val size: Vec3i) \ No newline at end of file
+data class Region(val origin: Vec3i, val size: Vec3i) {
+ val blockCount: Int get() = size.x * size.y * size.z
+} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt b/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt
index d400fac..a4655d0 100644
--- a/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt
@@ -1,13 +1,19 @@
package io.dico.parcels2.util
+import org.bukkit.World
+import org.bukkit.block.Block
+
data class Vec3i(
val x: Int,
val y: Int,
val z: Int
-)
+) {
+ operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z)
+ infix fun addX(o: Int) = Vec3i(x + o, y, z)
+ infix fun addY(o: Int) = Vec3i(x, y + o, z)
+ infix fun addZ(o: Int) = Vec3i(x, y, z + o)
+ fun add(ox: Int, oy: Int, oz: Int) = Vec3i(x + ox, y + oy, z + oz)
+}
-data class MutableVec3i(
- var x: Int,
- var y: Int,
- var z: Int
-) \ No newline at end of file
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun World.get(vec: Vec3i): Block = getBlockAt(vec.x, vec.y, vec.z) \ No newline at end of file