Add WorktimeLimiter API, basic /parcel clear functionality
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
67
src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
Normal file
67
src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
Normal file
@@ -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
|
||||
);
|
||||
@@ -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)
|
||||
@@ -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) } }
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
package io.dico.parcels2.blockvisitor
|
||||
|
||||
data class BlockVisitorOptions(var pauseTicks: Int = 1, var workMillis: Int = 30)
|
||||
@@ -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) } }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
207
src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt
Normal file
207
src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt
Normal file
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
package io.dico.parcels2.util
|
||||
|
||||
data class Region(val origin: Vec3i, val size: Vec3i)
|
||||
data class Region(val origin: Vec3i, val size: Vec3i) {
|
||||
val blockCount: Int get() = size.x * size.y * size.z
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun World.get(vec: Vec3i): Block = getBlockAt(vec.x, vec.y, vec.z)
|
||||
Reference in New Issue
Block a user