diff options
author | Dico <dico.karssiens@gmail.com> | 2018-09-26 07:08:42 +0100 |
---|---|---|
committer | Dico <dico.karssiens@gmail.com> | 2018-09-26 07:08:42 +0100 |
commit | 520ae530d2de076fa9e87da7f04fcf78e080f4de (patch) | |
tree | dafbe3c14d5b6cbe49eef4f87499d35d1c69676d /src | |
parent | 2225bdae95b3de4985347edf30ae31a28f73f35b (diff) |
Make progress
Diffstat (limited to 'src')
15 files changed, 217 insertions, 190 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt index c11d557..4e0aeb4 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt @@ -37,12 +37,12 @@ abstract class ParcelGenerator : ChunkGenerator() { abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId, container: ParcelContainer, coroutineScope: CoroutineScope, - worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager> + workDispatcher: WorkDispatcher): Pair<ParcelLocator, ParcelBlockManager> } interface ParcelBlockManager { val world: World - val worktimeLimiter: WorktimeLimiter + val workDispatcher: WorkDispatcher val parcelTraverser: RegionTraverser // fun getBottomBlock(parcel: ParcelId): Vec2i @@ -61,7 +61,7 @@ interface ParcelBlockManager { fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Worker - fun submitBlockVisitor(vararg parcelIds: ParcelId, task: TimeLimitedTask): Worker + fun submitBlockVisitor(vararg parcelIds: ParcelId, task: WorkerTask): Worker /** * Used to update owner blocks in the corner of the parcel diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt index 3e12ba5..7af7468 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt @@ -3,8 +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.blockvisitor.BukkitWorkDispatcher +import io.dico.parcels2.blockvisitor.WorkDispatcher import io.dico.parcels2.command.getParcelCommands import io.dico.parcels2.defaultimpl.GlobalPrivilegesManagerImpl import io.dico.parcels2.defaultimpl.ParcelProviderImpl @@ -44,7 +44,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler { override val coroutineContext: CoroutineContext = MainThreadDispatcher(this) override val plugin: Plugin get() = this - val worktimeLimiter: WorktimeLimiter by lazy { TickWorktimeLimiter(this, options.tickWorktime) } + val workDispatcher: WorkDispatcher by lazy { BukkitWorkDispatcher(this, options.tickWorktime) } override fun onEnable() { plogger.info("Debug enabled: ${plogger.isDebugEnabled}") @@ -55,11 +55,11 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler { } override fun onDisable() { - val hasWorkers = worktimeLimiter.workers.isNotEmpty() + val hasWorkers = workDispatcher.workers.isNotEmpty() if (hasWorkers) { - plogger.warn("Parcels is attempting to complete all ${worktimeLimiter.workers.size} remaining jobs before shutdown...") + plogger.warn("Parcels is attempting to complete all ${workDispatcher.workers.size} remaining jobs before shutdown...") } - worktimeLimiter.completeAllTasks() + workDispatcher.completeAllTasks() if (hasWorkers) { plogger.info("Parcels has completed the remaining jobs.") } diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt new file mode 100644 index 0000000..3f7e070 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt @@ -0,0 +1,38 @@ +package io.dico.parcels2.blockvisitor + +import org.bukkit.block.Block +import org.bukkit.block.BlockState +import org.bukkit.block.Sign +import kotlin.reflect.KClass + +interface ExtraBlockChange { + fun update(block: Block) +} + +abstract class BlockStateChange<T : BlockState> : ExtraBlockChange { + abstract val stateClass: KClass<T> + + abstract fun update(state: T) + + override fun update(block: Block) { + val state = block.state + if (stateClass.isInstance(state)) { + @Suppress("UNCHECKED_CAST") + update(state as T) + } + } +} + +class SignStateChange(state: Sign) : BlockStateChange<Sign>() { + val lines = state.lines + + override val stateClass: KClass<Sign> + get() = Sign::class + + override fun update(state: Sign) { + for (i in lines.indices) { + val line = lines[i] + state.setLine(i, line) + } + } +} diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt index 8f7f8f8..5326baa 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt @@ -235,87 +235,3 @@ inline class TraverseDirection(val bits: Int) { } } - -/* -private typealias Scope = SequenceScope<Vec3i> -private typealias ScopeAction = suspend Scope.(Int, Int, Int) -> Unit - -@Suppress("NON_EXHAUSTIVE_WHEN") -suspend fun Scope.traverserLogic( - region: Region, - order: TraverseOrder, - direction: TraverseDirection -) = with(direction) { - val (primary, secondary, tertiary) = order.toArray() - val (origin, size) = region - - when (order.primary) { - Dimension.X -> - when (order.secondary) { - Dimension.Y -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(p, s, t)) - } - } - } - } - Dimension.Z -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(p, t, s)) - } - } - } - } - } - - Dimension.Y -> - when (order.secondary) { - Dimension.X -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(s, p, t)) - } - } - } - } - Dimension.Z -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(t, p, s)) - } - } - } - } - } - - Dimension.Z -> - when (order.secondary) { - Dimension.X -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(s, t, p)) - } - } - } - } - Dimension.Y -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(t, s, p)) - } - } - } - } - } - } -} - -*/
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt index 9d9a2aa..9f88fd9 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt @@ -6,12 +6,11 @@ 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.Sign import org.bukkit.block.data.BlockData private val air = Bukkit.createBlockData(Material.AIR) -// TODO order paste such that attachables are placed after the block they depend on class Schematic { val size: Vec3i get() = _size!! private var _size: Vec3i? = null @@ -21,7 +20,7 @@ class Schematic { } private var blockDatas: Array<BlockData?>? = null - private val extra = mutableMapOf<Vec3i, (Block) -> Unit>() + private val extra = mutableListOf<Pair<Vec3i, ExtraBlockChange>>() private var isLoaded = false; private set private val traverser: RegionTraverser = RegionTraverser.upward @@ -32,7 +31,7 @@ class Schematic { val blocks = traverser.traverseRegion(region) val total = region.blockCount.toDouble() - for ((index, vec) in blocks.withIndex()) { + loop@ for ((index, vec) in blocks.withIndex()) { markSuspensionPoint() setProgress(index / total) @@ -40,6 +39,14 @@ class Schematic { if (block.y > 255) continue val blockData = block.blockData data[index] = blockData + + val extraChange = when (blockData.material) { + Material.SIGN, + Material.WALL_SIGN -> SignStateChange(block.state as Sign) + else -> continue@loop + } + + extra += (vec - region.origin) to extraChange } isLoaded = true @@ -47,53 +54,65 @@ class Schematic { 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>() - // 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) - - if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) { - block.blockData = type - } else { - postponed[vec] = type - } + val total = region.blockCount.toDouble() + var processed = 0 - } else { + for ((index, vec) in blocks.withIndex()) { + markSuspensionPoint() + setProgress(index / total) + + val block = world[vec] + val type = blockDatas[index] ?: air + if (type !== air && isAttachable(type.material)) { + val supportingBlock = vec + getSupportingBlock(type) + + if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) { block.blockData = type + setProgress(++processed / total) + } else { + postponed[vec] = type } + + } else { + block.blockData = type + setProgress(++processed / total) } } - 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 - } + while (!postponed.isEmpty()) { + markSuspensionPoint() + 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 + setProgress(++processed / total) } - postponed = newMap } + postponed = newMap + } + + // Should be negligible so we don't track progress + for ((vec, extraChange) in extra) { + markSuspensionPoint() + val block = world[position + vec] + extraChange.update(block) } } - fun getLoadTask(world: World, region: Region): TimeLimitedTask = { + fun getLoadTask(world: World, region: Region): WorkerTask = { load(world, region) } - fun getPasteTask(world: World, position: Vec3i): TimeLimitedTask = { + fun getPasteTask(world: World, position: Vec3i): WorkerTask = { paste(world, position) } diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/WorkDispatcher.kt index 553362e..7201a06 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/WorkDispatcher.kt @@ -16,17 +16,17 @@ import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn import kotlin.coroutines.resume -typealias TimeLimitedTask = suspend WorkerScope.() -> Unit +typealias WorkerTask = suspend WorkerScope.() -> Unit typealias WorkerUpdateLister = Worker.(Double, Long) -> Unit data class TickWorktimeOptions(var workTime: Int, var tickInterval: Int) -interface WorktimeLimiter { +interface WorkDispatcher { /** * Submit a [task] that should be run synchronously, but limited such that it does not stall the server * a bunch */ - fun submit(task: TimeLimitedTask): Worker + fun dispatch(task: WorkerTask): Worker /** * Get a list of all workers @@ -39,14 +39,20 @@ interface WorktimeLimiter { fun completeAllTasks() } -interface Timed { +interface WorkerAndScopeMembersUnion { /** * The time that elapsed since this worker was dispatched, in milliseconds */ val elapsedTime: Long + + /** + * 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 } -interface Worker : Timed { +interface Worker : WorkerAndScopeMembersUnion { /** * The coroutine associated with this worker */ @@ -64,12 +70,6 @@ interface Worker : Timed { val completionException: Throwable? /** - * 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 - - /** * Calls the given [block] whenever the progress of this worker is updated, * if [minInterval] milliseconds expired since the last call. * The first call occurs after at least [minDelay] milliseconds in a likewise manner. @@ -96,19 +96,13 @@ interface Worker : Timed { //val attachment: Any? } -interface WorkerScope : Timed { +interface WorkerScope : WorkerAndScopeMembersUnion { /** * A task should call this frequently during its execution, such that the timer can suspend it when necessary. */ 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) @@ -152,14 +146,14 @@ interface WorkerInternal : Worker, WorkerScope { * There is a configurable maxiumum amount of milliseconds that can be allocated to all workers together in each server tick * This object attempts to split that maximum amount of milliseconds equally between all jobs */ -class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWorktimeOptions) : WorktimeLimiter { +class BukkitWorkDispatcher(private val plugin: ParcelsPlugin, var options: TickWorktimeOptions) : WorkDispatcher { // The currently registered bukkit scheduler task private var bukkitTask: BukkitTask? = null // The workers. private val _workers = LinkedList<WorkerInternal>() override val workers: List<Worker> = _workers - override fun submit(task: TimeLimitedTask): Worker { + override fun dispatch(task: WorkerTask): Worker { val worker: WorkerInternal = WorkerImpl(plugin, task) if (bukkitTask == null) { @@ -209,7 +203,7 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo } -private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerInternal { +private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerInternal { override val job: Job = scope.launch(start = LAZY) { task() } private var continuation: Continuation<Unit>? = null @@ -239,7 +233,7 @@ private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerI // report any error that occurred completionException = exception?.also { if (it !is CancellationException) - logger.error("TimeLimitedTask generated an exception", it) + logger.error("WorkerTask generated an exception", it) } // convert to elapsed time here @@ -316,6 +310,7 @@ private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerI return true } + isStarted = true startTimeOrElapsedTime = System.currentTimeMillis() job.start() @@ -348,14 +343,3 @@ private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerI this@WorkerImpl.delegateWork(this.portion, portion) } } - -/* -/** - * 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 -} - */ diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt index 969d964..b6c7acd 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt @@ -8,7 +8,6 @@ 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 @@ -78,15 +77,15 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { @Cmd("visitors") fun cmdVisitors(): Any? { - val workers = plugin.worktimeLimiter.workers + val workers = plugin.workDispatcher.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() + val workers = plugin.workDispatcher.workers + plugin.workDispatcher.completeAllTasks() return "Task count: ${workers.size}" } diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt index 750fd96..67b873b 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt @@ -16,7 +16,7 @@ import io.dico.parcels2.util.ext.uuid import org.bukkit.block.Biome import org.bukkit.entity.Player -class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { +class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : AbstractParcelCommands(plugin) { @Cmd("auto") @Desc( @@ -43,6 +43,10 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { ) fun ParcelScope.cmdInfo(player: Player) = parcel.infoString + init { + parent.addSpeciallyTreatedKeys("home", "h") + } + @Cmd("home", aliases = ["h"]) @Desc( "Teleports you to your parcels,", diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt index 1eddf97..1f1e4a7 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt @@ -1,8 +1,6 @@ package io.dico.parcels2.command -import io.dico.dicore.command.CommandBuilder -import io.dico.dicore.command.ICommandAddress -import io.dico.dicore.command.ICommandDispatcher +import io.dico.dicore.command.* import io.dico.dicore.command.registration.reflect.ReflectiveRegistration import io.dico.parcels2.Interactables import io.dico.parcels2.ParcelsPlugin @@ -13,14 +11,16 @@ import java.util.Queue @Suppress("UsePropertyAccessSyntax") fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher = with(CommandBuilder()) { + val parcelsAddress = SpecialCommandAddress() + setChatController(ParcelsChatController()) addParameterType(false, ParcelParameterType(plugin.parcelProvider)) addParameterType(false, ProfileParameterType()) - addParameterType(true, ParcelTarget.PType(plugin.parcelProvider)) + addParameterType(true, ParcelTarget.PType(plugin.parcelProvider, parcelsAddress)) - group("parcel", "plot", "plots", "p") { + group(parcelsAddress, "parcel", "plot", "plots", "p") { addRequiredPermission("parcels.command") - registerCommands(CommandsGeneral(plugin)) + registerCommands(CommandsGeneral(plugin, parcelsAddress)) registerCommands(CommandsPrivilegesLocal(plugin)) group("option", "opt", "o") { @@ -63,6 +63,12 @@ inline fun CommandBuilder.group(name: String, vararg aliases: String, config: Co parent() } +inline fun CommandBuilder.group(address: ICommandAddress, name: String, vararg aliases: String, config: CommandBuilder.() -> Unit) { + group(address, name, *aliases) + config() + parent() +} + private fun CommandBuilder.generateHelpAndSyntaxCommands(): CommandBuilder { generateCommands(dispatcher as ICommandAddress, "help", "syntax") return this @@ -80,3 +86,37 @@ private fun generateCommands(address: ICommandAddress, vararg names: String) { } } } + +class SpecialCommandAddress : ChildCommandAddress() { + private val speciallyTreatedKeys = mutableListOf<String>() + + // Used to allow /p h:1 syntax, which is the same as what PlotMe uses. + var speciallyParsedIndex: Int? = null; private set + + fun addSpeciallyTreatedKeys(vararg keys: String) { + for (key in keys) { + speciallyTreatedKeys.add(key + ":") + } + } + + @Throws(CommandException::class) + override fun getChild(key: String, context: ExecutionContext): ChildCommandAddress? { + speciallyParsedIndex = null + + for (specialKey in speciallyTreatedKeys) { + if (key.startsWith(specialKey)) { + val result = getChild(specialKey.substring(0, specialKey.length - 1)) + ?: return null + + val text = key.substring(specialKey.length) + val num = text.toIntOrNull() ?: throw CommandException("$text is not a number") + speciallyParsedIndex = num + + return result + } + } + + return super.getChild(key) + } + +} diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt index 956da94..8c0d718 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt @@ -83,7 +83,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef } } - class PType(val parcelProvider: ParcelProvider) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, Config) { + class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, Config) { override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget { var input = buffer.next() @@ -124,11 +124,25 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef val ownerString: String val index: Int? + val speciallyParsedIndex = parcelAddress?.speciallyParsedIndex + if (splitIdx == -1) { - // just the index. - index = input.toIntOrNull() - ownerString = if (index == null) input else "" + + if (speciallyParsedIndex == null) { + // just the index. + index = input.toIntOrNull() + ownerString = if (index == null) input else "" + } else { + // just the owner. + index = speciallyParsedIndex + ownerString = input + } + } else { + if (speciallyParsedIndex != null) { + invalidInput(parameter, "Duplicate home index") + } + ownerString = input.substring(0, splitIdx) val indexString = input.substring(splitIdx + 1) @@ -165,7 +179,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef return ByID(world, id, kind, true) } - return ByOwner(world, PlayerProfile(player), 0, kind, true) + return ByOwner(world, PlayerProfile(player), parcelAddress?.speciallyParsedIndex ?: 0, kind, true) } } diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt index dddb221..90cadc6 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt @@ -119,9 +119,9 @@ class DefaultParcelGenerator( worldId: ParcelWorldId, container: ParcelContainer, coroutineScope: CoroutineScope, - worktimeLimiter: WorktimeLimiter + workDispatcher: WorkDispatcher ): Pair<ParcelLocator, ParcelBlockManager> { - return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, worktimeLimiter) + return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, workDispatcher) } private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? { @@ -156,7 +156,7 @@ class DefaultParcelGenerator( private inner class ParcelBlockManagerImpl( val worldId: ParcelWorldId, coroutineScope: CoroutineScope, - override val worktimeLimiter: WorktimeLimiter + override val workDispatcher: WorkDispatcher ) : ParcelBlockManagerBase(), CoroutineScope by coroutineScope { override val world: World = this@DefaultParcelGenerator.world override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight) @@ -222,12 +222,12 @@ class DefaultParcelGenerator( return world.getParcelById(parcelId) } - override fun submitBlockVisitor(vararg parcelIds: ParcelId, task: TimeLimitedTask): Worker { + override fun submitBlockVisitor(vararg parcelIds: ParcelId, task: WorkerTask): Worker { val parcels = parcelIds.mapNotNull { getParcel(it) } - if (parcels.isEmpty()) return worktimeLimiter.submit(task) + if (parcels.isEmpty()) return workDispatcher.dispatch(task) if (parcels.any { it.hasBlockVisitors }) throw IllegalArgumentException("This parcel already has a block visitor") - val worker = worktimeLimiter.submit(task) + val worker = workDispatcher.dispatch(task) for (parcel in parcels) { launch(start = UNDISPATCHED) { @@ -277,10 +277,10 @@ 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) } } + val schematicOf1 = delegateWork(0.25) { Schematic().apply { load(world, getRegion(parcel1)) } } + val schematicOf2 = delegateWork(0.25) { Schematic().apply { load(world, getRegion(parcel2)) } } + delegateWork(0.25) { with(schematicOf1) { paste(world, getRegion(parcel2).origin) } } + delegateWork(0.25) { with(schematicOf2) { paste(world, getRegion(parcel1).origin) } } } override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> { diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt index 1112047..ff2fcd4 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt @@ -59,7 +59,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") } parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage, - plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.worktimeLimiter) + plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.workDispatcher) if (!worldExists) { val time = DateTime.now() diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt index c143ff6..24bad79 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt @@ -3,7 +3,7 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.* -import io.dico.parcels2.blockvisitor.WorktimeLimiter +import io.dico.parcels2.blockvisitor.WorkDispatcher import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.storage.Storage import kotlinx.coroutines.CoroutineScope @@ -18,7 +18,7 @@ class ParcelWorldImpl(override val world: World, override val globalPrivileges: GlobalPrivilegesManager, containerFactory: ParcelContainerFactory, coroutineScope: CoroutineScope, - worktimeLimiter: WorktimeLimiter) + workDispatcher: WorkDispatcher) : ParcelWorld, ParcelWorldId, ParcelContainer, /* missing delegation */ @@ -39,7 +39,7 @@ class ParcelWorldImpl(override val world: World, override val blockManager: ParcelBlockManager init { - val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, worktimeLimiter) + val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, workDispatcher) locator = pair.first blockManager = pair.second diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt index 9d91cda..f838950 100644 --- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt +++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt @@ -198,8 +198,8 @@ class ParcelListeners( val type = clickedBlock.type val interactableClass = Interactables[type] - if (interactableClass != null && (parcel.effectiveInteractableConfig.isInteractable(type) || (parcel != null && parcel.canBuild(user)))) { - user.sendParcelMessage(nopermit = true, message = "You cannot interact with ${interactableClass.name} in this parcel") + if (interactableClass != null && !parcel.effectiveInteractableConfig.isInteractable(type) && (parcel == null || !parcel.canBuild(user))) { + user.sendParcelMessage(nopermit = true, message = "You cannot interact with ${interactableClass.name} here") event.isCancelled = true return@l } @@ -595,4 +595,16 @@ class ParcelListeners( storage.updatePlayerName(event.player.uuid, event.player.name) } + /** + * Attempts to prevent redstone contraptions from breaking while they are being swapped + * Might remove if it causes lag + */ + @ListenerMarker + val onBlockRedstoneEvent = RegistratorListener<BlockRedstoneEvent> l@{ event -> + val (_, area) = getWorldAndArea(event.block) ?: return@l + if (area == null || area.hasBlockVisitors) { + event.newCurrent = event.oldCurrent + } + } + }
\ 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 36a51c1..af71dc4 100644 --- a/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt +++ b/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt @@ -26,6 +26,7 @@ data class Vec3i( val z: Int ) { operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z) + operator fun minus(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) |