Archived
0

Fixes n tweaks

This commit is contained in:
Dico
2018-09-27 07:03:02 +01:00
parent 307b7aee4a
commit 842e52bd92
33 changed files with 393 additions and 242 deletions

View File

@@ -1,6 +1,6 @@
package io.dico.parcels2 package io.dico.parcels2
import io.dico.parcels2.util.ext.ceilDiv import io.dico.parcels2.util.math.ext.ceilDiv
import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
import org.bukkit.Material import org.bukkit.Material
import java.util.EnumMap import java.util.EnumMap

View File

@@ -1,6 +1,6 @@
package io.dico.parcels2 package io.dico.parcels2
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.math.Vec2i
import org.bukkit.Location import org.bukkit.Location
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.UUID import java.util.UUID

View File

@@ -1,9 +1,9 @@
package io.dico.parcels2 package io.dico.parcels2
import io.dico.parcels2.blockvisitor.* import io.dico.parcels2.blockvisitor.*
import io.dico.parcels2.util.Region import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.math.Vec2i
import io.dico.parcels2.util.get import io.dico.parcels2.util.math.get
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.Location import org.bukkit.Location
@@ -37,12 +37,12 @@ abstract class ParcelGenerator : ChunkGenerator() {
abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId, abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId,
container: ParcelContainer, container: ParcelContainer,
coroutineScope: CoroutineScope, coroutineScope: CoroutineScope,
workDispatcher: WorkDispatcher): Pair<ParcelLocator, ParcelBlockManager> jobDispatcher: JobDispatcher): Pair<ParcelLocator, ParcelBlockManager>
} }
interface ParcelBlockManager { interface ParcelBlockManager {
val world: World val world: World
val workDispatcher: WorkDispatcher val jobDispatcher: JobDispatcher
val parcelTraverser: RegionTraverser val parcelTraverser: RegionTraverser
// fun getBottomBlock(parcel: ParcelId): Vec2i // fun getBottomBlock(parcel: ParcelId): Vec2i
@@ -55,13 +55,13 @@ interface ParcelBlockManager {
fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?) fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?)
fun setBiome(parcel: ParcelId, biome: Biome): Worker fun setBiome(parcel: ParcelId, biome: Biome): Job
fun clearParcel(parcel: ParcelId): Worker fun clearParcel(parcel: ParcelId): Job
fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Worker fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Job
fun submitBlockVisitor(vararg parcelIds: ParcelId, task: WorkerTask): Worker fun submitBlockVisitor(vararg parcelIds: ParcelId, task: JobFunction): Job
/** /**
* Used to update owner blocks in the corner of the parcel * Used to update owner blocks in the corner of the parcel
@@ -71,7 +71,7 @@ interface ParcelBlockManager {
inline fun ParcelBlockManager.doBlockOperation(parcel: ParcelId, inline fun ParcelBlockManager.doBlockOperation(parcel: ParcelId,
traverser: RegionTraverser, traverser: RegionTraverser,
crossinline operation: suspend WorkerScope.(Block) -> Unit) = submitBlockVisitor(parcel) { crossinline operation: suspend JobScope.(Block) -> Unit) = submitBlockVisitor(parcel) {
val region = getRegion(parcel) val region = getRegion(parcel)
val blockCount = region.blockCount.toDouble() val blockCount = region.blockCount.toDouble()
val blocks = traverser.traverseRegion(region) val blocks = traverser.traverseRegion(region)

View File

@@ -1,6 +1,6 @@
package io.dico.parcels2 package io.dico.parcels2
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.math.Vec2i
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.World import org.bukkit.World
import java.util.UUID import java.util.UUID

View File

@@ -2,8 +2,8 @@ package io.dico.parcels2
import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.options.RuntimeWorldOptions
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.math.Vec2i
import io.dico.parcels2.util.ext.floor import io.dico.parcels2.util.math.ext.floor
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.World import org.bukkit.World
import org.bukkit.block.Block import org.bukkit.block.Block

View File

@@ -3,8 +3,8 @@ package io.dico.parcels2
import io.dico.dicore.Registrator import io.dico.dicore.Registrator
import io.dico.dicore.command.EOverridePolicy import io.dico.dicore.command.EOverridePolicy
import io.dico.dicore.command.ICommandDispatcher import io.dico.dicore.command.ICommandDispatcher
import io.dico.parcels2.blockvisitor.BukkitWorkDispatcher import io.dico.parcels2.blockvisitor.BukkitJobDispatcher
import io.dico.parcels2.blockvisitor.WorkDispatcher import io.dico.parcels2.blockvisitor.JobDispatcher
import io.dico.parcels2.command.getParcelCommands import io.dico.parcels2.command.getParcelCommands
import io.dico.parcels2.defaultimpl.GlobalPrivilegesManagerImpl import io.dico.parcels2.defaultimpl.GlobalPrivilegesManagerImpl
import io.dico.parcels2.defaultimpl.ParcelProviderImpl import io.dico.parcels2.defaultimpl.ParcelProviderImpl
@@ -44,7 +44,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
override val coroutineContext: CoroutineContext = MainThreadDispatcher(this) override val coroutineContext: CoroutineContext = MainThreadDispatcher(this)
override val plugin: Plugin get() = this override val plugin: Plugin get() = this
val workDispatcher: WorkDispatcher by lazy { BukkitWorkDispatcher(this, options.tickWorktime) } val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, options.tickJobtime) }
override fun onEnable() { override fun onEnable() {
plogger.info("Debug enabled: ${plogger.isDebugEnabled}") plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
@@ -55,11 +55,11 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
} }
override fun onDisable() { override fun onDisable() {
val hasWorkers = workDispatcher.workers.isNotEmpty() val hasWorkers = jobDispatcher.jobs.isNotEmpty()
if (hasWorkers) { if (hasWorkers) {
plogger.warn("Parcels is attempting to complete all ${workDispatcher.workers.size} remaining jobs before shutdown...") plogger.warn("Parcels is attempting to complete all ${jobDispatcher.jobs.size} remaining jobs before shutdown...")
} }
workDispatcher.completeAllTasks() jobDispatcher.completeAllTasks()
if (hasWorkers) { if (hasWorkers) {
plogger.info("Parcels has completed the remaining jobs.") plogger.info("Parcels has completed the remaining jobs.")
} }

View File

@@ -66,7 +66,7 @@ interface RawPrivileges {
open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = EmptyPrivilegeMap) : RawPrivileges { open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = EmptyPrivilegeMap) : RawPrivileges {
private var _privilegeOfStar: Privilege = DEFAULT private var _privilegeOfStar: Privilege = DEFAULT
override var privilegeOfStar: Privilege override /*open*/ var privilegeOfStar: Privilege
get() = _privilegeOfStar get() = _privilegeOfStar
set(value) = run { _privilegeOfStar = value } set(value) = run { _privilegeOfStar = value }
@@ -94,5 +94,10 @@ open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = Emp
return if (privilege == DEFAULT) privilegeMap.remove(key) != null return if (privilege == DEFAULT) privilegeMap.remove(key) != null
else privilegeMap.put(key, privilege) != privilege else privilegeMap.put(key, privilege) != privilege
} }
fun copyPrivilegesFrom(other: PrivilegesHolder) {
privilegeMap = other.privilegeMap
privilegeOfStar = other.privilegeOfStar
}
} }

View File

@@ -1,6 +1,6 @@
package io.dico.parcels2.blockvisitor package io.dico.parcels2.blockvisitor
import io.dico.parcels2.util.Vec3i import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
import io.dico.parcels2.util.ext.getMaterialsWithWoolColorPrefix import io.dico.parcels2.util.ext.getMaterialsWithWoolColorPrefix
import org.bukkit.Material import org.bukkit.Material

View File

@@ -2,11 +2,11 @@ package io.dico.parcels2.blockvisitor
import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.logger import io.dico.parcels2.logger
import io.dico.parcels2.util.ext.clampMin import io.dico.parcels2.util.math.ext.clampMin
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart.LAZY import kotlinx.coroutines.CoroutineStart.LAZY
import kotlinx.coroutines.Job import kotlinx.coroutines.Job as CoroutineJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import java.lang.System.currentTimeMillis import java.lang.System.currentTimeMillis
@@ -16,21 +16,21 @@ import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
import kotlin.coroutines.resume import kotlin.coroutines.resume
typealias WorkerTask = suspend WorkerScope.() -> Unit typealias JobFunction = suspend JobScope.() -> Unit
typealias WorkerUpdateLister = Worker.(Double, Long) -> Unit typealias JobUpdateLister = Job.(Double, Long) -> Unit
data class TickWorktimeOptions(var workTime: Int, var tickInterval: Int) data class TickJobtimeOptions(var jobTime: Int, var tickInterval: Int)
interface WorkDispatcher { interface JobDispatcher {
/** /**
* Submit a [task] that should be run synchronously, but limited such that it does not stall the server * Submit a [task] that should be run synchronously, but limited such that it does not stall the server
*/ */
fun dispatch(task: WorkerTask): Worker fun dispatch(task: JobFunction): Job
/** /**
* Get a list of all workers * Get a list of all jobs
*/ */
val workers: List<Worker> val jobs: List<Job>
/** /**
* Attempts to complete any remaining tasks immediately, without suspension. * Attempts to complete any remaining tasks immediately, without suspension.
@@ -38,27 +38,27 @@ interface WorkDispatcher {
fun completeAllTasks() fun completeAllTasks()
} }
interface WorkerAndScopeMembersUnion { interface JobAndScopeMembersUnion {
/** /**
* The time that elapsed since this worker was dispatched, in milliseconds * The time that elapsed since this job was dispatched, in milliseconds
*/ */
val elapsedTime: Long val elapsedTime: Long
/** /**
* A value indicating the progress of this worker, in the range 0.0 <= progress <= 1.0 * A value indicating the progress of this job, in the range 0.0 <= progress <= 1.0
* with no guarantees to its accuracy. * with no guarantees to its accuracy.
*/ */
val progress: Double val progress: Double
} }
interface Worker : WorkerAndScopeMembersUnion { interface Job : JobAndScopeMembersUnion {
/** /**
* The coroutine associated with this worker * The coroutine associated with this job
*/ */
val job: Job val job: CoroutineJob
/** /**
* true if this worker has completed * true if this job has completed
*/ */
val isComplete: Boolean val isComplete: Boolean
@@ -69,28 +69,28 @@ interface Worker : WorkerAndScopeMembersUnion {
val completionException: Throwable? val completionException: Throwable?
/** /**
* Calls the given [block] whenever the progress of this worker is updated, * Calls the given [block] whenever the progress of this job is updated,
* if [minInterval] milliseconds expired since the last call. * if [minInterval] milliseconds expired since the last call.
* The first call occurs after at least [minDelay] milliseconds in a likewise manner. * The first call occurs after at least [minDelay] milliseconds in a likewise manner.
* Repeated invocations of this method result in an [IllegalStateException] * Repeated invocations of this method result in an [IllegalStateException]
* *
* if [asCompletionListener] is true, [onCompleted] is called with the same [block] * if [asCompletionListener] is true, [onCompleted] is called with the same [block]
*/ */
fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean = true, block: WorkerUpdateLister): Worker fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean = true, block: JobUpdateLister): Job
/** /**
* Calls the given [block] when this worker completes, with the progress value 1.0. * Calls the given [block] when this job completes, with the progress value 1.0.
* Multiple listeners may be registered to this function. * Multiple listeners may be registered to this function.
*/ */
fun onCompleted(block: WorkerUpdateLister): Worker fun onCompleted(block: JobUpdateLister): Job
/** /**
* Await completion of this worker * Await completion of this job
*/ */
suspend fun awaitCompletion() suspend fun awaitCompletion()
} }
interface WorkerScope : WorkerAndScopeMembersUnion { interface JobScope : JobAndScopeMembersUnion {
/** /**
* A task should call this frequently during its execution, such that the timer can suspend it when necessary. * A task should call this frequently during its execution, such that the timer can suspend it when necessary.
*/ */
@@ -107,29 +107,29 @@ interface WorkerScope : WorkerAndScopeMembersUnion {
fun markComplete() = setProgress(1.0) fun markComplete() = setProgress(1.0)
/** /**
* Get a [WorkerScope] that is responsible for [portion] part of the progress * Get a [JobScope] that is responsible for [portion] part of the progress
* If [portion] is negative, the remaining progress is used * If [portion] is negative, the remaining progress is used
*/ */
fun delegateWork(portion: Double = -1.0): WorkerScope fun delegateProgress(portion: Double = -1.0): JobScope
} }
inline fun <T> WorkerScope.delegateWork(portion: Double = -1.0, block: WorkerScope.() -> T): T { inline fun <T> JobScope.delegateWork(portion: Double = -1.0, block: JobScope.() -> T): T {
delegateWork(portion).apply { delegateProgress(portion).apply {
val result = block() val result = block()
markComplete() markComplete()
return result return result
} }
} }
interface WorkerInternal : Worker, WorkerScope { interface JobInternal : Job, JobScope {
/** /**
* Start or resumes the execution of this worker * Start or resumes the execution of this job
* and returns true if the worker completed * and returns true if the job completed
* *
* [worktime] is the maximum amount of time, in milliseconds, * [worktime] is the maximum amount of time, in milliseconds,
* that this job may run for until suspension. * that this job may run for until suspension.
* *
* If [worktime] is not positive, the worker will complete * If [worktime] is not positive, the job will complete
* without suspension and this method will always return true. * without suspension and this method will always return true.
*/ */
fun resume(worktime: Long): Boolean fun resume(worktime: Long): Boolean
@@ -137,68 +137,68 @@ interface WorkerInternal : Worker, WorkerScope {
/** /**
* An object that controls one or more jobs, ensuring that they don't stall the server too much. * An object that controls one or more jobs, ensuring that they don't stall the server too much.
* There is a configurable maxiumum amount of milliseconds that can be allocated to all workers together in each server tick * There is a configurable maxiumum amount of milliseconds that can be allocated to all jobs together in each server tick
* This object attempts to split that maximum amount of milliseconds equally between all jobs * This object attempts to split that maximum amount of milliseconds equally between all jobs
*/ */
class BukkitWorkDispatcher(private val plugin: ParcelsPlugin, var options: TickWorktimeOptions) : WorkDispatcher { class BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJobtimeOptions) : JobDispatcher {
// The currently registered bukkit scheduler task // The currently registered bukkit scheduler task
private var bukkitTask: BukkitTask? = null private var bukkitTask: BukkitTask? = null
// The workers. // The jobs.
private val _workers = LinkedList<WorkerInternal>() private val _jobs = LinkedList<JobInternal>()
override val workers: List<Worker> = _workers override val jobs: List<Job> = _jobs
override fun dispatch(task: WorkerTask): Worker { override fun dispatch(task: JobFunction): Job {
val worker: WorkerInternal = WorkerImpl(plugin, task) val job: JobInternal = JobImpl(plugin, task)
if (bukkitTask == null) { if (bukkitTask == null) {
val completed = worker.resume(options.workTime.toLong()) val completed = job.resume(options.jobTime.toLong())
if (completed) return worker if (completed) return job
bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickJobs() } bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickCoroutineJobs() }
} }
_workers.addFirst(worker) _jobs.addFirst(job)
return worker return job
} }
private fun tickJobs() { private fun tickCoroutineJobs() {
val workers = _workers val jobs = _jobs
if (workers.isEmpty()) return if (jobs.isEmpty()) return
val tickStartTime = System.currentTimeMillis() val tickStartTime = System.currentTimeMillis()
val iterator = workers.listIterator(index = 0) val iterator = jobs.listIterator(index = 0)
while (iterator.hasNext()) { while (iterator.hasNext()) {
val time = System.currentTimeMillis() val time = System.currentTimeMillis()
val timeElapsed = time - tickStartTime val timeElapsed = time - tickStartTime
val timeLeft = options.workTime - timeElapsed val timeLeft = options.jobTime - timeElapsed
if (timeLeft <= 0) return if (timeLeft <= 0) return
val count = workers.size - iterator.nextIndex() val count = jobs.size - iterator.nextIndex()
val timePerJob = (timeLeft + count - 1) / count val timePerJob = (timeLeft + count - 1) / count
val worker = iterator.next() val job = iterator.next()
val completed = worker.resume(timePerJob) val completed = job.resume(timePerJob)
if (completed) { if (completed) {
iterator.remove() iterator.remove()
} }
} }
if (workers.isEmpty()) { if (jobs.isEmpty()) {
bukkitTask?.cancel() bukkitTask?.cancel()
bukkitTask = null bukkitTask = null
} }
} }
override fun completeAllTasks() { override fun completeAllTasks() {
_workers.forEach { _jobs.forEach {
it.resume(-1) it.resume(-1)
} }
_workers.clear() _jobs.clear()
bukkitTask?.cancel() bukkitTask?.cancel()
bukkitTask = null bukkitTask = null
} }
} }
private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerInternal { private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
override val job: Job = scope.launch(start = LAZY) { task() } override val job: CoroutineJob = scope.launch(start = LAZY) { task() }
private var continuation: Continuation<Unit>? = null private var continuation: Continuation<Unit>? = null
private var nextSuspensionTime: Long = 0L private var nextSuspensionTime: Long = 0L
@@ -217,17 +217,17 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
override var completionException: Throwable? = null; private set override var completionException: Throwable? = null; private set
private var startTimeOrElapsedTime: Long = 0L // startTime before completed, elapsed time otherwise private var startTimeOrElapsedTime: Long = 0L // startTime before completed, elapsed time otherwise
private var onProgressUpdate: WorkerUpdateLister? = null private var onProgressUpdate: JobUpdateLister? = null
private var progressUpdateInterval: Int = 0 private var progressUpdateInterval: Int = 0
private var lastUpdateTime: Long = 0L private var lastUpdateTime: Long = 0L
private var onCompleted: WorkerUpdateLister? = null private var onCompleted: JobUpdateLister? = null
init { init {
job.invokeOnCompletion { exception -> job.invokeOnCompletion { exception ->
// report any error that occurred // report any error that occurred
completionException = exception?.also { completionException = exception?.also {
if (it !is CancellationException) if (it !is CancellationException)
logger.error("WorkerTask generated an exception", it) logger.error("JobFunction generated an exception", it)
} }
// convert to elapsed time here // convert to elapsed time here
@@ -239,7 +239,7 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
} }
} }
override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: WorkerUpdateLister): Worker { override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: JobUpdateLister): Job {
onProgressUpdate?.let { throw IllegalStateException() } onProgressUpdate?.let { throw IllegalStateException() }
if (asCompletionListener) onCompleted(block) if (asCompletionListener) onCompleted(block)
if (isComplete) return this if (isComplete) return this
@@ -250,7 +250,7 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
return this return this
} }
override fun onCompleted(block: WorkerUpdateLister): Worker { override fun onCompleted(block: JobUpdateLister): Job {
if (isComplete) { if (isComplete) {
block(1.0, startTimeOrElapsedTime) block(1.0, startTimeOrElapsedTime)
return this return this
@@ -260,7 +260,7 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
onCompleted = if (cur == null) { onCompleted = if (cur == null) {
block block
} else { } else {
fun Worker.(prog: Double, el: Long) { fun Job.(prog: Double, el: Long) {
cur(prog, el) cur(prog, el)
block(prog, el) block(prog, el)
} }
@@ -315,25 +315,25 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
job.join() job.join()
} }
private fun delegateWork(curPortion: Double, portion: Double): WorkerScope = private fun delegateProgress(curPortion: Double, portion: Double): JobScope =
DelegateScope(progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0)) DelegateScope(progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0))
override fun delegateWork(portion: Double): WorkerScope = delegateWork(1.0, portion) override fun delegateProgress(portion: Double): JobScope = delegateProgress(1.0, portion)
private inner class DelegateScope(val progressStart: Double, val portion: Double) : WorkerScope { private inner class DelegateScope(val progressStart: Double, val portion: Double) : JobScope {
override val elapsedTime: Long override val elapsedTime: Long
get() = this@WorkerImpl.elapsedTime get() = this@JobImpl.elapsedTime
override suspend fun markSuspensionPoint() = override suspend fun markSuspensionPoint() =
this@WorkerImpl.markSuspensionPoint() this@JobImpl.markSuspensionPoint()
override val progress: Double override val progress: Double
get() = (this@WorkerImpl.progress - progressStart) / portion get() = (this@JobImpl.progress - progressStart) / portion
override fun setProgress(progress: Double) = override fun setProgress(progress: Double) =
this@WorkerImpl.setProgress(progressStart + progress * portion) this@JobImpl.setProgress(progressStart + progress * portion)
override fun delegateWork(portion: Double): WorkerScope = override fun delegateProgress(portion: Double): JobScope =
this@WorkerImpl.delegateWork(this.portion, portion) this@JobImpl.delegateProgress(this.portion, portion)
} }
} }

View File

@@ -1,8 +1,9 @@
package io.dico.parcels2.blockvisitor package io.dico.parcels2.blockvisitor
import io.dico.parcels2.util.Region import io.dico.parcels2.util.math.Dimension
import io.dico.parcels2.util.Vec3i import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.ext.clampMax import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.math.ext.clampMax
private typealias Scope = SequenceScope<Vec3i> private typealias Scope = SequenceScope<Vec3i>
@@ -54,9 +55,9 @@ sealed class RegionTraverser {
val (primary, secondary, tertiary) = order.toArray() val (primary, secondary, tertiary) = order.toArray()
val (origin, size) = region val (origin, size) = region
val maxOfPrimary = primary.extract(size) - 1 val maxOfPrimary = size[primary] - 1
val maxOfSecondary = secondary.extract(size) - 1 val maxOfSecondary = size[secondary] - 1
val maxOfTertiary = tertiary.extract(size) - 1 val maxOfTertiary = size[tertiary] - 1
val isPrimaryIncreasing = direction.isIncreasing(primary) val isPrimaryIncreasing = direction.isIncreasing(primary)
val isSecondaryIncreasing = direction.isIncreasing(secondary) val isSecondaryIncreasing = direction.isIncreasing(secondary)
@@ -137,24 +138,6 @@ sealed class RegionTraverser {
} }
enum class Dimension {
X,
Y,
Z;
fun extract(block: Vec3i) =
when (this) {
X -> block.x
Y -> block.y
Z -> block.z
}
companion object {
private val values = values()
operator fun get(ordinal: Int) = values[ordinal]
}
}
object TraverseOrderFactory { object TraverseOrderFactory {
private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3 private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3
@@ -194,15 +177,15 @@ inline class TraverseOrder(val orderNum: Int) {
*/ */
fun toArray() = arrayOf(primary, secondary, tertiary) fun toArray() = arrayOf(primary, secondary, tertiary)
fun add(vec: Vec3i, dp: Int, ds: Int, dt: Int): Vec3i = fun add(vec: Vec3i, p: Int, s: Int, t: Int): Vec3i =
// optimize this, will be called lots // optimize this, will be called lots
when (orderNum) { when (orderNum) {
0 -> vec.add(dp, ds, dt) // xyz 0 -> vec.add(p, s, t) // xyz
1 -> vec.add(dt, dp, ds) // yzx 1 -> vec.add(t, p, s) // yzx
2 -> vec.add(ds, dt, dp) // zxy 2 -> vec.add(s, t, p) // zxy
3 -> vec.add(dp, dt, ds) // xzy 3 -> vec.add(p, t, s) // xzy
4 -> vec.add(ds, dp, dt) // yxz 4 -> vec.add(s, p, t) // yxz
5 -> vec.add(dt, ds, dp) // zyx 5 -> vec.add(t, s, p) // zyx
else -> error("Invalid orderNum $orderNum") else -> error("Invalid orderNum $orderNum")
} }
} }
@@ -212,9 +195,9 @@ inline class TraverseDirection(val bits: Int) {
fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean = fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean =
if (isIncreasing(dimension)) if (isIncreasing(dimension))
dimension.extract(block) <= dimension.extract(current) block[dimension] <= current[dimension]
else else
dimension.extract(block) >= dimension.extract(current) block[dimension] >= current[dimension]
fun comesFirst(current: Vec3i, block: Vec3i) = fun comesFirst(current: Vec3i, block: Vec3i) =
comesFirst(current, block, Dimension.X) comesFirst(current, block, Dimension.X)

View File

@@ -1,8 +1,8 @@
package io.dico.parcels2.blockvisitor package io.dico.parcels2.blockvisitor
import io.dico.parcels2.util.Region import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.Vec3i import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.get import io.dico.parcels2.util.math.get
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.World import org.bukkit.World
@@ -24,7 +24,7 @@ class Schematic {
private var isLoaded = false; private set private var isLoaded = false; private set
private val traverser: RegionTraverser = RegionTraverser.upward private val traverser: RegionTraverser = RegionTraverser.upward
suspend fun WorkerScope.load(world: World, region: Region) { suspend fun JobScope.load(world: World, region: Region) {
_size = region.size _size = region.size
val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it } val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it }
@@ -52,7 +52,7 @@ class Schematic {
isLoaded = true isLoaded = true
} }
suspend fun WorkerScope.paste(world: World, position: Vec3i) { suspend fun JobScope.paste(world: World, position: Vec3i) {
if (!isLoaded) throw IllegalStateException() if (!isLoaded) throw IllegalStateException()
val region = Region(position, _size!!) val region = Region(position, _size!!)
@@ -108,11 +108,11 @@ class Schematic {
} }
} }
fun getLoadTask(world: World, region: Region): WorkerTask = { fun getLoadTask(world: World, region: Region): JobFunction = {
load(world, region) load(world, region)
} }
fun getPasteTask(world: World, position: Vec3i): WorkerTask = { fun getPasteTask(world: World, position: Vec3i): JobFunction = {
paste(world, position) paste(world, position)
} }

View File

@@ -6,7 +6,7 @@ import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.PlayerProfile.* import io.dico.parcels2.PlayerProfile.*
import io.dico.parcels2.PrivilegeKey import io.dico.parcels2.PrivilegeKey
import io.dico.parcels2.blockvisitor.Worker import io.dico.parcels2.blockvisitor.Job
import io.dico.parcels2.util.ext.hasPermAdminManage import io.dico.parcels2.util.ext.hasPermAdminManage
import io.dico.parcels2.util.ext.parcelLimit import io.dico.parcels2.util.ext.parcelLimit
import org.bukkit.entity.Player import org.bukkit.entity.Player
@@ -50,7 +50,7 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
world.blockManager.clearParcel(parcel.id) world.blockManager.clearParcel(parcel.id)
} }
protected fun Worker.reportProgressUpdates(context: ExecutionContext, action: String) { protected fun Job.reportProgressUpdates(context: ExecutionContext, action: String) {
onProgressUpdate(1000, 1000) { progress, elapsedTime -> onProgressUpdate(1000, 1000) { progress, elapsedTime ->
val alt = context.getFormat(EMessageType.NUMBER) val alt = context.getFormat(EMessageType.NUMBER)
val main = context.getFormat(EMessageType.INFORMATIVE) val main = context.getFormat(EMessageType.INFORMATIVE)

View File

@@ -81,14 +81,14 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("jobs") @Cmd("jobs")
fun cmdJobs(): Any? { fun cmdJobs(): Any? {
val workers = plugin.workDispatcher.workers val workers = plugin.jobDispatcher.jobs
println(workers.map { it.job }.joinToString(separator = "\n")) println(workers.map { it.job }.joinToString(separator = "\n"))
return "Task count: ${workers.size}" return "Task count: ${workers.size}"
} }
@Cmd("complete_jobs") @Cmd("complete_jobs")
fun cmdCompleteJobs(): Any? = cmdJobs().also { fun cmdCompleteJobs(): Any? = cmdJobs().also {
plugin.launch { plugin.workDispatcher.completeAllTasks() } plugin.launch { plugin.jobDispatcher.completeAllTasks() }
} }
@Cmd("message") @Cmd("message")

View File

@@ -16,8 +16,8 @@ import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_REAL
import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.PREFER_OWNED_FOR_DEFAULT import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.PREFER_OWNED_FOR_DEFAULT
import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.REAL import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.REAL
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.math.Vec2i
import io.dico.parcels2.util.ext.floor import io.dico.parcels2.util.math.ext.floor
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player

View File

@@ -3,12 +3,12 @@ package io.dico.parcels2.defaultimpl
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.blockvisitor.* import io.dico.parcels2.blockvisitor.*
import io.dico.parcels2.options.DefaultGeneratorOptions import io.dico.parcels2.options.DefaultGeneratorOptions
import io.dico.parcels2.util.Region import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.math.Vec2i
import io.dico.parcels2.util.Vec3i import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.ext.even import io.dico.parcels2.util.math.ext.even
import io.dico.parcels2.util.ext.umod import io.dico.parcels2.util.math.ext.umod
import io.dico.parcels2.util.get import io.dico.parcels2.util.math.get
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart.UNDISPATCHED import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -121,9 +121,9 @@ class DefaultParcelGenerator(
worldId: ParcelWorldId, worldId: ParcelWorldId,
container: ParcelContainer, container: ParcelContainer,
coroutineScope: CoroutineScope, coroutineScope: CoroutineScope,
workDispatcher: WorkDispatcher jobDispatcher: JobDispatcher
): Pair<ParcelLocator, ParcelBlockManager> { ): Pair<ParcelLocator, ParcelBlockManager> {
return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, workDispatcher) return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, jobDispatcher)
} }
private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? { private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? {
@@ -158,7 +158,7 @@ class DefaultParcelGenerator(
private inner class ParcelBlockManagerImpl( private inner class ParcelBlockManagerImpl(
val worldId: ParcelWorldId, val worldId: ParcelWorldId,
coroutineScope: CoroutineScope, coroutineScope: CoroutineScope,
override val workDispatcher: WorkDispatcher override val jobDispatcher: JobDispatcher
) : ParcelBlockManagerBase(), CoroutineScope by coroutineScope { ) : ParcelBlockManagerBase(), CoroutineScope by coroutineScope {
override val world: World = this@DefaultParcelGenerator.world override val world: World = this@DefaultParcelGenerator.world
override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight) override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight)
@@ -177,7 +177,10 @@ class DefaultParcelGenerator(
override fun getRegion(parcel: ParcelId): Region { override fun getRegion(parcel: ParcelId): Region {
val bottom = getBottomBlock(parcel) val bottom = getBottomBlock(parcel)
return Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight, o.parcelSize)) return Region(
Vec3i(bottom.x, 0, bottom.z),
Vec3i(o.parcelSize, maxHeight, o.parcelSize)
)
} }
private fun getRegionConsideringWorld(parcel: ParcelId): Region { private fun getRegionConsideringWorld(parcel: ParcelId): Region {
@@ -234,12 +237,12 @@ class DefaultParcelGenerator(
return world.getParcelById(parcelId) return world.getParcelById(parcelId)
} }
override fun submitBlockVisitor(vararg parcelIds: ParcelId, task: WorkerTask): Worker { override fun submitBlockVisitor(vararg parcelIds: ParcelId, task: JobFunction): Job {
val parcels = parcelIds.mapNotNull { getParcel(it) } val parcels = parcelIds.mapNotNull { getParcel(it) }
if (parcels.isEmpty()) return workDispatcher.dispatch(task) if (parcels.isEmpty()) return jobDispatcher.dispatch(task)
if (parcels.any { it.hasBlockVisitors }) throw IllegalArgumentException("This parcel already has a block visitor") if (parcels.any { it.hasBlockVisitors }) throw IllegalArgumentException("This parcel already has a block visitor")
val worker = workDispatcher.dispatch(task) val worker = jobDispatcher.dispatch(task)
for (parcel in parcels) { for (parcel in parcels) {
launch(start = UNDISPATCHED) { launch(start = UNDISPATCHED) {
@@ -252,7 +255,7 @@ class DefaultParcelGenerator(
return worker return worker
} }
override fun setBiome(parcel: ParcelId, biome: Biome): Worker = submitBlockVisitor(parcel) { override fun setBiome(parcel: ParcelId, biome: Biome): Job = submitBlockVisitor(parcel) {
val world = world val world = world
val b = getBottomBlock(parcel) val b = getBottomBlock(parcel)
val parcelSize = o.parcelSize val parcelSize = o.parcelSize
@@ -264,7 +267,7 @@ class DefaultParcelGenerator(
} }
} }
override fun clearParcel(parcel: ParcelId): Worker = submitBlockVisitor(parcel) { override fun clearParcel(parcel: ParcelId): Job = submitBlockVisitor(parcel) {
val region = getRegion(parcel) val region = getRegion(parcel)
val blocks = parcelTraverser.traverseRegion(region) val blocks = parcelTraverser.traverseRegion(region)
val blockCount = region.blockCount.toDouble() val blockCount = region.blockCount.toDouble()
@@ -288,7 +291,7 @@ class DefaultParcelGenerator(
} }
} }
override fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Worker = submitBlockVisitor(parcel1, parcel2) { override fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Job = submitBlockVisitor(parcel1, parcel2) {
var region1 = getRegionConsideringWorld(parcel1) var region1 = getRegionConsideringWorld(parcel1)
var region2 = getRegionConsideringWorld(parcel2) var region2 = getRegionConsideringWorld(parcel2)

View File

@@ -3,7 +3,7 @@ package io.dico.parcels2.defaultimpl
import io.dico.dicore.Formatting import io.dico.dicore.Formatting
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.Privilege.* import io.dico.parcels2.Privilege.*
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.math.Vec2i
import io.dico.parcels2.util.ext.alsoIfTrue import io.dico.parcels2.util.ext.alsoIfTrue
import org.bukkit.Material import org.bukkit.Material
import org.joda.time.DateTime import org.joda.time.DateTime

View File

@@ -58,8 +58,10 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
if (worldExists) Bukkit.getWorld(worldName)!! if (worldExists) Bukkit.getWorld(worldName)!!
else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") } else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") }
parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage, parcelWorld = ParcelWorldImpl(
plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.workDispatcher) bukkitWorld, generator, worldOptions.runtime, plugin.storage,
plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.jobDispatcher
)
if (!worldExists) { if (!worldExists) {
val time = DateTime.now() val time = DateTime.now()
@@ -95,11 +97,18 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
logger.info("Loading all parcel data...") logger.info("Loading all parcel data...")
val channel = plugin.storage.transmitAllParcelData() val channel = plugin.storage.transmitAllParcelData()
do { while (true) {
val pair = channel.receiveOrNull() ?: break val (id, data) = channel.receiveOrNull() ?: break
val parcel = getParcelById(pair.first) ?: continue val parcel = getParcelById(id) ?: continue
pair.second?.let { parcel.copyDataIgnoringDatabase(it) } data?.let { parcel.copyDataIgnoringDatabase(it) }
} while (true) }
val channel2 = plugin.storage.transmitAllGlobalPrivileges()
while (true) {
val (profile, data) = channel2.receiveOrNull() ?: break
val key = profile as? PrivilegeKey ?: continue
(plugin.globalPrivileges[key] as PrivilegesHolder).copyPrivilegesFrom(data)
}
logger.info("Loading data completed") logger.info("Loading data completed")
_dataIsLoaded = true _dataIsLoaded = true

View File

@@ -3,7 +3,7 @@
package io.dico.parcels2.defaultimpl package io.dico.parcels2.defaultimpl
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.blockvisitor.WorkDispatcher import io.dico.parcels2.blockvisitor.JobDispatcher
import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.options.RuntimeWorldOptions
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -18,7 +18,7 @@ class ParcelWorldImpl(override val world: World,
override val globalPrivileges: GlobalPrivilegesManager, override val globalPrivileges: GlobalPrivilegesManager,
containerFactory: ParcelContainerFactory, containerFactory: ParcelContainerFactory,
coroutineScope: CoroutineScope, coroutineScope: CoroutineScope,
workDispatcher: WorkDispatcher) jobDispatcher: JobDispatcher)
: ParcelWorld, : ParcelWorld,
ParcelWorldId, ParcelWorldId,
ParcelContainer, /* missing delegation */ ParcelContainer, /* missing delegation */
@@ -39,7 +39,7 @@ class ParcelWorldImpl(override val world: World,
override val blockManager: ParcelBlockManager override val blockManager: ParcelBlockManager
init { init {
val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, workDispatcher) val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, jobDispatcher)
locator = pair.first locator = pair.first
blockManager = pair.second blockManager = pair.second

View File

@@ -7,6 +7,12 @@ import io.dico.dicore.RegistratorListener
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.ext.* import io.dico.parcels2.util.ext.*
import io.dico.parcels2.util.math.Dimension
import io.dico.parcels2.util.math.Vec3d
import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.math.ext.clampMax
import io.dico.parcels2.util.math.ext.clampMin
import org.bukkit.Location
import org.bukkit.Material.* import org.bukkit.Material.*
import org.bukkit.World import org.bukkit.World
import org.bukkit.block.Biome import org.bukkit.block.Biome
@@ -52,6 +58,7 @@ class ParcelListeners(
return world to world.getParcelAt(block) return world to world.getParcelAt(block)
} }
/* /*
* Prevents players from entering plots they are banned from * Prevents players from entering plots they are banned from
*/ */
@@ -59,12 +66,36 @@ class ParcelListeners(
val onPlayerMoveEvent = RegistratorListener<PlayerMoveEvent> l@{ event -> val onPlayerMoveEvent = RegistratorListener<PlayerMoveEvent> l@{ event ->
val user = event.player val user = event.player
if (user.hasPermBanBypass) return@l if (user.hasPermBanBypass) return@l
val parcel = parcelProvider.getParcelAt(event.to) ?: return@l val toLoc = event.to
val parcel = parcelProvider.getParcelAt(toLoc) ?: return@l
if (!parcel.canEnterFast(user)) { if (!parcel.canEnterFast(user)) {
parcelProvider.getParcelAt(event.from)?.also { val region = parcel.world.blockManager.getRegion(parcel.id)
user.teleport(it.homeLocation) val dimension = region.getFirstUncontainedDimensionOf(Vec3i(event.from))
if (dimension == null) {
user.teleport(parcel.homeLocation)
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel") user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
} ?: run { event.to = event.from }
} else {
val speed = getPlayerSpeed(user)
val from = Vec3d(event.from)
val to = Vec3d(toLoc).with(dimension, from[dimension])
var newTo = to
dimension.otherDimensions.forEach {
val delta = to[it] - from[it]
newTo = newTo.add(it, delta * 100 * if (it == Dimension.Y) 0.5 else speed)
}
event.to = Location(
toLoc.world,
newTo.x, newTo.y.clampMin(0.0).clampMax(255.0), newTo.z,
toLoc.yaw, toLoc.pitch
)
}
} }
} }
@@ -607,4 +638,16 @@ class ParcelListeners(
} }
} }
private fun getPlayerSpeed(player: Player): Double =
if (player.isFlying) {
player.flySpeed * if (player.isSprinting) 21.6 else 10.92
} else {
player.walkSpeed * when {
player.isSprinting -> 5.612
player.isSneaking -> 1.31
else -> 4.317
} / 1.5 //?
} / 20.0
} }

View File

@@ -1,6 +1,6 @@
package io.dico.parcels2.options package io.dico.parcels2.options
import io.dico.parcels2.blockvisitor.TickWorktimeOptions import io.dico.parcels2.blockvisitor.TickJobtimeOptions
import org.bukkit.GameMode import org.bukkit.GameMode
import org.bukkit.Material import org.bukkit.Material
import java.io.Reader import java.io.Reader
@@ -11,7 +11,7 @@ class Options {
var worlds: Map<String, WorldOptions> = hashMapOf() var worlds: Map<String, WorldOptions> = hashMapOf()
private set private set
var storage: StorageOptions = StorageOptions() var storage: StorageOptions = StorageOptions()
var tickWorktime: TickWorktimeOptions = TickWorktimeOptions(20, 1) var tickJobtime: TickJobtimeOptions = TickJobtimeOptions(20, 1)
var migration = MigrationOptionsHolder() var migration = MigrationOptionsHolder()
fun addWorld(name: String, fun addWorld(name: String,

View File

@@ -61,9 +61,9 @@ interface Backing {
fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration)
fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>) fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>)
fun readGlobalPrivileges(owner: PlayerProfile): MutablePrivilegeMap fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder?
fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege)
} }

View File

@@ -14,7 +14,7 @@ import java.util.UUID
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
typealias DataPair = Pair<ParcelId, ParcelData?> typealias DataPair = Pair<ParcelId, ParcelData?>
typealias AddedDataPair<TAttach> = Pair<TAttach, MutablePrivilegeMap> typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder>
interface Storage { interface Storage {
val name: String val name: String
@@ -55,9 +55,9 @@ interface Storage {
fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job
fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>> fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>>
fun readGlobalPrivileges(owner: PlayerProfile): Deferred<MutablePrivilegeMap?> fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?>
fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job
@@ -104,9 +104,9 @@ class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineSco
override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) } override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) }
override fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalAddedData(it) } override fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalPrivileges(it) }
override fun readGlobalPrivileges(owner: PlayerProfile): Deferred<MutablePrivilegeMap?> = b.launchFuture { b.readGlobalPrivileges(owner) } override fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?> = b.launchFuture { b.readGlobalPrivileges(owner) }
override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setGlobalPrivilege(owner, player, privilege) } override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setGlobalPrivilege(owner, player, privilege) }

View File

@@ -6,7 +6,7 @@ import com.zaxxer.hikari.HikariDataSource
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.PlayerProfile.Star.name import io.dico.parcels2.PlayerProfile.Star.name
import io.dico.parcels2.storage.* import io.dico.parcels2.storage.*
import io.dico.parcels2.util.ext.clampMax import io.dico.parcels2.util.math.ext.clampMax
import io.dico.parcels2.util.ext.synchronized import io.dico.parcels2.util.ext.synchronized
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.ArrayChannel import kotlinx.coroutines.channels.ArrayChannel
@@ -193,6 +193,10 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
PrivilegesLocalT.setPrivilege(parcel, profile, privilege) PrivilegesLocalT.setPrivilege(parcel, profile, privilege)
} }
data.privilegeOfStar.takeIf { it != Privilege.DEFAULT }?.let { privilege ->
PrivilegesLocalT.setPrivilege(parcel, PlayerProfile.Star, privilege)
}
setParcelOptionsInteractConfig(parcel, data.interactableConfig) setParcelOptionsInteractConfig(parcel, data.interactableConfig)
} }
@@ -242,13 +246,13 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
} }
} }
override fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>) { override fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>) {
PrivilegesGlobalT.sendAllAddedData(channel) PrivilegesGlobalT.sendAllPrivilegesH(channel)
channel.close() channel.close()
} }
override fun readGlobalPrivileges(owner: PlayerProfile): MutablePrivilegeMap { override fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder? {
return PrivilegesGlobalT.readPrivileges(ProfilesT.getId(owner.toOwnerProfile()) ?: return hashMapOf()) return PrivilegesGlobalT.readPrivileges(ProfilesT.getId(owner.toOwnerProfile()) ?: return null)
} }
override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) { override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) {
@@ -267,7 +271,10 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
System.arraycopy(source, 0, target, 0, source.size.clampMax(target.size)) System.arraycopy(source, 0, target, 0, source.size.clampMax(target.size))
} }
privilegeMap = PrivilegesLocalT.readPrivileges(id) val privileges = PrivilegesLocalT.readPrivileges(id)
if (privileges != null) {
copyPrivilegesFrom(privileges)
}
} }
} }

View File

@@ -15,7 +15,7 @@ object ParcelOptionsT : Table("parcel_options") {
val interact_bitmask = binary("interact_bitmask", 4) val interact_bitmask = binary("interact_bitmask", 4)
} }
typealias PrivilegesSendChannel<AttachT> = SendChannel<Pair<AttachT, MutablePrivilegeMap>> typealias PrivilegesSendChannel<AttachT> = SendChannel<Pair<AttachT, PrivilegesHolder>>
sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) { sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) {
val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE) val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE)
@@ -43,32 +43,32 @@ sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsT
} }
} }
fun readPrivileges(id: Int): MutablePrivilegeMap { fun readPrivileges(id: Int): PrivilegesHolder? {
val list = slice(profile_id, privilege).select { attach_id eq id } val list = slice(profile_id, privilege).select { attach_id eq id }
val result = MutablePrivilegeMap() val result = PrivilegesHolder()
for (row in list) { for (row in list) {
val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
result[profile] = Privilege.getByNumber(row[privilege]) ?: continue result.setRawStoredPrivilege(profile, Privilege.getByNumber(row[privilege]) ?: continue)
} }
return result return result
} }
fun sendAllAddedData(channel: PrivilegesSendChannel<AttachT>) { fun sendAllPrivilegesH(channel: PrivilegesSendChannel<AttachT>) {
val iterator = selectAll().orderBy(attach_id).iterator() val iterator = selectAll().orderBy(attach_id).iterator()
if (iterator.hasNext()) { if (iterator.hasNext()) {
val firstRow = iterator.next() val firstRow = iterator.next()
var id: Int = firstRow[attach_id] var id: Int = firstRow[attach_id]
var attach: AttachT? = null var attach: AttachT? = null
var map: MutablePrivilegeMap? = null var map: PrivilegesHolder? = null
fun initAttachAndMap() { fun initAttachAndMap() {
attach = idTable.getItem(id) attach = idTable.getItem(id)
map = attach?.let { mutableMapOf() } map = attach?.let { PrivilegesHolder() }
} }
fun sendIfPresent() { fun sendIfPresent() {
if (attach != null && map != null && map!!.isNotEmpty()) { if (attach != null && map != null) {
channel.offer(attach!! to map!!) channel.offer(attach!! to map!!)
} }
attach = null attach = null
@@ -91,7 +91,7 @@ sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsT
val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
val privilege = Privilege.getByNumber(row[privilege]) ?: continue val privilege = Privilege.getByNumber(row[privilege]) ?: continue
map!![profile] = privilege map!!.setRawStoredPrivilege(profile, privilege)
} }
sendIfPresent() sendIfPresent()

View File

@@ -1,18 +0,0 @@
package io.dico.parcels2.util
data class Region(val origin: Vec3i, val size: Vec3i) {
val blockCount: Int get() = size.x * size.y * size.z
val center: Vec3d
get() {
val x = (origin.x + size.x) / 2.0
val y = (origin.y + size.y) / 2.0
val z = (origin.z + size.z) / 2.0
return Vec3d(x, y, z)
}
fun withSize(size: Vec3i): Region {
if (size == this.size) return this
return Region(origin, size)
}
}

View File

@@ -1,11 +0,0 @@
package io.dico.parcels2.util
data class Vec2i(
val x: Int,
val z: Int
)
data class Region2i(
val bottom: Vec2i,
val top: Vec2i
)

View File

@@ -77,3 +77,8 @@ class EditLoopScope<T, U>(val _map: MutableMap<T, U>) {
operator fun Formatting.plus(other: Formatting) = toString() + other operator fun Formatting.plus(other: Formatting) = toString() + other
operator fun Formatting.plus(other: String) = toString() + other operator fun Formatting.plus(other: String) = toString() + other
inline fun <T> Pair<T, T>.forEach(block: (T) -> Unit) {
block(first)
block(second)
}

View File

@@ -0,0 +1,19 @@
package io.dico.parcels2.util.math
enum class Dimension {
X,
Y,
Z;
val otherDimensions
get() = when (this) {
X -> Y to Z
Y -> X to Z
Z -> X to Y
}
companion object {
private val values = values()
operator fun get(ordinal: Int) = values[ordinal]
}
}

View File

@@ -0,0 +1,37 @@
package io.dico.parcels2.util.math
data class Region(val origin: Vec3i, val size: Vec3i) {
val blockCount: Int get() = size.x * size.y * size.z
val center: Vec3d
get() {
val x = (origin.x + size.x) / 2.0
val y = (origin.y + size.y) / 2.0
val z = (origin.z + size.z) / 2.0
return Vec3d(x, y, z)
}
val end: Vec3i
get() = origin + size
val max: Vec3i
get() = Vec3i(origin.x + size.x - 1, origin.y + size.y - 1, origin.z + size.z - 1)
fun withSize(size: Vec3i): Region {
if (size == this.size) return this
return Region(origin, size)
}
operator fun contains(loc: Vec3i): Boolean = getFirstUncontainedDimensionOf(loc) == null
fun getFirstUncontainedDimensionOf(loc: Vec3i): Dimension? {
val max = max
return when {
loc.x !in origin.x..max.x -> Dimension.X
loc.z !in origin.z..max.z -> Dimension.Z
loc.y !in origin.y..max.y -> Dimension.Y
else -> null
}
}
}

View File

@@ -0,0 +1,6 @@
package io.dico.parcels2.util.math
data class Vec2i(
val x: Int,
val z: Int
)

View File

@@ -0,0 +1,54 @@
package io.dico.parcels2.util.math
import io.dico.parcels2.util.math.ext.floor
import org.bukkit.Location
import kotlin.math.sqrt
data class Vec3d(
val x: Double,
val y: Double,
val z: Double
) {
constructor(loc: Location) : this(loc.x, loc.y, loc.z)
operator fun plus(o: Vec3d) = Vec3d(x + o.x, y + o.y, z + o.z)
operator fun minus(o: Vec3i) = Vec3d(x - o.x, y - o.y, z - o.z)
infix fun addX(o: Double) = Vec3d(x + o, y, z)
infix fun addY(o: Double) = Vec3d(x, y + o, z)
infix fun addZ(o: Double) = Vec3d(x, y, z + o)
infix fun withX(o: Double) = Vec3d(o, y, z)
infix fun withY(o: Double) = Vec3d(x, o, z)
infix fun withZ(o: Double) = Vec3d(x, y, o)
fun add(ox: Double, oy: Double, oz: Double) = Vec3d(x + ox, y + oy, z + oz)
fun toVec3i() = Vec3i(x.floor(), y.floor(), z.floor())
fun distanceSquared(o: Vec3d): Double {
val dx = o.x - x
val dy = o.y - y
val dz = o.z - z
return dx * dx + dy * dy + dz * dz
}
fun distance(o: Vec3d) = sqrt(distanceSquared(o))
operator fun get(dimension: Dimension) =
when (dimension) {
Dimension.X -> x
Dimension.Y -> y
Dimension.Z -> z
}
fun with(dimension: Dimension, value: Double) =
when (dimension) {
Dimension.X -> withX(value)
Dimension.Y -> withY(value)
Dimension.Z -> withZ(value)
}
fun add(dimension: Dimension, value: Double) =
when (dimension) {
Dimension.X -> addX(value)
Dimension.Y -> addY(value)
Dimension.Z -> addZ(value)
}
}

View File

@@ -1,31 +1,18 @@
package io.dico.parcels2.util package io.dico.parcels2.util.math
import io.dico.parcels2.util.ext.clampMax import io.dico.parcels2.util.math.ext.clampMax
import org.bukkit.Location
import org.bukkit.World import org.bukkit.World
import org.bukkit.block.Block import org.bukkit.block.Block
import org.bukkit.block.BlockFace import org.bukkit.block.BlockFace
data class Vec3d(
val x: Double,
val y: Double,
val z: Double
) {
operator fun plus(o: Vec3d) = Vec3d(x + o.x, y + o.y, z + o.z)
operator fun minus(o: Vec3i) = Vec3d(x - o.x, y - o.y, z - o.z)
infix fun addX(o: Double) = Vec3d(x + o, y, z)
infix fun addY(o: Double) = Vec3d(x, y + o, z)
infix fun addZ(o: Double) = Vec3d(x, y, z + o)
infix fun withX(o: Double) = Vec3d(o, y, z)
infix fun withY(o: Double) = Vec3d(x, o, z)
infix fun withZ(o: Double) = Vec3d(x, y, o)
fun add(ox: Double, oy: Double, oz: Double) = Vec3d(x + ox, y + oy, z + oz)
}
data class Vec3i( data class Vec3i(
val x: Int, val x: Int,
val y: Int, val y: Int,
val z: Int val z: Int
) { ) {
constructor(loc: Location) : this(loc.blockX, loc.blockY, loc.blockZ)
operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z) 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) 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 addX(o: Int) = Vec3i(x + o, y, z)
@@ -38,6 +25,27 @@ data class Vec3i(
fun neg() = Vec3i(-x, -y, -z) fun neg() = Vec3i(-x, -y, -z)
fun clampMax(o: Vec3i) = Vec3i(x.clampMax(o.x), y.clampMax(o.y), z.clampMax(o.z)) fun clampMax(o: Vec3i) = Vec3i(x.clampMax(o.x), y.clampMax(o.y), z.clampMax(o.z))
operator fun get(dimension: Dimension) =
when (dimension) {
Dimension.X -> x
Dimension.Y -> y
Dimension.Z -> z
}
fun with(dimension: Dimension, value: Int) =
when (dimension) {
Dimension.X -> withX(value)
Dimension.Y -> withY(value)
Dimension.Z -> withZ(value)
}
fun add(dimension: Dimension, value: Int) =
when (dimension) {
Dimension.X -> addX(value)
Dimension.Y -> addY(value)
Dimension.Z -> addZ(value)
}
companion object { companion object {
private operator fun invoke(face: BlockFace) = Vec3i(face.modX, face.modY, face.modZ) private operator fun invoke(face: BlockFace) = Vec3i(face.modX, face.modY, face.modZ)
val down = Vec3i(BlockFace.DOWN) val down = Vec3i(BlockFace.DOWN)

View File

@@ -1,4 +1,4 @@
package io.dico.parcels2.util.ext package io.dico.parcels2.util.math.ext
fun Double.floor(): Int { fun Double.floor(): Int {
val down = toInt() val down = toInt()
@@ -34,6 +34,7 @@ fun IntRange.clamp(min: Int, max: Int): IntRange {
// the name coerceAtMost is bad // the name coerceAtMost is bad
fun Int.clampMax(max: Int) = coerceAtMost(max) fun Int.clampMax(max: Int) = coerceAtMost(max)
fun Double.clampMin(min: Double) = coerceAtLeast(min) fun Double.clampMin(min: Double) = coerceAtLeast(min)
fun Double.clampMax(max: Double) = coerceAtMost(max)
// Why does this not exist? // Why does this not exist?
infix fun Int.ceilDiv(divisor: Int): Int { infix fun Int.ceilDiv(divisor: Int): Int {