diff options
author | Dico Karssiens <dico.karssiens@gmail.com> | 2018-07-30 04:49:43 +0100 |
---|---|---|
committer | Dico Karssiens <dico.karssiens@gmail.com> | 2018-07-30 04:49:43 +0100 |
commit | dee994b992b1c6df15f6c24b49cd27c25e0657d6 (patch) | |
tree | 0cdc22b00b1823b13468864ce0fc7a30fd9d7f79 | |
parent | 33bb19a54779547726165fa47d20f222cbe1e0a6 (diff) |
Add WorktimeLimiter API, basic /parcel clear functionality
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 |