Work on schematic and parcel swaps
This commit is contained in:
@@ -67,7 +67,7 @@ dependencies {
|
||||
c.kotlinStd(kotlin("stdlib-jdk8"))
|
||||
c.kotlinStd(kotlin("reflect"))
|
||||
c.kotlinStd(kotlinx("coroutines-core:0.26.1-eap13"))
|
||||
c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.7-rc-conf")
|
||||
c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.7-eap13")
|
||||
|
||||
// not on sk89q maven repo yet
|
||||
compileClasspath(files("$rootDir/debug/plugins/worldedit-bukkit-7.0.0-beta-01.jar"))
|
||||
|
||||
@@ -59,7 +59,9 @@ interface ParcelBlockManager {
|
||||
|
||||
fun clearParcel(parcel: ParcelId): Worker
|
||||
|
||||
fun submitBlockVisitor(parcelId: ParcelId, task: TimeLimitedTask): Worker
|
||||
fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Worker
|
||||
|
||||
fun submitBlockVisitor(vararg parcelIds: ParcelId, task: TimeLimitedTask): Worker
|
||||
|
||||
/**
|
||||
* Used to update owner blocks in the corner of the parcel
|
||||
|
||||
@@ -226,19 +226,19 @@ class PlayerProfile2 private constructor(uuid: UUID?,
|
||||
?: PlayerProfile(null, input, !allowFake)
|
||||
}
|
||||
|
||||
operator fun invoke(name: String): PlayerProfile {
|
||||
operator fun createWith(name: String): PlayerProfile {
|
||||
if (name == star.name) return star
|
||||
return PlayerProfile(null, name)
|
||||
}
|
||||
|
||||
operator fun invoke(uuid: UUID): PlayerProfile {
|
||||
operator fun createWith(uuid: UUID): PlayerProfile {
|
||||
if (uuid == star.uuid) return star
|
||||
return PlayerProfile(uuid, null)
|
||||
}
|
||||
|
||||
operator fun invoke(player: OfflinePlayer): PlayerProfile {
|
||||
operator fun createWith(player: OfflinePlayer): PlayerProfile {
|
||||
// avoid UUID comparison against STAR
|
||||
return if (player.isValid) PlayerProfile(player.uuid, player.name) else invoke(player.name)
|
||||
return if (player.isValid) PlayerProfile(player.uuid, player.name) else createWith(player.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ interface Privileges : PrivilegesMinimal {
|
||||
}
|
||||
|
||||
fun changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult =
|
||||
if (key != keyOfOwner) FAIL_OWNER
|
||||
if (key == keyOfOwner) FAIL_OWNER
|
||||
else if (getStoredPrivilege(key).isChangeInDirection(positive, update)
|
||||
&& setStoredPrivilege(key, update)
|
||||
) SUCCESS
|
||||
|
||||
@@ -46,7 +46,7 @@ private val attachables = EnumSet.of(
|
||||
|
||||
fun isAttachable(type: Material) = attachables.contains(type)
|
||||
|
||||
fun supportingBlock(data: BlockData): Vec3i = when (data) {
|
||||
fun getSupportingBlock(data: BlockData): Vec3i = when (data) {
|
||||
//is MultipleFacing -> // fuck it xD this is good enough
|
||||
|
||||
is Directional -> Vec3i.convert(when (data.material) {
|
||||
|
||||
@@ -28,8 +28,8 @@ sealed class RegionTraverser {
|
||||
protected abstract suspend fun Scope.build(region: Region)
|
||||
|
||||
companion object {
|
||||
val upward = Directional(TraverseDirection(1, 1, 1), TraverseOrder(Dimension.Y, Dimension.X))
|
||||
val downward = Directional(TraverseDirection(1, -1, 1), TraverseOrder(Dimension.Y, Dimension.X))
|
||||
val upward = Directional(TraverseDirection(1, 1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
|
||||
val downward = Directional(TraverseDirection(1, -1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
|
||||
val toClear get() = downward
|
||||
val toFill get() = upward
|
||||
|
||||
@@ -42,8 +42,34 @@ sealed class RegionTraverser {
|
||||
val direction: TraverseDirection,
|
||||
val order: TraverseOrder
|
||||
) : RegionTraverser() {
|
||||
|
||||
private inline fun iterate(max: Int, increasing: Boolean, action: (Int) -> Unit) {
|
||||
for (i in 0..max) {
|
||||
action(if (increasing) i else max - i)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun Scope.build(region: Region) {
|
||||
//traverserLogic(region, order, direction)
|
||||
val order = order
|
||||
val (primary, secondary, tertiary) = order.toArray()
|
||||
val (origin, size) = region
|
||||
|
||||
val maxOfPrimary = primary.extract(size) - 1
|
||||
val maxOfSecondary = secondary.extract(size) - 1
|
||||
val maxOfTertiary = tertiary.extract(size) - 1
|
||||
|
||||
val isPrimaryIncreasing = direction.isIncreasing(primary)
|
||||
val isSecondaryIncreasing = direction.isIncreasing(secondary)
|
||||
val isTertiaryIncreasing = direction.isIncreasing(tertiary)
|
||||
|
||||
iterate(maxOfPrimary, isPrimaryIncreasing) { p ->
|
||||
iterate(maxOfSecondary, isSecondaryIncreasing) { s ->
|
||||
iterate(maxOfTertiary, isTertiaryIncreasing) { t ->
|
||||
yield(order.add(origin, p, s, t))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -77,6 +103,38 @@ sealed class RegionTraverser {
|
||||
}
|
||||
}
|
||||
|
||||
fun childForPosition(position: Vec3i): Directional {
|
||||
var cur = this
|
||||
while (true) {
|
||||
when (cur) {
|
||||
is Directional ->
|
||||
return cur
|
||||
is Slicing ->
|
||||
cur =
|
||||
if (position.y <= cur.bottomSectionMaxY) cur.bottomTraverser
|
||||
else cur.topTraverser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun comesFirst(current: Vec3i, block: Vec3i): Boolean {
|
||||
var cur = this
|
||||
while (true) {
|
||||
when (cur) {
|
||||
is Directional -> return cur.direction.comesFirst(current, block)
|
||||
is Slicing -> {
|
||||
val border = cur.bottomSectionMaxY
|
||||
cur = when {
|
||||
current.y <= border && block.y <= border -> cur.bottomTraverser
|
||||
current.y <= border -> return !cur.bottomFirst
|
||||
block.y <= border -> return cur.bottomFirst
|
||||
else -> cur.topTraverser
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum class Dimension {
|
||||
@@ -97,8 +155,18 @@ enum class Dimension {
|
||||
}
|
||||
}
|
||||
|
||||
object TraverseOrderFactory {
|
||||
private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3
|
||||
|
||||
fun createWith(primary: Dimension, secondary: Dimension): TraverseOrder {
|
||||
// tertiary is implicit
|
||||
if (primary == secondary) throw IllegalArgumentException()
|
||||
return TraverseOrder(primary, isSwap(primary, secondary))
|
||||
}
|
||||
}
|
||||
|
||||
inline class TraverseOrder(val orderNum: Int) {
|
||||
private constructor(first: Dimension, swap: Boolean)
|
||||
constructor(first: Dimension, swap: Boolean)
|
||||
: this(if (swap) first.ordinal + 3 else first.ordinal)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@@ -126,18 +194,8 @@ inline class TraverseOrder(val orderNum: Int) {
|
||||
*/
|
||||
fun toArray() = arrayOf(primary, secondary, tertiary)
|
||||
|
||||
companion object {
|
||||
private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3
|
||||
|
||||
operator fun invoke(primary: Dimension, secondary: Dimension): TraverseOrder {
|
||||
// tertiary is implicit
|
||||
if (primary == secondary) throw IllegalArgumentException()
|
||||
return TraverseOrder(primary, isSwap(primary, secondary))
|
||||
}
|
||||
}
|
||||
|
||||
fun add(vec: Vec3i, dp: Int, ds: Int, dt: Int): Vec3i =
|
||||
// optimize this, will be called lots
|
||||
// optimize this, will be called lots
|
||||
when (orderNum) {
|
||||
0 -> vec.add(dp, ds, dt) // xyz
|
||||
1 -> vec.add(dt, dp, ds) // yzx
|
||||
@@ -149,48 +207,19 @@ inline class TraverseOrder(val orderNum: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
class AltTraverser(val size: Vec3i,
|
||||
val order: TraverseOrder,
|
||||
val direction: TraverseDirection) {
|
||||
|
||||
|
||||
suspend fun Scope.build() {
|
||||
doPrimary()
|
||||
}
|
||||
|
||||
private suspend fun Scope.doPrimary() {
|
||||
val dimension = order.primary
|
||||
direction.directionOf(dimension).traverse(dimension.extract(size)) { value ->
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun Dimension.setValue(value: Int) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum class Increment(val offset: Int) {
|
||||
UP(1),
|
||||
DOWN(-1);
|
||||
|
||||
companion object {
|
||||
fun convert(bool: Boolean) = if (bool) UP else DOWN
|
||||
}
|
||||
|
||||
inline fun traverse(size: Int, op: (Int) -> Unit) {
|
||||
when (this) {
|
||||
UP -> repeat(size, op)
|
||||
DOWN -> repeat(size) { op(size - it - 1) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline class TraverseDirection(val bits: Int) {
|
||||
fun isIncreasing(dimension: Dimension) = (1 shl dimension.ordinal) and bits != 0
|
||||
|
||||
fun directionOf(dimension: Dimension) = Increment.convert((1 shl dimension.ordinal) and bits != 0)
|
||||
fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean =
|
||||
if (isIncreasing(dimension))
|
||||
dimension.extract(block) <= dimension.extract(current)
|
||||
else
|
||||
dimension.extract(block) >= dimension.extract(current)
|
||||
|
||||
fun comesFirst(current: Vec3i, block: Vec3i) =
|
||||
comesFirst(current, block, Dimension.X)
|
||||
&& comesFirst(current, block, Dimension.Y)
|
||||
&& comesFirst(current, block, Dimension.Z)
|
||||
|
||||
companion object {
|
||||
operator fun invoke(x: Int, y: Int, z: Int) = invoke(Vec3i(x, y, z))
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.dico.parcels2.util.get
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.World
|
||||
import org.bukkit.block.Block
|
||||
import org.bukkit.block.data.BlockData
|
||||
|
||||
private val air = Bukkit.createBlockData(Material.AIR)
|
||||
@@ -20,19 +21,21 @@ class Schematic {
|
||||
}
|
||||
|
||||
private var blockDatas: Array<BlockData?>? = null
|
||||
//private var extra: Map<Vec3i, (Block) -> Unit>? = null
|
||||
private val extra = mutableMapOf<Vec3i, (Block) -> Unit>()
|
||||
private var isLoaded = false; private set
|
||||
private val traverser: RegionTraverser = RegionTraverser.upward
|
||||
|
||||
fun getLoadTask(world: World, region: Region): TimeLimitedTask = {
|
||||
suspend fun WorkerScope.load(world: World, region: Region) {
|
||||
_size = region.size
|
||||
|
||||
val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it }
|
||||
//val extra = mutableMapOf<Vec3i, (Block) -> Unit>().also { extra = it }
|
||||
val blocks = traverser.traverseRegion(region)
|
||||
val total = region.blockCount.toDouble()
|
||||
|
||||
for ((index, vec) in blocks.withIndex()) {
|
||||
markSuspensionPoint()
|
||||
setProgress(index / total)
|
||||
|
||||
val block = world[vec]
|
||||
if (block.y > 255) continue
|
||||
val blockData = block.blockData
|
||||
@@ -42,30 +45,56 @@ class Schematic {
|
||||
isLoaded = true
|
||||
}
|
||||
|
||||
fun getPasteTask(world: World, position: Vec3i): TimeLimitedTask = {
|
||||
suspend fun WorkerScope.paste(world: World, position: Vec3i) {
|
||||
if (!isLoaded) throw IllegalStateException()
|
||||
val region = Region(position, _size!!)
|
||||
val blocks = traverser.traverseRegion(region, worldHeight = world.maxHeight)
|
||||
val blockDatas = blockDatas!!
|
||||
var postponed = hashMapOf<Vec3i, BlockData>()
|
||||
|
||||
val postponed = mutableListOf<Pair<Vec3i, BlockData>>()
|
||||
// 90% of the progress of this job is allocated to this code block
|
||||
delegateWork(0.9) {
|
||||
for ((index, vec) in blocks.withIndex()) {
|
||||
markSuspensionPoint()
|
||||
val block = world[vec]
|
||||
val type = blockDatas[index] ?: air
|
||||
if (type !== air && isAttachable(type.material)) {
|
||||
val supportingBlock = vec + getSupportingBlock(type)
|
||||
|
||||
for ((index, vec) in blocks.withIndex()) {
|
||||
markSuspensionPoint()
|
||||
val block = world[vec]
|
||||
val type = blockDatas[index] ?: air
|
||||
if (type !== air && isAttachable(type.material)) {
|
||||
if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) {
|
||||
block.blockData = type
|
||||
} else {
|
||||
postponed[vec] = type
|
||||
}
|
||||
|
||||
|
||||
postponed += vec to type
|
||||
} else {
|
||||
block.blockData = type
|
||||
} else {
|
||||
block.blockData = type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ((vec, data) in postponed) {
|
||||
|
||||
delegateWork {
|
||||
while (!postponed.isEmpty()) {
|
||||
val newMap = hashMapOf<Vec3i, BlockData>()
|
||||
for ((vec, type) in postponed) {
|
||||
val supportingBlock = vec + getSupportingBlock(type)
|
||||
if (supportingBlock in postponed && supportingBlock != vec) {
|
||||
newMap[vec] = type
|
||||
} else {
|
||||
world[vec].blockData = type
|
||||
}
|
||||
}
|
||||
postponed = newMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getLoadTask(world: World, region: Region): TimeLimitedTask = {
|
||||
load(world, region)
|
||||
}
|
||||
|
||||
fun getPasteTask(world: World, position: Vec3i): TimeLimitedTask = {
|
||||
paste(world, position)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.dico.parcels2.blockvisitor
|
||||
|
||||
import io.dico.parcels2.ParcelsPlugin
|
||||
import io.dico.parcels2.logger
|
||||
import io.dico.parcels2.util.ext.clampMin
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart.LAZY
|
||||
@@ -14,7 +15,6 @@ 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
|
||||
@@ -48,9 +48,9 @@ interface Timed {
|
||||
|
||||
interface Worker : Timed {
|
||||
/**
|
||||
* The coroutine associated with this worker, if any
|
||||
* The coroutine associated with this worker
|
||||
*/
|
||||
val job: Job?
|
||||
val job: Job
|
||||
|
||||
/**
|
||||
* true if this worker has completed
|
||||
@@ -65,9 +65,9 @@ interface Worker : Timed {
|
||||
|
||||
/**
|
||||
* A value indicating the progress of this worker, in the range 0.0 <= progress <= 1.0
|
||||
* with no guarantees to its accuracy. May be null.
|
||||
* with no guarantees to its accuracy.
|
||||
*/
|
||||
val progress: Double?
|
||||
val progress: Double
|
||||
|
||||
/**
|
||||
* Calls the given [block] whenever the progress of this worker is updated,
|
||||
@@ -89,6 +89,11 @@ interface Worker : Timed {
|
||||
* Await completion of this worker
|
||||
*/
|
||||
suspend fun awaitCompletion()
|
||||
|
||||
/**
|
||||
* An object attached to this worker
|
||||
*/
|
||||
//val attachment: Any?
|
||||
}
|
||||
|
||||
interface WorkerScope : Timed {
|
||||
@@ -97,10 +102,35 @@ interface WorkerScope : Timed {
|
||||
*/
|
||||
suspend fun markSuspensionPoint()
|
||||
|
||||
/**
|
||||
* A value indicating the progress of this worker, in the range 0.0 <= progress <= 1.0
|
||||
* with no guarantees to its accuracy.
|
||||
*/
|
||||
val progress: Double
|
||||
|
||||
/**
|
||||
* A task should call this method to indicate its progress
|
||||
*/
|
||||
fun setProgress(progress: Double)
|
||||
|
||||
/**
|
||||
* Indicate that this job is complete
|
||||
*/
|
||||
fun markComplete() = setProgress(1.0)
|
||||
|
||||
/**
|
||||
* Get a [WorkerScope] that is responsible for [portion] part of the progress
|
||||
* If [portion] is negative, the remainder of the progress is used
|
||||
*/
|
||||
fun delegateWork(portion: Double = -1.0): WorkerScope
|
||||
}
|
||||
|
||||
inline fun <T> WorkerScope.delegateWork(portion: Double = -1.0, block: WorkerScope.() -> T): T {
|
||||
delegateWork(portion).apply {
|
||||
val result = block()
|
||||
markComplete()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
interface WorkerInternal : Worker, WorkerScope {
|
||||
@@ -179,37 +209,32 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo
|
||||
|
||||
}
|
||||
|
||||
private class WorkerImpl(
|
||||
val scope: CoroutineScope,
|
||||
val task: TimeLimitedTask
|
||||
) : WorkerInternal, CoroutineScope by scope {
|
||||
override var job: Job? = null; private set
|
||||
private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerInternal {
|
||||
override val job: Job = scope.launch(start = LAZY) { task() }
|
||||
|
||||
private var continuation: Continuation<Unit>? = null
|
||||
private var nextSuspensionTime: Long = 0L
|
||||
private var completeForcefully = false
|
||||
private var isStarted = false
|
||||
|
||||
override val elapsedTime
|
||||
get() = job?.let {
|
||||
if (it.isCompleted) startTimeOrElapsedTime
|
||||
get() =
|
||||
if (job.isCompleted) startTimeOrElapsedTime
|
||||
else currentTimeMillis() - startTimeOrElapsedTime
|
||||
} ?: 0L
|
||||
|
||||
override val isComplete get() = job?.isCompleted == true
|
||||
override val isComplete get() = job.isCompleted
|
||||
|
||||
private var _progress = 0.0
|
||||
override val progress get() = _progress
|
||||
override var completionException: Throwable? = null; private set
|
||||
|
||||
override var progress: Double? = null; private set
|
||||
|
||||
private var startTimeOrElapsedTime: Long = 0L // startTime before completed, elapsed time otherwise
|
||||
private var onProgressUpdate: WorkerUpdateLister? = null
|
||||
private var progressUpdateInterval: Int = 0
|
||||
private var lastUpdateTime: Long = 0L
|
||||
private var onCompleted: WorkerUpdateLister? = null
|
||||
private var continuation: Continuation<Unit>? = null
|
||||
private var nextSuspensionTime: Long = 0L
|
||||
private var completeForcefully = false
|
||||
|
||||
private fun initJob(job: Job) {
|
||||
this.job?.let { throw IllegalStateException() }
|
||||
this.job = job
|
||||
startTimeOrElapsedTime = System.currentTimeMillis()
|
||||
init {
|
||||
job.invokeOnCompletion { exception ->
|
||||
// report any error that occurred
|
||||
completionException = exception?.also {
|
||||
@@ -264,7 +289,7 @@ private class WorkerImpl(
|
||||
}
|
||||
|
||||
override fun setProgress(progress: Double) {
|
||||
this.progress = progress
|
||||
this._progress = progress
|
||||
val onProgressUpdate = onProgressUpdate ?: return
|
||||
val time = System.currentTimeMillis()
|
||||
if (time > lastUpdateTime + progressUpdateInterval) {
|
||||
@@ -274,46 +299,54 @@ private class WorkerImpl(
|
||||
}
|
||||
|
||||
override fun resume(worktime: Long): Boolean {
|
||||
if (isComplete) return true
|
||||
|
||||
if (worktime > 0) {
|
||||
nextSuspensionTime = currentTimeMillis() + worktime
|
||||
} else {
|
||||
completeForcefully = true
|
||||
}
|
||||
|
||||
continuation?.let {
|
||||
continuation = null
|
||||
it.resume(Unit)
|
||||
return continuation == null
|
||||
if (isStarted) {
|
||||
continuation?.let {
|
||||
continuation = null
|
||||
it.resume(Unit)
|
||||
return continuation == null
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
job?.let {
|
||||
nextSuspensionTime = 0L
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
try {
|
||||
val job = launch(start = LAZY) { task() }
|
||||
initJob(job = job)
|
||||
job.start()
|
||||
} catch (t: Throwable) {
|
||||
// do nothing: handled by job.invokeOnCompletion()
|
||||
}
|
||||
startTimeOrElapsedTime = System.currentTimeMillis()
|
||||
job.start()
|
||||
|
||||
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) }
|
||||
}
|
||||
job.join()
|
||||
}
|
||||
|
||||
private fun delegateWork(curPortion: Double, portion: Double): WorkerScope =
|
||||
DelegateScope(progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0))
|
||||
|
||||
override fun delegateWork(portion: Double): WorkerScope = delegateWork(1.0, portion)
|
||||
|
||||
private inner class DelegateScope(val progressStart: Double, val portion: Double) : WorkerScope {
|
||||
override val elapsedTime: Long
|
||||
get() = this@WorkerImpl.elapsedTime
|
||||
|
||||
override suspend fun markSuspensionPoint() =
|
||||
this@WorkerImpl.markSuspensionPoint()
|
||||
|
||||
override val progress: Double
|
||||
get() = (this@WorkerImpl.progress - progressStart) / portion
|
||||
|
||||
override fun setProgress(progress: Double) =
|
||||
this@WorkerImpl.setProgress(progressStart + progress * portion)
|
||||
|
||||
override fun delegateWork(portion: Double): WorkerScope =
|
||||
this@WorkerImpl.delegateWork(this.portion, portion)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@ import io.dico.dicore.command.*
|
||||
import io.dico.parcels2.ParcelWorld
|
||||
import io.dico.parcels2.ParcelsPlugin
|
||||
import io.dico.parcels2.PlayerProfile
|
||||
import io.dico.parcels2.PrivilegeChangeResult
|
||||
import io.dico.parcels2.blockvisitor.Worker
|
||||
import io.dico.parcels2.util.ext.hasPermAdminManage
|
||||
import io.dico.parcels2.util.ext.parcelLimit
|
||||
import org.bukkit.entity.Player
|
||||
@@ -43,14 +43,17 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
|
||||
protected fun ParcelScope.clearWithProgressUpdates(context: ExecutionContext, action: String) {
|
||||
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun Worker.reportProgressUpdates(context: ExecutionContext, action: String) {
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun err(message: String): Nothing = throw CommandException(message)
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package io.dico.parcels2.command
|
||||
|
||||
import io.dico.dicore.command.CommandException
|
||||
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.Flag
|
||||
import io.dico.parcels2.ParcelsPlugin
|
||||
import io.dico.parcels2.PlayerProfile
|
||||
import io.dico.parcels2.Privilege
|
||||
import io.dico.parcels2.command.ParcelTarget.Companion.ID
|
||||
import io.dico.parcels2.command.ParcelTarget.Kind
|
||||
|
||||
class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
||||
|
||||
@@ -28,18 +32,34 @@ class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
||||
@Cmd("reset")
|
||||
@RequireParcelPrivilege(Privilege.ADMIN)
|
||||
fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? {
|
||||
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
|
||||
if (!sure) return areYouSureMessage(context)
|
||||
|
||||
parcel.dispose()
|
||||
clearWithProgressUpdates(context, "Reset")
|
||||
return null
|
||||
world.blockManager.clearParcel(parcel.id).reportProgressUpdates(context, "Reset")
|
||||
return "Data of (${parcel.id.idString}) has been disposed"
|
||||
}
|
||||
|
||||
@Cmd("swap")
|
||||
@RequireParcelPrivilege(Privilege.ADMIN)
|
||||
fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? {
|
||||
fun ParcelScope.cmdSwap(context: ExecutionContext,
|
||||
@Kind(ID) target: ParcelTarget,
|
||||
@Flag sure: Boolean): Any? {
|
||||
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
|
||||
if (!sure) return areYouSureMessage(context)
|
||||
TODO("implement swap")
|
||||
|
||||
val parcel2 = (target as ParcelTarget.ByID).getParcel()
|
||||
?: throw CommandException("Invalid parcel target")
|
||||
|
||||
Validate.isTrue(parcel2.world == world, "Parcel must be in the same world")
|
||||
Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in this parcel")
|
||||
|
||||
val data = parcel.data
|
||||
parcel.copyData(parcel2.data)
|
||||
parcel2.copyData(data)
|
||||
|
||||
world.blockManager.swapParcels(parcel.id, parcel2.id).reportProgressUpdates(context, "Swap")
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -3,10 +3,12 @@ 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.Validate
|
||||
import io.dico.dicore.command.annotation.Cmd
|
||||
import io.dico.parcels2.ParcelsPlugin
|
||||
import io.dico.parcels2.Privilege
|
||||
import io.dico.parcels2.blockvisitor.RegionTraverser
|
||||
import io.dico.parcels2.blockvisitor.TickWorktimeLimiter
|
||||
import io.dico.parcels2.doBlockOperation
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Material
|
||||
@@ -35,6 +37,8 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
||||
@Cmd("make_mess")
|
||||
@RequireParcelPrivilege(Privilege.OWNER)
|
||||
fun ParcelScope.cmdMakeMess(context: ExecutionContext) {
|
||||
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
|
||||
|
||||
val server = plugin.server
|
||||
val blockDatas = arrayOf(
|
||||
server.createBlockData(Material.BLUE_WOOL),
|
||||
@@ -72,4 +76,18 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
||||
blockData.javaClass.interfaces!!.contentToString()
|
||||
}
|
||||
|
||||
@Cmd("visitors")
|
||||
fun cmdVisitors(): Any? {
|
||||
val workers = plugin.worktimeLimiter.workers
|
||||
println(workers.map { it.job }.joinToString(separator = "\n"))
|
||||
return "Task count: ${workers.size}"
|
||||
}
|
||||
|
||||
@Cmd("force_visitors")
|
||||
fun cmdForceVisitors(): Any? {
|
||||
val workers = plugin.worktimeLimiter.workers
|
||||
plugin.worktimeLimiter.completeAllTasks()
|
||||
return "Task count: ${workers.size}"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -120,9 +120,9 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
||||
@Cmd("clear")
|
||||
@RequireParcelPrivilege(Privilege.OWNER)
|
||||
fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? {
|
||||
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
|
||||
if (!sure) return areYouSureMessage(context)
|
||||
|
||||
clearWithProgressUpdates(context, "Clear")
|
||||
world.blockManager.clearParcel(parcel.id).reportProgressUpdates(context, "Clear")
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -130,8 +130,8 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
||||
@RequireParcelPrivilege(Privilege.OWNER)
|
||||
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"
|
||||
world.blockManager.setBiome(parcel.id, biome).reportProgressUpdates(context, "Biome change")
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,13 @@ import org.bukkit.entity.Player
|
||||
|
||||
class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
||||
|
||||
private fun ParcelScope.checkPrivilege(sender: Player, player: OfflinePlayer) {
|
||||
if (!sender.hasPermAdminManage) {
|
||||
Validate.isTrue(parcel.owner != null, "This parcel is unowned")
|
||||
Validate.isTrue(parcel.privilege(sender) > parcel.privilege(player), "You may not change the privilege of ${player.name}")
|
||||
}
|
||||
}
|
||||
|
||||
@Cmd("entrust")
|
||||
@Desc(
|
||||
"Allows a player to manage this parcel",
|
||||
@@ -52,8 +59,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
|
||||
)
|
||||
@RequireParcelPrivilege(Privilege.CAN_MANAGE)
|
||||
fun ParcelScope.cmdAllow(sender: Player, player: OfflinePlayer): Any? {
|
||||
Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
|
||||
Validate.isTrue(parcel.privilege(sender) > parcel.privilege(player), "You may not change the privilege of ${player.name}")
|
||||
checkPrivilege(sender, player)
|
||||
|
||||
return when (parcel.allowBuild(player)) {
|
||||
FAIL_OWNER -> err("The target already owns the parcel")
|
||||
@@ -70,8 +76,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
|
||||
)
|
||||
@RequireParcelPrivilege(Privilege.CAN_MANAGE)
|
||||
fun ParcelScope.cmdDisallow(sender: Player, player: OfflinePlayer): Any? {
|
||||
Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
|
||||
Validate.isTrue(parcel.privilege(sender) > parcel.privilege(player), "You may not change the privilege of ${player.name}")
|
||||
checkPrivilege(sender, player)
|
||||
|
||||
return when (parcel.disallowBuild(player)) {
|
||||
FAIL_OWNER -> err("The target owns the parcel")
|
||||
@@ -88,8 +93,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
|
||||
)
|
||||
@RequireParcelPrivilege(Privilege.CAN_MANAGE)
|
||||
fun ParcelScope.cmdBan(sender: Player, player: OfflinePlayer): Any? {
|
||||
Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
|
||||
Validate.isTrue(parcel.privilege(sender) > parcel.privilege(player), "You may not change the privilege of ${player.name}")
|
||||
checkPrivilege(sender, player)
|
||||
|
||||
return when (parcel.disallowBuild(player)) {
|
||||
FAIL_OWNER -> err("The target owns the parcel")
|
||||
@@ -106,8 +110,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
|
||||
)
|
||||
@RequireParcelPrivilege(Privilege.CAN_MANAGE)
|
||||
fun ParcelScope.cmdUnban(sender: Player, player: OfflinePlayer): Any? {
|
||||
Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
|
||||
Validate.isTrue(parcel.privilege(sender) > parcel.privilege(player), "You may not change the privilege of ${player.name}")
|
||||
checkPrivilege(sender, player)
|
||||
|
||||
return when (parcel.disallowBuild(player)) {
|
||||
FAIL_OWNER -> err("The target owns the parcel")
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
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.blockvisitor.*
|
||||
import io.dico.parcels2.options.DefaultGeneratorOptions
|
||||
import io.dico.parcels2.util.Region
|
||||
import io.dico.parcels2.util.Vec2i
|
||||
@@ -225,15 +222,18 @@ class DefaultParcelGenerator(
|
||||
return world.getParcelById(parcelId)
|
||||
}
|
||||
|
||||
override 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")
|
||||
override fun submitBlockVisitor(vararg parcelIds: ParcelId, task: TimeLimitedTask): Worker {
|
||||
val parcels = parcelIds.mapNotNull { getParcel(it) }
|
||||
if (parcels.isEmpty()) return worktimeLimiter.submit(task)
|
||||
if (parcels.any { it.hasBlockVisitors }) throw IllegalArgumentException("This parcel already has a block visitor")
|
||||
|
||||
val worker = worktimeLimiter.submit(task)
|
||||
|
||||
launch(start = UNDISPATCHED) {
|
||||
parcel.withBlockVisitorPermit {
|
||||
worker.awaitCompletion()
|
||||
for (parcel in parcels) {
|
||||
launch(start = UNDISPATCHED) {
|
||||
parcel.withBlockVisitorPermit {
|
||||
worker.awaitCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +276,13 @@ class DefaultParcelGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
override fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Worker = submitBlockVisitor(parcel1, parcel2) {
|
||||
val schematicOf1 = delegateWork(0.15) { Schematic().apply { load(world, getRegion(parcel1)) } }
|
||||
val schematicOf2 = delegateWork(0.15) { Schematic().apply { load(world, getRegion(parcel2)) } }
|
||||
delegateWork(0.35) { with(schematicOf1) { paste(world, getRegion(parcel2).origin) } }
|
||||
delegateWork(0.35) { with(schematicOf2) { paste(world, getRegion(parcel1).origin) } }
|
||||
}
|
||||
|
||||
override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> {
|
||||
/*
|
||||
* Get the offsets for the world out of the way
|
||||
|
||||
@@ -33,6 +33,7 @@ fun IntRange.clamp(min: Int, max: Int): IntRange {
|
||||
|
||||
// the name coerceAtMost is bad
|
||||
fun Int.clampMax(max: Int) = coerceAtMost(max)
|
||||
fun Double.clampMin(min: Double) = coerceAtLeast(min)
|
||||
|
||||
// Why does this not exist?
|
||||
infix fun Int.ceilDiv(divisor: Int): Int {
|
||||
|
||||
Reference in New Issue
Block a user