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
import io.dico.parcels2.util.ext.ceilDiv
import io.dico.parcels2.util.math.ext.ceilDiv
import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
import org.bukkit.Material
import java.util.EnumMap

View File

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

View File

@@ -1,9 +1,9 @@
package io.dico.parcels2
import io.dico.parcels2.blockvisitor.*
import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.get
import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.math.Vec2i
import io.dico.parcels2.util.math.get
import kotlinx.coroutines.CoroutineScope
import org.bukkit.Chunk
import org.bukkit.Location
@@ -37,12 +37,12 @@ abstract class ParcelGenerator : ChunkGenerator() {
abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId,
container: ParcelContainer,
coroutineScope: CoroutineScope,
workDispatcher: WorkDispatcher): Pair<ParcelLocator, ParcelBlockManager>
jobDispatcher: JobDispatcher): Pair<ParcelLocator, ParcelBlockManager>
}
interface ParcelBlockManager {
val world: World
val workDispatcher: WorkDispatcher
val jobDispatcher: JobDispatcher
val parcelTraverser: RegionTraverser
// fun getBottomBlock(parcel: ParcelId): Vec2i
@@ -55,13 +55,13 @@ interface ParcelBlockManager {
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
@@ -71,7 +71,7 @@ interface ParcelBlockManager {
inline fun ParcelBlockManager.doBlockOperation(parcel: ParcelId,
traverser: RegionTraverser,
crossinline operation: suspend WorkerScope.(Block) -> Unit) = submitBlockVisitor(parcel) {
crossinline operation: suspend JobScope.(Block) -> Unit) = submitBlockVisitor(parcel) {
val region = getRegion(parcel)
val blockCount = region.blockCount.toDouble()
val blocks = traverser.traverseRegion(region)

View File

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

View File

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

View File

@@ -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.BukkitWorkDispatcher
import io.dico.parcels2.blockvisitor.WorkDispatcher
import io.dico.parcels2.blockvisitor.BukkitJobDispatcher
import io.dico.parcels2.blockvisitor.JobDispatcher
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 workDispatcher: WorkDispatcher by lazy { BukkitWorkDispatcher(this, options.tickWorktime) }
val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, options.tickJobtime) }
override fun onEnable() {
plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
@@ -55,11 +55,11 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
}
override fun onDisable() {
val hasWorkers = workDispatcher.workers.isNotEmpty()
val hasWorkers = jobDispatcher.jobs.isNotEmpty()
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) {
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 {
private var _privilegeOfStar: Privilege = DEFAULT
override var privilegeOfStar: Privilege
override /*open*/ var privilegeOfStar: Privilege
get() = _privilegeOfStar
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
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
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.getMaterialsWithWoolColorPrefix
import org.bukkit.Material

View File

@@ -2,11 +2,11 @@ package io.dico.parcels2.blockvisitor
import io.dico.parcels2.ParcelsPlugin
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.CoroutineScope
import kotlinx.coroutines.CoroutineStart.LAZY
import kotlinx.coroutines.Job
import kotlinx.coroutines.Job as CoroutineJob
import kotlinx.coroutines.launch
import org.bukkit.scheduler.BukkitTask
import java.lang.System.currentTimeMillis
@@ -16,21 +16,21 @@ import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
import kotlin.coroutines.resume
typealias WorkerTask = suspend WorkerScope.() -> Unit
typealias WorkerUpdateLister = Worker.(Double, Long) -> Unit
typealias JobFunction = suspend JobScope.() -> 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
*/
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.
@@ -38,27 +38,27 @@ interface WorkDispatcher {
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
/**
* 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.
*/
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
@@ -69,28 +69,28 @@ interface Worker : WorkerAndScopeMembersUnion {
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.
* The first call occurs after at least [minDelay] milliseconds in a likewise manner.
* Repeated invocations of this method result in an [IllegalStateException]
*
* 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.
*/
fun onCompleted(block: WorkerUpdateLister): Worker
fun onCompleted(block: JobUpdateLister): Job
/**
* Await completion of this worker
* Await completion of this job
*/
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.
*/
@@ -107,29 +107,29 @@ interface WorkerScope : WorkerAndScopeMembersUnion {
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
*/
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 {
delegateWork(portion).apply {
inline fun <T> JobScope.delegateWork(portion: Double = -1.0, block: JobScope.() -> T): T {
delegateProgress(portion).apply {
val result = block()
markComplete()
return result
}
}
interface WorkerInternal : Worker, WorkerScope {
interface JobInternal : Job, JobScope {
/**
* Start or resumes the execution of this worker
* and returns true if the worker completed
* Start or resumes the execution of this job
* and returns true if the job completed
*
* [worktime] is the maximum amount of time, in milliseconds,
* 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.
*/
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.
* 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
*/
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
private var bukkitTask: BukkitTask? = null
// The workers.
private val _workers = LinkedList<WorkerInternal>()
override val workers: List<Worker> = _workers
// The jobs.
private val _jobs = LinkedList<JobInternal>()
override val jobs: List<Job> = _jobs
override fun dispatch(task: WorkerTask): Worker {
val worker: WorkerInternal = WorkerImpl(plugin, task)
override fun dispatch(task: JobFunction): Job {
val job: JobInternal = JobImpl(plugin, task)
if (bukkitTask == null) {
val completed = worker.resume(options.workTime.toLong())
if (completed) return worker
bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickJobs() }
val completed = job.resume(options.jobTime.toLong())
if (completed) return job
bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickCoroutineJobs() }
}
_workers.addFirst(worker)
return worker
_jobs.addFirst(job)
return job
}
private fun tickJobs() {
val workers = _workers
if (workers.isEmpty()) return
private fun tickCoroutineJobs() {
val jobs = _jobs
if (jobs.isEmpty()) return
val tickStartTime = System.currentTimeMillis()
val iterator = workers.listIterator(index = 0)
val iterator = jobs.listIterator(index = 0)
while (iterator.hasNext()) {
val time = System.currentTimeMillis()
val timeElapsed = time - tickStartTime
val timeLeft = options.workTime - timeElapsed
val timeLeft = options.jobTime - timeElapsed
if (timeLeft <= 0) return
val count = workers.size - iterator.nextIndex()
val count = jobs.size - iterator.nextIndex()
val timePerJob = (timeLeft + count - 1) / count
val worker = iterator.next()
val completed = worker.resume(timePerJob)
val job = iterator.next()
val completed = job.resume(timePerJob)
if (completed) {
iterator.remove()
}
}
if (workers.isEmpty()) {
if (jobs.isEmpty()) {
bukkitTask?.cancel()
bukkitTask = null
}
}
override fun completeAllTasks() {
_workers.forEach {
_jobs.forEach {
it.resume(-1)
}
_workers.clear()
_jobs.clear()
bukkitTask?.cancel()
bukkitTask = null
}
}
private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerInternal {
override val job: Job = scope.launch(start = LAZY) { task() }
private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
override val job: CoroutineJob = scope.launch(start = LAZY) { task() }
private var continuation: Continuation<Unit>? = null
private var nextSuspensionTime: Long = 0L
@@ -217,17 +217,17 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
override var completionException: Throwable? = null; private set
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 lastUpdateTime: Long = 0L
private var onCompleted: WorkerUpdateLister? = null
private var onCompleted: JobUpdateLister? = null
init {
job.invokeOnCompletion { exception ->
// report any error that occurred
completionException = exception?.also {
if (it !is CancellationException)
logger.error("WorkerTask generated an exception", it)
logger.error("JobFunction generated an exception", it)
}
// 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() }
if (asCompletionListener) onCompleted(block)
if (isComplete) return this
@@ -250,7 +250,7 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
return this
}
override fun onCompleted(block: WorkerUpdateLister): Worker {
override fun onCompleted(block: JobUpdateLister): Job {
if (isComplete) {
block(1.0, startTimeOrElapsedTime)
return this
@@ -260,7 +260,7 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
onCompleted = if (cur == null) {
block
} else {
fun Worker.(prog: Double, el: Long) {
fun Job.(prog: Double, el: Long) {
cur(prog, el)
block(prog, el)
}
@@ -315,25 +315,25 @@ private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerIntern
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))
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
get() = this@WorkerImpl.elapsedTime
get() = this@JobImpl.elapsedTime
override suspend fun markSuspensionPoint() =
this@WorkerImpl.markSuspensionPoint()
this@JobImpl.markSuspensionPoint()
override val progress: Double
get() = (this@WorkerImpl.progress - progressStart) / portion
get() = (this@JobImpl.progress - progressStart) / portion
override fun setProgress(progress: Double) =
this@WorkerImpl.setProgress(progressStart + progress * portion)
this@JobImpl.setProgress(progressStart + progress * portion)
override fun delegateWork(portion: Double): WorkerScope =
this@WorkerImpl.delegateWork(this.portion, portion)
override fun delegateProgress(portion: Double): JobScope =
this@JobImpl.delegateProgress(this.portion, portion)
}
}

View File

@@ -1,8 +1,9 @@
package io.dico.parcels2.blockvisitor
import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec3i
import io.dico.parcels2.util.ext.clampMax
import io.dico.parcels2.util.math.Dimension
import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.math.ext.clampMax
private typealias Scope = SequenceScope<Vec3i>
@@ -54,9 +55,9 @@ sealed class RegionTraverser {
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 maxOfPrimary = size[primary] - 1
val maxOfSecondary = size[secondary] - 1
val maxOfTertiary = size[tertiary] - 1
val isPrimaryIncreasing = direction.isIncreasing(primary)
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 {
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 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
when (orderNum) {
0 -> vec.add(dp, ds, dt) // xyz
1 -> vec.add(dt, dp, ds) // yzx
2 -> vec.add(ds, dt, dp) // zxy
3 -> vec.add(dp, dt, ds) // xzy
4 -> vec.add(ds, dp, dt) // yxz
5 -> vec.add(dt, ds, dp) // zyx
0 -> vec.add(p, s, t) // xyz
1 -> vec.add(t, p, s) // yzx
2 -> vec.add(s, t, p) // zxy
3 -> vec.add(p, t, s) // xzy
4 -> vec.add(s, p, t) // yxz
5 -> vec.add(t, s, p) // zyx
else -> error("Invalid orderNum $orderNum")
}
}
@@ -212,9 +195,9 @@ inline class TraverseDirection(val bits: Int) {
fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean =
if (isIncreasing(dimension))
dimension.extract(block) <= dimension.extract(current)
block[dimension] <= current[dimension]
else
dimension.extract(block) >= dimension.extract(current)
block[dimension] >= current[dimension]
fun comesFirst(current: Vec3i, block: Vec3i) =
comesFirst(current, block, Dimension.X)

View File

@@ -1,8 +1,8 @@
package io.dico.parcels2.blockvisitor
import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec3i
import io.dico.parcels2.util.get
import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.math.get
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.World
@@ -24,7 +24,7 @@ class Schematic {
private var isLoaded = false; private set
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
val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it }
@@ -52,7 +52,7 @@ class Schematic {
isLoaded = true
}
suspend fun WorkerScope.paste(world: World, position: Vec3i) {
suspend fun JobScope.paste(world: World, position: Vec3i) {
if (!isLoaded) throw IllegalStateException()
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)
}
fun getPasteTask(world: World, position: Vec3i): WorkerTask = {
fun getPasteTask(world: World, position: Vec3i): JobFunction = {
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.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.parcelLimit
import org.bukkit.entity.Player
@@ -50,7 +50,7 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
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 ->
val alt = context.getFormat(EMessageType.NUMBER)
val main = context.getFormat(EMessageType.INFORMATIVE)

View File

@@ -81,14 +81,14 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("jobs")
fun cmdJobs(): Any? {
val workers = plugin.workDispatcher.workers
val workers = plugin.jobDispatcher.jobs
println(workers.map { it.job }.joinToString(separator = "\n"))
return "Task count: ${workers.size}"
}
@Cmd("complete_jobs")
fun cmdCompleteJobs(): Any? = cmdJobs().also {
plugin.launch { plugin.workDispatcher.completeAllTasks() }
plugin.launch { plugin.jobDispatcher.completeAllTasks() }
}
@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.REAL
import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.ext.floor
import io.dico.parcels2.util.math.Vec2i
import io.dico.parcels2.util.math.ext.floor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player

View File

@@ -3,12 +3,12 @@ package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
import io.dico.parcels2.blockvisitor.*
import io.dico.parcels2.options.DefaultGeneratorOptions
import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.Vec3i
import io.dico.parcels2.util.ext.even
import io.dico.parcels2.util.ext.umod
import io.dico.parcels2.util.get
import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.math.Vec2i
import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.math.ext.even
import io.dico.parcels2.util.math.ext.umod
import io.dico.parcels2.util.math.get
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
import kotlinx.coroutines.launch
@@ -121,9 +121,9 @@ class DefaultParcelGenerator(
worldId: ParcelWorldId,
container: ParcelContainer,
coroutineScope: CoroutineScope,
workDispatcher: WorkDispatcher
jobDispatcher: JobDispatcher
): 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? {
@@ -158,7 +158,7 @@ class DefaultParcelGenerator(
private inner class ParcelBlockManagerImpl(
val worldId: ParcelWorldId,
coroutineScope: CoroutineScope,
override val workDispatcher: WorkDispatcher
override val jobDispatcher: JobDispatcher
) : ParcelBlockManagerBase(), CoroutineScope by coroutineScope {
override val world: World = this@DefaultParcelGenerator.world
override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight)
@@ -177,7 +177,10 @@ class DefaultParcelGenerator(
override fun getRegion(parcel: ParcelId): Region {
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 {
@@ -234,12 +237,12 @@ class DefaultParcelGenerator(
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) }
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")
val worker = workDispatcher.dispatch(task)
val worker = jobDispatcher.dispatch(task)
for (parcel in parcels) {
launch(start = UNDISPATCHED) {
@@ -252,7 +255,7 @@ class DefaultParcelGenerator(
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 b = getBottomBlock(parcel)
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 blocks = parcelTraverser.traverseRegion(region)
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 region2 = getRegionConsideringWorld(parcel2)

View File

@@ -3,7 +3,7 @@ package io.dico.parcels2.defaultimpl
import io.dico.dicore.Formatting
import io.dico.parcels2.*
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 org.bukkit.Material
import org.joda.time.DateTime

View File

@@ -58,8 +58,10 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
if (worldExists) Bukkit.getWorld(worldName)!!
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.workDispatcher)
parcelWorld = ParcelWorldImpl(
bukkitWorld, generator, worldOptions.runtime, plugin.storage,
plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.jobDispatcher
)
if (!worldExists) {
val time = DateTime.now()
@@ -95,11 +97,18 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
logger.info("Loading all parcel data...")
val channel = plugin.storage.transmitAllParcelData()
do {
val pair = channel.receiveOrNull() ?: break
val parcel = getParcelById(pair.first) ?: continue
pair.second?.let { parcel.copyDataIgnoringDatabase(it) }
} while (true)
while (true) {
val (id, data) = channel.receiveOrNull() ?: break
val parcel = getParcelById(id) ?: continue
data?.let { parcel.copyDataIgnoringDatabase(it) }
}
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")
_dataIsLoaded = true

View File

@@ -3,7 +3,7 @@
package io.dico.parcels2.defaultimpl
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.storage.Storage
import kotlinx.coroutines.CoroutineScope
@@ -18,7 +18,7 @@ class ParcelWorldImpl(override val world: World,
override val globalPrivileges: GlobalPrivilegesManager,
containerFactory: ParcelContainerFactory,
coroutineScope: CoroutineScope,
workDispatcher: WorkDispatcher)
jobDispatcher: JobDispatcher)
: 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, workDispatcher)
val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, jobDispatcher)
locator = pair.first
blockManager = pair.second

View File

@@ -7,6 +7,12 @@ import io.dico.dicore.RegistratorListener
import io.dico.parcels2.*
import io.dico.parcels2.storage.Storage
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.World
import org.bukkit.block.Biome
@@ -52,6 +58,7 @@ class ParcelListeners(
return world to world.getParcelAt(block)
}
/*
* Prevents players from entering plots they are banned from
*/
@@ -59,12 +66,36 @@ class ParcelListeners(
val onPlayerMoveEvent = RegistratorListener<PlayerMoveEvent> l@{ event ->
val user = event.player
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)) {
parcelProvider.getParcelAt(event.from)?.also {
user.teleport(it.homeLocation)
val region = parcel.world.blockManager.getRegion(parcel.id)
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")
} ?: 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
import io.dico.parcels2.blockvisitor.TickWorktimeOptions
import io.dico.parcels2.blockvisitor.TickJobtimeOptions
import org.bukkit.GameMode
import org.bukkit.Material
import java.io.Reader
@@ -11,7 +11,7 @@ class Options {
var worlds: Map<String, WorldOptions> = hashMapOf()
private set
var storage: StorageOptions = StorageOptions()
var tickWorktime: TickWorktimeOptions = TickWorktimeOptions(20, 1)
var tickJobtime: TickJobtimeOptions = TickJobtimeOptions(20, 1)
var migration = MigrationOptionsHolder()
fun addWorld(name: String,

View File

@@ -61,9 +61,9 @@ interface Backing {
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)
}

View File

@@ -14,7 +14,7 @@ import java.util.UUID
import kotlin.coroutines.CoroutineContext
typealias DataPair = Pair<ParcelId, ParcelData?>
typealias AddedDataPair<TAttach> = Pair<TAttach, MutablePrivilegeMap>
typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder>
interface Storage {
val name: String
@@ -55,9 +55,9 @@ interface Storage {
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
@@ -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 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) }

View File

@@ -6,7 +6,7 @@ import com.zaxxer.hikari.HikariDataSource
import io.dico.parcels2.*
import io.dico.parcels2.PlayerProfile.Star.name
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 kotlinx.coroutines.*
import kotlinx.coroutines.channels.ArrayChannel
@@ -193,6 +193,10 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
PrivilegesLocalT.setPrivilege(parcel, profile, privilege)
}
data.privilegeOfStar.takeIf { it != Privilege.DEFAULT }?.let { privilege ->
PrivilegesLocalT.setPrivilege(parcel, PlayerProfile.Star, privilege)
}
setParcelOptionsInteractConfig(parcel, data.interactableConfig)
}
@@ -242,13 +246,13 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
}
}
override fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>) {
PrivilegesGlobalT.sendAllAddedData(channel)
override fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>) {
PrivilegesGlobalT.sendAllPrivilegesH(channel)
channel.close()
}
override fun readGlobalPrivileges(owner: PlayerProfile): MutablePrivilegeMap {
return PrivilegesGlobalT.readPrivileges(ProfilesT.getId(owner.toOwnerProfile()) ?: return hashMapOf())
override fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder? {
return PrivilegesGlobalT.readPrivileges(ProfilesT.getId(owner.toOwnerProfile()) ?: return null)
}
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))
}
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)
}
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) {
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 result = MutablePrivilegeMap()
val result = PrivilegesHolder()
for (row in list) {
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
}
fun sendAllAddedData(channel: PrivilegesSendChannel<AttachT>) {
fun sendAllPrivilegesH(channel: PrivilegesSendChannel<AttachT>) {
val iterator = selectAll().orderBy(attach_id).iterator()
if (iterator.hasNext()) {
val firstRow = iterator.next()
var id: Int = firstRow[attach_id]
var attach: AttachT? = null
var map: MutablePrivilegeMap? = null
var map: PrivilegesHolder? = null
fun initAttachAndMap() {
attach = idTable.getItem(id)
map = attach?.let { mutableMapOf() }
map = attach?.let { PrivilegesHolder() }
}
fun sendIfPresent() {
if (attach != null && map != null && map!!.isNotEmpty()) {
if (attach != null && map != null) {
channel.offer(attach!! to map!!)
}
attach = null
@@ -91,7 +91,7 @@ sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsT
val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
val privilege = Privilege.getByNumber(row[privilege]) ?: continue
map!![profile] = privilege
map!!.setRawStoredPrivilege(profile, privilege)
}
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: 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.block.Block
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(
val x: Int,
val y: 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 minus(o: Vec3i) = Vec3i(x - o.x, y - o.y, z - o.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 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 {
private operator fun invoke(face: BlockFace) = Vec3i(face.modX, face.modY, face.modZ)
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 {
val down = toInt()
@@ -34,6 +34,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)
fun Double.clampMax(max: Double) = coerceAtMost(max)
// Why does this not exist?
infix fun Int.ceilDiv(divisor: Int): Int {