summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/kotlin/io/dico/parcels2/Interactable.kt346
-rw-r--r--src/main/kotlin/io/dico/parcels2/JobDispatcher.kt61
-rw-r--r--src/main/kotlin/io/dico/parcels2/Parcel.kt132
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt5
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelId.kt112
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelWorld.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt9
-rw-r--r--src/main/kotlin/io/dico/parcels2/PlayerProfile.kt44
-rw-r--r--src/main/kotlin/io/dico/parcels2/Privilege.kt250
-rw-r--r--src/main/kotlin/io/dico/parcels2/Privileges.kt128
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt122
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Entities.kt38
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt76
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt646
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt242
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt126
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt188
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt262
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt322
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt3
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt152
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt286
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt136
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt112
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt13
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt49
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt46
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt31
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt54
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt174
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt81
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt120
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt30
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt13
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt70
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt42
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/Options.kt114
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt132
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt176
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt116
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Backing.kt138
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt74
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Hikari.kt146
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Storage.kt228
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt10
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt148
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt328
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt222
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt18
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt234
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt62
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt9
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt84
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/PluginAware.kt19
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt20
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/ext/Material.kt214
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt162
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/ext/Player.kt114
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt36
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Math.kt82
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Region.kt72
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt18
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt8
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt2
-rw-r--r--src/main/resources/logback.xml32
-rw-r--r--src/main/resources/plugin.yml10
68 files changed, 3896 insertions, 3659 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/Interactable.kt b/src/main/kotlin/io/dico/parcels2/Interactable.kt
index c301340..f25b796 100644
--- a/src/main/kotlin/io/dico/parcels2/Interactable.kt
+++ b/src/main/kotlin/io/dico/parcels2/Interactable.kt
@@ -1,174 +1,174 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.util.math.ceilDiv
-import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
-import org.bukkit.Material
-import java.util.EnumMap
-
-class Interactables
-private constructor(
- val id: Int,
- val name: String,
- val interactableByDefault: Boolean,
- vararg val materials: Material
-) {
-
- companion object {
- val classesById: List<Interactables>
- val classesByName: Map<String, Interactables>
- val listedMaterials: Map<Material, Int>
-
- init {
- val array = getClassesArray()
- classesById = array.asList()
- classesByName = mapOf(*array.map { it.name to it }.toTypedArray())
- listedMaterials = EnumMap(mapOf(*array.flatMap { clazz -> clazz.materials.map { it to clazz.id } }.toTypedArray()))
- }
-
- operator fun get(material: Material): Interactables? {
- val id = listedMaterials[material] ?: return null
- return classesById[id]
- }
-
- operator fun get(name: String): Interactables {
- return classesByName[name] ?: throw IllegalArgumentException("Interactables class does not exist: $name")
- }
-
- operator fun get(id: Int): Interactables {
- return classesById[id]
- }
-
- private fun getClassesArray() = run {
- var id = 0
- @Suppress("UNUSED_CHANGED_VALUE")
- arrayOf(
- Interactables(
- id++, "buttons", true,
- Material.STONE_BUTTON,
- *getMaterialsWithWoodTypePrefix("BUTTON")
- ),
-
- Interactables(
- id++, "levers", true,
- Material.LEVER
- ),
-
- Interactables(
- id++, "pressure_plates", true,
- Material.STONE_PRESSURE_PLATE,
- *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
- Material.HEAVY_WEIGHTED_PRESSURE_PLATE,
- Material.LIGHT_WEIGHTED_PRESSURE_PLATE
- ),
-
- Interactables(
- id++, "redstone", false,
- Material.COMPARATOR,
- Material.REPEATER
- ),
-
- Interactables(
- id++, "containers", false,
- Material.CHEST,
- Material.TRAPPED_CHEST,
- Material.DISPENSER,
- Material.DROPPER,
- Material.HOPPER,
- Material.FURNACE
- ),
-
- Interactables(
- id++, "gates", true,
- *getMaterialsWithWoodTypePrefix("DOOR"),
- *getMaterialsWithWoodTypePrefix("TRAPDOOR"),
- *getMaterialsWithWoodTypePrefix("FENCE_GATE")
- )
- )
- }
-
- }
-
-}
-
-val Parcel?.effectiveInteractableConfig: InteractableConfiguration
- get() = this?.interactableConfig ?: pathInteractableConfig
-
-val pathInteractableConfig: InteractableConfiguration = run {
- val data = BitmaskInteractableConfiguration().apply {
- Interactables.classesById.forEach {
- setInteractable(it, false)
- }
- }
- object : InteractableConfiguration by data {
- override fun setInteractable(clazz: Interactables, interactable: Boolean) =
- throw IllegalStateException("pathInteractableConfig is immutable")
-
- override fun clear() =
- throw IllegalStateException("pathInteractableConfig is immutable")
-
- override fun copyFrom(other: InteractableConfiguration) =
- throw IllegalStateException("pathInteractableConfig is immutable")
- }
-}
-
-interface InteractableConfiguration {
- val interactableClasses: List<Interactables> get() = Interactables.classesById.filter { isInteractable(it) }
-
- fun isInteractable(material: Material): Boolean
- fun isInteractable(clazz: Interactables): Boolean
- fun isDefault(): Boolean
-
- fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean
- fun clear(): Boolean
- fun copyFrom(other: InteractableConfiguration) =
- Interactables.classesById.fold(false) { cur, elem -> setInteractable(elem, other.isInteractable(elem) || cur) }
-
- operator fun invoke(material: Material) = isInteractable(material)
- operator fun invoke(className: String) = isInteractable(Interactables[className])
-}
-
-fun InteractableConfiguration.isInteractable(clazz: Interactables?) = clazz != null && isInteractable(clazz)
-
-class BitmaskInteractableConfiguration : InteractableConfiguration {
- val bitmaskArray = IntArray(Interactables.classesById.size ceilDiv Int.SIZE_BITS)
-
- private fun isBitSet(classId: Int): Boolean {
- val idx = classId.ushr(5)
- return idx < bitmaskArray.size && bitmaskArray[idx].and(0x1.shl(classId.and(0x1F))) != 0
- }
-
- override fun isInteractable(material: Material): Boolean {
- val classId = Interactables.listedMaterials[material] ?: return false
- return isBitSet(classId) != Interactables.classesById[classId].interactableByDefault
- }
-
- override fun isInteractable(clazz: Interactables): Boolean {
- return isBitSet(clazz.id) != clazz.interactableByDefault
- }
-
- override fun isDefault(): Boolean {
- for (x in bitmaskArray) {
- if (x != 0) return false
- }
- return true
- }
-
- override fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean {
- val idx = clazz.id.ushr(5)
- if (idx >= bitmaskArray.size) return false
- val bit = 0x1.shl(clazz.id.and(0x1F))
- val oldBitmask = bitmaskArray[idx]
- bitmaskArray[idx] = if (interactable != clazz.interactableByDefault) oldBitmask.or(bit) else oldBitmask.and(bit.inv())
- return bitmaskArray[idx] != oldBitmask
- }
-
- override fun clear(): Boolean {
- var change = false
- for (i in bitmaskArray.indices) {
- if (!change && bitmaskArray[i] != 0) change = true
- bitmaskArray[i] = 0
- }
- return change
- }
-
+package io.dico.parcels2
+
+import io.dico.parcels2.util.math.ceilDiv
+import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
+import org.bukkit.Material
+import java.util.EnumMap
+
+class Interactables
+private constructor(
+ val id: Int,
+ val name: String,
+ val interactableByDefault: Boolean,
+ vararg val materials: Material
+) {
+
+ companion object {
+ val classesById: List<Interactables>
+ val classesByName: Map<String, Interactables>
+ val listedMaterials: Map<Material, Int>
+
+ init {
+ val array = getClassesArray()
+ classesById = array.asList()
+ classesByName = mapOf(*array.map { it.name to it }.toTypedArray())
+ listedMaterials = EnumMap(mapOf(*array.flatMap { clazz -> clazz.materials.map { it to clazz.id } }.toTypedArray()))
+ }
+
+ operator fun get(material: Material): Interactables? {
+ val id = listedMaterials[material] ?: return null
+ return classesById[id]
+ }
+
+ operator fun get(name: String): Interactables {
+ return classesByName[name] ?: throw IllegalArgumentException("Interactables class does not exist: $name")
+ }
+
+ operator fun get(id: Int): Interactables {
+ return classesById[id]
+ }
+
+ private fun getClassesArray() = run {
+ var id = 0
+ @Suppress("UNUSED_CHANGED_VALUE")
+ arrayOf(
+ Interactables(
+ id++, "buttons", true,
+ Material.STONE_BUTTON,
+ *getMaterialsWithWoodTypePrefix("BUTTON")
+ ),
+
+ Interactables(
+ id++, "levers", true,
+ Material.LEVER
+ ),
+
+ Interactables(
+ id++, "pressure_plates", true,
+ Material.STONE_PRESSURE_PLATE,
+ *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
+ Material.HEAVY_WEIGHTED_PRESSURE_PLATE,
+ Material.LIGHT_WEIGHTED_PRESSURE_PLATE
+ ),
+
+ Interactables(
+ id++, "redstone", false,
+ Material.COMPARATOR,
+ Material.REPEATER
+ ),
+
+ Interactables(
+ id++, "containers", false,
+ Material.CHEST,
+ Material.TRAPPED_CHEST,
+ Material.DISPENSER,
+ Material.DROPPER,
+ Material.HOPPER,
+ Material.FURNACE
+ ),
+
+ Interactables(
+ id++, "gates", true,
+ *getMaterialsWithWoodTypePrefix("DOOR"),
+ *getMaterialsWithWoodTypePrefix("TRAPDOOR"),
+ *getMaterialsWithWoodTypePrefix("FENCE_GATE")
+ )
+ )
+ }
+
+ }
+
+}
+
+val Parcel?.effectiveInteractableConfig: InteractableConfiguration
+ get() = this?.interactableConfig ?: pathInteractableConfig
+
+val pathInteractableConfig: InteractableConfiguration = run {
+ val data = BitmaskInteractableConfiguration().apply {
+ Interactables.classesById.forEach {
+ setInteractable(it, false)
+ }
+ }
+ object : InteractableConfiguration by data {
+ override fun setInteractable(clazz: Interactables, interactable: Boolean) =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+
+ override fun clear() =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+
+ override fun copyFrom(other: InteractableConfiguration) =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+ }
+}
+
+interface InteractableConfiguration {
+ val interactableClasses: List<Interactables> get() = Interactables.classesById.filter { isInteractable(it) }
+
+ fun isInteractable(material: Material): Boolean
+ fun isInteractable(clazz: Interactables): Boolean
+ fun isDefault(): Boolean
+
+ fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean
+ fun clear(): Boolean
+ fun copyFrom(other: InteractableConfiguration) =
+ Interactables.classesById.fold(false) { cur, elem -> setInteractable(elem, other.isInteractable(elem) || cur) }
+
+ operator fun invoke(material: Material) = isInteractable(material)
+ operator fun invoke(className: String) = isInteractable(Interactables[className])
+}
+
+fun InteractableConfiguration.isInteractable(clazz: Interactables?) = clazz != null && isInteractable(clazz)
+
+class BitmaskInteractableConfiguration : InteractableConfiguration {
+ val bitmaskArray = IntArray(Interactables.classesById.size ceilDiv Int.SIZE_BITS)
+
+ private fun isBitSet(classId: Int): Boolean {
+ val idx = classId.ushr(5)
+ return idx < bitmaskArray.size && bitmaskArray[idx].and(0x1.shl(classId.and(0x1F))) != 0
+ }
+
+ override fun isInteractable(material: Material): Boolean {
+ val classId = Interactables.listedMaterials[material] ?: return false
+ return isBitSet(classId) != Interactables.classesById[classId].interactableByDefault
+ }
+
+ override fun isInteractable(clazz: Interactables): Boolean {
+ return isBitSet(clazz.id) != clazz.interactableByDefault
+ }
+
+ override fun isDefault(): Boolean {
+ for (x in bitmaskArray) {
+ if (x != 0) return false
+ }
+ return true
+ }
+
+ override fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean {
+ val idx = clazz.id.ushr(5)
+ if (idx >= bitmaskArray.size) return false
+ val bit = 0x1.shl(clazz.id.and(0x1F))
+ val oldBitmask = bitmaskArray[idx]
+ bitmaskArray[idx] = if (interactable != clazz.interactableByDefault) oldBitmask.or(bit) else oldBitmask.and(bit.inv())
+ return bitmaskArray[idx] != oldBitmask
+ }
+
+ override fun clear(): Boolean {
+ var change = false
+ for (i in bitmaskArray.indices) {
+ if (!change && bitmaskArray[i] != 0) change = true
+ bitmaskArray[i] = 0
+ }
+ return change
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt b/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt
index 10da0da..ebbe334 100644
--- a/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt
+++ b/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt
@@ -1,6 +1,8 @@
package io.dico.parcels2
+import io.dico.parcels2.util.PluginAware
import io.dico.parcels2.util.math.clampMin
+import io.dico.parcels2.util.scheduleRepeating
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart.LAZY
@@ -74,7 +76,12 @@ interface Job : JobAndScopeMembersUnion {
*
* if [asCompletionListener] is true, [onCompleted] is called with the same [block]
*/
- fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean = true, block: JobUpdateLister): Job
+ fun onProgressUpdate(
+ minDelay: Int,
+ minInterval: Int,
+ asCompletionListener: Boolean = true,
+ block: JobUpdateLister
+ ): Job
/**
* Calls the given [block] when this job completes, with the progress value 1.0.
@@ -138,7 +145,11 @@ interface JobInternal : Job, JobScope {
* 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 BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJobtimeOptions) : JobDispatcher {
+class BukkitJobDispatcher(
+ private val plugin: PluginAware,
+ private val scope: CoroutineScope,
+ var options: TickJobtimeOptions
+) : JobDispatcher {
// The currently registered bukkit scheduler task
private var bukkitTask: BukkitTask? = null
// The jobs.
@@ -146,18 +157,18 @@ class BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJo
override val jobs: List<Job> = _jobs
override fun dispatch(function: JobFunction): Job {
- val job: JobInternal = JobImpl(plugin, function)
+ val job: JobInternal = JobImpl(scope, function)
if (bukkitTask == null) {
val completed = job.resume(options.jobTime.toLong())
if (completed) return job
- bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickCoroutineJobs() }
+ bukkitTask = plugin.scheduleRepeating(options.tickInterval) { tickJobs() }
}
_jobs.addFirst(job)
return job
}
- private fun tickCoroutineJobs() {
+ private fun tickJobs() {
val jobs = _jobs
if (jobs.isEmpty()) return
val tickStartTime = System.currentTimeMillis()
@@ -237,7 +248,12 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
}
}
- override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: JobUpdateLister): Job {
+ override fun onProgressUpdate(
+ minDelay: Int,
+ minInterval: Int,
+ asCompletionListener: Boolean,
+ block: JobUpdateLister
+ ): Job {
onProgressUpdate?.let { throw IllegalStateException() }
if (asCompletionListener) onCompleted(block)
if (isComplete) return this
@@ -296,7 +312,11 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
if (isStarted) {
continuation?.let {
continuation = null
- it.resume(Unit)
+
+ wrapExternalCall {
+ it.resume(Unit)
+ }
+
return continuation == null
}
return true
@@ -304,34 +324,45 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
isStarted = true
startTimeOrElapsedTime = System.currentTimeMillis()
- coroutine.start()
+
+ wrapExternalCall {
+ coroutine.start()
+ }
return continuation == null
}
+ private inline fun wrapExternalCall(block: () -> Unit) {
+ try {
+ block()
+ } catch (ex: Throwable) {
+ logger.error("Job $coroutine generated an exception", ex)
+ }
+ }
+
override suspend fun awaitCompletion() {
coroutine.join()
}
private fun delegateProgress(curPortion: Double, portion: Double): JobScope =
- DelegateScope(progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0))
+ DelegateScope(this, progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0))
override fun delegateProgress(portion: Double): JobScope = delegateProgress(1.0, portion)
- private inner class DelegateScope(val progressStart: Double, val portion: Double) : JobScope {
+ private class DelegateScope(val parent: JobImpl, val progressStart: Double, val portion: Double) : JobScope {
override val elapsedTime: Long
- get() = this@JobImpl.elapsedTime
+ get() = parent.elapsedTime
override suspend fun markSuspensionPoint() =
- this@JobImpl.markSuspensionPoint()
+ parent.markSuspensionPoint()
override val progress: Double
- get() = (this@JobImpl.progress - progressStart) / portion
+ get() = (parent.progress - progressStart) / portion
override fun setProgress(progress: Double) =
- this@JobImpl.setProgress(progressStart + progress * portion)
+ parent.setProgress(progressStart + progress * portion)
override fun delegateProgress(portion: Double): JobScope =
- this@JobImpl.delegateProgress(this.portion, portion)
+ parent.delegateProgress(this.portion, portion)
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt
index 4f89fe0..3527e15 100644
--- a/src/main/kotlin/io/dico/parcels2/Parcel.kt
+++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt
@@ -1,66 +1,66 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.util.math.Vec2i
-import io.dico.parcels2.util.math.Vec3i
-import org.bukkit.Location
-import org.joda.time.DateTime
-import java.util.UUID
-
-/**
- * Parcel implementation of ParcelData will update the database when changes are made.
- * To change the data without updating the database, defer to the data delegate instance.
- *
- * This should be used for example in database query callbacks.
- * However, this implementation is intentionally not thread-safe.
- * Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
- */
-interface Parcel : ParcelData, Privileges {
- val id: ParcelId
- val world: ParcelWorld
- val pos: Vec2i
- val x: Int
- val z: Int
- val data: ParcelDataHolder
- val infoString: String
- val hasBlockVisitors: Boolean
- val globalPrivileges: GlobalPrivileges?
-
- override val keyOfOwner: PlayerProfile.Real?
- get() = owner as? PlayerProfile.Real
-
- fun copyData(newData: ParcelDataHolder, callerIsDatabase: Boolean = false)
-
- fun dispose() = copyData(ParcelDataHolder())
-
- fun updateOwnerSign(force: Boolean = false)
-
- val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
-}
-
-
-
-interface ParcelData : RawPrivileges {
- var owner: PlayerProfile?
- val lastClaimTime: DateTime?
- var isOwnerSignOutdated: Boolean
- var interactableConfig: InteractableConfiguration
-
- //fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
-
- fun isOwner(uuid: UUID): Boolean {
- return owner?.uuid == uuid
- }
-
- fun isOwner(profile: PlayerProfile?): Boolean {
- return owner == profile
- }
-}
-
-class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf())
- : ParcelData, PrivilegesHolder(addedMap) {
- override var owner: PlayerProfile? = null
- override var lastClaimTime: DateTime? = null
- override var isOwnerSignOutdated = false
- override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration()
-}
-
+package io.dico.parcels2
+
+import io.dico.parcels2.util.math.Vec2i
+import io.dico.parcels2.util.math.Vec3i
+import org.bukkit.Location
+import org.joda.time.DateTime
+import java.util.UUID
+
+/**
+ * Parcel implementation of ParcelData will update the database when changes are made.
+ * To change the data without updating the database, defer to the data delegate instance.
+ *
+ * This should be used for example in database query callbacks.
+ * However, this implementation is intentionally not thread-safe.
+ * Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
+ */
+interface Parcel : ParcelData, Privileges {
+ val id: ParcelId
+ val world: ParcelWorld
+ val pos: Vec2i
+ val x: Int
+ val z: Int
+ val data: ParcelDataHolder
+ val infoString: String
+ val hasBlockVisitors: Boolean
+ val globalPrivileges: GlobalPrivileges?
+
+ override val keyOfOwner: PlayerProfile.Real?
+ get() = owner as? PlayerProfile.Real
+
+ fun copyData(newData: ParcelDataHolder, callerIsDatabase: Boolean = false)
+
+ fun dispose() = copyData(ParcelDataHolder())
+
+ fun updateOwnerSign(force: Boolean = false)
+
+ val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
+}
+
+
+
+interface ParcelData : RawPrivileges {
+ var owner: PlayerProfile?
+ val lastClaimTime: DateTime?
+ var isOwnerSignOutdated: Boolean
+ var interactableConfig: InteractableConfiguration
+
+ //fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
+
+ fun isOwner(uuid: UUID): Boolean {
+ return owner?.uuid == uuid
+ }
+
+ fun isOwner(profile: PlayerProfile?): Boolean {
+ return owner == profile
+ }
+}
+
+class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf())
+ : ParcelData, PrivilegesHolder(addedMap) {
+ override var owner: PlayerProfile? = null
+ override var lastClaimTime: DateTime? = null
+ override var isOwnerSignOutdated = false
+ override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration()
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
index 109c5dc..ada6d12 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
@@ -56,7 +56,7 @@ interface ParcelBlockManager {
fun getRegion(parcel: ParcelId): Region
- fun getEntities(parcel: ParcelId): Collection<Entity>
+ fun getEntities(region: Region): Collection<Entity>
fun isParcelInfoSectionLoaded(parcel: ParcelId): Boolean
@@ -92,8 +92,7 @@ inline fun ParcelBlockManager.tryDoBlockOperation(
abstract class ParcelBlockManagerBase : ParcelBlockManager {
- override fun getEntities(parcel: ParcelId): Collection<Entity> {
- val region = getRegion(parcel)
+ override fun getEntities(region: Region): Collection<Entity> {
val center = region.center
val centerLoc = Location(world, center.x, center.y, center.z)
val centerDist = (center - region.origin).add(0.2, 0.2, 0.2)
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelId.kt b/src/main/kotlin/io/dico/parcels2/ParcelId.kt
index eef7129..77a1835 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelId.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelId.kt
@@ -1,56 +1,56 @@
-@file:Suppress("FunctionName")
-
-package io.dico.parcels2
-
-import io.dico.parcels2.util.math.Vec2i
-import org.bukkit.Bukkit
-import org.bukkit.World
-import java.util.UUID
-
-/**
- * Used by storage backing options to encompass the identity of a world
- * Does NOT support equality operator.
- */
-interface ParcelWorldId {
- val name: String
- val uid: UUID?
- fun equals(id: ParcelWorldId): Boolean = name == id.name || (uid != null && uid == id.uid)
-
- val bukkitWorld: World? get() = Bukkit.getWorld(name) ?: uid?.let { Bukkit.getWorld(it) }
-}
-
-fun ParcelWorldId.parcelWorldIdToString() = "ParcelWorld($name)"
-
-fun ParcelWorldId(worldName: String, worldUid: UUID? = null): ParcelWorldId = ParcelWorldIdImpl(worldName, worldUid)
-fun ParcelWorldId(world: World) = ParcelWorldId(world.name, world.uid)
-
-/**
- * Used by storage backing options to encompass the location of a parcel
- * Does NOT support equality operator.
- */
-interface ParcelId {
- val worldId: ParcelWorldId
- val x: Int
- val z: Int
- val pos: Vec2i get() = Vec2i(x, z)
- val idString get() = "$x,$z"
- fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId)
-}
-
-fun ParcelId.parcelIdToString() = "Parcel(${worldId.name},$idString)"
-
-fun ParcelId(worldId: ParcelWorldId, pos: Vec2i) = ParcelId(worldId, pos.x, pos.z)
-fun ParcelId(worldName: String, worldUid: UUID?, pos: Vec2i) = ParcelId(worldName, worldUid, pos.x, pos.z)
-fun ParcelId(worldName: String, worldUid: UUID?, x: Int, z: Int) = ParcelId(ParcelWorldId(worldName, worldUid), x, z)
-fun ParcelId(worldId: ParcelWorldId, x: Int, z: Int): ParcelId = ParcelIdImpl(worldId, x, z)
-
-private class ParcelWorldIdImpl(override val name: String,
- override val uid: UUID?) : ParcelWorldId {
- override fun toString() = parcelWorldIdToString()
-}
-
-private class ParcelIdImpl(override val worldId: ParcelWorldId,
- override val x: Int,
- override val z: Int) : ParcelId {
- override fun toString() = parcelIdToString()
-}
+@file:Suppress("FunctionName")
+
+package io.dico.parcels2
+
+import io.dico.parcels2.util.math.Vec2i
+import org.bukkit.Bukkit
+import org.bukkit.World
+import java.util.UUID
+
+/**
+ * Used by storage backing options to encompass the identity of a world
+ * Does NOT support equality operator.
+ */
+interface ParcelWorldId {
+ val name: String
+ val uid: UUID?
+ fun equals(id: ParcelWorldId): Boolean = name == id.name || (uid != null && uid == id.uid)
+
+ val bukkitWorld: World? get() = Bukkit.getWorld(name) ?: uid?.let { Bukkit.getWorld(it) }
+}
+
+fun ParcelWorldId.parcelWorldIdToString() = "ParcelWorld($name)"
+
+fun ParcelWorldId(worldName: String, worldUid: UUID? = null): ParcelWorldId = ParcelWorldIdImpl(worldName, worldUid)
+fun ParcelWorldId(world: World) = ParcelWorldId(world.name, world.uid)
+
+/**
+ * Used by storage backing options to encompass the location of a parcel
+ * Does NOT support equality operator.
+ */
+interface ParcelId {
+ val worldId: ParcelWorldId
+ val x: Int
+ val z: Int
+ val pos: Vec2i get() = Vec2i(x, z)
+ val idString get() = "$x,$z"
+ fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId)
+}
+
+fun ParcelId.parcelIdToString() = "Parcel(${worldId.name},$idString)"
+
+fun ParcelId(worldId: ParcelWorldId, pos: Vec2i) = ParcelId(worldId, pos.x, pos.z)
+fun ParcelId(worldName: String, worldUid: UUID?, pos: Vec2i) = ParcelId(worldName, worldUid, pos.x, pos.z)
+fun ParcelId(worldName: String, worldUid: UUID?, x: Int, z: Int) = ParcelId(ParcelWorldId(worldName, worldUid), x, z)
+fun ParcelId(worldId: ParcelWorldId, x: Int, z: Int): ParcelId = ParcelIdImpl(worldId, x, z)
+
+private class ParcelWorldIdImpl(override val name: String,
+ override val uid: UUID?) : ParcelWorldId {
+ override fun toString() = parcelWorldIdToString()
+}
+
+private class ParcelIdImpl(override val worldId: ParcelWorldId,
+ override val x: Int,
+ override val z: Int) : ParcelId {
+ override fun toString() = parcelIdToString()
+}
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
index c31b11a..054d9d6 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
@@ -83,7 +83,7 @@ interface ParcelContainer {
fun getParcelById(id: ParcelId): Parcel?
- fun nextEmptyParcel(): Parcel?
+ suspend fun nextEmptyParcel(): Parcel?
}
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
index a6ebcd8..2ffef06 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
@@ -13,9 +13,10 @@ import io.dico.parcels2.options.Options
import io.dico.parcels2.options.optionsMapper
import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.MainThreadDispatcher
-import io.dico.parcels2.util.PluginScheduler
+import io.dico.parcels2.util.PluginAware
import io.dico.parcels2.util.ext.tryCreate
import io.dico.parcels2.util.isServerThread
+import io.dico.parcels2.util.scheduleRepeating
import kotlinx.coroutines.CoroutineScope
import org.bukkit.Bukkit
import org.bukkit.generator.ChunkGenerator
@@ -29,7 +30,7 @@ import kotlin.coroutines.CoroutineContext
val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin")
private inline val plogger get() = logger
-class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
+class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginAware {
lateinit var optionsFile: File; private set
lateinit var options: Options; private set
lateinit var parcelProvider: ParcelProvider; private set
@@ -43,7 +44,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
override val coroutineContext: CoroutineContext = MainThreadDispatcher(this)
override val plugin: Plugin get() = this
- val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, options.tickJobtime) }
+ val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, this, options.tickJobtime) }
override fun onEnable() {
plogger.info("Is server thread: ${isServerThread()}")
@@ -147,7 +148,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
}
}
- scheduleRepeating(100, 5, entityTracker::tick)
+ scheduleRepeating(5, delay = 100, task = entityTracker::tick)
}
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
index e3e0f55..b73f7ba 100644
--- a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
+++ b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
@@ -3,11 +3,13 @@
package io.dico.parcels2
import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.util.checkPlayerNameValid
import io.dico.parcels2.util.ext.PLAYER_NAME_PLACEHOLDER
import io.dico.parcels2.util.ext.isValid
import io.dico.parcels2.util.ext.uuid
import io.dico.parcels2.util.getOfflinePlayer
import io.dico.parcels2.util.getPlayerName
+import io.dico.parcels2.util.isPlayerNameValid
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
import java.util.UUID
@@ -32,7 +34,7 @@ interface PlayerProfile {
companion object {
fun safe(uuid: UUID?, name: String?): PlayerProfile? {
- if (uuid != null) return Real(uuid, name)
+ if (uuid != null) return Real(uuid, if (name != null && !isPlayerNameValid(name)) null else name)
if (name != null) return invoke(name)
return null
}
@@ -47,7 +49,7 @@ interface PlayerProfile {
}
operator fun invoke(name: String): PlayerProfile {
- if (name == Star.name) return Star
+ if (name equalsIgnoreCase Star.name) return Star
return Fake(name)
}
@@ -60,12 +62,17 @@ interface PlayerProfile {
return RealImpl(player.uuid, null)
}
- fun byName(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile {
+ fun byName(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile? {
if (!allowReal) {
if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true")
return Fake(input)
}
+ if (!isPlayerNameValid(input)) {
+ if (!allowFake) return null
+ return Fake(input)
+ }
+
if (input == Star.name) return Star
return getOfflinePlayer(input)?.let { PlayerProfile(it) } ?: Unresolved(input)
@@ -92,19 +99,19 @@ interface PlayerProfile {
companion object {
fun byName(name: String): PlayerProfile {
- if (name == Star.name) return Star
+ if (name equalsIgnoreCase Star.name) return Star
return Unresolved(name)
}
operator fun invoke(uuid: UUID, name: String?): Real {
- if (name == Star.name || uuid == Star.uuid) return Star
+ if (name equalsIgnoreCase Star.name || uuid == Star.uuid) return Star
return RealImpl(uuid, name)
}
fun safe(uuid: UUID?, name: String?): Real? {
- if (name == Star.name || uuid == Star.uuid) return Star
+ if (name equalsIgnoreCase Star.name || uuid == Star.uuid) return Star
if (uuid == null) return null
- return RealImpl(uuid, name)
+ return RealImpl(uuid, if (name != null && !isPlayerNameValid(name)) null else name)
}
}
@@ -130,7 +137,7 @@ interface PlayerProfile {
override val nameOrBukkitName: String get() = name
override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
- return allowNameMatch && player.name == name
+ return allowNameMatch && player.name equalsIgnoreCase name
}
override fun toString() = "${javaClass.simpleName}($name)"
@@ -138,13 +145,17 @@ interface PlayerProfile {
class Fake(name: String) : NameOnly(name) {
override fun equals(other: PlayerProfile): Boolean {
- return other is Fake && other.name == name
+ return other is Fake && other.name equalsIgnoreCase name
}
}
class Unresolved(name: String) : NameOnly(name) {
+ init {
+ checkPlayerNameValid(name)
+ }
+
override fun equals(other: PlayerProfile): Boolean {
- return other is Unresolved && name == other.name
+ return other is Unresolved && name equalsIgnoreCase other.name
}
suspend fun tryResolveSuspendedly(storage: Storage): Real? {
@@ -171,11 +182,24 @@ interface PlayerProfile {
}
private class RealImpl(override val uuid: UUID, override val name: String?) : BaseImpl(), Real {
+ init {
+ name?.let { checkPlayerNameValid(it) }
+ }
+
override fun toString() = "Real($notNullName)"
}
}
+private infix fun String?.equalsIgnoreCase(other: String): Boolean {
+ if (this == null) return false
+ if (length != other.length) return false
+ repeat(length) { i ->
+ if (this[i].toLowerCase() != other[i].toLowerCase()) return false
+ }
+ return true
+}
+
suspend fun PlayerProfile.resolved(storage: Storage, resolveToFake: Boolean = false): PlayerProfile? =
when (this) {
is PlayerProfile.Unresolved -> tryResolveSuspendedly(storage)
diff --git a/src/main/kotlin/io/dico/parcels2/Privilege.kt b/src/main/kotlin/io/dico/parcels2/Privilege.kt
index 135d7c1..892ed4c 100644
--- a/src/main/kotlin/io/dico/parcels2/Privilege.kt
+++ b/src/main/kotlin/io/dico/parcels2/Privilege.kt
@@ -1,125 +1,125 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.Privilege.DEFAULT
-import java.util.Collections
-
-typealias PrivilegeKey = PlayerProfile.Real
-typealias PrivilegeMap = Map<PrivilegeKey, Privilege>
-typealias MutablePrivilegeMap = MutableMap<PrivilegeKey, Privilege>
-
-@Suppress("FunctionName")
-fun MutablePrivilegeMap(): MutablePrivilegeMap = hashMapOf()
-
-@Suppress("UNCHECKED_CAST")
-val EmptyPrivilegeMap = Collections.emptyMap<Any, Any>() as MutablePrivilegeMap
-
-enum class Privilege(
- val number: Int,
- val transient: Boolean = false
-) {
- BANNED(1),
- DEFAULT(2),
- CAN_BUILD(3),
- CAN_MANAGE(4),
-
- OWNER(-1, transient = true),
- ADMIN(-1, transient = true);
-
- fun implies(other: Privilege): Boolean =
- when {
- other > DEFAULT -> this >= other
- other == DEFAULT -> this == other
- else -> this <= other
- }
-
- fun isChangeInDirection(positiveDirection: Boolean, update: Privilege): Boolean =
- if (positiveDirection) update > this
- else update < this
-
- fun requireNonTransient(): Privilege {
- if (transient) {
- throw IllegalArgumentException("Transient privilege $this is invalid")
- }
- return this
- }
-
- companion object {
- fun getByNumber(id: Int) =
- when (id) {
- 1 -> BANNED
- 2 -> DEFAULT
- 3 -> CAN_BUILD
- 4 -> CAN_MANAGE
- else -> null
- }
- }
-}
-
-interface RawPrivileges {
- val privilegeMap: PrivilegeMap
- var privilegeOfStar: Privilege
-
- fun getRawStoredPrivilege(key: PrivilegeKey): Privilege
- fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean
- fun hasAnyDeclaredPrivileges(): Boolean
-}
-
-open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = EmptyPrivilegeMap) : RawPrivileges {
- private var _privilegeOfStar: Privilege = DEFAULT
-
- override /*open*/ var privilegeOfStar: Privilege
- get() = _privilegeOfStar
- set(value) = run { _privilegeOfStar = value }
-
- protected val isEmpty
- inline get() = privilegeMap === EmptyPrivilegeMap
-
- override fun getRawStoredPrivilege(key: PrivilegeKey) =
- if (key.isStar) _privilegeOfStar
- else privilegeMap.getOrDefault(key, _privilegeOfStar)
-
- override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
- privilege.requireNonTransient()
-
- if (key.isStar) {
- if (_privilegeOfStar == privilege) return false
- _privilegeOfStar = privilege
- return true
- }
-
- if (isEmpty) {
- if (privilege == DEFAULT) return false
- privilegeMap = MutablePrivilegeMap()
- }
-
- return if (privilege == DEFAULT) privilegeMap.remove(key) != null
- else privilegeMap.put(key, privilege) != privilege
- }
-
- override fun hasAnyDeclaredPrivileges(): Boolean {
- return privilegeMap.isNotEmpty() || privilegeOfStar != DEFAULT
- }
-
- fun copyPrivilegesFrom(other: PrivilegesHolder) {
- privilegeMap = other.privilegeMap
- privilegeOfStar = other.privilegeOfStar
- }
-
-}
-
-private fun <K, V> MutableMap<K, V>.put(key: K, value: V, override: Boolean) {
- if (override) this[key] = value
- else putIfAbsent(key, value)
-}
-
-fun RawPrivileges.filterProfilesWithPrivilegeTo(map: MutableMap<PrivilegeKey, Privilege>, privilege: Privilege) {
- if (privilegeOfStar.implies(privilege)) {
- map.putIfAbsent(PlayerProfile.Star, privilegeOfStar)
- }
-
- for ((profile, declaredPrivilege) in privilegeMap) {
- if (declaredPrivilege.implies(privilege)) {
- map.putIfAbsent(profile, declaredPrivilege)
- }
- }
-}
+package io.dico.parcels2
+
+import io.dico.parcels2.Privilege.DEFAULT
+import java.util.Collections
+
+typealias PrivilegeKey = PlayerProfile.Real
+typealias PrivilegeMap = Map<PrivilegeKey, Privilege>
+typealias MutablePrivilegeMap = MutableMap<PrivilegeKey, Privilege>
+
+@Suppress("FunctionName")
+fun MutablePrivilegeMap(): MutablePrivilegeMap = hashMapOf()
+
+@Suppress("UNCHECKED_CAST")
+val EmptyPrivilegeMap = Collections.emptyMap<Any, Any>() as MutablePrivilegeMap
+
+enum class Privilege(
+ val number: Int,
+ val transient: Boolean = false
+) {
+ BANNED(1),
+ DEFAULT(2),
+ CAN_BUILD(3),
+ CAN_MANAGE(4),
+
+ OWNER(-1, transient = true),
+ ADMIN(-1, transient = true);
+
+ fun implies(other: Privilege): Boolean =
+ when {
+ other > DEFAULT -> this >= other
+ other == DEFAULT -> this == other
+ else -> this <= other
+ }
+
+ fun isChangeInDirection(positiveDirection: Boolean, update: Privilege): Boolean =
+ if (positiveDirection) update > this
+ else update < this
+
+ fun requireNonTransient(): Privilege {
+ if (transient) {
+ throw IllegalArgumentException("Transient privilege $this is invalid")
+ }
+ return this
+ }
+
+ companion object {
+ fun getByNumber(id: Int) =
+ when (id) {
+ 1 -> BANNED
+ 2 -> DEFAULT
+ 3 -> CAN_BUILD
+ 4 -> CAN_MANAGE
+ else -> null
+ }
+ }
+}
+
+interface RawPrivileges {
+ val privilegeMap: PrivilegeMap
+ var privilegeOfStar: Privilege
+
+ fun getRawStoredPrivilege(key: PrivilegeKey): Privilege
+ fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean
+ fun hasAnyDeclaredPrivileges(): Boolean
+}
+
+open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = EmptyPrivilegeMap) : RawPrivileges {
+ private var _privilegeOfStar: Privilege = DEFAULT
+
+ override /*open*/ var privilegeOfStar: Privilege
+ get() = _privilegeOfStar
+ set(value) = run { _privilegeOfStar = value }
+
+ protected val isEmpty
+ inline get() = privilegeMap === EmptyPrivilegeMap
+
+ override fun getRawStoredPrivilege(key: PrivilegeKey) =
+ if (key.isStar) _privilegeOfStar
+ else privilegeMap.getOrDefault(key, _privilegeOfStar)
+
+ override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ privilege.requireNonTransient()
+
+ if (key.isStar) {
+ if (_privilegeOfStar == privilege) return false
+ _privilegeOfStar = privilege
+ return true
+ }
+
+ if (isEmpty) {
+ if (privilege == DEFAULT) return false
+ privilegeMap = MutablePrivilegeMap()
+ }
+
+ return if (privilege == DEFAULT) privilegeMap.remove(key) != null
+ else privilegeMap.put(key, privilege) != privilege
+ }
+
+ override fun hasAnyDeclaredPrivileges(): Boolean {
+ return privilegeMap.isNotEmpty() || privilegeOfStar != DEFAULT
+ }
+
+ fun copyPrivilegesFrom(other: PrivilegesHolder) {
+ privilegeMap = other.privilegeMap
+ privilegeOfStar = other.privilegeOfStar
+ }
+
+}
+
+private fun <K, V> MutableMap<K, V>.put(key: K, value: V, override: Boolean) {
+ if (override) this[key] = value
+ else putIfAbsent(key, value)
+}
+
+fun RawPrivileges.filterProfilesWithPrivilegeTo(map: MutableMap<PrivilegeKey, Privilege>, privilege: Privilege) {
+ if (privilegeOfStar.implies(privilege)) {
+ map.putIfAbsent(PlayerProfile.Star, privilegeOfStar)
+ }
+
+ for ((profile, declaredPrivilege) in privilegeMap) {
+ if (declaredPrivilege.implies(privilege)) {
+ map.putIfAbsent(profile, declaredPrivilege)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/Privileges.kt b/src/main/kotlin/io/dico/parcels2/Privileges.kt
index 632f1a4..6cdd6d0 100644
--- a/src/main/kotlin/io/dico/parcels2/Privileges.kt
+++ b/src/main/kotlin/io/dico/parcels2/Privileges.kt
@@ -1,64 +1,64 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.PrivilegeChangeResult.*
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
-import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
-import org.bukkit.OfflinePlayer
-import org.bukkit.entity.Player
-
-interface Privileges : RawPrivileges {
- val keyOfOwner: PlayerProfile.Real?
-
- fun getStoredPrivilege(key: PrivilegeKey): Privilege {
- return if (key == keyOfOwner) Privilege.OWNER
- else getRawStoredPrivilege(key)
- }
-
- override var privilegeOfStar: Privilege
- get() = getStoredPrivilege(PlayerProfile.Star)
- set(value) {
- setRawStoredPrivilege(PlayerProfile.Star, value)
- }
-}
-
-val OfflinePlayer.privilegeKey: PrivilegeKey
- inline get() = PlayerProfile.nameless(this)
-
-fun Privileges.getEffectivePrivilege(player: OfflinePlayer, adminPerm: String): Privilege =
- if (player is Player && player.hasPermission(adminPerm)) Privilege.ADMIN
- else getStoredPrivilege(player.privilegeKey)
-
-fun Privileges.canManage(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_ADMIN_MANAGE) >= Privilege.CAN_MANAGE
-fun Privileges.canManageFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_MANAGE
-fun Privileges.canBuild(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BUILD_ANYWHERE) >= Privilege.CAN_BUILD
-fun Privileges.canBuildFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_BUILD
-fun Privileges.canEnter(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BAN_BYPASS) >= Privilege.DEFAULT
-fun Privileges.canEnterFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.DEFAULT
-
-enum class PrivilegeChangeResult {
- SUCCESS, FAIL, FAIL_OWNER
-}
-
-fun Privileges.changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult =
- when {
- key == keyOfOwner -> FAIL_OWNER
- getRawStoredPrivilege(key).isChangeInDirection(positive, update) && setRawStoredPrivilege(key, update) -> SUCCESS
- else -> FAIL
- }
-
-fun Privileges.allowManage(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_MANAGE)
-fun Privileges.disallowManage(key: PrivilegeKey) = changePrivilege(key, false, Privilege.CAN_BUILD)
-fun Privileges.allowBuild(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_BUILD)
-fun Privileges.disallowBuild(key: PrivilegeKey) = changePrivilege(key, false, Privilege.DEFAULT)
-fun Privileges.allowEnter(key: PrivilegeKey) = changePrivilege(key, true, Privilege.DEFAULT)
-fun Privileges.disallowEnter(key: PrivilegeKey) = changePrivilege(key, false, Privilege.BANNED)
-
-interface GlobalPrivileges : RawPrivileges, Privileges {
- override val keyOfOwner: PlayerProfile.Real
-}
-
-interface GlobalPrivilegesManager {
- operator fun get(owner: PlayerProfile.Real): GlobalPrivileges
- operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey)
-}
+package io.dico.parcels2
+
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
+import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
+import org.bukkit.OfflinePlayer
+import org.bukkit.entity.Player
+
+interface Privileges : RawPrivileges {
+ val keyOfOwner: PlayerProfile.Real?
+
+ fun getStoredPrivilege(key: PrivilegeKey): Privilege {
+ return if (key == keyOfOwner) Privilege.OWNER
+ else getRawStoredPrivilege(key)
+ }
+
+ override var privilegeOfStar: Privilege
+ get() = getStoredPrivilege(PlayerProfile.Star)
+ set(value) {
+ setRawStoredPrivilege(PlayerProfile.Star, value)
+ }
+}
+
+val OfflinePlayer.privilegeKey: PrivilegeKey
+ inline get() = PlayerProfile.nameless(this)
+
+fun Privileges.getEffectivePrivilege(player: OfflinePlayer, adminPerm: String): Privilege =
+ if (player is Player && player.hasPermission(adminPerm)) Privilege.ADMIN
+ else getStoredPrivilege(player.privilegeKey)
+
+fun Privileges.canManage(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_ADMIN_MANAGE) >= Privilege.CAN_MANAGE
+fun Privileges.canManageFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_MANAGE
+fun Privileges.canBuild(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BUILD_ANYWHERE) >= Privilege.CAN_BUILD
+fun Privileges.canBuildFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_BUILD
+fun Privileges.canEnter(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BAN_BYPASS) >= Privilege.DEFAULT
+fun Privileges.canEnterFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.DEFAULT
+
+enum class PrivilegeChangeResult {
+ SUCCESS, FAIL, FAIL_OWNER
+}
+
+fun Privileges.changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult =
+ when {
+ key == keyOfOwner -> FAIL_OWNER
+ getRawStoredPrivilege(key).isChangeInDirection(positive, update) && setRawStoredPrivilege(key, update) -> SUCCESS
+ else -> FAIL
+ }
+
+fun Privileges.allowManage(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_MANAGE)
+fun Privileges.disallowManage(key: PrivilegeKey) = changePrivilege(key, false, Privilege.CAN_BUILD)
+fun Privileges.allowBuild(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_BUILD)
+fun Privileges.disallowBuild(key: PrivilegeKey) = changePrivilege(key, false, Privilege.DEFAULT)
+fun Privileges.allowEnter(key: PrivilegeKey) = changePrivilege(key, true, Privilege.DEFAULT)
+fun Privileges.disallowEnter(key: PrivilegeKey) = changePrivilege(key, false, Privilege.BANNED)
+
+interface GlobalPrivileges : RawPrivileges, Privileges {
+ override val keyOfOwner: PlayerProfile.Real
+}
+
+interface GlobalPrivilegesManager {
+ operator fun get(owner: PlayerProfile.Real): GlobalPrivileges
+ operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
index 8f2c565..83567c5 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
@@ -1,61 +1,61 @@
-package io.dico.parcels2.blockvisitor
-
-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
-import org.bukkit.Material.*
-import org.bukkit.block.BlockFace
-import org.bukkit.block.data.BlockData
-import org.bukkit.block.data.Directional
-import java.util.EnumSet
-
-private val attachables = EnumSet.of(
- REPEATER, COMPARATOR,
- *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
- STONE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE,
- *getMaterialsWithWoodTypePrefix("BUTTON"),
- STONE_BUTTON, LEVER,
- *getMaterialsWithWoodTypePrefix("DOOR"), IRON_DOOR,
- ACTIVATOR_RAIL, POWERED_RAIL, DETECTOR_RAIL, RAIL,
- PISTON, STICKY_PISTON,
- REDSTONE_TORCH, REDSTONE_WALL_TORCH, REDSTONE_WIRE,
- TRIPWIRE, TRIPWIRE_HOOK,
-
- BROWN_MUSHROOM, RED_MUSHROOM, CACTUS, CARROT, COCOA,
- WHEAT, DEAD_BUSH, CHORUS_FLOWER, DANDELION, SUGAR_CANE,
- TALL_GRASS, TALL_SEAGRASS, NETHER_WART, MELON_STEM,
- PUMPKIN_STEM, SUNFLOWER, POTATO, LILY_PAD, VINE,
- *getMaterialsWithWoodTypePrefix("SAPLING"),
-
- SAND, RED_SAND, DRAGON_EGG, ANVIL,
- *getMaterialsWithWoolColorPrefix("CONCRETE_POWDER"),
-
- *getMaterialsWithWoolColorPrefix("CARPET"),
- CAKE, FIRE,
- FLOWER_POT,
- LADDER,
- // NETHER_PORTAL, fuck nether portals
- FLOWER_POT,
- SNOW,
- TORCH, WALL_TORCH,
- *getMaterialsWithWoolColorPrefix("BANNER"),
- *getMaterialsWithWoolColorPrefix("WALL_BANNER"),
- SIGN, WALL_SIGN
-)
-
-fun isAttachable(type: Material) = attachables.contains(type)
-
-fun getSupportingBlock(data: BlockData): Vec3i = when (data) {
- //is MultipleFacing -> // fuck it xD this is good enough
-
- is Directional -> Vec3i.convert(when (data.material) {
- // exceptions
- COCOA -> data.facing
- OAK_DOOR, BIRCH_DOOR, SPRUCE_DOOR, JUNGLE_DOOR, ACACIA_DOOR, DARK_OAK_DOOR, IRON_DOOR -> BlockFace.DOWN
-
- else -> data.facing.oppositeFace
- })
-
- else -> Vec3i.down
-}
+package io.dico.parcels2.blockvisitor
+
+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
+import org.bukkit.Material.*
+import org.bukkit.block.BlockFace
+import org.bukkit.block.data.BlockData
+import org.bukkit.block.data.Directional
+import java.util.EnumSet
+
+private val attachables = EnumSet.of(
+ REPEATER, COMPARATOR,
+ *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
+ STONE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE,
+ *getMaterialsWithWoodTypePrefix("BUTTON"),
+ STONE_BUTTON, LEVER,
+ *getMaterialsWithWoodTypePrefix("DOOR"), IRON_DOOR,
+ ACTIVATOR_RAIL, POWERED_RAIL, DETECTOR_RAIL, RAIL,
+ PISTON, STICKY_PISTON,
+ REDSTONE_TORCH, REDSTONE_WALL_TORCH, REDSTONE_WIRE,
+ TRIPWIRE, TRIPWIRE_HOOK,
+
+ BROWN_MUSHROOM, RED_MUSHROOM, CACTUS, CARROT, COCOA,
+ WHEAT, DEAD_BUSH, CHORUS_FLOWER, DANDELION, SUGAR_CANE,
+ TALL_GRASS, TALL_SEAGRASS, NETHER_WART, MELON_STEM,
+ PUMPKIN_STEM, SUNFLOWER, POTATO, LILY_PAD, VINE,
+ *getMaterialsWithWoodTypePrefix("SAPLING"),
+
+ SAND, RED_SAND, DRAGON_EGG, ANVIL,
+ *getMaterialsWithWoolColorPrefix("CONCRETE_POWDER"),
+
+ *getMaterialsWithWoolColorPrefix("CARPET"),
+ CAKE, FIRE,
+ FLOWER_POT,
+ LADDER,
+ // NETHER_PORTAL, fuck nether portals
+ FLOWER_POT,
+ SNOW,
+ TORCH, WALL_TORCH,
+ *getMaterialsWithWoolColorPrefix("BANNER"),
+ *getMaterialsWithWoolColorPrefix("WALL_BANNER"),
+ SIGN, WALL_SIGN
+)
+
+fun isAttachable(type: Material) = attachables.contains(type)
+
+fun getSupportingBlock(data: BlockData): Vec3i = when (data) {
+ //is MultipleFacing -> // fuck it xD this is good enough
+
+ is Directional -> Vec3i.convert(when (data.material) {
+ // exceptions
+ COCOA -> data.facing
+ OAK_DOOR, BIRCH_DOOR, SPRUCE_DOOR, JUNGLE_DOOR, ACACIA_DOOR, DARK_OAK_DOOR, IRON_DOOR -> BlockFace.DOWN
+
+ else -> data.facing.oppositeFace
+ })
+
+ else -> Vec3i.down
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Entities.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Entities.kt
new file mode 100644
index 0000000..d9ea09f
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Entities.kt
@@ -0,0 +1,38 @@
+package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.util.math.Vec3d
+import org.bukkit.Location
+import org.bukkit.World
+import org.bukkit.entity.Entity
+import org.bukkit.entity.Minecart
+
+/*
+open class EntityCopy<T : Entity>(entity: T) {
+ val type = entity.type
+
+ @Suppress("UNCHECKED_CAST")
+ fun spawn(world: World, position: Vec3d): T {
+ val entity = world.spawnEntity(Location(null, position.x, position.y, position.z), type) as T
+ setAttributes(entity)
+ return entity
+ }
+
+ open fun setAttributes(entity: T) {}
+}
+
+open class MinecartCopy<T : Minecart>(entity: T) : EntityCopy<T>(entity) {
+ val damage = entity.damage
+ val maxSpeed = entity.maxSpeed
+ val isSlowWhenEmpty = entity.isSlowWhenEmpty
+ val flyingVelocityMod = entity.flyingVelocityMod
+ val derailedVelocityMod = entity.derailedVelocityMod
+ val displayBlockData = entity.displayBlockData
+ val displayBlockOffset = entity.displayBlockOffset
+
+ override fun setAttributes(entity: T) {
+ super.setAttributes(entity)
+ entity.damage = damage
+ entity.displayBlockData = displayBlockData
+ entity.displayBlockOffset = displayBlockOffset
+ }
+}*/ \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt
index 3f7e070..ddfec27 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt
@@ -1,38 +1,38 @@
-package io.dico.parcels2.blockvisitor
-
-import org.bukkit.block.Block
-import org.bukkit.block.BlockState
-import org.bukkit.block.Sign
-import kotlin.reflect.KClass
-
-interface ExtraBlockChange {
- fun update(block: Block)
-}
-
-abstract class BlockStateChange<T : BlockState> : ExtraBlockChange {
- abstract val stateClass: KClass<T>
-
- abstract fun update(state: T)
-
- override fun update(block: Block) {
- val state = block.state
- if (stateClass.isInstance(state)) {
- @Suppress("UNCHECKED_CAST")
- update(state as T)
- }
- }
-}
-
-class SignStateChange(state: Sign) : BlockStateChange<Sign>() {
- val lines = state.lines
-
- override val stateClass: KClass<Sign>
- get() = Sign::class
-
- override fun update(state: Sign) {
- for (i in lines.indices) {
- val line = lines[i]
- state.setLine(i, line)
- }
- }
-}
+package io.dico.parcels2.blockvisitor
+
+import org.bukkit.block.Block
+import org.bukkit.block.BlockState
+import org.bukkit.block.Sign
+import kotlin.reflect.KClass
+
+interface ExtraBlockChange {
+ fun update(block: Block)
+}
+
+abstract class BlockStateChange<T : BlockState> : ExtraBlockChange {
+ abstract val stateClass: KClass<T>
+
+ abstract fun update(state: T)
+
+ override fun update(block: Block) {
+ val state = block.state
+ if (stateClass.isInstance(state)) {
+ @Suppress("UNCHECKED_CAST")
+ update(state as T)
+ }
+ }
+}
+
+class SignStateChange(state: Sign) : BlockStateChange<Sign>() {
+ val lines = state.lines
+
+ override val stateClass: KClass<Sign>
+ get() = Sign::class
+
+ override fun update(state: Sign) {
+ for (i in lines.indices) {
+ val line = lines[i]
+ state.setLine(i, line)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
index b749b36..6525655 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
@@ -1,323 +1,323 @@
-package io.dico.parcels2.blockvisitor
-
-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.clampMax
-
-private typealias Scope = SequenceScope<Vec3i>
-/*
-class ParcelTraverser(
- val parcelProvider: ParcelProvider,
- val delegate: RegionTraverser,
- scope: CoroutineScope
-) : RegionTraverser(), CoroutineScope by scope {
-
- class OccupiedException(parcelId: ParcelId) : Exception("Parcel $parcelId is occupied")
-
- /**
- * Traverse the blocks of parcel's land
- * The iterator must be exhausted, else the permit to traverse it will not be reclaimed.
- *
- * @throws OccupiedException if a parcel is maintained with the given parcel id and an
- * iterator exists for it that has not been exhausted
- */
- fun traverseParcel(parcelId: ParcelId): Iterator<Vec3i> {
- val world = parcelProvider.getWorldById(parcelId.worldId)
- ?: throw IllegalArgumentException()
- val parcel = parcelProvider.getParcelById(parcelId)
-
- val medium = if (parcel != null) {
- if (parcel.hasBlockVisitors || parcel !is ParcelImpl) {
- throw OccupiedException(parcelId)
- }
- parcel.hasBlockVisitors = true
- TraverserMedium { parcel.hasBlockVisitors = false }
- } else {
- TraverserMedium.DoNothing
- }
-
- val region = world.blockManager.getRegion(parcelId)
- return traverseRegion(region, world.world.maxHeight, medium)
- }
-
- override suspend fun Scope.build(region: Region, medium: TraverserMedium) {
- with(delegate) {
- return build(region, medium)
- }
- }
-
-}
-
-@Suppress("FunctionName")
-inline fun TraverserMedium(crossinline whenComplete: () -> Unit) =
- object : TraverserMedium {
- override fun iterationCompleted() {
- whenComplete()
- }
- }
-
-/**
- * An object that is able to communicate with an iterator returned by [RegionTraverser]
- *
- */
-interface TraverserMedium {
-
- /**
- * Called by the traverser during first [Iterator.hasNext] call that returns false
- */
- fun iterationCompleted()
-
- /**
- * The default [TraverserMedium], which does nothing.
- */
- object DoNothing : TraverserMedium {
- override fun iterationCompleted() {}
- }
-}*/
-
-sealed class RegionTraverser {
-
- /**
- * Get an iterator traversing [region] using this traverser.
- * Depending on the implementation, [region] might be traversed in a specific order and direction.
- */
- fun traverseRegion(
- region: Region,
- worldHeight: Int = 256/*,
- medium: TraverserMedium = TraverserMedium.DoNothing*/
- ): Iterator<Vec3i> = iterator { build(validify(region, worldHeight)/*, medium*/) }
-
- abstract suspend fun Scope.build(region: Region/*, medium: TraverserMedium = TraverserMedium.DoNothing*/)
-
- companion object {
- val upward = Directional(TraverseDirection(1, 1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
- val downward = Directional(TraverseDirection(1, -1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
- val toClear get() = downward
- val toFill get() = upward
-
- /**
- * The returned [RegionTraverser] will traverse the regions
- * * below and including absolute level [y] first, in [upward] direction.
- * * above absolute level [y] last, in [downward] direction.
- */
- fun convergingTo(y: Int) = Slicing(y, upward, downward, true)
-
- /**
- * The returned [RegionTraverser] will traverse the regions
- * * above absolute level [y] first, in [upward] direction.
- * * below and including absolute level [y] second, in [downward] direction.
- */
- fun separatingFrom(y: Int) = Slicing(y, downward, upward, false)
-
- private fun validify(region: Region, worldHeight: Int): Region {
- if (region.origin.y < 0) {
- val origin = region.origin withY 0
- val size = region.size.withY((region.size.y + region.origin.y).clampMax(worldHeight))
- return Region(origin, size)
- }
-
- if (region.origin.y + region.size.y > worldHeight) {
- val size = region.size.withY(worldHeight - region.origin.y)
- return Region(region.origin, size)
- }
-
- return region
- }
-
- }
-
- class Directional(
- val direction: TraverseDirection,
- val order: TraverseOrder
- ) : RegionTraverser() {
-
- private inline fun iterate(max: Int, increasing: Boolean, action: (Int) -> Unit) {
- for (i in 0..max) {
- action(if (increasing) i else max - i)
- }
- }
-
- override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
- val order = order
- val (primary, secondary, tertiary) = order.toArray()
- val (origin, size) = region
-
- 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)
- val isTertiaryIncreasing = direction.isIncreasing(tertiary)
-
- iterate(maxOfPrimary, isPrimaryIncreasing) { p ->
- iterate(maxOfSecondary, isSecondaryIncreasing) { s ->
- iterate(maxOfTertiary, isTertiaryIncreasing) { t ->
- yield(order.add(origin, p, s, t))
- }
- }
- }
-
- /*medium.iterationCompleted()*/
- }
-
- }
-
- class Slicing(
- val bottomSectionMaxY: Int,
- val bottomTraverser: RegionTraverser,
- val topTraverser: RegionTraverser,
- val bottomFirst: Boolean = true
- ) : RegionTraverser() {
-
- private fun slice(region: Region, atY: Int): Pair<Region, Region?> {
- if (atY < region.size.y + 1) {
- val bottom = Region(region.origin, region.size.withY(atY + 1))
- val top = Region(region.origin.withY(atY + 1), region.size.addY(-atY - 1))
- return bottom to top
- }
- return region to null
- }
-
- override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
- val (bottom, top) = slice(region, bottomSectionMaxY)
-
- if (bottomFirst) {
- with(bottomTraverser) { build(bottom) }
- top?.let { with(topTraverser) { build(it) } }
- } else {
- top?.let { with(topTraverser) { build(it) } }
- with(bottomTraverser) { build(bottom) }
- }
-
- /*medium.iterationCompleted()*/
- }
- }
-
- /**
- * Returns [Directional] instance that would be responsible for
- * emitting the given position if it is contained in a region.
- * [Directional] instance has a set order and direction
- */
- fun childForPosition(position: Vec3i): Directional {
- var cur = this
- while (true) {
- when (cur) {
- /*is ParcelTraverser -> cur = cur.delegate*/
- is Directional -> return cur
- is Slicing ->
- cur =
- if (position.y <= cur.bottomSectionMaxY) cur.bottomTraverser
- else cur.topTraverser
- }
- }
- }
-
- /**
- * Returns true if and only if this traverser would visit the given
- * [block] position before the given [current] position.
- * If at least one of [block] and [current] is not contained in a
- * region being traversed the result is undefined.
- */
- fun comesFirst(current: Vec3i, block: Vec3i): Boolean {
- var cur = this
- while (true) {
- when (cur) {
- /*is ParcelTraverser -> cur = cur.delegate*/
- is Directional -> return cur.direction.comesFirst(current, block)
- is Slicing -> {
- val border = cur.bottomSectionMaxY
- cur = when {
- current.y <= border && block.y <= border -> cur.bottomTraverser
- current.y <= border -> return !cur.bottomFirst
- block.y <= border -> return cur.bottomFirst
- else -> cur.topTraverser
- }
- }
- }
- }
- }
-
-}
-
-object TraverseOrderFactory {
- private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3
-
- fun createWith(primary: Dimension, secondary: Dimension): TraverseOrder {
- // tertiary is implicit
- if (primary == secondary) throw IllegalArgumentException()
- return TraverseOrder(primary, isSwap(primary, secondary))
- }
-}
-
-inline class TraverseOrder(val orderNum: Int) {
- constructor(first: Dimension, swap: Boolean)
- : this(if (swap) first.ordinal + 3 else first.ordinal)
-
- @Suppress("NOTHING_TO_INLINE")
- private inline fun element(index: Int) = Dimension[(orderNum + index) % 3]
-
- private val swap inline get() = orderNum >= 3
-
- /**
- * The slowest changing dimension
- */
- val primary: Dimension get() = element(0)
-
- /**
- * Second slowest changing dimension
- */
- val secondary: Dimension get() = element(if (swap) 2 else 1)
-
- /**
- * Dimension that changes every block
- */
- val tertiary: Dimension get() = element(if (swap) 1 else 2)
-
- /**
- * All 3 dimensions in this order
- */
- fun toArray() = arrayOf(primary, secondary, tertiary)
-
- fun add(vec: Vec3i, p: Int, s: Int, t: Int): Vec3i =
- // optimize this, will be called lots
- when (orderNum) {
- 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")
- }
-}
-
-inline class TraverseDirection(val bits: Int) {
- fun isIncreasing(dimension: Dimension) = (1 shl dimension.ordinal) and bits != 0
-
- fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean =
- if (isIncreasing(dimension))
- block[dimension] <= current[dimension]
- else
- block[dimension] >= current[dimension]
-
- fun comesFirst(current: Vec3i, block: Vec3i) =
- comesFirst(current, block, Dimension.X)
- && comesFirst(current, block, Dimension.Y)
- && comesFirst(current, block, Dimension.Z)
-
- companion object {
- operator fun invoke(x: Int, y: Int, z: Int) = invoke(Vec3i(x, y, z))
-
- operator fun invoke(block: Vec3i): TraverseDirection {
- if (block.x == 0 || block.y == 0 || block.z == 0) throw IllegalArgumentException()
- var bits = 0
- if (block.x > 0) bits = bits or 1
- if (block.y > 0) bits = bits or 2
- if (block.z > 0) bits = bits or 4
- return TraverseDirection(bits)
- }
- }
-
-}
+package io.dico.parcels2.blockvisitor
+
+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.clampMax
+
+private typealias Scope = SequenceScope<Vec3i>
+/*
+class ParcelTraverser(
+ val parcelProvider: ParcelProvider,
+ val delegate: RegionTraverser,
+ scope: CoroutineScope
+) : RegionTraverser(), CoroutineScope by scope {
+
+ class OccupiedException(parcelId: ParcelId) : Exception("Parcel $parcelId is occupied")
+
+ /**
+ * Traverse the blocks of parcel's land
+ * The iterator must be exhausted, else the permit to traverse it will not be reclaimed.
+ *
+ * @throws OccupiedException if a parcel is maintained with the given parcel id and an
+ * iterator exists for it that has not been exhausted
+ */
+ fun traverseParcel(parcelId: ParcelId): Iterator<Vec3i> {
+ val world = parcelProvider.getWorldById(parcelId.worldId)
+ ?: throw IllegalArgumentException()
+ val parcel = parcelProvider.getParcelById(parcelId)
+
+ val medium = if (parcel != null) {
+ if (parcel.hasBlockVisitors || parcel !is ParcelImpl) {
+ throw OccupiedException(parcelId)
+ }
+ parcel.hasBlockVisitors = true
+ TraverserMedium { parcel.hasBlockVisitors = false }
+ } else {
+ TraverserMedium.DoNothing
+ }
+
+ val region = world.blockManager.getRegion(parcelId)
+ return traverseRegion(region, world.world.maxHeight, medium)
+ }
+
+ override suspend fun Scope.build(region: Region, medium: TraverserMedium) {
+ with(delegate) {
+ return build(region, medium)
+ }
+ }
+
+}
+
+@Suppress("FunctionName")
+inline fun TraverserMedium(crossinline whenComplete: () -> Unit) =
+ object : TraverserMedium {
+ override fun iterationCompleted() {
+ whenComplete()
+ }
+ }
+
+/**
+ * An object that is able to communicate with an iterator returned by [RegionTraverser]
+ *
+ */
+interface TraverserMedium {
+
+ /**
+ * Called by the traverser during first [Iterator.hasNext] call that returns false
+ */
+ fun iterationCompleted()
+
+ /**
+ * The default [TraverserMedium], which does nothing.
+ */
+ object DoNothing : TraverserMedium {
+ override fun iterationCompleted() {}
+ }
+}*/
+
+sealed class RegionTraverser {
+
+ /**
+ * Get an iterator traversing [region] using this traverser.
+ * Depending on the implementation, [region] might be traversed in a specific order and direction.
+ */
+ fun traverseRegion(
+ region: Region,
+ worldHeight: Int = 256/*,
+ medium: TraverserMedium = TraverserMedium.DoNothing*/
+ ): Iterator<Vec3i> = iterator { build(validify(region, worldHeight)/*, medium*/) }
+
+ abstract suspend fun Scope.build(region: Region/*, medium: TraverserMedium = TraverserMedium.DoNothing*/)
+
+ companion object {
+ val upward = Directional(TraverseDirection(1, 1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
+ val downward = Directional(TraverseDirection(1, -1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
+ val toClear get() = downward
+ val toFill get() = upward
+
+ /**
+ * The returned [RegionTraverser] will traverse the regions
+ * * below and including absolute level [y] first, in [upward] direction.
+ * * above absolute level [y] last, in [downward] direction.
+ */
+ fun convergingTo(y: Int) = Slicing(y, upward, downward, true)
+
+ /**
+ * The returned [RegionTraverser] will traverse the regions
+ * * above absolute level [y] first, in [upward] direction.
+ * * below and including absolute level [y] second, in [downward] direction.
+ */
+ fun separatingFrom(y: Int) = Slicing(y, downward, upward, false)
+
+ private fun validify(region: Region, worldHeight: Int): Region {
+ if (region.origin.y < 0) {
+ val origin = region.origin withY 0
+ val size = region.size.withY((region.size.y + region.origin.y).clampMax(worldHeight))
+ return Region(origin, size)
+ }
+
+ if (region.origin.y + region.size.y > worldHeight) {
+ val size = region.size.withY(worldHeight - region.origin.y)
+ return Region(region.origin, size)
+ }
+
+ return region
+ }
+
+ }
+
+ class Directional(
+ val direction: TraverseDirection,
+ val order: TraverseOrder
+ ) : RegionTraverser() {
+
+ private inline fun iterate(max: Int, increasing: Boolean, action: (Int) -> Unit) {
+ for (i in 0..max) {
+ action(if (increasing) i else max - i)
+ }
+ }
+
+ override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
+ val order = order
+ val (primary, secondary, tertiary) = order.toArray()
+ val (origin, size) = region
+
+ 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)
+ val isTertiaryIncreasing = direction.isIncreasing(tertiary)
+
+ iterate(maxOfPrimary, isPrimaryIncreasing) { p ->
+ iterate(maxOfSecondary, isSecondaryIncreasing) { s ->
+ iterate(maxOfTertiary, isTertiaryIncreasing) { t ->
+ yield(order.add(origin, p, s, t))
+ }
+ }
+ }
+
+ /*medium.iterationCompleted()*/
+ }
+
+ }
+
+ class Slicing(
+ val bottomSectionMaxY: Int,
+ val bottomTraverser: RegionTraverser,
+ val topTraverser: RegionTraverser,
+ val bottomFirst: Boolean = true
+ ) : RegionTraverser() {
+
+ private fun slice(region: Region, atY: Int): Pair<Region, Region?> {
+ if (atY < region.size.y + 1) {
+ val bottom = Region(region.origin, region.size.withY(atY + 1))
+ val top = Region(region.origin.withY(atY + 1), region.size.addY(-atY - 1))
+ return bottom to top
+ }
+ return region to null
+ }
+
+ override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
+ val (bottom, top) = slice(region, bottomSectionMaxY)
+
+ if (bottomFirst) {
+ with(bottomTraverser) { build(bottom) }
+ top?.let { with(topTraverser) { build(it) } }
+ } else {
+ top?.let { with(topTraverser) { build(it) } }
+ with(bottomTraverser) { build(bottom) }
+ }
+
+ /*medium.iterationCompleted()*/
+ }
+ }
+
+ /**
+ * Returns [Directional] instance that would be responsible for
+ * emitting the given position if it is contained in a region.
+ * [Directional] instance has a set order and direction
+ */
+ fun childForPosition(position: Vec3i): Directional {
+ var cur = this
+ while (true) {
+ when (cur) {
+ /*is ParcelTraverser -> cur = cur.delegate*/
+ is Directional -> return cur
+ is Slicing ->
+ cur =
+ if (position.y <= cur.bottomSectionMaxY) cur.bottomTraverser
+ else cur.topTraverser
+ }
+ }
+ }
+
+ /**
+ * Returns true if and only if this traverser would visit the given
+ * [block] position before the given [current] position.
+ * If at least one of [block] and [current] is not contained in a
+ * region being traversed the result is undefined.
+ */
+ fun comesFirst(current: Vec3i, block: Vec3i): Boolean {
+ var cur = this
+ while (true) {
+ when (cur) {
+ /*is ParcelTraverser -> cur = cur.delegate*/
+ is Directional -> return cur.direction.comesFirst(current, block)
+ is Slicing -> {
+ val border = cur.bottomSectionMaxY
+ cur = when {
+ current.y <= border && block.y <= border -> cur.bottomTraverser
+ current.y <= border -> return !cur.bottomFirst
+ block.y <= border -> return cur.bottomFirst
+ else -> cur.topTraverser
+ }
+ }
+ }
+ }
+ }
+
+}
+
+object TraverseOrderFactory {
+ private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3
+
+ fun createWith(primary: Dimension, secondary: Dimension): TraverseOrder {
+ // tertiary is implicit
+ if (primary == secondary) throw IllegalArgumentException()
+ return TraverseOrder(primary, isSwap(primary, secondary))
+ }
+}
+
+inline class TraverseOrder(val orderNum: Int) {
+ constructor(first: Dimension, swap: Boolean)
+ : this(if (swap) first.ordinal + 3 else first.ordinal)
+
+ @Suppress("NOTHING_TO_INLINE")
+ private inline fun element(index: Int) = Dimension[(orderNum + index) % 3]
+
+ private val swap inline get() = orderNum >= 3
+
+ /**
+ * The slowest changing dimension
+ */
+ val primary: Dimension get() = element(0)
+
+ /**
+ * Second slowest changing dimension
+ */
+ val secondary: Dimension get() = element(if (swap) 2 else 1)
+
+ /**
+ * Dimension that changes every block
+ */
+ val tertiary: Dimension get() = element(if (swap) 1 else 2)
+
+ /**
+ * All 3 dimensions in this order
+ */
+ fun toArray() = arrayOf(primary, secondary, tertiary)
+
+ fun add(vec: Vec3i, p: Int, s: Int, t: Int): Vec3i =
+ // optimize this, will be called lots
+ when (orderNum) {
+ 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")
+ }
+}
+
+inline class TraverseDirection(val bits: Int) {
+ fun isIncreasing(dimension: Dimension) = (1 shl dimension.ordinal) and bits != 0
+
+ fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean =
+ if (isIncreasing(dimension))
+ block[dimension] <= current[dimension]
+ else
+ block[dimension] >= current[dimension]
+
+ fun comesFirst(current: Vec3i, block: Vec3i) =
+ comesFirst(current, block, Dimension.X)
+ && comesFirst(current, block, Dimension.Y)
+ && comesFirst(current, block, Dimension.Z)
+
+ companion object {
+ operator fun invoke(x: Int, y: Int, z: Int) = invoke(Vec3i(x, y, z))
+
+ operator fun invoke(block: Vec3i): TraverseDirection {
+ if (block.x == 0 || block.y == 0 || block.z == 0) throw IllegalArgumentException()
+ var bits = 0
+ if (block.x > 0) bits = bits or 1
+ if (block.y > 0) bits = bits or 2
+ if (block.z > 0) bits = bits or 4
+ return TraverseDirection(bits)
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
index df3cfab..39b4345 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
@@ -1,121 +1,121 @@
-package io.dico.parcels2.blockvisitor
-
-import io.dico.parcels2.JobFunction
-import io.dico.parcels2.JobScope
-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
-import org.bukkit.block.Sign
-import org.bukkit.block.data.BlockData
-
-private val air = Bukkit.createBlockData(Material.AIR)
-
-class Schematic {
- val size: Vec3i get() = _size!!
- private var _size: Vec3i? = null
- set(value) {
- field?.let { throw IllegalStateException() }
- field = value
- }
-
- private var blockDatas: Array<BlockData?>? = null
- private val extra = mutableListOf<Pair<Vec3i, ExtraBlockChange>>()
- private var isLoaded = false; private set
- private val traverser: RegionTraverser = RegionTraverser.upward
-
- suspend fun JobScope.load(world: World, region: Region) {
- _size = region.size
-
- val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it }
- val blocks = traverser.traverseRegion(region)
- val total = region.blockCount.toDouble()
-
- loop@ for ((index, vec) in blocks.withIndex()) {
- markSuspensionPoint()
- setProgress(index / total)
-
- val block = world[vec]
- if (block.y > 255) continue
- val blockData = block.blockData
- data[index] = blockData
-
- val extraChange = when (blockData.material) {
- Material.SIGN,
- Material.WALL_SIGN -> SignStateChange(block.state as Sign)
- else -> continue@loop
- }
-
- extra += (vec - region.origin) to extraChange
- }
-
- isLoaded = true
- }
-
- suspend fun JobScope.paste(world: World, position: Vec3i) {
- if (!isLoaded) throw IllegalStateException()
-
- val region = Region(position, _size!!)
- val blocks = traverser.traverseRegion(region, worldHeight = world.maxHeight)
- val blockDatas = blockDatas!!
- var postponed = hashMapOf<Vec3i, BlockData>()
-
- val total = region.blockCount.toDouble()
- var processed = 0
-
- for ((index, vec) in blocks.withIndex()) {
- markSuspensionPoint()
- setProgress(index / total)
-
- val block = world[vec]
- val type = blockDatas[index] ?: air
- if (type !== air && isAttachable(type.material)) {
- val supportingBlock = vec + getSupportingBlock(type)
-
- if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) {
- block.blockData = type
- setProgress(++processed / total)
- } else {
- postponed[vec] = type
- }
-
- } else {
- block.blockData = type
- setProgress(++processed / total)
- }
- }
-
- while (!postponed.isEmpty()) {
- markSuspensionPoint()
- val newMap = hashMapOf<Vec3i, BlockData>()
- for ((vec, type) in postponed) {
- val supportingBlock = vec + getSupportingBlock(type)
- if (supportingBlock in postponed && supportingBlock != vec) {
- newMap[vec] = type
- } else {
- world[vec].blockData = type
- setProgress(++processed / total)
- }
- }
- postponed = newMap
- }
-
- // Should be negligible so we don't track progress
- for ((vec, extraChange) in extra) {
- markSuspensionPoint()
- val block = world[position + vec]
- extraChange.update(block)
- }
- }
-
- fun getLoadTask(world: World, region: Region): JobFunction = {
- load(world, region)
- }
-
- fun getPasteTask(world: World, position: Vec3i): JobFunction = {
- paste(world, position)
- }
-
-}
+package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.JobFunction
+import io.dico.parcels2.JobScope
+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
+import org.bukkit.block.Sign
+import org.bukkit.block.data.BlockData
+
+private val air = Bukkit.createBlockData(Material.AIR)
+
+class Schematic {
+ val size: Vec3i get() = _size!!
+ private var _size: Vec3i? = null
+ set(value) {
+ field?.let { throw IllegalStateException() }
+ field = value
+ }
+
+ private var blockDatas: Array<BlockData?>? = null
+ private val extra = mutableListOf<Pair<Vec3i, ExtraBlockChange>>()
+ private var isLoaded = false; private set
+ private val traverser: RegionTraverser = RegionTraverser.upward
+
+ suspend fun JobScope.load(world: World, region: Region) {
+ _size = region.size
+
+ val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it }
+ val blocks = traverser.traverseRegion(region)
+ val total = region.blockCount.toDouble()
+
+ loop@ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ setProgress(index / total)
+
+ val block = world[vec]
+ if (block.y > 255) continue
+ val blockData = block.blockData
+ data[index] = blockData
+
+ val extraChange = when (blockData.material) {
+ Material.SIGN,
+ Material.WALL_SIGN -> SignStateChange(block.state as Sign)
+ else -> continue@loop
+ }
+
+ extra += (vec - region.origin) to extraChange
+ }
+
+ isLoaded = true
+ }
+
+ suspend fun JobScope.paste(world: World, position: Vec3i) {
+ if (!isLoaded) throw IllegalStateException()
+
+ val region = Region(position, _size!!)
+ val blocks = traverser.traverseRegion(region, worldHeight = world.maxHeight)
+ val blockDatas = blockDatas!!
+ var postponed = hashMapOf<Vec3i, BlockData>()
+
+ val total = region.blockCount.toDouble()
+ var processed = 0
+
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ setProgress(index / total)
+
+ val block = world[vec]
+ val type = blockDatas[index] ?: air
+ if (type !== air && isAttachable(type.material)) {
+ val supportingBlock = vec + getSupportingBlock(type)
+
+ if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) {
+ block.blockData = type
+ setProgress(++processed / total)
+ } else {
+ postponed[vec] = type
+ }
+
+ } else {
+ block.blockData = type
+ setProgress(++processed / total)
+ }
+ }
+
+ while (!postponed.isEmpty()) {
+ markSuspensionPoint()
+ val newMap = hashMapOf<Vec3i, BlockData>()
+ for ((vec, type) in postponed) {
+ val supportingBlock = vec + getSupportingBlock(type)
+ if (supportingBlock in postponed && supportingBlock != vec) {
+ newMap[vec] = type
+ } else {
+ world[vec].blockData = type
+ setProgress(++processed / total)
+ }
+ }
+ postponed = newMap
+ }
+
+ // Should be negligible so we don't track progress
+ for ((vec, extraChange) in extra) {
+ markSuspensionPoint()
+ val block = world[position + vec]
+ extraChange.update(block)
+ }
+ }
+
+ fun getLoadTask(world: World, region: Region): JobFunction = {
+ load(world, region)
+ }
+
+ fun getPasteTask(world: World, position: Vec3i): JobFunction = {
+ paste(world, position)
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
index eaa9f57..32f4299 100644
--- a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
@@ -1,64 +1,64 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.*
-import io.dico.dicore.command.registration.reflect.ICommandInterceptor
-import io.dico.dicore.command.registration.reflect.ICommandReceiver
-import io.dico.parcels2.*
-import io.dico.parcels2.PlayerProfile.Real
-import io.dico.parcels2.PlayerProfile.Unresolved
-import io.dico.parcels2.util.ext.hasPermAdminManage
-import io.dico.parcels2.util.ext.parcelLimit
-import org.bukkit.entity.Player
-import org.bukkit.plugin.Plugin
-import java.lang.reflect.Method
-
-abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandInterceptor {
-
- override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver {
- return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName)
- }
-
- override fun getCoroutineContext(context: ExecutionContext?, target: Method?, cmdName: String?): Any {
- return plugin.coroutineContext
- }
-
- protected fun checkConnected(action: String) {
- if (!plugin.storage.isConnected) err("Parcels cannot $action right now because of a database error")
- }
-
- protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) {
- if (player.hasPermAdminManage) return
- val numOwnedParcels = plugin.storage.getOwnedParcels(PlayerProfile(player)).await()
- .filter { it.worldId.equals(world.id) }.size
-
- val limit = player.parcelLimit
- if (numOwnedParcels >= limit) {
- err("You have enough plots for now")
- }
- }
-
- protected suspend fun toPrivilegeKey(profile: PlayerProfile): PrivilegeKey = when (profile) {
- is Real -> profile
- is Unresolved -> profile.tryResolveSuspendedly(plugin.storage)
- ?: throw CommandException()
- else -> throw CommandException()
- }
-
- protected fun areYouSureMessage(context: ExecutionContext): String {
- val command = (context.route + context.original).joinToString(" ") + " -sure"
- return "Are you sure? You cannot undo this action!\n" +
- "Run \"/$command\" if you want to go through with this."
- }
-
- protected fun Job.reportProgressUpdates(context: ExecutionContext, action: String): Job =
- onProgressUpdate(1000, 1000) { progress, elapsedTime ->
- val alt = context.getFormat(EMessageType.NUMBER)
- val main = context.getFormat(EMessageType.INFORMATIVE)
- context.sendMessage(
- EMessageType.INFORMATIVE, false, "$action progress: $alt%.02f$main%%, $alt%.2f${main}s elapsed"
- .format(progress * 100, elapsedTime / 1000.0)
- )
- }
-}
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.*
+import io.dico.dicore.command.registration.reflect.ICommandInterceptor
+import io.dico.dicore.command.registration.reflect.ICommandReceiver
+import io.dico.parcels2.*
+import io.dico.parcels2.PlayerProfile.Real
+import io.dico.parcels2.PlayerProfile.Unresolved
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import io.dico.parcels2.util.ext.parcelLimit
+import org.bukkit.entity.Player
+import org.bukkit.plugin.Plugin
+import java.lang.reflect.Method
+
+abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandInterceptor {
+
+ override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver {
+ return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName)
+ }
+
+ override fun getCoroutineContext(context: ExecutionContext?, target: Method?, cmdName: String?): Any {
+ return plugin.coroutineContext
+ }
+
+ protected fun checkConnected(action: String) {
+ if (!plugin.storage.isConnected) err("Parcels cannot $action right now because of a database error")
+ }
+
+ protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) {
+ if (player.hasPermAdminManage) return
+ val numOwnedParcels = plugin.storage.getOwnedParcels(PlayerProfile(player)).await()
+ .filter { it.worldId.equals(world.id) }.size
+
+ val limit = player.parcelLimit
+ if (numOwnedParcels >= limit) {
+ err("You have enough plots for now")
+ }
+ }
+
+ protected suspend fun toPrivilegeKey(profile: PlayerProfile): PrivilegeKey = when (profile) {
+ is Real -> profile
+ is Unresolved -> profile.tryResolveSuspendedly(plugin.storage)
+ ?: throw CommandException()
+ else -> throw CommandException()
+ }
+
+ protected fun areYouSureMessage(context: ExecutionContext): String {
+ val command = (context.route + context.original).joinToString(" ") + " -sure"
+ return "Are you sure? You cannot undo this action!\n" +
+ "Run \"/$command\" if you want to go through with this."
+ }
+
+ protected fun Job.reportProgressUpdates(context: ExecutionContext, action: String): Job =
+ onProgressUpdate(1000, 1000) { progress, elapsedTime ->
+ val alt = context.getFormat(EMessageType.NUMBER)
+ val main = context.getFormat(EMessageType.INFORMATIVE)
+ context.sendMessage(
+ EMessageType.INFORMATIVE, false, "$action progress: $alt%.02f$main%%, $alt%.2f${main}s elapsed"
+ .format(progress * 100, elapsedTime / 1000.0)
+ )
+ }
+}
+
fun err(message: String): Nothing = throw CommandException(message) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
index e700d6d..38adc43 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
@@ -1,95 +1,95 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.CommandException
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Flag
-import io.dico.parcels2.*
-import io.dico.parcels2.command.ParcelTarget.TargetKind
-import io.dico.parcels2.defaultimpl.DefaultParcelContainer
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-
-class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
-
- @Cmd("setowner")
- @RequireParcelPrivilege(Privilege.ADMIN)
- suspend fun ParcelScope.cmdSetowner(@ProfileKind(ProfileKind.ANY) target: PlayerProfile): Any? {
- val profile = target.resolved(plugin.storage, resolveToFake = true)!!
- parcel.owner = profile
-
- val fakeString = if (profile.isFake) " (fake)" else ""
- return "${profile.notNullName}$fakeString is the new owner of (${parcel.id.idString})"
- }
-
- @Cmd("update_all_owner_signs")
- fun cmdUpdateAllOwnerSigns(context: ExecutionContext): Any? {
- Validate.isAuthorized(context.sender, PERM_ADMIN_MANAGE)
- plugin.jobDispatcher.dispatch {
- fun getParcelCount(world: ParcelWorld) = (world.options.axisLimit * 2 + 1).let { it * it }
- val parcelCount = plugin.parcelProvider.worlds.values.sumBy { getParcelCount(it) }.toDouble()
- var processed = 0
- for (world in plugin.parcelProvider.worlds.values) {
- markSuspensionPoint()
-
- val container = world.container as? DefaultParcelContainer
- if (container == null) {
- processed += getParcelCount(world)
- setProgress(processed / parcelCount)
- continue
- }
-
- for (parcel in container.getAllParcels()) {
- parcel.updateOwnerSign(force = true)
- processed++
- setProgress(processed / parcelCount)
- }
- }
- }.reportProgressUpdates(context, "Updating")
- return null
- }
-
- @Cmd("dispose")
- @RequireParcelPrivilege(Privilege.ADMIN)
- fun ParcelScope.cmdDispose(): Any? {
- parcel.dispose()
- return "Data of (${parcel.id.idString}) has been disposed"
- }
-
- @Cmd("reset")
- @RequireParcelPrivilege(Privilege.ADMIN)
- fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
- if (!sure) return areYouSureMessage(context)
-
- parcel.dispose()
- world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Reset")
- return "Data of (${parcel.id.idString}) has been disposed"
- }
-
- @Cmd("swap")
- @RequireParcelPrivilege(Privilege.ADMIN)
- fun ParcelScope.cmdSwap(
- context: ExecutionContext,
- @TargetKind(TargetKind.ID) target: ParcelTarget,
- @Flag sure: Boolean
- ): Any? {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
- if (!sure) return areYouSureMessage(context)
-
- val parcel2 = (target as ParcelTarget.ByID).getParcel()
- ?: throw CommandException("Invalid parcel target")
-
- // Validate.isTrue(parcel2.world == world, "Parcel must be in the same world")
- Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in that parcel")
-
- val data = parcel.data
- parcel.copyData(parcel2.data)
- parcel2.copyData(data)
-
- val job = plugin.parcelProvider.swapParcels(parcel.id, parcel2.id)?.reportProgressUpdates(context, "Swap")
- Validate.notNull(job, "A process is already running in some parcel (internal error)")
- return null
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Flag
+import io.dico.parcels2.*
+import io.dico.parcels2.command.ParcelTarget.TargetKind
+import io.dico.parcels2.defaultimpl.DefaultParcelContainer
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+
+class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ @Cmd("setowner")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ suspend fun ParcelScope.cmdSetowner(@ProfileKind(ProfileKind.ANY) target: PlayerProfile): Any? {
+ val profile = target.resolved(plugin.storage, resolveToFake = true)!!
+ parcel.owner = profile
+
+ val fakeString = if (profile.isFake) " (fake)" else ""
+ return "${profile.notNullName}$fakeString is the new owner of (${parcel.id.idString})"
+ }
+
+ @Cmd("update_all_owner_signs")
+ fun cmdUpdateAllOwnerSigns(context: ExecutionContext): Any? {
+ Validate.isAuthorized(context.sender, PERM_ADMIN_MANAGE)
+ plugin.jobDispatcher.dispatch {
+ fun getParcelCount(world: ParcelWorld) = (world.options.axisLimit * 2 + 1).let { it * it }
+ val parcelCount = plugin.parcelProvider.worlds.values.sumBy { getParcelCount(it) }.toDouble()
+ var processed = 0
+ for (world in plugin.parcelProvider.worlds.values) {
+ markSuspensionPoint()
+
+ val container = world.container as? DefaultParcelContainer
+ if (container == null) {
+ processed += getParcelCount(world)
+ setProgress(processed / parcelCount)
+ continue
+ }
+
+ for (parcel in container.getAllParcels()) {
+ parcel.updateOwnerSign(force = true)
+ processed++
+ setProgress(processed / parcelCount)
+ }
+ }
+ }.reportProgressUpdates(context, "Updating")
+ return null
+ }
+
+ @Cmd("dispose")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdDispose(): Any? {
+ parcel.dispose()
+ return "Data of (${parcel.id.idString}) has been disposed"
+ }
+
+ @Cmd("reset")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+ if (!sure) return areYouSureMessage(context)
+
+ parcel.dispose()
+ world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Reset")
+ return "Data of (${parcel.id.idString}) has been disposed"
+ }
+
+ @Cmd("swap")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdSwap(
+ context: ExecutionContext,
+ @TargetKind(TargetKind.ID) target: ParcelTarget,
+ @Flag sure: Boolean
+ ): Any? {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+ if (!sure) return areYouSureMessage(context)
+
+ val parcel2 = (target as ParcelTarget.ByID).getParcel()
+ ?: throw CommandException("Invalid parcel target")
+
+ // Validate.isTrue(parcel2.world == world, "Parcel must be in the same world")
+ Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in that parcel")
+
+ val data = parcel.data
+ parcel.copyData(parcel2.data)
+ parcel2.copyData(data)
+
+ val job = plugin.parcelProvider.swapParcels(parcel.id, parcel2.id)?.reportProgressUpdates(context, "Swap")
+ Validate.notNull(job, "A process is already running in some parcel (internal error)")
+ return null
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt
index cee3e62..a2bd8d1 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt
@@ -1,132 +1,132 @@
-@file:Suppress("NON_EXHAUSTIVE_WHEN")
-
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.parcels2.*
-import io.dico.parcels2.Privilege.BANNED
-import io.dico.parcels2.Privilege.CAN_BUILD
-import io.dico.parcels2.PrivilegeChangeResult.*
-import io.dico.parcels2.defaultimpl.InfoBuilder
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-import org.bukkit.OfflinePlayer
-
-class CommandsAdminPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
- private val data
- inline get() = plugin.globalPrivileges
-
- private fun checkContext(context: ExecutionContext, owner: OfflinePlayer, changing: Boolean = true): OfflinePlayer {
- if (changing) {
- checkConnected("have privileges changed")
- }
- val sender = context.sender
- if (sender !== owner) {
- Validate.isAuthorized(sender, PERM_ADMIN_MANAGE)
- }
- return owner
- }
-
- @Cmd("list", aliases = ["l"])
- @Desc(
- "List globally declared privileges, players you",
- "allowed to build on or banned from all your parcels",
- shortVersion = "lists globally declared privileges"
- )
- fun cmdList(context: ExecutionContext, owner: OfflinePlayer): Any? {
- checkContext(context, owner, changing = false)
- val map = plugin.globalPrivileges[owner]
- Validate.isTrue(map.hasAnyDeclaredPrivileges(), "This user has not declared any global privileges")
-
- return StringBuilder().apply {
- with(InfoBuilder) {
- appendProfilesWithPrivilege("Globally Allowed", map, null, CAN_BUILD)
- appendProfilesWithPrivilege("Globally Banned", map, null, BANNED)
- }
- }.toString()
- }
-
- @Cmd("entrust")
- @Desc(
- "Allows a player to manage globally",
- shortVersion = "allows a player to manage globally"
- )
- suspend fun cmdEntrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].allowManage(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is already allowed to manage globally")
- SUCCESS -> "${player.name} is now allowed to manage globally"
- }
-
- @Cmd("distrust")
- @Desc(
- "Disallows a player to manage globally,",
- "they will still be able to build",
- shortVersion = "disallows a player to manage globally"
- )
- suspend fun cmdDistrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].disallowManage(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is not currently allowed to manage globally")
- SUCCESS -> "${player.name} is not allowed to manage globally anymore"
- }
-
- @Cmd("allow", aliases = ["add", "permit"])
- @Desc(
- "Globally allows a player to build on all",
- "the parcels that you own.",
- shortVersion = "globally allows a player to build on your parcels"
- )
- suspend fun cmdAllow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].allowBuild(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is already allowed globally")
- SUCCESS -> "${player.name} is now allowed to build globally"
- }
-
- @Cmd("disallow", aliases = ["remove", "forbid"])
- @Desc(
- "Globally disallows a player to build on",
- "the parcels that you own.",
- "If the player is allowed to build on specific",
- "parcels, they can still build there.",
- shortVersion = "globally disallows a player to build on your parcels"
- )
- suspend fun cmdDisallow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].disallowBuild(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is not currently allowed globally")
- SUCCESS -> "${player.name} is not allowed to build globally anymore"
- }
-
- @Cmd("ban", aliases = ["deny"])
- @Desc(
- "Globally bans a player from all the parcels",
- "that you own, making them unable to enter.",
- shortVersion = "globally bans a player from your parcels"
- )
- suspend fun cmdBan(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].disallowEnter(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is already banned globally")
- SUCCESS -> "${player.name} is now banned globally"
- }
-
- @Cmd("unban", aliases = ["undeny"])
- @Desc(
- "Globally unbans a player from all the parcels",
- "that you own, they can enter again.",
- "If the player is banned from specific parcels,",
- "they will still be banned there.",
- shortVersion = "globally unbans a player from your parcels"
- )
- suspend fun cmdUnban(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].allowEnter(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is not currently banned globally")
- SUCCESS -> "${player.name} is not banned globally anymore"
- }
-
+@file:Suppress("NON_EXHAUSTIVE_WHEN")
+
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.BANNED
+import io.dico.parcels2.Privilege.CAN_BUILD
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.defaultimpl.InfoBuilder
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import org.bukkit.OfflinePlayer
+
+class CommandsAdminPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+ private val data
+ inline get() = plugin.globalPrivileges
+
+ private fun checkContext(context: ExecutionContext, owner: OfflinePlayer, changing: Boolean = true): OfflinePlayer {
+ if (changing) {
+ checkConnected("have privileges changed")
+ }
+ val sender = context.sender
+ if (sender !== owner) {
+ Validate.isAuthorized(sender, PERM_ADMIN_MANAGE)
+ }
+ return owner
+ }
+
+ @Cmd("list", aliases = ["l"])
+ @Desc(
+ "List globally declared privileges, players you",
+ "allowed to build on or banned from all your parcels",
+ shortVersion = "lists globally declared privileges"
+ )
+ fun cmdList(context: ExecutionContext, owner: OfflinePlayer): Any? {
+ checkContext(context, owner, changing = false)
+ val map = plugin.globalPrivileges[owner]
+ Validate.isTrue(map.hasAnyDeclaredPrivileges(), "This user has not declared any global privileges")
+
+ return StringBuilder().apply {
+ with(InfoBuilder) {
+ appendProfilesWithPrivilege("Globally Allowed", map, null, CAN_BUILD)
+ appendProfilesWithPrivilege("Globally Banned", map, null, BANNED)
+ }
+ }.toString()
+ }
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage globally",
+ shortVersion = "allows a player to manage globally"
+ )
+ suspend fun cmdEntrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowManage(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already allowed to manage globally")
+ SUCCESS -> "${player.name} is now allowed to manage globally"
+ }
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage globally,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage globally"
+ )
+ suspend fun cmdDistrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowManage(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently allowed to manage globally")
+ SUCCESS -> "${player.name} is not allowed to manage globally anymore"
+ }
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Globally allows a player to build on all",
+ "the parcels that you own.",
+ shortVersion = "globally allows a player to build on your parcels"
+ )
+ suspend fun cmdAllow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowBuild(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already allowed globally")
+ SUCCESS -> "${player.name} is now allowed to build globally"
+ }
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Globally disallows a player to build on",
+ "the parcels that you own.",
+ "If the player is allowed to build on specific",
+ "parcels, they can still build there.",
+ shortVersion = "globally disallows a player to build on your parcels"
+ )
+ suspend fun cmdDisallow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowBuild(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently allowed globally")
+ SUCCESS -> "${player.name} is not allowed to build globally anymore"
+ }
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Globally bans a player from all the parcels",
+ "that you own, making them unable to enter.",
+ shortVersion = "globally bans a player from your parcels"
+ )
+ suspend fun cmdBan(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowEnter(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already banned globally")
+ SUCCESS -> "${player.name} is now banned globally"
+ }
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Globally unbans a player from all the parcels",
+ "that you own, they can enter again.",
+ "If the player is banned from specific parcels,",
+ "they will still be banned there.",
+ shortVersion = "globally unbans a player from your parcels"
+ )
+ suspend fun cmdUnban(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowEnter(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently banned globally")
+ SUCCESS -> "${player.name} is not banned globally anymore"
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
index e0bde7d..3f166ad 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
@@ -1,162 +1,162 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.Formatting
-import io.dico.dicore.command.*
-import io.dico.dicore.command.IContextFilter.Priority.PERMISSION
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.PreprocessArgs
-import io.dico.dicore.command.annotation.RequireParameters
-import io.dico.dicore.command.parameter.ArgumentBuffer
-import io.dico.parcels2.*
-import io.dico.parcels2.blockvisitor.RegionTraverser
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
-import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
-import kotlinx.coroutines.launch
-import org.bukkit.Bukkit
-import org.bukkit.Material
-import org.bukkit.block.BlockFace
-import org.bukkit.block.data.Directional
-import org.bukkit.command.CommandSender
-import org.bukkit.entity.Player
-import java.util.Random
-
-class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
-
- @Cmd("reloadoptions")
- fun reloadOptions() {
- plugin.loadOptions()
- }
-
- @Cmd("tpworld")
- fun tpWorld(sender: Player, worldName: String): String {
- if (worldName == "list") {
- return Bukkit.getWorlds().joinToString("\n- ", "- ", "")
- }
- val world = Bukkit.getWorld(worldName) ?: err("World $worldName is not loaded")
- sender.teleport(world.spawnLocation)
- return "Teleported you to $worldName spawn"
- }
-
- @Cmd("make_mess")
- @RequireParcelPrivilege(Privilege.OWNER)
- fun ParcelScope.cmdMakeMess(context: ExecutionContext) {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
-
- val server = plugin.server
- val blockDatas = arrayOf(
- server.createBlockData(Material.BLUE_WOOL),
- server.createBlockData(Material.LIME_WOOL),
- server.createBlockData(Material.GLASS),
- server.createBlockData(Material.STONE_SLAB),
- server.createBlockData(Material.STONE),
- server.createBlockData(Material.QUARTZ_BLOCK),
- server.createBlockData(Material.BROWN_CONCRETE)
- )
- val random = Random()
-
- world.blockManager.tryDoBlockOperation(plugin.parcelProvider, parcel.id, traverser = RegionTraverser.upward) { block ->
- block.blockData = blockDatas[random.nextInt(7)]
- }?.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
- context.sendMessage(
- EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
- .format(progress * 100, elapsedTime / 1000.0)
- )
- }
- }
-
- @Cmd("directionality", aliases = ["dir"])
- fun cmdDirectionality(sender: Player, context: ExecutionContext, material: Material): Any? {
- val senderLoc = sender.location
- val block = senderLoc.add(senderLoc.direction.setY(0).normalize().multiply(2).toLocation(sender.world)).block
-
- val blockData = Bukkit.createBlockData(material)
- if (blockData is Directional) {
- blockData.facing = BlockFace.SOUTH
- }
-
- block.blockData = blockData
- return if (blockData is Directional) "The block is facing south" else "The block is not directional, however it implements " +
- blockData.javaClass.interfaces!!.contentToString()
- }
-
- @Cmd("jobs")
- fun cmdJobs(): Any? {
- val workers = plugin.jobDispatcher.jobs
- println(workers.map { it.coroutine }.joinToString(separator = "\n"))
- return "Task count: ${workers.size}"
- }
-
- @Cmd("complete_jobs")
- fun cmdCompleteJobs(): Any? = cmdJobs().also {
- plugin.launch { plugin.jobDispatcher.completeAllTasks() }
- }
-
- @Cmd("message")
- @PreprocessArgs
- fun cmdMessage(sender: CommandSender, message: String): Any? {
- // testing @PreprocessArgs which merges "hello there" into a single argument
- sender.sendMessage(Formatting.translate(message))
- return null
- }
-
- @Cmd("hasperm")
- fun cmdHasperm(target: Player, permission: String): Any? {
- return target.hasPermission(permission).toString()
- }
-
- @Cmd("permissions")
- fun cmdPermissions(context: ExecutionContext, of: Player, vararg address: String): Any? {
- val target = context.address.dispatcherForTree.getDeepChild(ArgumentBuffer(address))
- Validate.isTrue(target.depth == address.size && target.hasCommand(), "Not found: /${address.joinToString(separator = " ")}")
- return getPermissionsOf(target).joinToString(separator = "\n") { "$it: ${of.hasPermission(it)}" }
- }
-
- @Cmd("privilege")
- @RequireParameters(1)
- suspend fun ParcelScope.cmdPrivilege(target: PlayerProfile, adminPerm: String?): Any? {
- val key = toPrivilegeKey(target)
-
- val perm = when (adminPerm) {
- "none" -> null
- "build" -> PERM_BUILD_ANYWHERE
- "manage", null -> PERM_ADMIN_MANAGE
- "enter" -> PERM_BAN_BYPASS
- else -> err("adminPerm should be build, manager or enter")
- }
-
- val privilege = if (perm == null) {
- parcel.getStoredPrivilege(key)
- } else {
- if (key is PlayerProfile.Star) err("* can't have permissions")
- parcel.getEffectivePrivilege(key.player!!, perm)
- }
-
- return privilege.toString()
- }
-
- private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
-
- private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
- val command = address.command ?: return result
-
- var inherited = false
- for (filter in command.contextFilters) {
- when (filter) {
- is PermissionContextFilter -> {
- if (path.isEmpty()) result.add(filter.permission)
- else if (filter.isInheritable) result.add(filter.getInheritedPermission(path))
- }
- is InheritingContextFilter -> {
- if (filter.priority == PERMISSION && address.hasParent() && !inherited) {
- inherited = true
- getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result)
- }
- }
- }
- }
-
- return result
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.Formatting
+import io.dico.dicore.command.*
+import io.dico.dicore.command.IContextFilter.Priority.PERMISSION
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.PreprocessArgs
+import io.dico.dicore.command.annotation.RequireParameters
+import io.dico.dicore.command.parameter.ArgumentBuffer
+import io.dico.parcels2.*
+import io.dico.parcels2.blockvisitor.RegionTraverser
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
+import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
+import kotlinx.coroutines.launch
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.block.BlockFace
+import org.bukkit.block.data.Directional
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+import java.util.Random
+
+class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ @Cmd("reloadoptions")
+ fun reloadOptions() {
+ plugin.loadOptions()
+ }
+
+ @Cmd("tpworld")
+ fun tpWorld(sender: Player, worldName: String): String {
+ if (worldName == "list") {
+ return Bukkit.getWorlds().joinToString("\n- ", "- ", "")
+ }
+ val world = Bukkit.getWorld(worldName) ?: err("World $worldName is not loaded")
+ sender.teleport(world.spawnLocation)
+ return "Teleported you to $worldName spawn"
+ }
+
+ @Cmd("make_mess")
+ @RequireParcelPrivilege(Privilege.OWNER)
+ fun ParcelScope.cmdMakeMess(context: ExecutionContext) {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+
+ val server = plugin.server
+ val blockDatas = arrayOf(
+ server.createBlockData(Material.BLUE_WOOL),
+ server.createBlockData(Material.LIME_WOOL),
+ server.createBlockData(Material.GLASS),
+ server.createBlockData(Material.STONE_SLAB),
+ server.createBlockData(Material.STONE),
+ server.createBlockData(Material.QUARTZ_BLOCK),
+ server.createBlockData(Material.BROWN_CONCRETE)
+ )
+ val random = Random()
+
+ world.blockManager.tryDoBlockOperation(plugin.parcelProvider, parcel.id, traverser = RegionTraverser.upward) { block ->
+ block.blockData = blockDatas[random.nextInt(7)]
+ }?.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
+ context.sendMessage(
+ EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
+ .format(progress * 100, elapsedTime / 1000.0)
+ )
+ }
+ }
+
+ @Cmd("directionality", aliases = ["dir"])
+ fun cmdDirectionality(sender: Player, context: ExecutionContext, material: Material): Any? {
+ val senderLoc = sender.location
+ val block = senderLoc.add(senderLoc.direction.setY(0).normalize().multiply(2).toLocation(sender.world)).block
+
+ val blockData = Bukkit.createBlockData(material)
+ if (blockData is Directional) {
+ blockData.facing = BlockFace.SOUTH
+ }
+
+ block.blockData = blockData
+ return if (blockData is Directional) "The block is facing south" else "The block is not directional, however it implements " +
+ blockData.javaClass.interfaces!!.contentToString()
+ }
+
+ @Cmd("jobs")
+ fun cmdJobs(): Any? {
+ val workers = plugin.jobDispatcher.jobs
+ println(workers.map { it.coroutine }.joinToString(separator = "\n"))
+ return "Task count: ${workers.size}"
+ }
+
+ @Cmd("complete_jobs")
+ fun cmdCompleteJobs(): Any? = cmdJobs().also {
+ plugin.launch { plugin.jobDispatcher.completeAllTasks() }
+ }
+
+ @Cmd("message")
+ @PreprocessArgs
+ fun cmdMessage(sender: CommandSender, message: String): Any? {
+ // testing @PreprocessArgs which merges "hello there" into a single argument
+ sender.sendMessage(Formatting.translate(message))
+ return null
+ }
+
+ @Cmd("hasperm")
+ fun cmdHasperm(target: Player, permission: String): Any? {
+ return target.hasPermission(permission).toString()
+ }
+
+ @Cmd("permissions")
+ fun cmdPermissions(context: ExecutionContext, of: Player, vararg address: String): Any? {
+ val target = context.address.dispatcherForTree.getDeepChild(ArgumentBuffer(address))
+ Validate.isTrue(target.depth == address.size && target.hasCommand(), "Not found: /${address.joinToString(separator = " ")}")
+ return getPermissionsOf(target).joinToString(separator = "\n") { "$it: ${of.hasPermission(it)}" }
+ }
+
+ @Cmd("privilege")
+ @RequireParameters(1)
+ suspend fun ParcelScope.cmdPrivilege(target: PlayerProfile, adminPerm: String?): Any? {
+ val key = toPrivilegeKey(target)
+
+ val perm = when (adminPerm) {
+ "none" -> null
+ "build" -> PERM_BUILD_ANYWHERE
+ "manage", null -> PERM_ADMIN_MANAGE
+ "enter" -> PERM_BAN_BYPASS
+ else -> err("adminPerm should be build, manager or enter")
+ }
+
+ val privilege = if (perm == null) {
+ parcel.getStoredPrivilege(key)
+ } else {
+ if (key is PlayerProfile.Star) err("* can't have permissions")
+ parcel.getEffectivePrivilege(key.player!!, perm)
+ }
+
+ return privilege.toString()
+ }
+
+ private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
+
+ private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
+ val command = address.command ?: return result
+
+ var inherited = false
+ for (filter in command.contextFilters) {
+ when (filter) {
+ is PermissionContextFilter -> {
+ if (path.isEmpty()) result.add(filter.permission)
+ else if (filter.isInheritable) result.add(filter.getInheritedPermission(path))
+ }
+ is InheritingContextFilter -> {
+ if (filter.priority == PERMISSION && address.hasParent() && !inherited) {
+ inherited = true
+ getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result)
+ }
+ }
+ }
+ }
+
+ return result
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
index c918b80..9929a4c 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
@@ -113,6 +113,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
return "Enjoy your new parcel!"
}
+ /*
@Cmd("unclaim")
@Desc("Unclaims this parcel")
@RequireParcelPrivilege(Privilege.OWNER)
@@ -120,7 +121,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
checkConnected("be unclaimed")
parcel.dispose()
return "Your parcel has been disposed"
- }
+ }*/
@Cmd("clear")
@RequireParcelPrivilege(Privilege.OWNER)
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt
index 3b4234c..33ffb8b 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt
@@ -1,77 +1,77 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.PlayerProfile
-import org.bukkit.entity.Player
-
-class CommandsPrivilegesGlobal(plugin: ParcelsPlugin,
- val adminVersion: CommandsAdminPrivilegesGlobal) : AbstractParcelCommands(plugin) {
- @Cmd("list", aliases = ["l"])
- @Desc(
- "List globally declared privileges, players you",
- "allowed to build on or banned from all your parcels",
- shortVersion = "lists globally declared privileges"
- )
- fun cmdList(sender: Player, context: ExecutionContext) =
- adminVersion.cmdList(context, sender)
-
- @Cmd("entrust")
- @Desc(
- "Allows a player to manage globally",
- shortVersion = "allows a player to manage globally"
- )
- suspend fun cmdEntrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdEntrust(context, sender, player)
-
- @Cmd("distrust")
- @Desc(
- "Disallows a player to manage globally,",
- "they will still be able to build",
- shortVersion = "disallows a player to manage globally"
- )
- suspend fun cmdDistrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdDistrust(context, sender, player)
-
- @Cmd("allow", aliases = ["add", "permit"])
- @Desc(
- "Globally allows a player to build on all",
- "the parcels that you own.",
- shortVersion = "globally allows a player to build on your parcels"
- )
- suspend fun cmdAllow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdAllow(context, sender, player)
-
- @Cmd("disallow", aliases = ["remove", "forbid"])
- @Desc(
- "Globally disallows a player to build on",
- "the parcels that you own.",
- "If the player is allowed to build on specific",
- "parcels, they can still build there.",
- shortVersion = "globally disallows a player to build on your parcels"
- )
- suspend fun cmdDisallow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdDisallow(context, sender, player)
-
- @Cmd("ban", aliases = ["deny"])
- @Desc(
- "Globally bans a player from all the parcels",
- "that you own, making them unable to enter.",
- shortVersion = "globally bans a player from your parcels"
- )
- suspend fun cmdBan(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdBan(context, sender, player)
-
- @Cmd("unban", aliases = ["undeny"])
- @Desc(
- "Globally unbans a player from all the parcels",
- "that you own, they can enter again.",
- "If the player is banned from specific parcels,",
- "they will still be banned there.",
- shortVersion = "globally unbans a player from your parcels"
- )
- suspend fun cmdUnban(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdUnban(context, sender, player)
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.PlayerProfile
+import org.bukkit.entity.Player
+
+class CommandsPrivilegesGlobal(plugin: ParcelsPlugin,
+ val adminVersion: CommandsAdminPrivilegesGlobal) : AbstractParcelCommands(plugin) {
+ @Cmd("list", aliases = ["l"])
+ @Desc(
+ "List globally declared privileges, players you",
+ "allowed to build on or banned from all your parcels",
+ shortVersion = "lists globally declared privileges"
+ )
+ fun cmdList(sender: Player, context: ExecutionContext) =
+ adminVersion.cmdList(context, sender)
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage globally",
+ shortVersion = "allows a player to manage globally"
+ )
+ suspend fun cmdEntrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdEntrust(context, sender, player)
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage globally,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage globally"
+ )
+ suspend fun cmdDistrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdDistrust(context, sender, player)
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Globally allows a player to build on all",
+ "the parcels that you own.",
+ shortVersion = "globally allows a player to build on your parcels"
+ )
+ suspend fun cmdAllow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdAllow(context, sender, player)
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Globally disallows a player to build on",
+ "the parcels that you own.",
+ "If the player is allowed to build on specific",
+ "parcels, they can still build there.",
+ shortVersion = "globally disallows a player to build on your parcels"
+ )
+ suspend fun cmdDisallow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdDisallow(context, sender, player)
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Globally bans a player from all the parcels",
+ "that you own, making them unable to enter.",
+ shortVersion = "globally bans a player from your parcels"
+ )
+ suspend fun cmdBan(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdBan(context, sender, player)
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Globally unbans a player from all the parcels",
+ "that you own, they can enter again.",
+ "If the player is banned from specific parcels,",
+ "they will still be banned there.",
+ shortVersion = "globally unbans a player from your parcels"
+ )
+ suspend fun cmdUnban(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdUnban(context, sender, player)
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt
index 21910b1..e3aba22 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt
@@ -1,144 +1,144 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.parcels2.*
-import io.dico.parcels2.PrivilegeChangeResult.*
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-import io.dico.parcels2.util.ext.hasPermAdminManage
-import org.bukkit.entity.Player
-
-class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
-
- private fun ParcelScope.checkPrivilege(sender: Player, key: PrivilegeKey) {
- val senderPrivilege = parcel.getEffectivePrivilege(sender, PERM_ADMIN_MANAGE)
- val targetPrivilege = parcel.getStoredPrivilege(key)
- Validate.isTrue(senderPrivilege > targetPrivilege, "You may not change the privilege of ${key.notNullName}")
- }
-
- private fun ParcelScope.checkOwned(sender: Player) {
- Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
- }
-
- @Cmd("entrust")
- @Desc(
- "Allows a player to manage this parcel",
- shortVersion = "allows a player to manage this parcel"
- )
- @RequireParcelPrivilege(Privilege.OWNER)
- suspend fun ParcelScope.cmdEntrust(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- return when (parcel.allowManage(key)) {
- FAIL_OWNER -> err("The target already owns the parcel")
- FAIL -> err("${player.name} is already allowed to manage this parcel")
- SUCCESS -> "${player.name} is now allowed to manage this parcel"
- }
- }
-
- @Cmd("distrust")
- @Desc(
- "Disallows a player to manage this parcel,",
- "they will still be able to build",
- shortVersion = "disallows a player to manage this parcel"
- )
- @RequireParcelPrivilege(Privilege.OWNER)
- suspend fun ParcelScope.cmdDistrust(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- return when (parcel.disallowManage(key)) {
- FAIL_OWNER -> err("The target owns the parcel and can't be distrusted")
- FAIL -> err("${player.name} is not currently allowed to manage this parcel")
- SUCCESS -> "${player.name} is not allowed to manage this parcel anymore"
- }
- }
-
- @Cmd("allow", aliases = ["add", "permit"])
- @Desc(
- "Allows a player to build on this parcel",
- shortVersion = "allows a player to build on this parcel"
- )
- @RequireParcelPrivilege(Privilege.CAN_MANAGE)
- suspend fun ParcelScope.cmdAllow(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- checkPrivilege(sender, key)
-
- return when (parcel.allowBuild(key)) {
- FAIL_OWNER -> err("The target already owns the parcel")
- FAIL -> err("${player.name} is already allowed to build on this parcel")
- SUCCESS -> "${player.name} is now allowed to build on this parcel"
- }
- }
-
- @Cmd("disallow", aliases = ["remove", "forbid"])
- @Desc(
- "Disallows a player to build on this parcel,",
- "they won't be allowed to anymore",
- shortVersion = "disallows a player to build on this parcel"
- )
- @RequireParcelPrivilege(Privilege.CAN_MANAGE)
- suspend fun ParcelScope.cmdDisallow(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- checkPrivilege(sender, key)
-
- return when (parcel.disallowBuild(key)) {
- FAIL_OWNER -> err("The target owns the parcel")
- FAIL -> err("${player.name} is not currently allowed to build on this parcel")
- SUCCESS -> "${player.name} is not allowed to build on this parcel anymore"
- }
- }
-
- @Cmd("ban", aliases = ["deny"])
- @Desc(
- "Bans a player from this parcel,",
- "making them unable to enter",
- shortVersion = "bans a player from this parcel"
- )
- @RequireParcelPrivilege(Privilege.CAN_MANAGE)
- suspend fun ParcelScope.cmdBan(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- checkPrivilege(sender, key)
-
- return when (parcel.disallowEnter(key)) {
- FAIL_OWNER -> err("The target owns the parcel")
- FAIL -> err("${player.name} is already banned from this parcel")
- SUCCESS -> "${player.name} is now banned from this parcel"
- }
- }
-
- @Cmd("unban", aliases = ["undeny"])
- @Desc(
- "Unbans a player from this parcel,",
- "they will be able to enter it again",
- shortVersion = "unbans a player from this parcel"
- )
- @RequireParcelPrivilege(Privilege.CAN_MANAGE)
- suspend fun ParcelScope.cmdUnban(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- checkPrivilege(sender, key)
-
- return when (parcel.allowEnter(key)) {
- FAIL_OWNER -> err("The target owns the parcel")
- FAIL -> err("${player.name} is not currently banned from this parcel")
- SUCCESS -> "${player.name} is not banned from this parcel anymore"
- }
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.*
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import org.bukkit.entity.Player
+
+class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ private fun ParcelScope.checkPrivilege(sender: Player, key: PrivilegeKey) {
+ val senderPrivilege = parcel.getEffectivePrivilege(sender, PERM_ADMIN_MANAGE)
+ val targetPrivilege = parcel.getStoredPrivilege(key)
+ Validate.isTrue(senderPrivilege > targetPrivilege, "You may not change the privilege of ${key.notNullName}")
+ }
+
+ private fun ParcelScope.checkOwned(sender: Player) {
+ Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
+ }
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage this parcel",
+ shortVersion = "allows a player to manage this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.OWNER)
+ suspend fun ParcelScope.cmdEntrust(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ return when (parcel.allowManage(key)) {
+ FAIL_OWNER -> err("The target already owns the parcel")
+ FAIL -> err("${player.name} is already allowed to manage this parcel")
+ SUCCESS -> "${player.name} is now allowed to manage this parcel"
+ }
+ }
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage this parcel,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.OWNER)
+ suspend fun ParcelScope.cmdDistrust(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ return when (parcel.disallowManage(key)) {
+ FAIL_OWNER -> err("The target owns the parcel and can't be distrusted")
+ FAIL -> err("${player.name} is not currently allowed to manage this parcel")
+ SUCCESS -> "${player.name} is not allowed to manage this parcel anymore"
+ }
+ }
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Allows a player to build on this parcel",
+ shortVersion = "allows a player to build on this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdAllow(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.allowBuild(key)) {
+ FAIL_OWNER -> err("The target already owns the parcel")
+ FAIL -> err("${player.name} is already allowed to build on this parcel")
+ SUCCESS -> "${player.name} is now allowed to build on this parcel"
+ }
+ }
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Disallows a player to build on this parcel,",
+ "they won't be allowed to anymore",
+ shortVersion = "disallows a player to build on this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdDisallow(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.disallowBuild(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is not currently allowed to build on this parcel")
+ SUCCESS -> "${player.name} is not allowed to build on this parcel anymore"
+ }
+ }
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Bans a player from this parcel,",
+ "making them unable to enter",
+ shortVersion = "bans a player from this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdBan(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.disallowEnter(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is already banned from this parcel")
+ SUCCESS -> "${player.name} is now banned from this parcel"
+ }
+ }
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Unbans a player from this parcel,",
+ "they will be able to enter it again",
+ shortVersion = "unbans a player from this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdUnban(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.allowEnter(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is not currently banned from this parcel")
+ SUCCESS -> "${player.name} is not banned from this parcel anymore"
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
index 15548b4..e41fd37 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
@@ -1,68 +1,68 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.CommandException
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.registration.reflect.ICommandReceiver
-import io.dico.dicore.command.Validate
-import io.dico.parcels2.*
-import io.dico.parcels2.Privilege.*
-import io.dico.parcels2.util.ext.hasPermAdminManage
-import io.dico.parcels2.util.ext.uuid
-import org.bukkit.entity.Player
-import java.lang.reflect.Method
-import kotlin.reflect.full.extensionReceiverParameter
-import kotlin.reflect.full.findAnnotation
-import kotlin.reflect.jvm.jvmErasure
-import kotlin.reflect.jvm.kotlinFunction
-
-@Target(AnnotationTarget.FUNCTION)
-@Retention(AnnotationRetention.RUNTIME)
-annotation class RequireParcelPrivilege(val privilege: Privilege)
-
-/*
-@Target(AnnotationTarget.FUNCTION)
-@Retention(AnnotationRetention.RUNTIME)
-annotation class SuspensionTimeout(val millis: Int)
-*/
-
-open class WorldScope(val world: ParcelWorld) : ICommandReceiver
-open class ParcelScope(val parcel: Parcel) : WorldScope(parcel.world) {
- fun checkCanManage(player: Player, action: String) = Validate.isTrue(parcel.canManage(player), "You must own this parcel to $action")
-}
-
-fun getParcelCommandReceiver(parcelProvider: ParcelProvider, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver {
- val player = context.sender as Player
- val function = method.kotlinFunction!!
- val receiverType = function.extensionReceiverParameter!!.type
- val require = function.findAnnotation<RequireParcelPrivilege>()
-
- return when (receiverType.jvmErasure) {
- ParcelScope::class -> ParcelScope(parcelProvider.getParcelRequired(player, require?.privilege))
- WorldScope::class -> WorldScope(parcelProvider.getWorldRequired(player, require?.privilege == ADMIN))
- else -> throw InternalError("Invalid command receiver type")
- }
-}
-
-fun ParcelProvider.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld {
- if (admin) Validate.isTrue(player.hasPermAdminManage, "You must have admin rights to use that command")
- return getWorld(player.world)
- ?: throw CommandException("You must be in a parcel world to use that command")
-}
-
-fun ParcelProvider.getParcelRequired(player: Player, privilege: Privilege? = null): Parcel {
- val parcel = getWorldRequired(player, admin = privilege == ADMIN).getParcelAt(player)
- ?: throw CommandException("You must be in a parcel to use that command")
-
- if (!player.hasPermAdminManage) {
- @Suppress("NON_EXHAUSTIVE_WHEN")
- when (privilege) {
- OWNER ->
- Validate.isTrue(parcel.isOwner(player.uuid), "You must own this parcel to use that command")
- CAN_MANAGE ->
- Validate.isTrue(parcel.canManage(player), "You must have management privileges on this parcel to use that command")
- }
- }
-
- return parcel
-}
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.registration.reflect.ICommandReceiver
+import io.dico.dicore.command.Validate
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.*
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import io.dico.parcels2.util.ext.uuid
+import org.bukkit.entity.Player
+import java.lang.reflect.Method
+import kotlin.reflect.full.extensionReceiverParameter
+import kotlin.reflect.full.findAnnotation
+import kotlin.reflect.jvm.jvmErasure
+import kotlin.reflect.jvm.kotlinFunction
+
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class RequireParcelPrivilege(val privilege: Privilege)
+
+/*
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SuspensionTimeout(val millis: Int)
+*/
+
+open class WorldScope(val world: ParcelWorld) : ICommandReceiver
+open class ParcelScope(val parcel: Parcel) : WorldScope(parcel.world) {
+ fun checkCanManage(player: Player, action: String) = Validate.isTrue(parcel.canManage(player), "You must own this parcel to $action")
+}
+
+fun getParcelCommandReceiver(parcelProvider: ParcelProvider, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver {
+ val player = context.sender as Player
+ val function = method.kotlinFunction!!
+ val receiverType = function.extensionReceiverParameter!!.type
+ val require = function.findAnnotation<RequireParcelPrivilege>()
+
+ return when (receiverType.jvmErasure) {
+ ParcelScope::class -> ParcelScope(parcelProvider.getParcelRequired(player, require?.privilege))
+ WorldScope::class -> WorldScope(parcelProvider.getWorldRequired(player, require?.privilege == ADMIN))
+ else -> throw InternalError("Invalid command receiver type")
+ }
+}
+
+fun ParcelProvider.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld {
+ if (admin) Validate.isTrue(player.hasPermAdminManage, "You must have admin rights to use that command")
+ return getWorld(player.world)
+ ?: throw CommandException("You must be in a parcel world to use that command")
+}
+
+fun ParcelProvider.getParcelRequired(player: Player, privilege: Privilege? = null): Parcel {
+ val parcel = getWorldRequired(player, admin = privilege == ADMIN).getParcelAt(player)
+ ?: throw CommandException("You must be in a parcel to use that command")
+
+ if (!player.hasPermAdminManage) {
+ @Suppress("NON_EXHAUSTIVE_WHEN")
+ when (privilege) {
+ OWNER ->
+ Validate.isTrue(parcel.isOwner(player.uuid), "You must own this parcel to use that command")
+ CAN_MANAGE ->
+ Validate.isTrue(parcel.canManage(player), "You must have management privileges on this parcel to use that command")
+ }
+ }
+
+ return parcel
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt
index 23087dc..fbe4333 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt
@@ -1,56 +1,56 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.*
-import io.dico.dicore.command.parameter.type.ParameterTypes
-import io.dico.parcels2.Interactables
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.Privilege
-import org.bukkit.command.CommandSender
-import org.bukkit.entity.Player
-
-class ParcelOptionsInteractCommand(val plugin: ParcelsPlugin) : Command() {
-
- init {
- setShortDescription("View and/or change the setting")
- setDescription(shortDescription)
- addContextFilter(IContextFilter.PLAYER_ONLY)
- addContextFilter(IContextFilter.INHERIT_PERMISSIONS)
- addParameter("allowed", "new setting", ParameterTypes.BOOLEAN)
- requiredParameters(0)
- }
-
- override fun execute(sender: CommandSender, context: ExecutionContext): String? {
- if (!plugin.storage.isConnected) err("Parcels cannot have their options changed right now because of a database error")
-
- val interactableClass = Interactables[context.address.mainKey]
- val allowed: Boolean? = context.get("allowed")
-
- val parcel = plugin.parcelProvider.getParcelRequired(sender as Player,
- if (allowed == null) Privilege.DEFAULT else Privilege.CAN_MANAGE)
-
- if (allowed == null) {
- val setting = parcel.interactableConfig.isInteractable(interactableClass)
- val default = setting == interactableClass.interactableByDefault
-
- val canColor = context.address.chatHandler.getChatFormatForType(EMessageType.BAD_NEWS)
- val cannotColor = context.address.chatHandler.getChatFormatForType(EMessageType.GOOD_NEWS)
- val resetColor = context.address.chatHandler.getChatFormatForType(EMessageType.RESULT)
-
- val settingString = (if (setting) "${canColor}can" else "${cannotColor}cannot") + resetColor
- val defaultString = if (default) " (default)" else ""
-
- return "Players $settingString interact with ${interactableClass.name} on this parcel$defaultString"
- }
-
- val change = parcel.interactableConfig.setInteractable(interactableClass, allowed)
-
- val interactableClassName = interactableClass.name
- return when {
- allowed && change -> "Other players can now interact with $interactableClassName"
- allowed && !change -> err("Other players could already interact with $interactableClassName")
- change -> "Other players can not interact with $interactableClassName anymore"
- else -> err("Other players were not allowed to interact with $interactableClassName")
- }
- }
-
-}
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.*
+import io.dico.dicore.command.parameter.type.ParameterTypes
+import io.dico.parcels2.Interactables
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.Privilege
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+
+class ParcelOptionsInteractCommand(val plugin: ParcelsPlugin) : Command() {
+
+ init {
+ setShortDescription("View and/or change the setting")
+ setDescription(shortDescription)
+ addContextFilter(IContextFilter.PLAYER_ONLY)
+ addContextFilter(IContextFilter.INHERIT_PERMISSIONS)
+ addParameter("allowed", "new setting", ParameterTypes.BOOLEAN)
+ requiredParameters(0)
+ }
+
+ override fun execute(sender: CommandSender, context: ExecutionContext): String? {
+ if (!plugin.storage.isConnected) err("Parcels cannot have their options changed right now because of a database error")
+
+ val interactableClass = Interactables[context.address.mainKey]
+ val allowed: Boolean? = context.get("allowed")
+
+ val parcel = plugin.parcelProvider.getParcelRequired(sender as Player,
+ if (allowed == null) Privilege.DEFAULT else Privilege.CAN_MANAGE)
+
+ if (allowed == null) {
+ val setting = parcel.interactableConfig.isInteractable(interactableClass)
+ val default = setting == interactableClass.interactableByDefault
+
+ val canColor = context.address.chatHandler.getChatFormatForType(EMessageType.BAD_NEWS)
+ val cannotColor = context.address.chatHandler.getChatFormatForType(EMessageType.GOOD_NEWS)
+ val resetColor = context.address.chatHandler.getChatFormatForType(EMessageType.RESULT)
+
+ val settingString = (if (setting) "${canColor}can" else "${cannotColor}cannot") + resetColor
+ val defaultString = if (default) " (default)" else ""
+
+ return "Players $settingString interact with ${interactableClass.name} on this parcel$defaultString"
+ }
+
+ val change = parcel.interactableConfig.setInteractable(interactableClass, allowed)
+
+ val interactableClassName = interactableClass.name
+ return when {
+ allowed && change -> "Other players can now interact with $interactableClassName"
+ allowed && !change -> err("Other players could already interact with $interactableClassName")
+ change -> "Other players can not interact with $interactableClassName anymore"
+ else -> err("Other players were not allowed to interact with $interactableClassName")
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
index c7083a1..1b20f72 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
@@ -6,7 +6,6 @@ import io.dico.dicore.command.parameter.Parameter
import io.dico.dicore.command.parameter.type.ParameterConfig
import io.dico.dicore.command.parameter.type.ParameterType
import io.dico.parcels2.*
-import io.dico.parcels2.command.ProfileKind.Companion.ANY
import io.dico.parcels2.command.ProfileKind.Companion.FAKE
import io.dico.parcels2.command.ProfileKind.Companion.REAL
import org.bukkit.Location
@@ -53,6 +52,7 @@ annotation class ProfileKind(val kind: Int) {
const val REAL = 1
const val FAKE = 2
const val ANY = REAL or FAKE
+ const val ALLOW_INVALID = 4
override fun toParameterInfo(annotation: ProfileKind): Int {
return annotation.kind
@@ -62,13 +62,20 @@ annotation class ProfileKind(val kind: Int) {
class ProfileParameterType : ParameterType<PlayerProfile, Int>(PlayerProfile::class.java, ProfileKind) {
- override fun parse(parameter: Parameter<PlayerProfile, Int>, sender: CommandSender, buffer: ArgumentBuffer): PlayerProfile {
+ override fun parse(parameter: Parameter<PlayerProfile, Int>, sender: CommandSender, buffer: ArgumentBuffer): PlayerProfile? {
val info = parameter.paramInfo ?: REAL
val allowReal = (info and REAL) != 0
val allowFake = (info and FAKE) != 0
val input = buffer.next()!!
- return PlayerProfile.byName(input, allowReal, allowFake)
+
+ val profile = PlayerProfile.byName(input, allowReal, allowFake)
+
+ if (profile == null && (info and ProfileKind.ALLOW_INVALID) == 0) {
+ invalidInput(parameter, "\'$input\' is not a valid player name")
+ }
+
+ return profile
}
override fun complete(
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
index 8891912..9ba3c25 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
@@ -25,7 +25,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
abstract suspend fun getParcelSuspend(storage: Storage): Parcel?
- class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) {
+ class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) :
+ ParcelTarget(world, parsedKind, isDefault) {
override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel()
fun getParcel() = id?.let { world.getParcelById(it) }
val isPath: Boolean get() = id == null
@@ -78,7 +79,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
const val DEFAULT_KIND = REAL
- const val PREFER_OWNED_FOR_DEFAULT = 8 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default
+ const val PREFER_OWNED_FOR_DEFAULT =
+ 8 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default
// instead of parcel that the player is in
override fun toParameterInfo(annotation: TargetKind): Int {
@@ -90,7 +92,11 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) :
ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, TargetKind) {
- override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget {
+ override fun parse(
+ parameter: Parameter<ParcelTarget, Int>,
+ sender: CommandSender,
+ buffer: ArgumentBuffer
+ ): ParcelTarget {
var input = buffer.next()!!
val worldString = input.substringBefore("/", missingDelimiterValue = "")
input = input.substringAfter("/")
@@ -100,18 +106,26 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
parcelProvider.getWorld(player.world)
?: invalidInput(parameter, "You cannot omit the world if you're not in a parcel world")
} else {
- parcelProvider.getWorld(worldString) ?: invalidInput(parameter, "$worldString is not a parcel world")
+ parcelProvider.getWorld(worldString)
+ ?: invalidInput(parameter, "$worldString is not a parcel world")
}
val kind = parameter.paramInfo ?: DEFAULT_KIND
if (input.contains(',')) {
- if (kind and ID == 0) invalidInput(parameter, "You must specify a parcel by OWNER, that is, an owner and index")
+ if (kind and ID == 0) invalidInput(parameter,
+ "You must specify a parcel by OWNER, that is, an owner and index")
return ByID(world, getId(parameter, input), kind, false)
}
- if (kind and OWNER == 0) invalidInput(parameter, "You must specify a parcel by ID, that is, the x and z component separated by a comma")
+ if (kind and OWNER == 0) invalidInput(parameter,
+ "You must specify a parcel by ID, that is, the x and z component separated by a comma")
val (owner, index) = getHomeIndex(parameter, kind, sender, input)
- return ByOwner(world, owner, index, kind, false, onResolveFailure = { invalidInput(parameter, "The player $input does not exist") })
+ return ByOwner(world,
+ owner,
+ index,
+ kind,
+ false,
+ onResolveFailure = { invalidInput(parameter, "The player $input does not exist") })
}
private fun getId(parameter: Parameter<*, *>, input: String): Vec2i {
@@ -124,7 +138,12 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
return Vec2i(x, z)
}
- private fun getHomeIndex(parameter: Parameter<*, *>, kind: Int, sender: CommandSender, input: String): Pair<PlayerProfile, Int> {
+ private fun getHomeIndex(
+ parameter: Parameter<*, *>,
+ kind: Int,
+ sender: CommandSender,
+ input: String
+ ): Pair<PlayerProfile, Int> {
val splitIdx = input.indexOf(':')
val ownerString: String
val index: Int?
@@ -155,10 +174,11 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer")
}
- val owner = if (ownerString.isEmpty())
+ val owner = (if (ownerString.isEmpty())
PlayerProfile(requirePlayer(sender, parameter, "the player"))
else
- PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0)
+ PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0))
+ ?: invalidInput(parameter, "\'$ownerString\' is not a valid player name")
return owner to (index ?: 0)
}
@@ -168,7 +188,11 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
return sender
}
- override fun getDefaultValue(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget? {
+ override fun getDefaultValue(
+ parameter: Parameter<ParcelTarget, Int>,
+ sender: CommandSender,
+ buffer: ArgumentBuffer
+ ): ParcelTarget? {
val kind = parameter.paramInfo ?: DEFAULT_KIND
val useLocation = when {
kind and REAL == REAL -> kind and PREFER_OWNED_FOR_DEFAULT == 0
@@ -178,7 +202,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
}
val player = requirePlayer(sender, parameter, "the parcel")
- val world = parcelProvider.getWorld(player.world) ?: invalidInput(parameter, "You must be in a parcel world to omit the parcel")
+ val world = parcelProvider.getWorld(player.world) ?: invalidInput(parameter,
+ "You must be in a parcel world to omit the parcel")
if (useLocation) {
val id = player.location.let { world.getParcelIdAt(it.x.floor(), it.z.floor())?.pos }
return ByID(world, id, kind, true)
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt
index b616f77..52f1104 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt
@@ -1,24 +1,24 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.Formatting
-import io.dico.dicore.command.EMessageType
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.chat.AbstractChatHandler
-import io.dico.parcels2.util.ext.plus
-
-class ParcelsChatHandler : AbstractChatHandler() {
-
- override fun getMessagePrefixForType(type: EMessageType?): String {
- return Formatting.RED + "[Parcels] "
- }
-
- override fun createMessage(context: ExecutionContext, type: EMessageType, message: String?): String? {
- if (message.isNullOrEmpty()) return null
- var result = getChatFormatForType(type) + message
- if (context.address.mainKey != "info") {
- result = getMessagePrefixForType(type) + result
- }
- return result
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.Formatting
+import io.dico.dicore.command.EMessageType
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.chat.AbstractChatHandler
+import io.dico.parcels2.util.ext.plus
+
+class ParcelsChatHandler : AbstractChatHandler() {
+
+ override fun getMessagePrefixForType(type: EMessageType?): String {
+ return Formatting.RED + "[Parcels] "
+ }
+
+ override fun createMessage(context: ExecutionContext, type: EMessageType, message: String?): String? {
+ if (message.isNullOrEmpty()) return null
+ var result = getChatFormatForType(type) + message
+ if (context.address.mainKey != "info") {
+ result = getMessagePrefixForType(type) + result
+ }
+ return result
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
index 1193af3..f3cd8d7 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
@@ -45,7 +45,7 @@ class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer {
}
}
- override fun nextEmptyParcel(): Parcel? {
+ override suspend fun nextEmptyParcel(): Parcel? {
return walkInCircle().find { it.owner == null }
}
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
index 9e43c05..73b6b4d 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
@@ -11,6 +11,7 @@ import org.bukkit.block.BlockFace
import org.bukkit.block.Skull
import org.bukkit.block.data.type.Slab
import org.bukkit.block.data.type.WallSign
+import org.bukkit.entity.Player
import java.util.Random
private val airType = Bukkit.createBlockData(Material.AIR)
@@ -285,17 +286,29 @@ class DefaultParcelGenerator(
val floorType = o.floorType
val fillType = o.fillType
- for ((index, vec) in blocks.withIndex()) {
- markSuspensionPoint()
- val y = vec.y
- val blockType = when {
- y > floorHeight -> airType
- y == floorHeight -> floorType
- else -> fillType
+ delegateWork(0.95) {
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ val y = vec.y
+ val blockType = when {
+ y > floorHeight -> airType
+ y == floorHeight -> floorType
+ else -> fillType
+ }
+ world[vec].blockData = blockType
+ setProgress((index + 1) / blockCount)
+ }
+ }
+
+ delegateWork {
+ val entities = getEntities(region)
+ for ((index, entity) in entities.withIndex()) {
+ if (entity is Player) continue
+ entity.remove()
+ setProgress((index + 1) / entities.size.toDouble())
}
- world[vec].blockData = blockType
- setProgress((index + 1) / blockCount)
}
+
}
override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> {
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt
index 769cee6..670dd94 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt
@@ -1,28 +1,28 @@
-@file:Suppress("UNCHECKED_CAST")
-
-package io.dico.parcels2.defaultimpl
-
-import io.dico.parcels2.*
-import io.dico.parcels2.util.ext.alsoIfTrue
-import java.util.Collections
-
-class GlobalPrivilegesManagerImpl(val plugin: ParcelsPlugin) : GlobalPrivilegesManager {
- private val map = mutableMapOf<PlayerProfile, GlobalPrivileges>()
-
- override fun get(owner: PlayerProfile.Real): GlobalPrivileges {
- return map[owner] ?: GlobalPrivilegesImpl(owner).also { map[owner] = it }
- }
-
- private inner class GlobalPrivilegesImpl(override val keyOfOwner: PlayerProfile.Real) : PrivilegesHolder(), GlobalPrivileges {
- override var privilegeOfStar: Privilege
- get() = super<GlobalPrivileges>.privilegeOfStar
- set(value) = run { super<GlobalPrivileges>.privilegeOfStar = value }
-
- override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
- return super.setRawStoredPrivilege(key, privilege).alsoIfTrue {
- plugin.storage.setGlobalPrivilege(keyOfOwner, key, privilege)
- }
- }
- }
-
+@file:Suppress("UNCHECKED_CAST")
+
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.*
+import io.dico.parcels2.util.ext.alsoIfTrue
+import java.util.Collections
+
+class GlobalPrivilegesManagerImpl(val plugin: ParcelsPlugin) : GlobalPrivilegesManager {
+ private val map = mutableMapOf<PlayerProfile, GlobalPrivileges>()
+
+ override fun get(owner: PlayerProfile.Real): GlobalPrivileges {
+ return map[owner] ?: GlobalPrivilegesImpl(owner).also { map[owner] = it }
+ }
+
+ private inner class GlobalPrivilegesImpl(override val keyOfOwner: PlayerProfile.Real) : PrivilegesHolder(), GlobalPrivileges {
+ override var privilegeOfStar: Privilege
+ get() = super<GlobalPrivileges>.privilegeOfStar
+ set(value) = run { super<GlobalPrivileges>.privilegeOfStar = value }
+
+ override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ return super.setRawStoredPrivilege(key, privilege).alsoIfTrue {
+ plugin.storage.setGlobalPrivilege(keyOfOwner, key, privilege)
+ }
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt
index a3704c7..99df82a 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt
@@ -1,88 +1,88 @@
-package io.dico.parcels2.defaultimpl
-
-import io.dico.dicore.Formatting
-import io.dico.parcels2.Privilege
-import io.dico.parcels2.PrivilegeKey
-import io.dico.parcels2.RawPrivileges
-import io.dico.parcels2.filterProfilesWithPrivilegeTo
-
-object InfoBuilder {
- val infoStringColor1 = Formatting.GREEN
- val infoStringColor2 = Formatting.AQUA
-
- inline fun StringBuilder.appendField(field: StringBuilder.() -> Unit, value: StringBuilder.() -> Unit) {
- append(infoStringColor1)
- field()
- append(": ")
- append(infoStringColor2)
- value()
- append(' ')
- }
-
- inline fun StringBuilder.appendField(name: String, value: StringBuilder.() -> Unit) {
- appendField({ append(name) }, value)
- }
-
- inline fun StringBuilder.appendFieldWithCount(name: String, count: Int, value: StringBuilder.() -> Unit) {
- appendField({
- append(name)
- append('(')
- append(infoStringColor2)
- append(count)
- append(infoStringColor1)
- append(')')
- }, value)
- }
-
- fun StringBuilder.appendProfilesWithPrivilege(fieldName: String, local: RawPrivileges, global: RawPrivileges?, privilege: Privilege) {
- val map = linkedMapOf<PrivilegeKey, Privilege>()
- local.filterProfilesWithPrivilegeTo(map, privilege)
- val localCount = map.size
- global?.filterProfilesWithPrivilegeTo(map, privilege)
- appendPrivilegeProfiles(fieldName, map, localCount)
- }
-
- fun StringBuilder.appendPrivilegeProfiles(fieldName: String, map: LinkedHashMap<PrivilegeKey, Privilege>, localCount: Int) {
- if (map.isEmpty()) return
-
- appendFieldWithCount(fieldName, map.size) {
- // first [localCount] entries are local
- val separator = "$infoStringColor1, $infoStringColor2"
- val iterator = map.iterator()
-
- if (localCount != 0) {
- appendPrivilegeEntry(false, iterator.next().toPair())
- repeat(localCount - 1) {
- append(separator)
- appendPrivilegeEntry(false, iterator.next().toPair())
- }
-
- } else if (iterator.hasNext()) {
- // ensure there is never a leading or trailing separator
- appendPrivilegeEntry(true, iterator.next().toPair())
- }
-
- iterator.forEach { next ->
- append(separator)
- appendPrivilegeEntry(true, next.toPair())
- }
- }
- }
-
- fun StringBuilder.appendPrivilegeEntry(global: Boolean, pair: Pair<PrivilegeKey, Privilege>) {
- val (key, priv) = pair
-
- append(key.notNullName)
-
- // suffix. Maybe T should be M for mod or something. T means they have CAN_MANAGE privilege.
- append(
- when {
- global && priv == Privilege.CAN_MANAGE -> " (G) (T)"
- global -> " (G)"
- priv == Privilege.CAN_MANAGE -> " (T)"
- else -> ""
- }
- )
- }
-
+package io.dico.parcels2.defaultimpl
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.Privilege
+import io.dico.parcels2.PrivilegeKey
+import io.dico.parcels2.RawPrivileges
+import io.dico.parcels2.filterProfilesWithPrivilegeTo
+
+object InfoBuilder {
+ val infoStringColor1 = Formatting.GREEN
+ val infoStringColor2 = Formatting.AQUA
+
+ inline fun StringBuilder.appendField(field: StringBuilder.() -> Unit, value: StringBuilder.() -> Unit) {
+ append(infoStringColor1)
+ field()
+ append(": ")
+ append(infoStringColor2)
+ value()
+ append(' ')
+ }
+
+ inline fun StringBuilder.appendField(name: String, value: StringBuilder.() -> Unit) {
+ appendField({ append(name) }, value)
+ }
+
+ inline fun StringBuilder.appendFieldWithCount(name: String, count: Int, value: StringBuilder.() -> Unit) {
+ appendField({
+ append(name)
+ append('(')
+ append(infoStringColor2)
+ append(count)
+ append(infoStringColor1)
+ append(')')
+ }, value)
+ }
+
+ fun StringBuilder.appendProfilesWithPrivilege(fieldName: String, local: RawPrivileges, global: RawPrivileges?, privilege: Privilege) {
+ val map = linkedMapOf<PrivilegeKey, Privilege>()
+ local.filterProfilesWithPrivilegeTo(map, privilege)
+ val localCount = map.size
+ global?.filterProfilesWithPrivilegeTo(map, privilege)
+ appendPrivilegeProfiles(fieldName, map, localCount)
+ }
+
+ fun StringBuilder.appendPrivilegeProfiles(fieldName: String, map: LinkedHashMap<PrivilegeKey, Privilege>, localCount: Int) {
+ if (map.isEmpty()) return
+
+ appendFieldWithCount(fieldName, map.size) {
+ // first [localCount] entries are local
+ val separator = "$infoStringColor1, $infoStringColor2"
+ val iterator = map.iterator()
+
+ if (localCount != 0) {
+ appendPrivilegeEntry(false, iterator.next().toPair())
+ repeat(localCount - 1) {
+ append(separator)
+ appendPrivilegeEntry(false, iterator.next().toPair())
+ }
+
+ } else if (iterator.hasNext()) {
+ // ensure there is never a leading or trailing separator
+ appendPrivilegeEntry(true, iterator.next().toPair())
+ }
+
+ iterator.forEach { next ->
+ append(separator)
+ appendPrivilegeEntry(true, next.toPair())
+ }
+ }
+ }
+
+ fun StringBuilder.appendPrivilegeEntry(global: Boolean, pair: Pair<PrivilegeKey, Privilege>) {
+ val (key, priv) = pair
+
+ append(key.notNullName)
+
+ // suffix. Maybe T should be M for mod or something. T means they have CAN_MANAGE privilege.
+ append(
+ when {
+ global && priv == Privilege.CAN_MANAGE -> " (G) (T)"
+ global -> " (G)"
+ priv == Privilege.CAN_MANAGE -> " (T)"
+ else -> ""
+ }
+ )
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
index 48a7fee..7748fc7 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
@@ -2,12 +2,18 @@ package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
import io.dico.parcels2.blockvisitor.Schematic
+import io.dico.parcels2.util.math.Region
+import io.dico.parcels2.util.math.Vec3d
+import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.schedule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.bukkit.Bukkit
+import org.bukkit.World
import org.bukkit.WorldCreator
+import org.bukkit.entity.Entity
+import org.bukkit.util.Vector
import org.joda.time.DateTime
class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
@@ -45,7 +51,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
private fun loadWorlds0() {
if (Bukkit.getWorlds().isEmpty()) {
- plugin.schedule(::loadWorlds0)
+ plugin.schedule { loadWorlds0() }
plugin.logger.warning("Scheduling to load worlds in the next tick because no bukkit worlds are loaded yet")
return
}
@@ -64,7 +70,8 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
WorldCreator(worldName).generator(generator).createWorld()
}
- parcelWorld = ParcelWorldImpl(plugin, bukkitWorld, generator, worldOptions.runtime,::DefaultParcelContainer)
+ parcelWorld =
+ ParcelWorldImpl(plugin, bukkitWorld, generator, worldOptions.runtime, ::DefaultParcelContainer)
if (!worldExists) {
val time = DateTime.now()
@@ -73,7 +80,8 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
newlyCreatedWorlds.add(parcelWorld)
} else {
GlobalScope.launch(context = Dispatchers.Unconfined) {
- parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: DateTime.now()
+ parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?:
+ DateTime.now()
}
}
@@ -84,7 +92,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
}
private fun loadStoredData(newlyCreatedWorlds: Collection<ParcelWorld> = emptyList()) {
- plugin.launch(Dispatchers.Default) {
+ plugin.launch {
val migration = plugin.options.migration
if (migration.enabled) {
migration.instance?.newInstance()?.apply {
@@ -155,10 +163,32 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
}
override fun swapParcels(parcelId1: ParcelId, parcelId2: ParcelId): Job? {
- val blockManager1 = getWorldById(parcelId1.worldId)?.blockManager ?: return null
- val blockManager2 = getWorldById(parcelId2.worldId)?.blockManager ?: return null
+ val world1 = getWorldById(parcelId1.worldId) ?: return null
+ val world2 = getWorldById(parcelId2.worldId) ?: return null
+ val blockManager1 = world1.blockManager
+ val blockManager2 = world2.blockManager
+
+ class CopyTarget(val world: World, val region: Region)
+ class CopySource(val origin: Vec3i, val schematic: Schematic, val entities: Collection<Entity>)
+
+ suspend fun JobScope.copy(source: CopySource, target: CopyTarget) {
+ with(source.schematic) { paste(target.world, target.region.origin) }
+
+ for (entity in source.entities) {
+ entity.velocity = Vector(0, 0, 0)
+ val location = entity.location
+ location.world = target.world
+ val coords = target.region.origin + (Vec3d(entity.location) - source.origin)
+ coords.copyInto(location)
+ entity.teleport(location)
+ }
+ }
return trySubmitBlockVisitor(Permit(), parcelId1, parcelId2) {
+ val temporaryParcel = world1.nextEmptyParcel()
+ ?: world2.nextEmptyParcel()
+ ?: return@trySubmitBlockVisitor
+
var region1 = blockManager1.getRegion(parcelId1)
var region2 = blockManager2.getRegion(parcelId2)
@@ -168,10 +198,41 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
region2 = region2.withSize(size)
}
- val schematicOf1 = delegateWork(0.25) { Schematic().apply { load(blockManager1.world, region1) } }
- val schematicOf2 = delegateWork(0.25) { Schematic().apply { load(blockManager2.world, region2) } }
- delegateWork(0.25) { with(schematicOf1) { paste(blockManager2.world, region2.origin) } }
- delegateWork(0.25) { with(schematicOf2) { paste(blockManager1.world, region1.origin) } }
+ // Teleporting entities safely requires a different approach:
+ // * Copy schematic1 into temporary location
+ // * Teleport entities1 into temporary location
+ // * Copy schematic2 into parcel1
+ // * Teleport entities2 into parcel1
+ // * Copy schematic1 into parcel2
+ // * Teleport entities1 into parcel2
+ // * Clear temporary location
+
+ lateinit var source1: CopySource
+ lateinit var source2: CopySource
+
+ delegateWork(0.30) {
+ val schematicOf1 = delegateWork(0.50) { Schematic().apply { load(blockManager1.world, region1) } }
+ val schematicOf2 = delegateWork(0.50) { Schematic().apply { load(blockManager2.world, region2) } }
+
+ source1 = CopySource(region1.origin, schematicOf1, blockManager1.getEntities(region1))
+ source2 = CopySource(region2.origin, schematicOf2, blockManager2.getEntities(region2))
+ }
+
+ val target1 = CopyTarget(blockManager1.world, region1)
+ val target2 = CopyTarget(blockManager2.world, region2)
+ val targetTemp = CopyTarget(
+ temporaryParcel.world.world,
+ temporaryParcel.world.blockManager.getRegion(temporaryParcel.id)
+ )
+
+ delegateWork {
+ delegateWork(1.0 / 3.0) { copy(source1, targetTemp) }
+ delegateWork(1.0 / 3.0) { copy(source2, target1) }
+ delegateWork(1.0 / 3.0) { copy(source1, target2) }
+ }
+
+ // Separate job. Whatever
+ temporaryParcel.world.blockManager.clearParcel(temporaryParcel.id)
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
index 531a25f..cb322d4 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
@@ -69,7 +69,7 @@ class ParcelWorldImpl(
override fun getParcelById(id: ParcelId): Parcel? = container.getParcelById(id)
- override fun nextEmptyParcel(): Parcel? = container.nextEmptyParcel()
+ override suspend fun nextEmptyParcel(): Parcel? = container.nextEmptyParcel()
override fun toString() = parcelWorldIdToString()
}
diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt
index 198e0e7..78b8b03 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt
@@ -1,61 +1,61 @@
-package io.dico.parcels2.listener
-
-import io.dico.parcels2.Parcel
-import io.dico.parcels2.ParcelProvider
-import io.dico.parcels2.util.ext.editLoop
-import org.bukkit.entity.Entity
-
-class ParcelEntityTracker(val parcelProvider: ParcelProvider) {
- val map = mutableMapOf<Entity, Parcel?>()
-
- fun untrack(entity: Entity) {
- map.remove(entity)
- }
-
- fun track(entity: Entity, parcel: Parcel?) {
- map[entity] = parcel
- }
-
- /*
- * Tracks entities. If the entity is dead, they are removed from the collection.
- * If the entity is found to have left the parcel it was created in, it will be removed from the world and from the list.
- * If it is still in the parcel it was created in, and it is on the ground, it is removed from the list.
- *
- * Start after 5 seconds, run every 0.25 seconds
- */
- fun tick() {
- map.editLoop { entity, parcel ->
- if (entity.isDead) {
- remove(); return@editLoop
- }
-
- if (parcel != null && parcel.hasBlockVisitors) {
- remove()
-
- val newParcel = parcelProvider.getParcelAt(entity.location)
- if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
- entity.remove()
- }
-
- return@editLoop
- }
-
- val newParcel = parcelProvider.getParcelAt(entity.location)
- if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
- remove()
- entity.remove()
- }
- }
- }
-
- fun swapParcels(parcel1: Parcel, parcel2: Parcel) {
- map.editLoop { ->
- if (value === parcel1) {
- value = parcel2
- } else if (value === parcel2) {
- value = parcel1
- }
- }
- }
-
+package io.dico.parcels2.listener
+
+import io.dico.parcels2.Parcel
+import io.dico.parcels2.ParcelProvider
+import io.dico.parcels2.util.ext.editLoop
+import org.bukkit.entity.Entity
+
+class ParcelEntityTracker(val parcelProvider: ParcelProvider) {
+ val map = mutableMapOf<Entity, Parcel?>()
+
+ fun untrack(entity: Entity) {
+ map.remove(entity)
+ }
+
+ fun track(entity: Entity, parcel: Parcel?) {
+ map[entity] = parcel
+ }
+
+ /*
+ * Tracks entities. If the entity is dead, they are removed from the collection.
+ * If the entity is found to have left the parcel it was created in, it will be removed from the world and from the list.
+ * If it is still in the parcel it was created in, and it is on the ground, it is removed from the list.
+ *
+ * Start after 5 seconds, run every 0.25 seconds
+ */
+ fun tick() {
+ map.editLoop { entity, parcel ->
+ if (entity.isDead) {
+ remove(); return@editLoop
+ }
+
+ if (parcel != null && parcel.hasBlockVisitors) {
+ remove()
+
+ val newParcel = parcelProvider.getParcelAt(entity.location)
+ if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
+ entity.remove()
+ }
+
+ return@editLoop
+ }
+
+ val newParcel = parcelProvider.getParcelAt(entity.location)
+ if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
+ remove()
+ entity.remove()
+ }
+ }
+ }
+
+ fun swapParcels(parcel1: Parcel, parcel2: Parcel) {
+ map.editLoop { ->
+ if (value === parcel1) {
+ value = parcel2
+ } else if (value === parcel2) {
+ value = parcel1
+ }
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
index 9c9bdc2..e87dd68 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
@@ -10,6 +10,7 @@ import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.ext.*
import io.dico.parcels2.util.math.*
import org.bukkit.Location
+import org.bukkit.Material
import org.bukkit.Material.*
import org.bukkit.World
import org.bukkit.block.Biome
@@ -209,7 +210,7 @@ class ParcelListeners(
* Prevents player from using beds in HELL or SKY biomes if explosions are disabled.
*/
@Suppress("NON_EXHAUSTIVE_WHEN")
- @field:ListenerMarker(priority = NORMAL)
+ @field:ListenerMarker(priority = NORMAL, ignoreCancelled = false)
val onPlayerInteractEvent = RegistratorListener<PlayerInteractEvent> l@{ event ->
val user = event.player
val world = parcelProvider.getWorld(user.world) ?: return@l
@@ -223,6 +224,7 @@ class ParcelListeners(
when (event.action) {
Action.RIGHT_CLICK_BLOCK -> run {
+ if (event.isCancelled) return@l
val type = clickedBlock.type
val interactableClass = Interactables[type]
@@ -259,9 +261,15 @@ class ParcelListeners(
}
Action.RIGHT_CLICK_AIR -> onPlayerRightClick(event, world, parcel)
- Action.PHYSICAL -> if (!canBuildOnArea(user, parcel) && !(parcel != null && parcel.interactableConfig("pressure_plates"))) {
- user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel")
- event.isCancelled = true; return@l
+ Action.PHYSICAL -> if (!event.isCancelled && !canBuildOnArea(user, parcel)) {
+ if (clickedBlock.type == Material.TURTLE_EGG) {
+ event.isCancelled = true; return@l
+ }
+
+ if (!(parcel != null && parcel.interactableConfig("pressure_plates"))) {
+ user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel")
+ event.isCancelled = true; return@l
+ }
}
}
}
@@ -437,13 +445,15 @@ class ParcelListeners(
@field:ListenerMarker(priority = NORMAL)
val onEntitySpawnEvent = RegistratorListener<EntitySpawnEvent> l@{ event ->
val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- if (event.entity is Creature && world.options.blockMobSpawning) {
+ if (event.entity is Mob && world.options.blockMobSpawning) {
event.isCancelled = true
} else if (world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
event.isCancelled = true
}
}
+
+
/*
* Prevents minecarts/boats from moving outside a plot
*/
@@ -471,7 +481,7 @@ class ParcelListeners(
@field:ListenerMarker(priority = NORMAL)
val onEntityDamageByEntityEvent = RegistratorListener<EntityDamageByEntityEvent> l@{ event ->
val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- if (world.options.disableExplosions && event.damager is ExplosiveMinecart || event.damager is Creeper) {
+ if (world.options.disableExplosions && (event.damager is ExplosiveMinecart || event.damager is Creeper)) {
event.isCancelled = true; return@l
}
@@ -538,6 +548,14 @@ class ParcelListeners(
event.blocks.removeIf { world.getParcelAt(it.block) !== area }
}
+ @field:ListenerMarker(priority = NORMAL)
+ val onBlockGrowEvent = RegistratorListener<BlockGrowEvent> l@{ event ->
+ val (world, area) = getWorldAndArea(event.block) ?: return@l
+ if (area == null) {
+ event.isCancelled = true
+ }
+ }
+
/*
* Prevents dispensers/droppers from dispensing out of parcels
*/
diff --git a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
index 4d35a53..5bab29a 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
@@ -1,13 +1,13 @@
package io.dico.parcels2.listener
import com.sk89q.worldedit.EditSession.Stage.BEFORE_REORDER
-import com.sk89q.worldedit.Vector
-import com.sk89q.worldedit.Vector2D
import com.sk89q.worldedit.WorldEdit
import com.sk89q.worldedit.bukkit.WorldEditPlugin
import com.sk89q.worldedit.event.extent.EditSessionEvent
import com.sk89q.worldedit.extent.AbstractDelegateExtent
import com.sk89q.worldedit.extent.Extent
+import com.sk89q.worldedit.math.BlockVector2
+import com.sk89q.worldedit.math.BlockVector3
import com.sk89q.worldedit.util.eventbus.EventHandler.Priority.VERY_EARLY
import com.sk89q.worldedit.util.eventbus.Subscribe
import com.sk89q.worldedit.world.biome.BaseBiome
@@ -57,14 +57,13 @@ class WorldEditListener(val parcels: ParcelsPlugin, val worldEdit: WorldEdit) {
return false
}
- override fun setBlock(location: Vector, block: BlockStateHolder<*>): Boolean {
- return canBuild(location.blockX, location.blockZ) && super.setBlock(location, block)
- }
-
- override fun setBiome(coord: Vector2D, biome: BaseBiome): Boolean {
+ override fun setBiome(coord: BlockVector2, biome: BaseBiome): Boolean {
return canBuild(coord.blockX, coord.blockZ) && super.setBiome(coord, biome)
}
+ override fun <T : BlockStateHolder<T>> setBlock(location: BlockVector3, block: T): Boolean {
+ return canBuild(location.blockX, location.blockZ) && super.setBlock(location, block)
+ }
}
companion object {
diff --git a/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt b/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt
index d0626dc..a6a57e5 100644
--- a/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt
@@ -1,36 +1,36 @@
-package io.dico.parcels2.options
-
-import io.dico.parcels2.ParcelGenerator
-import io.dico.parcels2.defaultimpl.DefaultParcelGenerator
-import org.bukkit.Bukkit
-import org.bukkit.Material
-import org.bukkit.block.Biome
-import org.bukkit.block.data.BlockData
-import kotlin.reflect.KClass
-
-object GeneratorOptionsFactories : PolymorphicOptionsFactories<ParcelGenerator>("name", GeneratorOptions::class, DefaultGeneratorOptionsFactory())
-
-class GeneratorOptions (name: String = "default", options: Any = DefaultGeneratorOptions()) : PolymorphicOptions<ParcelGenerator>(name, options, GeneratorOptionsFactories) {
- fun newInstance(worldName: String) = factory.newInstance(key, options, worldName)
-}
-
-private class DefaultGeneratorOptionsFactory : PolymorphicOptionsFactory<ParcelGenerator> {
- override val supportedKeys: List<String> = listOf("default")
- override val optionsClass: KClass<out Any> get() = DefaultGeneratorOptions::class
-
- override fun newInstance(key: String, options: Any, vararg extra: Any?): ParcelGenerator {
- return DefaultParcelGenerator(extra.first() as String, options as DefaultGeneratorOptions)
- }
-}
-
-class DefaultGeneratorOptions(val defaultBiome: Biome = Biome.JUNGLE,
- val wallType: BlockData = Bukkit.createBlockData(Material.STONE_SLAB),
- val floorType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
- val fillType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
- val pathMainType: BlockData = Bukkit.createBlockData(Material.SANDSTONE),
- val pathAltType: BlockData = Bukkit.createBlockData(Material.REDSTONE_BLOCK),
- val parcelSize: Int = 101,
- val pathSize: Int = 9,
- val floorHeight: Int = 64,
- val offsetX: Int = 0,
+package io.dico.parcels2.options
+
+import io.dico.parcels2.ParcelGenerator
+import io.dico.parcels2.defaultimpl.DefaultParcelGenerator
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.block.Biome
+import org.bukkit.block.data.BlockData
+import kotlin.reflect.KClass
+
+object GeneratorOptionsFactories : PolymorphicOptionsFactories<ParcelGenerator>("name", GeneratorOptions::class, DefaultGeneratorOptionsFactory())
+
+class GeneratorOptions (name: String = "default", options: Any = DefaultGeneratorOptions()) : PolymorphicOptions<ParcelGenerator>(name, options, GeneratorOptionsFactories) {
+ fun newInstance(worldName: String) = factory.newInstance(key, options, worldName)
+}
+
+private class DefaultGeneratorOptionsFactory : PolymorphicOptionsFactory<ParcelGenerator> {
+ override val supportedKeys: List<String> = listOf("default")
+ override val optionsClass: KClass<out Any> get() = DefaultGeneratorOptions::class
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): ParcelGenerator {
+ return DefaultParcelGenerator(extra.first() as String, options as DefaultGeneratorOptions)
+ }
+}
+
+class DefaultGeneratorOptions(val defaultBiome: Biome = Biome.JUNGLE,
+ val wallType: BlockData = Bukkit.createBlockData(Material.STONE_SLAB),
+ val floorType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
+ val fillType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
+ val pathMainType: BlockData = Bukkit.createBlockData(Material.SANDSTONE),
+ val pathAltType: BlockData = Bukkit.createBlockData(Material.REDSTONE_BLOCK),
+ val parcelSize: Int = 101,
+ val pathSize: Int = 9,
+ val floorHeight: Int = 64,
+ val offsetX: Int = 0,
val offsetZ: Int = 0) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt b/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
index 5e36099..7dd752e 100644
--- a/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
@@ -1,22 +1,22 @@
-package io.dico.parcels2.options
-
-import io.dico.parcels2.storage.migration.Migration
-import io.dico.parcels2.storage.migration.plotme.PlotmeMigration
-import kotlin.reflect.KClass
-
-object MigrationOptionsFactories : PolymorphicOptionsFactories<Migration>("kind", MigrationOptions::class, PlotmeMigrationFactory())
-
-class MigrationOptions(kind: String = "plotme-0.17", options: Any = PlotmeMigrationOptions()) : SimplePolymorphicOptions<Migration>(kind, options, MigrationOptionsFactories)
-
-private class PlotmeMigrationFactory : PolymorphicOptionsFactory<Migration> {
- override val supportedKeys = listOf("plotme-0.17")
- override val optionsClass: KClass<out Any> get() = PlotmeMigrationOptions::class
-
- override fun newInstance(key: String, options: Any, vararg extra: Any?): Migration {
- return PlotmeMigration(options as PlotmeMigrationOptions)
- }
-}
-
-class PlotmeMigrationOptions(val worldsFromTo: Map<String, String> = mapOf("plotworld" to "parcels"),
- val storage: StorageOptions = StorageOptions(options = DataConnectionOptions(database = "plotme")),
+package io.dico.parcels2.options
+
+import io.dico.parcels2.storage.migration.Migration
+import io.dico.parcels2.storage.migration.plotme.PlotmeMigration
+import kotlin.reflect.KClass
+
+object MigrationOptionsFactories : PolymorphicOptionsFactories<Migration>("kind", MigrationOptions::class, PlotmeMigrationFactory())
+
+class MigrationOptions(kind: String = "plotme-0.17", options: Any = PlotmeMigrationOptions()) : SimplePolymorphicOptions<Migration>(kind, options, MigrationOptionsFactories)
+
+private class PlotmeMigrationFactory : PolymorphicOptionsFactory<Migration> {
+ override val supportedKeys = listOf("plotme-0.17")
+ override val optionsClass: KClass<out Any> get() = PlotmeMigrationOptions::class
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): Migration {
+ return PlotmeMigration(options as PlotmeMigrationOptions)
+ }
+}
+
+class PlotmeMigrationOptions(val worldsFromTo: Map<String, String> = mapOf("plotworld" to "parcels"),
+ val storage: StorageOptions = StorageOptions(options = DataConnectionOptions(database = "plotme")),
val tableNamesUppercase: Boolean = false) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/Options.kt b/src/main/kotlin/io/dico/parcels2/options/Options.kt
index 35d48ba..412c783 100644
--- a/src/main/kotlin/io/dico/parcels2/options/Options.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/Options.kt
@@ -1,58 +1,58 @@
-package io.dico.parcels2.options
-
-import io.dico.parcels2.TickJobtimeOptions
-import org.bukkit.GameMode
-import org.bukkit.Material
-import java.io.Reader
-import java.io.Writer
-import java.util.EnumSet
-
-class Options {
- var worlds: Map<String, WorldOptions> = hashMapOf()
- private set
- var storage: StorageOptions = StorageOptions()
- var tickJobtime: TickJobtimeOptions = TickJobtimeOptions(20, 1)
- var migration = MigrationOptionsHolder()
-
- fun addWorld(name: String,
- generatorOptions: GeneratorOptions? = null,
- worldOptions: RuntimeWorldOptions? = null) {
- val optionsHolder = WorldOptions(
- generatorOptions ?: GeneratorOptions(),
- worldOptions ?: RuntimeWorldOptions()
- )
-
- (worlds as MutableMap).put(name, optionsHolder)
- }
-
- fun writeTo(writer: Writer) = optionsMapper.writeValue(writer, this)
-
- fun mergeFrom(reader: Reader) = optionsMapper.readerForUpdating(this).readValue<Options>(reader)
-
- override fun toString(): String = optionsMapper.writeValueAsString(this)
-
-}
-
-class WorldOptions(val generator: GeneratorOptions,
- var runtime: RuntimeWorldOptions = RuntimeWorldOptions())
-
-class RuntimeWorldOptions(var gameMode: GameMode? = GameMode.CREATIVE,
- var dayTime: Boolean = true,
- var noWeather: Boolean = true,
- var preventWeatherBlockChanges: Boolean = true,
- var preventBlockSpread: Boolean = true, // TODO
- var dropEntityItems: Boolean = true,
- var doTileDrops: Boolean = false,
- var disableExplosions: Boolean = true,
- var blockPortalCreation: Boolean = true,
- var blockMobSpawning: Boolean = true,
- var blockedItems: Set<Material> = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL),
- var axisLimit: Int = 10)
-
-class DataFileOptions(val location: String = "/flatfile-storage/")
-
-class MigrationOptionsHolder {
- var enabled = false
- var disableWhenComplete = true
- var instance: MigrationOptions? = MigrationOptions()
+package io.dico.parcels2.options
+
+import io.dico.parcels2.TickJobtimeOptions
+import org.bukkit.GameMode
+import org.bukkit.Material
+import java.io.Reader
+import java.io.Writer
+import java.util.EnumSet
+
+class Options {
+ var worlds: Map<String, WorldOptions> = hashMapOf()
+ private set
+ var storage: StorageOptions = StorageOptions()
+ var tickJobtime: TickJobtimeOptions = TickJobtimeOptions(20, 1)
+ var migration = MigrationOptionsHolder()
+
+ fun addWorld(name: String,
+ generatorOptions: GeneratorOptions? = null,
+ worldOptions: RuntimeWorldOptions? = null) {
+ val optionsHolder = WorldOptions(
+ generatorOptions ?: GeneratorOptions(),
+ worldOptions ?: RuntimeWorldOptions()
+ )
+
+ (worlds as MutableMap).put(name, optionsHolder)
+ }
+
+ fun writeTo(writer: Writer) = optionsMapper.writeValue(writer, this)
+
+ fun mergeFrom(reader: Reader) = optionsMapper.readerForUpdating(this).readValue<Options>(reader)
+
+ override fun toString(): String = optionsMapper.writeValueAsString(this)
+
+}
+
+class WorldOptions(val generator: GeneratorOptions,
+ var runtime: RuntimeWorldOptions = RuntimeWorldOptions())
+
+class RuntimeWorldOptions(var gameMode: GameMode? = GameMode.CREATIVE,
+ var dayTime: Boolean = true,
+ var noWeather: Boolean = true,
+ var preventWeatherBlockChanges: Boolean = true,
+ var preventBlockSpread: Boolean = true, // TODO
+ var dropEntityItems: Boolean = true,
+ var doTileDrops: Boolean = false,
+ var disableExplosions: Boolean = true,
+ var blockPortalCreation: Boolean = true,
+ var blockMobSpawning: Boolean = true,
+ var blockedItems: Set<Material> = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL),
+ var axisLimit: Int = 10)
+
+class DataFileOptions(val location: String = "/flatfile-storage/")
+
+class MigrationOptionsHolder {
+ var enabled = false
+ var disableWhenComplete = true
+ var instance: MigrationOptions? = MigrationOptions()
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt b/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt
index 671a25f..d741617 100644
--- a/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt
@@ -1,66 +1,66 @@
-package io.dico.parcels2.options
-
-import com.fasterxml.jackson.core.JsonGenerator
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.databind.PropertyNamingStrategy
-import com.fasterxml.jackson.databind.SerializerProvider
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer
-import com.fasterxml.jackson.databind.ser.std.StdSerializer
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
-import com.fasterxml.jackson.module.kotlin.KotlinModule
-import org.bukkit.Bukkit
-import org.bukkit.block.data.BlockData
-
-val optionsMapper = ObjectMapper(YAMLFactory()).apply {
- propertyNamingStrategy = PropertyNamingStrategy.KEBAB_CASE
-
- val kotlinModule = KotlinModule()
-
- with(kotlinModule) {
- /*
- setSerializerModifier(object : BeanSerializerModifier() {
- @Suppress("UNCHECKED_CAST")
- override fun modifySerializer(config: SerializationConfig?, beanDesc: BeanDescription, serializer: JsonSerializer<*>): JsonSerializer<*> {
-
- val newSerializer = if (GeneratorOptions::class.isSuperclassOf(beanDesc.beanClass.kotlin)) {
- GeneratorOptionsSerializer(serializer as JsonSerializer<GeneratorOptions>)
- } else {
- serializer
- }
-
- return super.modifySerializer(config, beanDesc, newSerializer)
- }
- })*/
-
- addSerializer(BlockDataSerializer())
- addDeserializer(BlockData::class.java, BlockDataDeserializer())
-
- GeneratorOptionsFactories.registerSerialization(this)
- StorageOptionsFactories.registerSerialization(this)
- MigrationOptionsFactories.registerSerialization(this)
- }
-
- registerModule(kotlinModule)
-}
-
-private class BlockDataSerializer : StdSerializer<BlockData>(BlockData::class.java) {
-
- override fun serialize(value: BlockData, gen: JsonGenerator, provider: SerializerProvider) {
- gen.writeString(value.asString)
- }
-
-}
-
-private class BlockDataDeserializer : StdDeserializer<BlockData>(BlockData::class.java) {
-
- override fun deserialize(p: JsonParser, ctxt: DeserializationContext): BlockData? {
- try {
- return Bukkit.createBlockData(p.valueAsString)
- } catch (ex: Exception) {
- throw RuntimeException("Exception occurred at ${p.currentLocation}", ex)
- }
- }
-
-}
+package io.dico.parcels2.options
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.PropertyNamingStrategy
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer
+import com.fasterxml.jackson.databind.ser.std.StdSerializer
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
+import com.fasterxml.jackson.module.kotlin.KotlinModule
+import org.bukkit.Bukkit
+import org.bukkit.block.data.BlockData
+
+val optionsMapper = ObjectMapper(YAMLFactory()).apply {
+ propertyNamingStrategy = PropertyNamingStrategy.KEBAB_CASE
+
+ val kotlinModule = KotlinModule()
+
+ with(kotlinModule) {
+ /*
+ setSerializerModifier(object : BeanSerializerModifier() {
+ @Suppress("UNCHECKED_CAST")
+ override fun modifySerializer(config: SerializationConfig?, beanDesc: BeanDescription, serializer: JsonSerializer<*>): JsonSerializer<*> {
+
+ val newSerializer = if (GeneratorOptions::class.isSuperclassOf(beanDesc.beanClass.kotlin)) {
+ GeneratorOptionsSerializer(serializer as JsonSerializer<GeneratorOptions>)
+ } else {
+ serializer
+ }
+
+ return super.modifySerializer(config, beanDesc, newSerializer)
+ }
+ })*/
+
+ addSerializer(BlockDataSerializer())
+ addDeserializer(BlockData::class.java, BlockDataDeserializer())
+
+ GeneratorOptionsFactories.registerSerialization(this)
+ StorageOptionsFactories.registerSerialization(this)
+ MigrationOptionsFactories.registerSerialization(this)
+ }
+
+ registerModule(kotlinModule)
+}
+
+private class BlockDataSerializer : StdSerializer<BlockData>(BlockData::class.java) {
+
+ override fun serialize(value: BlockData, gen: JsonGenerator, provider: SerializerProvider) {
+ gen.writeString(value.asString)
+ }
+
+}
+
+private class BlockDataDeserializer : StdDeserializer<BlockData>(BlockData::class.java) {
+
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext): BlockData? {
+ try {
+ return Bukkit.createBlockData(p.valueAsString)
+ } catch (ex: Exception) {
+ throw RuntimeException("Exception occurred at ${p.currentLocation}", ex)
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt b/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt
index aa60f39..f65efa1 100644
--- a/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt
@@ -1,89 +1,89 @@
-package io.dico.parcels2.options
-
-import com.fasterxml.jackson.core.JsonGenerator
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.JsonDeserializer
-import com.fasterxml.jackson.databind.JsonNode
-import com.fasterxml.jackson.databind.SerializerProvider
-import com.fasterxml.jackson.databind.module.SimpleModule
-import com.fasterxml.jackson.databind.ser.std.StdSerializer
-import io.dico.parcels2.logger
-import kotlin.reflect.KClass
-
-abstract class PolymorphicOptions<T : Any>(val key: String,
- val options: Any,
- factories: PolymorphicOptionsFactories<T>) {
- val factory = factories.getFactory(key)!!
-}
-
-abstract class SimplePolymorphicOptions<T : Any>(key: String, options: Any, factories: PolymorphicOptionsFactories<T>)
- : PolymorphicOptions<T>(key, options, factories) {
- fun newInstance(): T = factory.newInstance(key, options)
-}
-
-interface PolymorphicOptionsFactory<T : Any> {
- val supportedKeys: List<String>
- val optionsClass: KClass<out Any>
- fun newInstance(key: String, options: Any, vararg extra: Any?): T
-}
-
-@Suppress("UNCHECKED_CAST")
-abstract class PolymorphicOptionsFactories<T : Any>(val serializeKeyAs: String,
- rootClass: KClass<out PolymorphicOptions<T>>,
- vararg defaultFactories: PolymorphicOptionsFactory<T>) {
- val rootClass = rootClass as KClass<PolymorphicOptions<T>>
- private val map: MutableMap<String, PolymorphicOptionsFactory<T>> = linkedMapOf()
- val availableKeys: Collection<String> get() = map.keys
-
- fun registerFactory(factory: PolymorphicOptionsFactory<T>) = factory.supportedKeys.forEach { map.putIfAbsent(it.toLowerCase(), factory) }
-
- fun getFactory(key: String): PolymorphicOptionsFactory<T>? = map[key.toLowerCase()]
-
- fun registerSerialization(module: SimpleModule) {
- module.addSerializer(PolymorphicOptionsSerializer(this))
- module.addDeserializer(rootClass.java, PolymorphicOptionsDeserializer(this))
- }
-
- init {
- defaultFactories.forEach { registerFactory(it) }
- }
-}
-
-
-private class PolymorphicOptionsDeserializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : JsonDeserializer<PolymorphicOptions<T>>() {
-
- override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): PolymorphicOptions<T> {
- val node = p.readValueAsTree<JsonNode>()
- val key = node.get(factories.serializeKeyAs).asText()
- val factory = getFactory(key)
- val optionsNode = node.get("options")
- val options = p.codec.treeToValue(optionsNode, factory.optionsClass.java)
- return factories.rootClass.constructors.first().call(key, options)
- }
-
- private fun getFactory(key: String): PolymorphicOptionsFactory<T> {
- factories.getFactory(key)?.let { return it }
-
- logger.warn("Unknown ${factories.rootClass.simpleName} ${factories.serializeKeyAs}: $key. " +
- "\nAvailable options: ${factories.availableKeys}")
-
- val default = factories.getFactory(factories.availableKeys.first())
- ?: throw IllegalStateException("No default ${factories.rootClass.simpleName} factory registered.")
- return default
- }
-
-}
-
-private class PolymorphicOptionsSerializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : StdSerializer<PolymorphicOptions<T>>(factories.rootClass.java) {
-
- override fun serialize(value: PolymorphicOptions<T>, gen: JsonGenerator, sp: SerializerProvider?) {
- with(gen) {
- writeStartObject()
- writeStringField(factories.serializeKeyAs, value.key)
- writeFieldName("options")
- writeObject(value.options)
- writeEndObject()
- }
- }
+package io.dico.parcels2.options
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.JsonDeserializer
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.module.SimpleModule
+import com.fasterxml.jackson.databind.ser.std.StdSerializer
+import io.dico.parcels2.logger
+import kotlin.reflect.KClass
+
+abstract class PolymorphicOptions<T : Any>(val key: String,
+ val options: Any,
+ factories: PolymorphicOptionsFactories<T>) {
+ val factory = factories.getFactory(key)!!
+}
+
+abstract class SimplePolymorphicOptions<T : Any>(key: String, options: Any, factories: PolymorphicOptionsFactories<T>)
+ : PolymorphicOptions<T>(key, options, factories) {
+ fun newInstance(): T = factory.newInstance(key, options)
+}
+
+interface PolymorphicOptionsFactory<T : Any> {
+ val supportedKeys: List<String>
+ val optionsClass: KClass<out Any>
+ fun newInstance(key: String, options: Any, vararg extra: Any?): T
+}
+
+@Suppress("UNCHECKED_CAST")
+abstract class PolymorphicOptionsFactories<T : Any>(val serializeKeyAs: String,
+ rootClass: KClass<out PolymorphicOptions<T>>,
+ vararg defaultFactories: PolymorphicOptionsFactory<T>) {
+ val rootClass = rootClass as KClass<PolymorphicOptions<T>>
+ private val map: MutableMap<String, PolymorphicOptionsFactory<T>> = linkedMapOf()
+ val availableKeys: Collection<String> get() = map.keys
+
+ fun registerFactory(factory: PolymorphicOptionsFactory<T>) = factory.supportedKeys.forEach { map.putIfAbsent(it.toLowerCase(), factory) }
+
+ fun getFactory(key: String): PolymorphicOptionsFactory<T>? = map[key.toLowerCase()]
+
+ fun registerSerialization(module: SimpleModule) {
+ module.addSerializer(PolymorphicOptionsSerializer(this))
+ module.addDeserializer(rootClass.java, PolymorphicOptionsDeserializer(this))
+ }
+
+ init {
+ defaultFactories.forEach { registerFactory(it) }
+ }
+}
+
+
+private class PolymorphicOptionsDeserializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : JsonDeserializer<PolymorphicOptions<T>>() {
+
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): PolymorphicOptions<T> {
+ val node = p.readValueAsTree<JsonNode>()
+ val key = node.get(factories.serializeKeyAs).asText()
+ val factory = getFactory(key)
+ val optionsNode = node.get("options")
+ val options = p.codec.treeToValue(optionsNode, factory.optionsClass.java)
+ return factories.rootClass.constructors.first().call(key, options)
+ }
+
+ private fun getFactory(key: String): PolymorphicOptionsFactory<T> {
+ factories.getFactory(key)?.let { return it }
+
+ logger.warn("Unknown ${factories.rootClass.simpleName} ${factories.serializeKeyAs}: $key. " +
+ "\nAvailable options: ${factories.availableKeys}")
+
+ val default = factories.getFactory(factories.availableKeys.first())
+ ?: throw IllegalStateException("No default ${factories.rootClass.simpleName} factory registered.")
+ return default
+ }
+
+}
+
+private class PolymorphicOptionsSerializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : StdSerializer<PolymorphicOptions<T>>(factories.rootClass.java) {
+
+ override fun serialize(value: PolymorphicOptions<T>, gen: JsonGenerator, sp: SerializerProvider?) {
+ with(gen) {
+ writeStartObject()
+ writeStringField(factories.serializeKeyAs, value.key)
+ writeFieldName("options")
+ writeObject(value.options)
+ writeEndObject()
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt b/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt
index 3d68701..29aab7a 100644
--- a/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt
@@ -1,59 +1,59 @@
-package io.dico.parcels2.options
-
-import com.zaxxer.hikari.HikariDataSource
-import io.dico.parcels2.logger
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.storage.BackedStorage
-import io.dico.parcels2.storage.exposed.ExposedBacking
-import io.dico.parcels2.storage.getHikariConfig
-import javax.sql.DataSource
-
-object StorageOptionsFactories : PolymorphicOptionsFactories<Storage>("dialect", StorageOptions::class, ConnectionStorageFactory())
-
-class StorageOptions(dialect: String = "mariadb", options: Any = DataConnectionOptions()) : SimplePolymorphicOptions<Storage>(dialect, options, StorageOptionsFactories) {
-
- fun getDataSourceFactory(): DataSourceFactory? {
- return when (factory) {
- is ConnectionStorageFactory -> factory.getDataSourceFactory(key, options)
- else -> return null
- }
- }
-}
-
-typealias DataSourceFactory = () -> DataSource
-
-private class ConnectionStorageFactory : PolymorphicOptionsFactory<Storage> {
- override val optionsClass = DataConnectionOptions::class
- override val supportedKeys: List<String> = listOf("postgresql", "mariadb")
-
- fun getDataSourceFactory(key: String, options: Any): DataSourceFactory {
- val hikariConfig = getHikariConfig(key, options as DataConnectionOptions)
- return { HikariDataSource(hikariConfig) }
- }
-
- override fun newInstance(key: String, options: Any, vararg extra: Any?): Storage {
- return BackedStorage(ExposedBacking(getDataSourceFactory(key, options), (options as DataConnectionOptions).poolSize))
- }
-}
-
-data class DataConnectionOptions(val address: String = "localhost",
- val database: String = "parcels",
- val username: String = "root",
- val password: String = "",
- val poolSize: Int = 4) {
-
- fun splitAddressAndPort(defaultPort: Int = 3306): Pair<String, Int>? {
- val idx = address.indexOf(":").takeUnless { it == -1 } ?: return Pair(address, defaultPort)
-
- val addressName = address.substring(0, idx).takeUnless { it.isBlank() } ?: return null.also {
- logger.error("(Invalidly) blank address in data storage options")
- }
-
- val port = address.substring(idx + 1).toIntOrNull() ?: return null.also {
- logger.error("Invalid port number in data storage options: $it, using $defaultPort as default")
- }
-
- return Pair(addressName, port)
- }
-
+package io.dico.parcels2.options
+
+import com.zaxxer.hikari.HikariDataSource
+import io.dico.parcels2.logger
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.storage.BackedStorage
+import io.dico.parcels2.storage.exposed.ExposedBacking
+import io.dico.parcels2.storage.getHikariConfig
+import javax.sql.DataSource
+
+object StorageOptionsFactories : PolymorphicOptionsFactories<Storage>("dialect", StorageOptions::class, ConnectionStorageFactory())
+
+class StorageOptions(dialect: String = "mariadb", options: Any = DataConnectionOptions()) : SimplePolymorphicOptions<Storage>(dialect, options, StorageOptionsFactories) {
+
+ fun getDataSourceFactory(): DataSourceFactory? {
+ return when (factory) {
+ is ConnectionStorageFactory -> factory.getDataSourceFactory(key, options)
+ else -> return null
+ }
+ }
+}
+
+typealias DataSourceFactory = () -> DataSource
+
+private class ConnectionStorageFactory : PolymorphicOptionsFactory<Storage> {
+ override val optionsClass = DataConnectionOptions::class
+ override val supportedKeys: List<String> = listOf("postgresql", "mariadb")
+
+ fun getDataSourceFactory(key: String, options: Any): DataSourceFactory {
+ val hikariConfig = getHikariConfig(key, options as DataConnectionOptions)
+ return { HikariDataSource(hikariConfig) }
+ }
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): Storage {
+ return BackedStorage(ExposedBacking(getDataSourceFactory(key, options), (options as DataConnectionOptions).poolSize))
+ }
+}
+
+data class DataConnectionOptions(val address: String = "localhost",
+ val database: String = "parcels",
+ val username: String = "root",
+ val password: String = "",
+ val poolSize: Int = 4) {
+
+ fun splitAddressAndPort(defaultPort: Int = 3306): Pair<String, Int>? {
+ val idx = address.indexOf(":").takeUnless { it == -1 } ?: return Pair(address, defaultPort)
+
+ val addressName = address.substring(0, idx).takeUnless { it.isBlank() } ?: return null.also {
+ logger.error("(Invalidly) blank address in data storage options")
+ }
+
+ val port = address.substring(idx + 1).toIntOrNull() ?: return null.also {
+ logger.error("Invalid port number in data storage options: $it, using $defaultPort as default")
+ }
+
+ return Pair(addressName, port)
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
index 12d5bc7..63a5db6 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
@@ -1,69 +1,69 @@
-package io.dico.parcels2.storage
-
-import io.dico.parcels2.*
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.channels.ReceiveChannel
-import kotlinx.coroutines.channels.SendChannel
-import org.joda.time.DateTime
-import java.util.UUID
-import kotlin.coroutines.CoroutineContext
-
-interface Backing {
-
- val name: String
-
- val isConnected: Boolean
-
- val coroutineContext: CoroutineContext
-
- fun launchJob(job: Backing.() -> Unit): Job
-
- fun <T> launchFuture(future: Backing.() -> T): Deferred<T>
-
- fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T>
-
- fun <T> openChannelForWriting(future: Backing.(T) -> Unit): SendChannel<T>
-
-
- fun init()
-
- fun shutdown()
-
-
- fun getWorldCreationTime(worldId: ParcelWorldId): DateTime?
-
- fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime)
-
- fun getPlayerUuidForName(name: String): UUID?
-
- fun updatePlayerName(uuid: UUID, name: String)
-
- fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
-
- fun transmitAllParcelData(channel: SendChannel<DataPair>)
-
- fun readParcelData(parcel: ParcelId): ParcelDataHolder?
-
- fun getOwnedParcels(user: PlayerProfile): List<ParcelId>
-
- fun getNumParcels(user: PlayerProfile): Int = getOwnedParcels(user).size
-
-
- fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?)
-
- fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
-
- fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean)
-
- fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege)
-
- fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration)
-
-
- fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>)
-
- fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder?
-
- fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege)
-}
+package io.dico.parcels2.storage
+
+import io.dico.parcels2.*
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.ReceiveChannel
+import kotlinx.coroutines.channels.SendChannel
+import org.joda.time.DateTime
+import java.util.UUID
+import kotlin.coroutines.CoroutineContext
+
+interface Backing {
+
+ val name: String
+
+ val isConnected: Boolean
+
+ val coroutineContext: CoroutineContext
+
+ fun launchJob(job: Backing.() -> Unit): Job
+
+ fun <T> launchFuture(future: Backing.() -> T): Deferred<T>
+
+ fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T>
+
+ fun <T> openChannelForWriting(future: Backing.(T) -> Unit): SendChannel<T>
+
+
+ fun init()
+
+ fun shutdown()
+
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): DateTime?
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime)
+
+ fun getPlayerUuidForName(name: String): UUID?
+
+ fun updatePlayerName(uuid: UUID, name: String)
+
+ fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
+
+ fun transmitAllParcelData(channel: SendChannel<DataPair>)
+
+ fun readParcelData(parcel: ParcelId): ParcelDataHolder?
+
+ fun getOwnedParcels(user: PlayerProfile): List<ParcelId>
+
+ fun getNumParcels(user: PlayerProfile): Int = getOwnedParcels(user).size
+
+
+ fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?)
+
+ fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
+
+ fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean)
+
+ fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege)
+
+ fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration)
+
+
+ fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>)
+
+ fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder?
+
+ fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt b/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt
index 80f41b2..54f7677 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt
@@ -1,38 +1,38 @@
-package io.dico.parcels2.storage
-
-import java.lang.IllegalArgumentException
-import java.nio.ByteBuffer
-import java.util.UUID
-
-/* For putting it into the database */
-fun UUID.toByteArray(): ByteArray =
- ByteBuffer.allocate(16).apply {
- putLong(mostSignificantBits)
- putLong(leastSignificantBits)
- }.array()
-
-/* For getting it out of the database */
-fun ByteArray.toUUID(): UUID =
- ByteBuffer.wrap(this).run {
- val mostSignificantBits = getLong()
- val leastSignificantBits = getLong()
- UUID(mostSignificantBits, leastSignificantBits)
- }
-
-/* For putting it into the database */
-fun IntArray.toByteArray(): ByteArray =
- ByteBuffer.allocate(size * Int.SIZE_BYTES).also { buf ->
- buf.asIntBuffer().put(this)
- }.array()
-
-/* For getting it out of the database */
-fun ByteArray.toIntArray(): IntArray {
- if (this.size % Int.SIZE_BYTES != 0)
- throw IllegalArgumentException("Size must be divisible by ${Int.SIZE_BYTES}")
-
- return ByteBuffer.wrap(this).run {
- IntArray(remaining() / 4).also { array ->
- asIntBuffer().get(array)
- }
- }
+package io.dico.parcels2.storage
+
+import java.lang.IllegalArgumentException
+import java.nio.ByteBuffer
+import java.util.UUID
+
+/* For putting it into the database */
+fun UUID.toByteArray(): ByteArray =
+ ByteBuffer.allocate(16).apply {
+ putLong(mostSignificantBits)
+ putLong(leastSignificantBits)
+ }.array()
+
+/* For getting it out of the database */
+fun ByteArray.toUUID(): UUID =
+ ByteBuffer.wrap(this).run {
+ val mostSignificantBits = getLong()
+ val leastSignificantBits = getLong()
+ UUID(mostSignificantBits, leastSignificantBits)
+ }
+
+/* For putting it into the database */
+fun IntArray.toByteArray(): ByteArray =
+ ByteBuffer.allocate(size * Int.SIZE_BYTES).also { buf ->
+ buf.asIntBuffer().put(this)
+ }.array()
+
+/* For getting it out of the database */
+fun ByteArray.toIntArray(): IntArray {
+ if (this.size % Int.SIZE_BYTES != 0)
+ throw IllegalArgumentException("Size must be divisible by ${Int.SIZE_BYTES}")
+
+ return ByteBuffer.wrap(this).run {
+ IntArray(remaining() / 4).also { array ->
+ asIntBuffer().get(array)
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt
index 480d533..f3030f6 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt
@@ -1,73 +1,73 @@
-package io.dico.parcels2.storage
-
-import com.zaxxer.hikari.HikariConfig
-import io.dico.parcels2.options.DataConnectionOptions
-
-fun getHikariConfig(dialectName: String,
- dco: DataConnectionOptions): HikariConfig = HikariConfig().apply {
-
- val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}")
-
- when (dialectName) {
- "postgresql" -> run {
- dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
- dataSourceProperties["serverName"] = address
- dataSourceProperties["portNumber"] = port.toString()
- dataSourceProperties["databaseName"] = dco.database
- }
-
- "mariadb" -> run {
- dataSourceClassName = "org.mariadb.jdbc.MariaDbDataSource"
- dataSourceProperties["serverName"] = address
- dataSourceProperties["port"] = port.toString()
- dataSourceProperties["databaseName"] = dco.database
- dataSourceProperties["properties"] = "useUnicode=true;characterEncoding=utf8"
- }
-
- else -> throw IllegalArgumentException("Unsupported dialect: $dialectName")
- }
-
- poolName = "parcels"
- maximumPoolSize = dco.poolSize
- username = dco.username
- password = dco.password
- connectionTimeout = 15000
- leakDetectionThreshold = 10000
- connectionTestQuery = "SELECT 1"
-
-
- /*
-
- addDataSourceProperty("serverName", address)
- addDataSourceProperty("port", port.toString())
- addDataSourceProperty("databaseName", dco.database)
-
- // copied from github.com/lucko/LuckPerms
- if (dialectName.toLowerCase() == "mariadb") {
- addDataSourceProperty("properties", "useUnicode=true;characterEncoding=utf8")
- } else if (dialectName.toLowerCase() == "h2") {
- dataSourceProperties.remove("serverName")
- dataSourceProperties.remove("port")
- dataSourceProperties.remove("databaseName")
- addDataSourceProperty("url", "jdbc:h2:${if (address.isBlank()) "" else "tcp://$address/"}~/${dco.database}")
- } else if (dialectName.toLowerCase() == "mysql") {
- // doesn't exist on the MariaDB driver
- addDataSourceProperty("cachePrepStmts", "true")
- addDataSourceProperty("alwaysSendSetIsolation", "false")
- addDataSourceProperty("cacheServerConfiguration", "true")
- addDataSourceProperty("elideSetAutoCommits", "true")
- addDataSourceProperty("useLocalSessionState", "true")
-
- // already set as default on mariadb
- addDataSourceProperty("useServerPrepStmts", "true")
- addDataSourceProperty("prepStmtCacheSize", "250")
- addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
- addDataSourceProperty("cacheCallableStmts", "true")
-
- // make sure unicode characters can be used.
- addDataSourceProperty("characterEncoding", "utf8")
- addDataSourceProperty("useUnicode", "true")
- } else {
-
- }*/
-}
+package io.dico.parcels2.storage
+
+import com.zaxxer.hikari.HikariConfig
+import io.dico.parcels2.options.DataConnectionOptions
+
+fun getHikariConfig(dialectName: String,
+ dco: DataConnectionOptions): HikariConfig = HikariConfig().apply {
+
+ val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}")
+
+ when (dialectName) {
+ "postgresql" -> run {
+ dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
+ dataSourceProperties["serverName"] = address
+ dataSourceProperties["portNumber"] = port.toString()
+ dataSourceProperties["databaseName"] = dco.database
+ }
+
+ "mariadb" -> run {
+ dataSourceClassName = "org.mariadb.jdbc.MariaDbDataSource"
+ dataSourceProperties["serverName"] = address
+ dataSourceProperties["port"] = port.toString()
+ dataSourceProperties["databaseName"] = dco.database
+ dataSourceProperties["properties"] = "useUnicode=true;characterEncoding=utf8"
+ }
+
+ else -> throw IllegalArgumentException("Unsupported dialect: $dialectName")
+ }
+
+ poolName = "parcels"
+ maximumPoolSize = dco.poolSize
+ username = dco.username
+ password = dco.password
+ connectionTimeout = 15000
+ leakDetectionThreshold = 10000
+ connectionTestQuery = "SELECT 1"
+
+
+ /*
+
+ addDataSourceProperty("serverName", address)
+ addDataSourceProperty("port", port.toString())
+ addDataSourceProperty("databaseName", dco.database)
+
+ // copied from github.com/lucko/LuckPerms
+ if (dialectName.toLowerCase() == "mariadb") {
+ addDataSourceProperty("properties", "useUnicode=true;characterEncoding=utf8")
+ } else if (dialectName.toLowerCase() == "h2") {
+ dataSourceProperties.remove("serverName")
+ dataSourceProperties.remove("port")
+ dataSourceProperties.remove("databaseName")
+ addDataSourceProperty("url", "jdbc:h2:${if (address.isBlank()) "" else "tcp://$address/"}~/${dco.database}")
+ } else if (dialectName.toLowerCase() == "mysql") {
+ // doesn't exist on the MariaDB driver
+ addDataSourceProperty("cachePrepStmts", "true")
+ addDataSourceProperty("alwaysSendSetIsolation", "false")
+ addDataSourceProperty("cacheServerConfiguration", "true")
+ addDataSourceProperty("elideSetAutoCommits", "true")
+ addDataSourceProperty("useLocalSessionState", "true")
+
+ // already set as default on mariadb
+ addDataSourceProperty("useServerPrepStmts", "true")
+ addDataSourceProperty("prepStmtCacheSize", "250")
+ addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
+ addDataSourceProperty("cacheCallableStmts", "true")
+
+ // make sure unicode characters can be used.
+ addDataSourceProperty("characterEncoding", "utf8")
+ addDataSourceProperty("useUnicode", "true")
+ } else {
+
+ }*/
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
index d718f20..f76aad6 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
@@ -1,114 +1,114 @@
-@file:Suppress("NOTHING_TO_INLINE")
-
-package io.dico.parcels2.storage
-
-import io.dico.parcels2.*
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.channels.ReceiveChannel
-import kotlinx.coroutines.channels.SendChannel
-import kotlinx.coroutines.launch
-import org.joda.time.DateTime
-import java.util.UUID
-import kotlin.coroutines.CoroutineContext
-
-typealias DataPair = Pair<ParcelId, ParcelDataHolder?>
-typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder>
-
-interface Storage {
- val name: String
- val isConnected: Boolean
-
- fun init(): Job
-
- fun shutdown(): Job
-
-
- fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?>
-
- fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job
-
- fun getPlayerUuidForName(name: String): Deferred<UUID?>
-
- fun updatePlayerName(uuid: UUID, name: String): Job
-
- fun readParcelData(parcel: ParcelId): Deferred<ParcelDataHolder?>
-
- fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
-
- fun transmitAllParcelData(): ReceiveChannel<DataPair>
-
- fun getOwnedParcels(user: PlayerProfile): Deferred<List<ParcelId>>
-
- fun getNumParcels(user: PlayerProfile): Deferred<Int>
-
-
- fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?): Job
-
- fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
-
- fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job
-
- fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege): Job
-
- fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job
-
-
- fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>>
-
- fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?>
-
- fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job
-
-
- fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>>
-}
-
-class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope {
- override val name get() = b.name
- override val isConnected get() = b.isConnected
- override val coroutineContext: CoroutineContext get() = b.coroutineContext
-
- override fun init() = launch { b.init() }
-
- override fun shutdown() = launch { b.shutdown() }
-
-
- override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) }
-
- override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job = b.launchJob { b.setWorldCreationTime(worldId, time) }
-
- override fun getPlayerUuidForName(name: String): Deferred<UUID?> = b.launchFuture { b.getPlayerUuidForName(name) }
-
- override fun updatePlayerName(uuid: UUID, name: String): Job = b.launchJob { b.updatePlayerName(uuid, name) }
-
- override fun readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) }
-
- override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) }
-
- override fun transmitAllParcelData() = b.openChannel<DataPair> { b.transmitAllParcelData(it) }
-
- override fun getOwnedParcels(user: PlayerProfile) = b.launchFuture { b.getOwnedParcels(user) }
-
- override fun getNumParcels(user: PlayerProfile) = b.launchFuture { b.getNumParcels(user) }
-
- override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) = b.launchJob { b.setParcelData(parcel, data) }
-
- override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) = b.launchJob { b.setParcelOwner(parcel, owner) }
-
- override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) }
-
- override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setLocalPrivilege(parcel, player, privilege) }
-
- override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) }
-
-
- override fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalPrivileges(it) }
-
- 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 getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) }
-}
+@file:Suppress("NOTHING_TO_INLINE")
+
+package io.dico.parcels2.storage
+
+import io.dico.parcels2.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.ReceiveChannel
+import kotlinx.coroutines.channels.SendChannel
+import kotlinx.coroutines.launch
+import org.joda.time.DateTime
+import java.util.UUID
+import kotlin.coroutines.CoroutineContext
+
+typealias DataPair = Pair<ParcelId, ParcelDataHolder?>
+typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder>
+
+interface Storage {
+ val name: String
+ val isConnected: Boolean
+
+ fun init(): Job
+
+ fun shutdown(): Job
+
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?>
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job
+
+ fun getPlayerUuidForName(name: String): Deferred<UUID?>
+
+ fun updatePlayerName(uuid: UUID, name: String): Job
+
+ fun readParcelData(parcel: ParcelId): Deferred<ParcelDataHolder?>
+
+ fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
+
+ fun transmitAllParcelData(): ReceiveChannel<DataPair>
+
+ fun getOwnedParcels(user: PlayerProfile): Deferred<List<ParcelId>>
+
+ fun getNumParcels(user: PlayerProfile): Deferred<Int>
+
+
+ fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?): Job
+
+ fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
+
+ fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job
+
+ fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege): Job
+
+ fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job
+
+
+ fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>>
+
+ fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?>
+
+ fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job
+
+
+ fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>>
+}
+
+class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope {
+ override val name get() = b.name
+ override val isConnected get() = b.isConnected
+ override val coroutineContext: CoroutineContext get() = b.coroutineContext
+
+ override fun init() = launch { b.init() }
+
+ override fun shutdown() = launch { b.shutdown() }
+
+
+ override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) }
+
+ override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job = b.launchJob { b.setWorldCreationTime(worldId, time) }
+
+ override fun getPlayerUuidForName(name: String): Deferred<UUID?> = b.launchFuture { b.getPlayerUuidForName(name) }
+
+ override fun updatePlayerName(uuid: UUID, name: String): Job = b.launchJob { b.updatePlayerName(uuid, name) }
+
+ override fun readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) }
+
+ override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) }
+
+ override fun transmitAllParcelData() = b.openChannel<DataPair> { b.transmitAllParcelData(it) }
+
+ override fun getOwnedParcels(user: PlayerProfile) = b.launchFuture { b.getOwnedParcels(user) }
+
+ override fun getNumParcels(user: PlayerProfile) = b.launchFuture { b.getNumParcels(user) }
+
+ override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) = b.launchJob { b.setParcelData(parcel, data) }
+
+ override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) = b.launchJob { b.setParcelOwner(parcel, owner) }
+
+ override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) }
+
+ override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setLocalPrivilege(parcel, player, privilege) }
+
+ override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) }
+
+
+ override fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalPrivileges(it) }
+
+ 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 getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
index e49be79..d9e3071 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
@@ -72,9 +72,11 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
override fun init() {
synchronized {
if (isShutdown || isConnected) throw IllegalStateException()
- dataSource = dataSourceFactory()
- database = Database.connect(dataSource!!)
- transaction(database!!) {
+ val dataSource = dataSourceFactory()
+ this.dataSource = dataSource
+ val database = Database.connect(dataSource)
+ this.database = database
+ transaction(database) {
create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, PrivilegesLocalT, PrivilegesGlobalT)
}
}
@@ -84,7 +86,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
synchronized {
if (isShutdown) throw IllegalStateException()
isShutdown = true
- coroutineContext[Job]!!.cancel(CancellationException("ExposedBacking shutdown"))
+ coroutineContext.cancel(CancellationException("ExposedBacking shutdown"))
dataSource?.let {
(it as? HikariDataSource)?.close()
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
index 0245625..7640c0c 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
@@ -1,74 +1,74 @@
-package io.dico.parcels2.storage.exposed
-
-import org.jetbrains.exposed.sql.*
-import org.jetbrains.exposed.sql.Function
-import org.jetbrains.exposed.sql.statements.InsertStatement
-import org.jetbrains.exposed.sql.transactions.TransactionManager
-
-class UpsertStatement<Key : Any>(table: Table, conflictColumn: Column<*>? = null, conflictIndex: Index? = null) : InsertStatement<Key>(table, false) {
- val indexName: String
- val indexColumns: List<Column<*>>
-
- init {
- when {
- conflictIndex != null -> {
- indexName = conflictIndex.indexName
- indexColumns = conflictIndex.columns
- }
- conflictColumn != null -> {
- indexName = conflictColumn.name
- indexColumns = listOf(conflictColumn)
- }
- else -> throw IllegalArgumentException()
- }
- }
-
- override fun prepareSQL(transaction: Transaction) = buildString {
- append(super.prepareSQL(transaction))
-
- val dialect = transaction.db.vendor
- if (dialect == "postgresql") {
-
- append(" ON CONFLICT(")
- append(indexName)
- append(") DO UPDATE SET ")
-
- values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=EXCLUDED.${transaction.identity(it)}" }
-
- } else {
-
- append(" ON DUPLICATE KEY UPDATE ")
- values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=VALUES(${transaction.identity(it)})" }
-
- }
- }
-
-}
-
-inline fun <T : Table> T.upsert(conflictColumn: Column<*>? = null, conflictIndex: Index? = null, body: T.(UpsertStatement<Number>) -> Unit) =
- UpsertStatement<Number>(this, conflictColumn, conflictIndex).apply {
- body(this)
- execute(TransactionManager.current())
- }
-
-fun Table.indexR(customIndexName: String? = null, isUnique: Boolean = false, vararg columns: Column<*>): Index {
- val index = Index(columns.toList(), isUnique, customIndexName)
- indices.add(index)
- return index
-}
-
-fun Table.uniqueIndexR(customIndexName: String? = null, vararg columns: Column<*>): Index = indexR(customIndexName, true, *columns)
-
-fun <T : Int?> ExpressionWithColumnType<T>.abs(): Function<T> = Abs(this)
-
-class Abs<T : Int?>(val expr: Expression<T>) : Function<T>(IntegerColumnType()) {
- override fun toSQL(queryBuilder: QueryBuilder): String = "ABS(${expr.toSQL(queryBuilder)})"
-}
-
-fun <T : Comparable<T>> greaterOf(col1: ExpressionWithColumnType<T>, col2: ExpressionWithColumnType<T>): Expression<T> =
- with(SqlExpressionBuilder) {
- case(col1)
- .When(col1.greater(col2), col1)
- .Else(col2)
- }
-
+package io.dico.parcels2.storage.exposed
+
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.Function
+import org.jetbrains.exposed.sql.statements.InsertStatement
+import org.jetbrains.exposed.sql.transactions.TransactionManager
+
+class UpsertStatement<Key : Any>(table: Table, conflictColumn: Column<*>? = null, conflictIndex: Index? = null) : InsertStatement<Key>(table, false) {
+ val indexName: String
+ val indexColumns: List<Column<*>>
+
+ init {
+ when {
+ conflictIndex != null -> {
+ indexName = conflictIndex.indexName
+ indexColumns = conflictIndex.columns
+ }
+ conflictColumn != null -> {
+ indexName = conflictColumn.name
+ indexColumns = listOf(conflictColumn)
+ }
+ else -> throw IllegalArgumentException()
+ }
+ }
+
+ override fun prepareSQL(transaction: Transaction) = buildString {
+ append(super.prepareSQL(transaction))
+
+ val dialect = transaction.db.vendor
+ if (dialect == "postgresql") {
+
+ append(" ON CONFLICT(")
+ append(indexName)
+ append(") DO UPDATE SET ")
+
+ values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=EXCLUDED.${transaction.identity(it)}" }
+
+ } else {
+
+ append(" ON DUPLICATE KEY UPDATE ")
+ values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=VALUES(${transaction.identity(it)})" }
+
+ }
+ }
+
+}
+
+inline fun <T : Table> T.upsert(conflictColumn: Column<*>? = null, conflictIndex: Index? = null, body: T.(UpsertStatement<Number>) -> Unit) =
+ UpsertStatement<Number>(this, conflictColumn, conflictIndex).apply {
+ body(this)
+ execute(TransactionManager.current())
+ }
+
+fun Table.indexR(customIndexName: String? = null, isUnique: Boolean = false, vararg columns: Column<*>): Index {
+ val index = Index(columns.toList(), isUnique, customIndexName)
+ indices.add(index)
+ return index
+}
+
+fun Table.uniqueIndexR(customIndexName: String? = null, vararg columns: Column<*>): Index = indexR(customIndexName, true, *columns)
+
+fun <T : Int?> ExpressionWithColumnType<T>.abs(): Function<T> = Abs(this)
+
+class Abs<T : Int?>(val expr: Expression<T>) : Function<T>(IntegerColumnType()) {
+ override fun toSQL(queryBuilder: QueryBuilder): String = "ABS(${expr.toSQL(queryBuilder)})"
+}
+
+fun <T : Comparable<T>> greaterOf(col1: ExpressionWithColumnType<T>, col2: ExpressionWithColumnType<T>): Expression<T> =
+ with(SqlExpressionBuilder) {
+ case(col1)
+ .When(col1.greater(col2), col1)
+ .Else(col2)
+ }
+
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
index 6f6ad6b..8428b3a 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
@@ -1,165 +1,165 @@
-@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "unused", "MemberVisibilityCanBePrivate")
-
-package io.dico.parcels2.storage.exposed
-
-import io.dico.parcels2.ParcelId
-import io.dico.parcels2.ParcelWorldId
-import io.dico.parcels2.PlayerProfile
-import io.dico.parcels2.storage.toByteArray
-import io.dico.parcels2.storage.toUUID
-import org.jetbrains.exposed.sql.*
-import org.jetbrains.exposed.sql.statements.UpdateBuilder
-import org.joda.time.DateTime
-import java.util.UUID
-
-abstract class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String)
- : Table(tableName) {
- val id = integer(columnName).autoIncrement().primaryKey()
-
- @Suppress("UNCHECKED_CAST")
- inline val table: TableT
- get() = this as TableT
-
- internal inline fun getId(where: SqlExpressionBuilder.(TableT) -> Op<Boolean>): Int? {
- return select { where(table) }.firstOrNull()?.let { it[id] }
- }
-
- internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int {
- return getId() ?: table.insertIgnore(body)[id] ?: getId()
- ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its number")
- }
-
- abstract fun getId(obj: QueryObj): Int?
- abstract fun getOrInitId(obj: QueryObj): Int
- fun getItem(id: Int): QueryObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getItem(it) }
- abstract fun getItem(row: ResultRow): QueryObj?
-
- fun getId(obj: QueryObj, init: Boolean): Int? = if (init) getOrInitId(obj) else getId(obj)
-}
-
-object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcels_worlds", "world_id") {
- val name = varchar("name", 50)
- val uid = binary("uid", 16).nullable()
- val creation_time = datetime("creation_time").nullable()
- val index_name = uniqueIndexR("index_name", name)
- val index_uid = uniqueIndexR("index_uid", uid)
-
- internal inline fun getId(worldName: String, binaryUid: ByteArray?): Int? = getId { (name eq worldName).let { if (binaryUid == null) it else it or (uid eq binaryUid) } }
- internal inline fun getId(worldName: String, uid: UUID?): Int? = getId(worldName, uid?.toByteArray())
- internal inline fun getOrInitId(worldName: String, worldUid: UUID?): Int = worldUid?.toByteArray().let { binaryUid ->
- return getOrInitId(
- { getId(worldName, binaryUid) },
- { it[name] = worldName; it[uid] = binaryUid },
- { "world named $worldName" })
- }
-
- override fun getId(world: ParcelWorldId): Int? = getId(world.name, world.uid)
- override fun getOrInitId(world: ParcelWorldId): Int = getOrInitId(world.name, world.uid)
-
- override fun getItem(row: ResultRow): ParcelWorldId {
- return ParcelWorldId(row[name], row[uid]?.toUUID())
- }
-
- fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
- val id = getId(worldId) ?: return null
- return select { WorldsT.id eq id }.firstOrNull()?.let { it[WorldsT.creation_time] }
- }
-
- fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
- val id = getOrInitId(worldId)
- update({ WorldsT.id eq id }) {
- it[WorldsT.creation_time] = time
- }
- }
-}
-
-object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id") {
- val world_id = integer("world_id").references(WorldsT.id)
- val px = integer("px")
- val pz = integer("pz")
- val owner_id = integer("owner_id").references(ProfilesT.id).nullable()
- val sign_oudated = bool("sign_outdated").default(false)
- val claim_time = datetime("claim_time").nullable()
- val index_location = uniqueIndexR("index_location", world_id, px, pz)
-
- private inline fun getId(worldId: Int, parcelX: Int, parcelZ: Int): Int? = getId { world_id.eq(worldId) and px.eq(parcelX) and pz.eq(parcelZ) }
- private inline fun getId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int? = WorldsT.getId(worldName, worldUid)?.let { getId(it, parcelX, parcelZ) }
- private inline fun getOrInitId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int {
- val worldId = WorldsT.getOrInitId(worldName, worldUid)
- return getOrInitId(
- { getId(worldId, parcelX, parcelZ) },
- { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ },
- { "parcel at $worldName($parcelX, $parcelZ)" })
- }
-
- override fun getId(parcel: ParcelId): Int? = getId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
- override fun getOrInitId(parcel: ParcelId): Int = getOrInitId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
-
- private inline fun getRow(id: Int): ResultRow? = select { ParcelsT.id eq id }.firstOrNull()
- fun getRow(parcel: ParcelId): ResultRow? = getId(parcel)?.let { getRow(it) }
-
- override fun getItem(row: ResultRow): ParcelId? {
- val worldId = row[world_id]
- val world = WorldsT.getItem(worldId) ?: return null
- return ParcelId(world, row[px], row[pz])
- }
-}
-
-object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcels_profiles", "owner_id") {
- val uuid = binary("uuid", 16).nullable()
- val name = varchar("name", 32).nullable()
-
- // MySQL dialect MUST permit multiple null values for this to work. Server SQL does not allow this. That dialect is shit anyway.
- val uuid_constraint = uniqueIndexR("uuid_constraint", uuid)
- val index_pair = uniqueIndexR("index_pair", uuid, name)
-
-
- private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
- private inline fun getId(uuid: UUID) = getId(uuid.toByteArray())
- private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
- private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
-
- private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid ->
- getOrInitId(
- { getId(binaryUuid) },
- { it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
- { "profile(uuid = $uuid, name = $name)" })
- }
-
- private inline fun getOrInitId(name: String) = getOrInitId(
- { getId(name) },
- { it[ProfilesT.name] = name },
- { "owner(name = $name)" })
-
-
- override fun getId(profile: PlayerProfile): Int? = when (profile) {
- is PlayerProfile.Real -> getId(profile.uuid)
- is PlayerProfile.Fake -> getId(profile.name)
- is PlayerProfile.Unresolved -> getRealId(profile.name)
- else -> throw IllegalArgumentException()
- }
-
- override fun getOrInitId(profile: PlayerProfile): Int = when (profile) {
- is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.nameOrBukkitName)
- is PlayerProfile.Fake -> getOrInitId(profile.name)
- else -> throw IllegalArgumentException() // Unresolved profiles cannot be added to the database
- }
-
- override fun getItem(row: ResultRow): PlayerProfile {
- return PlayerProfile(row[uuid]?.toUUID(), row[name])
- }
-
- fun getRealItem(id: Int): PlayerProfile.Real? {
- return getItem(id) as? PlayerProfile.Real
- }
-
- /*
- fun updatePlayerProfile(profile: PlayerProfile.Real) {
- update({ uuid eq profile.uuid.toByteArray() }) {
- it[name] = profile.nameOrBukkitName
- }
- }*/
-
-}
-
+@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "unused", "MemberVisibilityCanBePrivate")
+
+package io.dico.parcels2.storage.exposed
+
+import io.dico.parcels2.ParcelId
+import io.dico.parcels2.ParcelWorldId
+import io.dico.parcels2.PlayerProfile
+import io.dico.parcels2.storage.toByteArray
+import io.dico.parcels2.storage.toUUID
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.statements.UpdateBuilder
+import org.joda.time.DateTime
+import java.util.UUID
+
+abstract class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String)
+ : Table(tableName) {
+ val id = integer(columnName).autoIncrement().primaryKey()
+
+ @Suppress("UNCHECKED_CAST")
+ inline val table: TableT
+ get() = this as TableT
+
+ internal inline fun getId(where: SqlExpressionBuilder.(TableT) -> Op<Boolean>): Int? {
+ return select { where(table) }.firstOrNull()?.let { it[id] }
+ }
+
+ internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int {
+ return getId() ?: table.insertIgnore(body)[id] ?: getId()
+ ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its number")
+ }
+
+ abstract fun getId(obj: QueryObj): Int?
+ abstract fun getOrInitId(obj: QueryObj): Int
+ fun getItem(id: Int): QueryObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getItem(it) }
+ abstract fun getItem(row: ResultRow): QueryObj?
+
+ fun getId(obj: QueryObj, init: Boolean): Int? = if (init) getOrInitId(obj) else getId(obj)
+}
+
+object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcels_worlds", "world_id") {
+ val name = varchar("name", 50)
+ val uid = binary("uid", 16).nullable()
+ val creation_time = datetime("creation_time").nullable()
+ val index_name = uniqueIndexR("index_name", name)
+ val index_uid = uniqueIndexR("index_uid", uid)
+
+ internal inline fun getId(worldName: String, binaryUid: ByteArray?): Int? = getId { (name eq worldName).let { if (binaryUid == null) it else it or (uid eq binaryUid) } }
+ internal inline fun getId(worldName: String, uid: UUID?): Int? = getId(worldName, uid?.toByteArray())
+ internal inline fun getOrInitId(worldName: String, worldUid: UUID?): Int = worldUid?.toByteArray().let { binaryUid ->
+ return getOrInitId(
+ { getId(worldName, binaryUid) },
+ { it[name] = worldName; it[uid] = binaryUid },
+ { "world named $worldName" })
+ }
+
+ override fun getId(world: ParcelWorldId): Int? = getId(world.name, world.uid)
+ override fun getOrInitId(world: ParcelWorldId): Int = getOrInitId(world.name, world.uid)
+
+ override fun getItem(row: ResultRow): ParcelWorldId {
+ return ParcelWorldId(row[name], row[uid]?.toUUID())
+ }
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
+ val id = getId(worldId) ?: return null
+ return select { WorldsT.id eq id }.firstOrNull()?.let { it[WorldsT.creation_time] }
+ }
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
+ val id = getOrInitId(worldId)
+ update({ WorldsT.id eq id }) {
+ it[WorldsT.creation_time] = time
+ }
+ }
+}
+
+object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id") {
+ val world_id = integer("world_id").references(WorldsT.id)
+ val px = integer("px")
+ val pz = integer("pz")
+ val owner_id = integer("owner_id").references(ProfilesT.id).nullable()
+ val sign_oudated = bool("sign_outdated").default(false)
+ val claim_time = datetime("claim_time").nullable()
+ val index_location = uniqueIndexR("index_location", world_id, px, pz)
+
+ private inline fun getId(worldId: Int, parcelX: Int, parcelZ: Int): Int? = getId { world_id.eq(worldId) and px.eq(parcelX) and pz.eq(parcelZ) }
+ private inline fun getId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int? = WorldsT.getId(worldName, worldUid)?.let { getId(it, parcelX, parcelZ) }
+ private inline fun getOrInitId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int {
+ val worldId = WorldsT.getOrInitId(worldName, worldUid)
+ return getOrInitId(
+ { getId(worldId, parcelX, parcelZ) },
+ { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ },
+ { "parcel at $worldName($parcelX, $parcelZ)" })
+ }
+
+ override fun getId(parcel: ParcelId): Int? = getId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
+ override fun getOrInitId(parcel: ParcelId): Int = getOrInitId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
+
+ private inline fun getRow(id: Int): ResultRow? = select { ParcelsT.id eq id }.firstOrNull()
+ fun getRow(parcel: ParcelId): ResultRow? = getId(parcel)?.let { getRow(it) }
+
+ override fun getItem(row: ResultRow): ParcelId? {
+ val worldId = row[world_id]
+ val world = WorldsT.getItem(worldId) ?: return null
+ return ParcelId(world, row[px], row[pz])
+ }
+}
+
+object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcels_profiles", "owner_id") {
+ val uuid = binary("uuid", 16).nullable()
+ val name = varchar("name", 32).nullable()
+
+ // MySQL dialect MUST permit multiple null values for this to work. Server SQL does not allow this. That dialect is shit anyway.
+ val uuid_constraint = uniqueIndexR("uuid_constraint", uuid)
+ val index_pair = uniqueIndexR("index_pair", uuid, name)
+
+
+ private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
+ private inline fun getId(uuid: UUID) = getId(uuid.toByteArray())
+ private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
+ private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
+
+ private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid ->
+ getOrInitId(
+ { getId(binaryUuid) },
+ { it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
+ { "profile(uuid = $uuid, name = $name)" })
+ }
+
+ private inline fun getOrInitId(name: String) = getOrInitId(
+ { getId(name) },
+ { it[ProfilesT.name] = name },
+ { "owner(name = $name)" })
+
+
+ override fun getId(profile: PlayerProfile): Int? = when (profile) {
+ is PlayerProfile.Real -> getId(profile.uuid)
+ is PlayerProfile.Fake -> getId(profile.name)
+ is PlayerProfile.Unresolved -> getRealId(profile.name)
+ else -> throw IllegalArgumentException()
+ }
+
+ override fun getOrInitId(profile: PlayerProfile): Int = when (profile) {
+ is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.nameOrBukkitName)
+ is PlayerProfile.Fake -> getOrInitId(profile.name)
+ else -> throw IllegalArgumentException() // Unresolved profiles cannot be added to the database
+ }
+
+ override fun getItem(row: ResultRow): PlayerProfile {
+ return PlayerProfile(row[uuid]?.toUUID(), row[name])
+ }
+
+ fun getRealItem(id: Int): PlayerProfile.Real? {
+ return getItem(id) as? PlayerProfile.Real
+ }
+
+ /*
+ fun updatePlayerProfile(profile: PlayerProfile.Real) {
+ update({ uuid eq profile.uuid.toByteArray() }) {
+ it[name] = profile.nameOrBukkitName
+ }
+ }*/
+
+}
+
// val ParcelsWithOptionsT = ParcelsT.join(ParcelOptionsT, JoinType.INNER, onColumn = ParcelsT.id, otherColumn = ParcelOptionsT.parcel_id) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
index b9d16fc..26cfc7a 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
@@ -1,111 +1,111 @@
-@file:Suppress("PropertyName", "LocalVariableName", "NOTHING_TO_INLINE")
-
-package io.dico.parcels2.storage.exposed
-
-import io.dico.parcels2.*
-import io.dico.parcels2.Privilege.DEFAULT
-import io.dico.parcels2.util.ext.alsoIfTrue
-import kotlinx.coroutines.channels.SendChannel
-import org.jetbrains.exposed.sql.*
-
-object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_privilege_local", ParcelsT)
-object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_privilege_global", ProfilesT)
-
-object ParcelOptionsT : Table("parcels_options") {
- val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
- val interact_bitmask = binary("interact_bitmask", 4)
-}
-
-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)
- val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE)
- val privilege = integer("privilege")
- val index_pair = uniqueIndexR("index_pair", attach_id, profile_id)
-
- fun setPrivilege(attachedOn: AttachT, player: PlayerProfile.Real, privilege: Privilege) {
- privilege.requireNonTransient()
-
- if (privilege == DEFAULT) {
- val player_id = ProfilesT.getId(player) ?: return
- idTable.getId(attachedOn)?.let { holder ->
- deleteWhere { (attach_id eq holder) and (profile_id eq player_id) }
- }
- return
- }
-
- val holder = idTable.getOrInitId(attachedOn)
- val player_id = ProfilesT.getOrInitId(player)
- upsert(conflictIndex = index_pair) {
- it[attach_id] = holder
- it[profile_id] = player_id
- it[this.privilege] = privilege.number
- }
- }
-
- fun readPrivileges(id: Int): PrivilegesHolder? {
- val list = slice(profile_id, privilege).select { attach_id eq id }
- val result = PrivilegesHolder()
- for (row in list) {
- val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
- result.setRawStoredPrivilege(profile, Privilege.getByNumber(row[privilege]) ?: continue)
- }
- return result
- }
-
- fun sendAllPrivilegesH(channel: PrivilegesSendChannel<AttachT>) {
- val iterator = selectAll().orderBy(attach_id).iterator()
-
- if (iterator.hasNext()) {
- var row = iterator.next()
- var id: Int = row[attach_id]
- var attach: AttachT? = null
- var map: PrivilegesHolder? = null
-
- fun initAttachAndMap() {
- attach = idTable.getItem(id)
- map = attach?.let { PrivilegesHolder() }
- }
-
- fun sendIfPresent() {
- if (attach != null && map != null) {
- channel.offer(attach!! to map!!)
- }
- attach = null
- map = null
- }
-
- initAttachAndMap()
-
- do {
- val rowId = row[attach_id]
- if (rowId != id) {
- sendIfPresent()
- id = rowId
- initAttachAndMap()
- }
-
- if (attach == null) {
- continue // owner not found for this owner id
- }
-
- val profile = ProfilesT.getRealItem(row[profile_id])
- if (profile == null) {
- logger.error("Privilege from database is null, id ${row[profile_id]}")
- continue
- }
- val privilege = Privilege.getByNumber(row[privilege])
- if (privilege == null) {
- logger.error("Privilege from database is null, number ${row[this.privilege]}")
- continue
- }
- map!!.setRawStoredPrivilege(profile, privilege)
-
- } while (iterator.hasNext().alsoIfTrue { row = iterator.next() })
-
- sendIfPresent()
- }
- }
-
-}
+@file:Suppress("PropertyName", "LocalVariableName", "NOTHING_TO_INLINE")
+
+package io.dico.parcels2.storage.exposed
+
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.DEFAULT
+import io.dico.parcels2.util.ext.alsoIfTrue
+import kotlinx.coroutines.channels.SendChannel
+import org.jetbrains.exposed.sql.*
+
+object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_privilege_local", ParcelsT)
+object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_privilege_global", ProfilesT)
+
+object ParcelOptionsT : Table("parcels_options") {
+ val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
+ val interact_bitmask = binary("interact_bitmask", 4)
+}
+
+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)
+ val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE)
+ val privilege = integer("privilege")
+ val index_pair = uniqueIndexR("index_pair", attach_id, profile_id)
+
+ fun setPrivilege(attachedOn: AttachT, player: PlayerProfile.Real, privilege: Privilege) {
+ privilege.requireNonTransient()
+
+ if (privilege == DEFAULT) {
+ val player_id = ProfilesT.getId(player) ?: return
+ idTable.getId(attachedOn)?.let { holder ->
+ deleteWhere { (attach_id eq holder) and (profile_id eq player_id) }
+ }
+ return
+ }
+
+ val holder = idTable.getOrInitId(attachedOn)
+ val player_id = ProfilesT.getOrInitId(player)
+ upsert(conflictIndex = index_pair) {
+ it[attach_id] = holder
+ it[profile_id] = player_id
+ it[this.privilege] = privilege.number
+ }
+ }
+
+ fun readPrivileges(id: Int): PrivilegesHolder? {
+ val list = slice(profile_id, privilege).select { attach_id eq id }
+ val result = PrivilegesHolder()
+ for (row in list) {
+ val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
+ result.setRawStoredPrivilege(profile, Privilege.getByNumber(row[privilege]) ?: continue)
+ }
+ return result
+ }
+
+ fun sendAllPrivilegesH(channel: PrivilegesSendChannel<AttachT>) {
+ val iterator = selectAll().orderBy(attach_id).iterator()
+
+ if (iterator.hasNext()) {
+ var row = iterator.next()
+ var id: Int = row[attach_id]
+ var attach: AttachT? = null
+ var map: PrivilegesHolder? = null
+
+ fun initAttachAndMap() {
+ attach = idTable.getItem(id)
+ map = attach?.let { PrivilegesHolder() }
+ }
+
+ fun sendIfPresent() {
+ if (attach != null && map != null) {
+ channel.offer(attach!! to map!!)
+ }
+ attach = null
+ map = null
+ }
+
+ initAttachAndMap()
+
+ do {
+ val rowId = row[attach_id]
+ if (rowId != id) {
+ sendIfPresent()
+ id = rowId
+ initAttachAndMap()
+ }
+
+ if (attach == null) {
+ continue // owner not found for this owner id
+ }
+
+ val profile = ProfilesT.getRealItem(row[profile_id])
+ if (profile == null) {
+ logger.error("Privilege from database is null, id ${row[profile_id]}")
+ continue
+ }
+ val privilege = Privilege.getByNumber(row[privilege])
+ if (privilege == null) {
+ logger.error("Privilege from database is null, number ${row[this.privilege]}")
+ continue
+ }
+ map!!.setRawStoredPrivilege(profile, privilege)
+
+ } while (iterator.hasNext().alsoIfTrue { row = iterator.next() })
+
+ sendIfPresent()
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt
index acc7c5e..a512f2a 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt
@@ -1,9 +1,9 @@
-package io.dico.parcels2.storage.migration
-
-import io.dico.parcels2.storage.Storage
-import kotlinx.coroutines.Job
-
-interface Migration {
- fun migrateTo(storage: Storage): Job
-}
-
+package io.dico.parcels2.storage.migration
+
+import io.dico.parcels2.storage.Storage
+import kotlinx.coroutines.Job
+
+interface Migration {
+ fun migrateTo(storage: Storage): Job
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
index 831fe42..954da5d 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
@@ -1,118 +1,118 @@
-@file:Suppress("RedundantSuspendModifier", "DEPRECATION")
-
-package io.dico.parcels2.storage.migration.plotme
-
-import com.zaxxer.hikari.HikariDataSource
-import io.dico.parcels2.*
-import io.dico.parcels2.options.PlotmeMigrationOptions
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.storage.exposed.abs
-import io.dico.parcels2.storage.exposed.greaterOf
-import io.dico.parcels2.storage.migration.Migration
-import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmePlotPlayerMap
-import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmeTable
-import io.dico.parcels2.storage.toUUID
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.newFixedThreadPoolContext
-import org.jetbrains.exposed.sql.*
-import org.slf4j.LoggerFactory
-import java.sql.Blob
-import java.util.UUID
-import javax.sql.DataSource
-
-class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
- private var dataSource: DataSource? = null
- private var database: Database? = null
- private var isShutdown: Boolean = false
- private val mlogger = LoggerFactory.getLogger("PlotMe Migrator")
- private val tables = PlotmeTables(options.tableNamesUppercase)
- val dispatcher = newFixedThreadPoolContext(1, "PlotMe Migration Thread")
-
- private fun <T> transaction(statement: Transaction.() -> T) = org.jetbrains.exposed.sql.transactions.transaction(database!!, statement)
-
- override fun migrateTo(storage: Storage): Job {
- return launch(dispatcher) {
- init()
- doWork(storage)
- shutdown()
- }
- }
-
- fun init() {
- if (isShutdown || database != null) throw IllegalStateException()
- dataSource = options.storage.getDataSourceFactory()!!()
- database = Database.connect(dataSource!!)
- }
-
- fun shutdown() {
- if (isShutdown) throw IllegalStateException()
- dataSource?.let {
- (it as? HikariDataSource)?.close()
- }
- database = null
- isShutdown = true
- }
-
- suspend fun doWork(target: Storage) = with (tables) {
- val exit = transaction {
- (!PlotmePlots.exists()).also {
- if (it) mlogger.warn("Plotme tables don't appear to exist. Exiting.")
- }
- }
- if (exit) return
-
- val worldCache = options.worldsFromTo.mapValues { ParcelWorldId(it.value) }
-
- fun getParcelId(table: PlotmeTable, row: ResultRow): ParcelId? {
- val world = worldCache[row[table.world_name]] ?: return null
- return ParcelId(world, row[table.px], row[table.pz])
- }
-
- fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: Privilege) {
- selectAll().forEach { row ->
- val parcel = getParcelId(this, row) ?: return@forEach
- val profile = PrivilegeKey.safe(row[player_uuid]?.toUUID(), row[player_name]) ?: return@forEach
- target.setLocalPrivilege(parcel, profile, kind)
- }
- }
-
- mlogger.info("Transmitting data from plotmeplots table")
- var count = 0
- transaction {
-
- PlotmePlots.selectAll()
- .orderBy(PlotmePlots.world_name)
- .orderBy(greaterOf(PlotmePlots.px.abs(), PlotmePlots.pz.abs()))
- .forEach { row ->
- val parcel = getParcelId(PlotmePlots, row) ?: return@forEach
- val owner = PlayerProfile.safe(row[PlotmePlots.owner_uuid]?.toUUID(), row[PlotmePlots.owner_name])
- target.setParcelOwner(parcel, owner)
- target.setParcelOwnerSignOutdated(parcel, true)
- ++count
- }
- }
-
- mlogger.info("Transmitting data from plotmeallowed table")
- transaction {
- PlotmeAllowed.transmitPlotmeAddedTable(Privilege.CAN_BUILD)
- }
-
- mlogger.info("Transmitting data from plotmedenied table")
- transaction {
- PlotmeDenied.transmitPlotmeAddedTable(Privilege.BANNED)
- }
-
- mlogger.warn("Data has been **transmitted**. $count plots were migrated to the parcels database.")
- mlogger.warn("Loading parcel data might take a while as enqueued transactions from this migration are completed.")
- }
-
- private fun Blob.toUUID(): UUID? {
- val ba = ByteArray(16)
- val count = binaryStream.read(ba, 0, 16)
- if (count < 16) return null
- return ba.toUUID()
- }
-
-
+@file:Suppress("RedundantSuspendModifier", "DEPRECATION")
+
+package io.dico.parcels2.storage.migration.plotme
+
+import com.zaxxer.hikari.HikariDataSource
+import io.dico.parcels2.*
+import io.dico.parcels2.options.PlotmeMigrationOptions
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.storage.exposed.abs
+import io.dico.parcels2.storage.exposed.greaterOf
+import io.dico.parcels2.storage.migration.Migration
+import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmePlotPlayerMap
+import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmeTable
+import io.dico.parcels2.storage.toUUID
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.newFixedThreadPoolContext
+import org.jetbrains.exposed.sql.*
+import org.slf4j.LoggerFactory
+import java.sql.Blob
+import java.util.UUID
+import javax.sql.DataSource
+
+class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
+ private var dataSource: DataSource? = null
+ private var database: Database? = null
+ private var isShutdown: Boolean = false
+ private val mlogger = LoggerFactory.getLogger("PlotMe Migrator")
+ private val tables = PlotmeTables(options.tableNamesUppercase)
+ val dispatcher = newFixedThreadPoolContext(1, "PlotMe Migration Thread")
+
+ private fun <T> transaction(statement: Transaction.() -> T) = org.jetbrains.exposed.sql.transactions.transaction(database!!, statement)
+
+ override fun migrateTo(storage: Storage): Job {
+ return launch(dispatcher) {
+ init()
+ doWork(storage)
+ shutdown()
+ }
+ }
+
+ fun init() {
+ if (isShutdown || database != null) throw IllegalStateException()
+ dataSource = options.storage.getDataSourceFactory()!!()
+ database = Database.connect(dataSource!!)
+ }
+
+ fun shutdown() {
+ if (isShutdown) throw IllegalStateException()
+ dataSource?.let {
+ (it as? HikariDataSource)?.close()
+ }
+ database = null
+ isShutdown = true
+ }
+
+ suspend fun doWork(target: Storage) = with (tables) {
+ val exit = transaction {
+ (!PlotmePlots.exists()).also {
+ if (it) mlogger.warn("Plotme tables don't appear to exist. Exiting.")
+ }
+ }
+ if (exit) return
+
+ val worldCache = options.worldsFromTo.mapValues { ParcelWorldId(it.value) }
+
+ fun getParcelId(table: PlotmeTable, row: ResultRow): ParcelId? {
+ val world = worldCache[row[table.world_name]] ?: return null
+ return ParcelId(world, row[table.px], row[table.pz])
+ }
+
+ fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: Privilege) {
+ selectAll().forEach { row ->
+ val parcel = getParcelId(this, row) ?: return@forEach
+ val profile = PrivilegeKey.safe(row[player_uuid]?.toUUID(), row[player_name]) ?: return@forEach
+ target.setLocalPrivilege(parcel, profile, kind)
+ }
+ }
+
+ mlogger.info("Transmitting data from plotmeplots table")
+ var count = 0
+ transaction {
+
+ PlotmePlots.selectAll()
+ .orderBy(PlotmePlots.world_name)
+ .orderBy(greaterOf(PlotmePlots.px.abs(), PlotmePlots.pz.abs()))
+ .forEach { row ->
+ val parcel = getParcelId(PlotmePlots, row) ?: return@forEach
+ val owner = PlayerProfile.safe(row[PlotmePlots.owner_uuid]?.toUUID(), row[PlotmePlots.owner_name])
+ target.setParcelOwner(parcel, owner)
+ target.setParcelOwnerSignOutdated(parcel, true)
+ ++count
+ }
+ }
+
+ mlogger.info("Transmitting data from plotmeallowed table")
+ transaction {
+ PlotmeAllowed.transmitPlotmeAddedTable(Privilege.CAN_BUILD)
+ }
+
+ mlogger.info("Transmitting data from plotmedenied table")
+ transaction {
+ PlotmeDenied.transmitPlotmeAddedTable(Privilege.BANNED)
+ }
+
+ mlogger.warn("Data has been **transmitted**. $count plots were migrated to the parcels database.")
+ mlogger.warn("Loading parcel data might take a while as enqueued transactions from this migration are completed.")
+ }
+
+ private fun Blob.toUUID(): UUID? {
+ val ba = ByteArray(16)
+ val count = binaryStream.read(ba, 0, 16)
+ if (count < 16) return null
+ return ba.toUUID()
+ }
+
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt
index dc788c8..b276e11 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt
@@ -1,31 +1,31 @@
-package io.dico.parcels2.storage.migration.plotme
-
-import org.jetbrains.exposed.sql.Table
-
-class PlotmeTables(val uppercase: Boolean) {
- fun String.toCorrectCase() = if (uppercase) this else toLowerCase()
-
- val PlotmePlots = PlotmePlotsT()
- val PlotmeAllowed = PlotmeAllowedT()
- val PlotmeDenied = PlotmeDeniedT()
-
- inner abstract class PlotmeTable(name: String) : Table(name) {
- val px = integer("idX").primaryKey()
- val pz = integer("idZ").primaryKey()
- val world_name = varchar("world", 32).primaryKey()
- }
-
- inner abstract class PlotmePlotPlayerMap(name: String) : PlotmeTable(name) {
- val player_name = varchar("player", 32)
- val player_uuid = blob("playerid").nullable()
- }
-
- inner class PlotmePlotsT : PlotmeTable("plotmePlots".toCorrectCase()) {
- val owner_name = varchar("owner", 32)
- val owner_uuid = blob("ownerid").nullable()
- }
-
- inner class PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase())
- inner class PlotmeDeniedT : PlotmePlotPlayerMap("plotmeDenied".toCorrectCase())
-}
-
+package io.dico.parcels2.storage.migration.plotme
+
+import org.jetbrains.exposed.sql.Table
+
+class PlotmeTables(val uppercase: Boolean) {
+ fun String.toCorrectCase() = if (uppercase) this else toLowerCase()
+
+ val PlotmePlots = PlotmePlotsT()
+ val PlotmeAllowed = PlotmeAllowedT()
+ val PlotmeDenied = PlotmeDeniedT()
+
+ inner abstract class PlotmeTable(name: String) : Table(name) {
+ val px = integer("idX").primaryKey()
+ val pz = integer("idZ").primaryKey()
+ val world_name = varchar("world", 32).primaryKey()
+ }
+
+ inner abstract class PlotmePlotPlayerMap(name: String) : PlotmeTable(name) {
+ val player_name = varchar("player", 32)
+ val player_uuid = blob("playerid").nullable()
+ }
+
+ inner class PlotmePlotsT : PlotmeTable("plotmePlots".toCorrectCase()) {
+ val owner_name = varchar("owner", 32)
+ val owner_uuid = blob("ownerid").nullable()
+ }
+
+ inner class PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase())
+ inner class PlotmeDeniedT : PlotmePlotPlayerMap("plotmeDenied".toCorrectCase())
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt b/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt
index 618eaed..c7d813b 100644
--- a/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt
@@ -3,6 +3,7 @@ package io.dico.parcels2.util
import io.dico.parcels2.util.ext.isValid
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
+import java.lang.IllegalArgumentException
import java.util.UUID
fun getPlayerName(uuid: UUID): String? = getOfflinePlayer(uuid)?.name
@@ -12,3 +13,11 @@ fun getOfflinePlayer(uuid: UUID): OfflinePlayer? = Bukkit.getOfflinePlayer(uuid)
fun getOfflinePlayer(name: String): OfflinePlayer? = Bukkit.getOfflinePlayer(name).takeIf { it.isValid }
fun isServerThread(): Boolean = Thread.currentThread().name == "Server thread"
+
+fun isPlayerNameValid(name: String): Boolean =
+ name.length in 3..16
+ && name.find { it !in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" } == null
+
+fun checkPlayerNameValid(name: String) {
+ if (!isPlayerNameValid(name)) throw IllegalArgumentException("Invalid player name: $name")
+}
diff --git a/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt b/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt
index 3eb2e81..3904026 100644
--- a/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt
@@ -1,43 +1,43 @@
-package io.dico.parcels2.util
-
-import kotlinx.coroutines.CancellableContinuation
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Delay
-import kotlinx.coroutines.Runnable
-import kotlinx.coroutines.timeunit.TimeUnit
-import org.bukkit.plugin.Plugin
-import kotlin.coroutines.CoroutineContext
-
-abstract class MainThreadDispatcher : CoroutineDispatcher(), Delay {
- abstract val mainThread: Thread
- abstract fun runOnMainThread(task: Runnable)
-}
-
-@Suppress("FunctionName")
-fun MainThreadDispatcher(plugin: Plugin): MainThreadDispatcher {
- return object : MainThreadDispatcher() {
- override val mainThread: Thread = Thread.currentThread()
-
- override fun dispatch(context: CoroutineContext, block: Runnable) {
- doDispatch(block)
- }
-
- override fun runOnMainThread(task: Runnable) {
- doDispatch(task)
- }
-
- private fun doDispatch(task: Runnable) {
- if (Thread.currentThread() === mainThread) task.run()
- else plugin.server.scheduler.runTaskLater(plugin, task, 0)
- }
-
- override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
- val task = Runnable {
- with (continuation) { resumeUndispatched(Unit) }
- }
-
- val millis = unit.toMillis(time)
- plugin.server.scheduler.runTaskLater(plugin, task, (millis + 25) / 50 - 1)
- }
- }
+package io.dico.parcels2.util
+
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Delay
+import kotlinx.coroutines.Runnable
+import kotlinx.coroutines.timeunit.TimeUnit
+import org.bukkit.plugin.Plugin
+import kotlin.coroutines.CoroutineContext
+
+abstract class MainThreadDispatcher : CoroutineDispatcher(), Delay {
+ abstract val mainThread: Thread
+ abstract fun runOnMainThread(task: Runnable)
+}
+
+@Suppress("FunctionName")
+fun MainThreadDispatcher(plugin: Plugin): MainThreadDispatcher {
+ return object : MainThreadDispatcher() {
+ override val mainThread: Thread = Thread.currentThread()
+
+ override fun dispatch(context: CoroutineContext, block: Runnable) {
+ doDispatch(block)
+ }
+
+ override fun runOnMainThread(task: Runnable) {
+ doDispatch(task)
+ }
+
+ private fun doDispatch(task: Runnable) {
+ if (Thread.currentThread() === mainThread) task.run()
+ else plugin.server.scheduler.runTaskLater(plugin, task, 0)
+ }
+
+ override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
+ val task = Runnable {
+ with (continuation) { resumeUndispatched(Unit) }
+ }
+
+ val millis = unit.toMillis(time)
+ plugin.server.scheduler.runTaskLater(plugin, task, (millis + 25) / 50 - 1)
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/PluginAware.kt b/src/main/kotlin/io/dico/parcels2/util/PluginAware.kt
new file mode 100644
index 0000000..b55f991
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/util/PluginAware.kt
@@ -0,0 +1,19 @@
+@file:Suppress("RedundantLambdaArrow")
+
+package io.dico.parcels2.util
+
+import org.bukkit.plugin.Plugin
+import org.bukkit.scheduler.BukkitTask
+
+interface PluginAware {
+ val plugin: Plugin
+}
+
+inline fun PluginAware.schedule(delay: Int = 0, crossinline task: () -> Unit): BukkitTask {
+ return plugin.server.scheduler.runTaskLater(plugin, { -> task() }, delay.toLong())
+}
+
+inline fun PluginAware.scheduleRepeating(interval: Int, delay: Int = 0, crossinline task: () -> Unit): BukkitTask {
+ return plugin.server.scheduler.runTaskTimer(plugin, { -> task() }, delay.toLong(), interval.toLong())
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt b/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt
deleted file mode 100644
index f29ba2b..0000000
--- a/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package io.dico.parcels2.util
-
-import org.bukkit.plugin.Plugin
-import org.bukkit.scheduler.BukkitTask
-
-interface PluginScheduler {
- val plugin: Plugin
-
- fun schedule(delay: Int, task: () -> Unit): BukkitTask {
- return plugin.server.scheduler.runTaskLater(plugin, task, delay.toLong())
- }
-
- fun scheduleRepeating(delay: Int, interval: Int, task: () -> Unit): BukkitTask {
- return plugin.server.scheduler.runTaskTimer(plugin, task, delay.toLong(), interval.toLong())
- }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline fun PluginScheduler.schedule(noinline task: () -> Unit) = schedule(0, task)
-
diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt
index e160e55..1351b5d 100644
--- a/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt
@@ -1,108 +1,108 @@
-package io.dico.parcels2.util.ext
-
-import org.bukkit.Material
-import org.bukkit.Material.*
-
-/*
-colors:
-WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$,
-wood:
-OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$,
- */
-
-val Material.isBed
- get() = when (this) {
- WHITE_BED,
- ORANGE_BED,
- MAGENTA_BED,
- LIGHT_BLUE_BED,
- YELLOW_BED,
- LIME_BED,
- PINK_BED,
- GRAY_BED,
- LIGHT_GRAY_BED,
- CYAN_BED,
- PURPLE_BED,
- BLUE_BED,
- BROWN_BED,
- GREEN_BED,
- RED_BED,
- BLACK_BED -> true
- else -> false
- }
-
-val Material.isWoodDoor
- get() = when (this) {
- OAK_DOOR,
- BIRCH_DOOR,
- SPRUCE_DOOR,
- JUNGLE_DOOR,
- ACACIA_DOOR,
- DARK_OAK_DOOR -> true
- else -> false
- }
-
-val Material.isWoodTrapdoor
- get() = when (this) {
- OAK_TRAPDOOR,
- BIRCH_TRAPDOOR,
- SPRUCE_TRAPDOOR,
- JUNGLE_TRAPDOOR,
- ACACIA_TRAPDOOR,
- DARK_OAK_TRAPDOOR -> true
- else -> false
- }
-
-val Material.isWoodFenceGate
- get() = when (this) {
- OAK_FENCE_GATE,
- BIRCH_FENCE_GATE,
- SPRUCE_FENCE_GATE,
- JUNGLE_FENCE_GATE,
- ACACIA_FENCE_GATE,
- DARK_OAK_FENCE_GATE -> true
- else -> false
- }
-
-val Material.isWoodButton
- get() = when (this) {
- OAK_BUTTON,
- BIRCH_BUTTON,
- SPRUCE_BUTTON,
- JUNGLE_BUTTON,
- ACACIA_BUTTON,
- DARK_OAK_BUTTON -> true
- else -> false
- }
-
-private fun getMaterialPrefixed(prefix: String, name: String): Material {
- return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist")
-}
-
-fun getMaterialsWithWoodTypePrefix(name: String) = arrayOf(
- getMaterialPrefixed("OAK", name),
- getMaterialPrefixed("BIRCH", name),
- getMaterialPrefixed("SPRUCE", name),
- getMaterialPrefixed("JUNGLE", name),
- getMaterialPrefixed("ACACIA", name),
- getMaterialPrefixed("DARK_OAK", name)
-)
-
-fun getMaterialsWithWoolColorPrefix(name: String) = arrayOf(
- getMaterialPrefixed("WHITE", name),
- getMaterialPrefixed("ORANGE", name),
- getMaterialPrefixed("MAGENTA", name),
- getMaterialPrefixed("LIGHT_BLUE", name),
- getMaterialPrefixed("YELLOW", name),
- getMaterialPrefixed("LIME", name),
- getMaterialPrefixed("PINK", name),
- getMaterialPrefixed("GRAY", name),
- getMaterialPrefixed("LIGHT_GRAY", name),
- getMaterialPrefixed("CYAN", name),
- getMaterialPrefixed("PURPLE", name),
- getMaterialPrefixed("BLUE", name),
- getMaterialPrefixed("BROWN", name),
- getMaterialPrefixed("GREEN", name),
- getMaterialPrefixed("RED", name),
- getMaterialPrefixed("BLACK", name)
+package io.dico.parcels2.util.ext
+
+import org.bukkit.Material
+import org.bukkit.Material.*
+
+/*
+colors:
+WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$,
+wood:
+OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$,
+ */
+
+val Material.isBed
+ get() = when (this) {
+ WHITE_BED,
+ ORANGE_BED,
+ MAGENTA_BED,
+ LIGHT_BLUE_BED,
+ YELLOW_BED,
+ LIME_BED,
+ PINK_BED,
+ GRAY_BED,
+ LIGHT_GRAY_BED,
+ CYAN_BED,
+ PURPLE_BED,
+ BLUE_BED,
+ BROWN_BED,
+ GREEN_BED,
+ RED_BED,
+ BLACK_BED -> true
+ else -> false
+ }
+
+val Material.isWoodDoor
+ get() = when (this) {
+ OAK_DOOR,
+ BIRCH_DOOR,
+ SPRUCE_DOOR,
+ JUNGLE_DOOR,
+ ACACIA_DOOR,
+ DARK_OAK_DOOR -> true
+ else -> false
+ }
+
+val Material.isWoodTrapdoor
+ get() = when (this) {
+ OAK_TRAPDOOR,
+ BIRCH_TRAPDOOR,
+ SPRUCE_TRAPDOOR,
+ JUNGLE_TRAPDOOR,
+ ACACIA_TRAPDOOR,
+ DARK_OAK_TRAPDOOR -> true
+ else -> false
+ }
+
+val Material.isWoodFenceGate
+ get() = when (this) {
+ OAK_FENCE_GATE,
+ BIRCH_FENCE_GATE,
+ SPRUCE_FENCE_GATE,
+ JUNGLE_FENCE_GATE,
+ ACACIA_FENCE_GATE,
+ DARK_OAK_FENCE_GATE -> true
+ else -> false
+ }
+
+val Material.isWoodButton
+ get() = when (this) {
+ OAK_BUTTON,
+ BIRCH_BUTTON,
+ SPRUCE_BUTTON,
+ JUNGLE_BUTTON,
+ ACACIA_BUTTON,
+ DARK_OAK_BUTTON -> true
+ else -> false
+ }
+
+private fun getMaterialPrefixed(prefix: String, name: String): Material {
+ return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist")
+}
+
+fun getMaterialsWithWoodTypePrefix(name: String) = arrayOf(
+ getMaterialPrefixed("OAK", name),
+ getMaterialPrefixed("BIRCH", name),
+ getMaterialPrefixed("SPRUCE", name),
+ getMaterialPrefixed("JUNGLE", name),
+ getMaterialPrefixed("ACACIA", name),
+ getMaterialPrefixed("DARK_OAK", name)
+)
+
+fun getMaterialsWithWoolColorPrefix(name: String) = arrayOf(
+ getMaterialPrefixed("WHITE", name),
+ getMaterialPrefixed("ORANGE", name),
+ getMaterialPrefixed("MAGENTA", name),
+ getMaterialPrefixed("LIGHT_BLUE", name),
+ getMaterialPrefixed("YELLOW", name),
+ getMaterialPrefixed("LIME", name),
+ getMaterialPrefixed("PINK", name),
+ getMaterialPrefixed("GRAY", name),
+ getMaterialPrefixed("LIGHT_GRAY", name),
+ getMaterialPrefixed("CYAN", name),
+ getMaterialPrefixed("PURPLE", name),
+ getMaterialPrefixed("BLUE", name),
+ getMaterialPrefixed("BROWN", name),
+ getMaterialPrefixed("GREEN", name),
+ getMaterialPrefixed("RED", name),
+ getMaterialPrefixed("BLACK", name)
) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt
index 75aba35..e5e8aa9 100644
--- a/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt
@@ -1,81 +1,81 @@
-package io.dico.parcels2.util.ext
-
-import io.dico.dicore.Formatting
-import io.dico.parcels2.logger
-import java.io.File
-
-fun File.tryCreate(): Boolean {
- if (exists()) {
- return !isDirectory
- }
- val parent = parentFile
- if (parent == null || !(parent.exists() || parent.mkdirs()) || !createNewFile()) {
- logger.warn("Failed to create file $canonicalPath")
- return false
- }
- return true
-}
-
-inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() }
-inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() }
-
-inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
-
-//inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
-//inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
-inline fun <T> T?.ifNullRun(block: () -> Unit): T? {
- if (this == null) block()
- return this
-}
-
-inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
- return EditLoopScope(this).doEditLoop(block)
-}
-
-inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.() -> Unit) {
- return EditLoopScope(this).doEditLoop(block)
-}
-
-class EditLoopScope<T, U>(val _map: MutableMap<T, U>) {
- private var iterator: MutableIterator<MutableMap.MutableEntry<T, U>>? = null
- lateinit var _entry: MutableMap.MutableEntry<T, U>
-
- inline val key get() = _entry.key
- inline var value
- get() = _entry.value
- set(target) = run { _entry.setValue(target) }
-
- inline fun doEditLoop(block: EditLoopScope<T, U>.() -> Unit) {
- val it = _initIterator()
- while (it.hasNext()) {
- _entry = it.next()
- block()
- }
- }
-
- inline fun doEditLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
- val it = _initIterator()
- while (it.hasNext()) {
- val entry = it.next().also { _entry = it }
- block(entry.key, entry.value)
- }
- }
-
- fun remove() {
- iterator!!.remove()
- }
-
- fun _initIterator(): MutableIterator<MutableMap.MutableEntry<T, U>> {
- iterator?.let { throw IllegalStateException() }
- return _map.entries.iterator().also { iterator = it }
- }
-
-}
-
-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)
-}
+package io.dico.parcels2.util.ext
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.logger
+import java.io.File
+
+fun File.tryCreate(): Boolean {
+ if (exists()) {
+ return !isDirectory
+ }
+ val parent = parentFile
+ if (parent == null || !(parent.exists() || parent.mkdirs()) || !createNewFile()) {
+ logger.warn("Failed to create file $canonicalPath")
+ return false
+ }
+ return true
+}
+
+inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() }
+inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() }
+
+inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
+
+//inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
+//inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
+inline fun <T> T?.ifNullRun(block: () -> Unit): T? {
+ if (this == null) block()
+ return this
+}
+
+inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
+ return EditLoopScope(this).doEditLoop(block)
+}
+
+inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.() -> Unit) {
+ return EditLoopScope(this).doEditLoop(block)
+}
+
+class EditLoopScope<T, U>(val _map: MutableMap<T, U>) {
+ private var iterator: MutableIterator<MutableMap.MutableEntry<T, U>>? = null
+ lateinit var _entry: MutableMap.MutableEntry<T, U>
+
+ inline val key get() = _entry.key
+ inline var value
+ get() = _entry.value
+ set(target) = run { _entry.setValue(target) }
+
+ inline fun doEditLoop(block: EditLoopScope<T, U>.() -> Unit) {
+ val it = _initIterator()
+ while (it.hasNext()) {
+ _entry = it.next()
+ block()
+ }
+ }
+
+ inline fun doEditLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
+ val it = _initIterator()
+ while (it.hasNext()) {
+ val entry = it.next().also { _entry = it }
+ block(entry.key, entry.value)
+ }
+ }
+
+ fun remove() {
+ iterator!!.remove()
+ }
+
+ fun _initIterator(): MutableIterator<MutableMap.MutableEntry<T, U>> {
+ iterator?.let { throw IllegalStateException() }
+ return _map.entries.iterator().also { iterator = it }
+ }
+
+}
+
+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)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
index a7f21c5..4c502a0 100644
--- a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
@@ -1,57 +1,57 @@
-package io.dico.parcels2.util.ext
-
-import io.dico.dicore.Formatting
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.logger
-import org.bukkit.OfflinePlayer
-import org.bukkit.entity.Player
-import org.bukkit.permissions.Permissible
-import org.bukkit.plugin.java.JavaPlugin
-
-inline val OfflinePlayer.uuid get() = uniqueId
-
-@Suppress("UsePropertyAccessSyntax")
-inline val OfflinePlayer.isValid
- get() = isOnline() || hasPlayedBefore()
-
-const val PERM_BAN_BYPASS = "parcels.admin.bypass.ban"
-const val PERM_BUILD_ANYWHERE = "parcels.admin.bypass.build"
-const val PERM_ADMIN_MANAGE = "parcels.admin.manage"
-
-inline val Permissible.hasPermBanBypass get() = hasPermission(PERM_BAN_BYPASS)
-inline val Permissible.hasPermGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode")
-inline val Permissible.hasPermBuildAnywhere get() = hasPermission(PERM_BUILD_ANYWHERE)
-inline val Permissible.hasPermAdminManage get() = hasPermission(PERM_ADMIN_MANAGE)
-inline val Permissible.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others")
-inline val Permissible.hasPermRandomSpecific get() = hasPermission("parcels.command.random.specific")
-val Player.parcelLimit: Int
- get() {
- for (info in effectivePermissions) {
- val perm = info.permission
- if (perm.startsWith("parcels.limit.")) {
- val limitString = perm.substring("parcels.limit.".length)
- if (limitString == "*") {
- return Int.MAX_VALUE
- }
- return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also {
- logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).")
- }
- }
- }
- return DEFAULT_LIMIT
- }
-
-private const val DEFAULT_LIMIT = 1
-private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a")
-
-fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) {
- if (except) {
- sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message))
- } else if (nopermit) {
- sendMessage(prefix + Formatting.RED + Formatting.translateChars('&', message))
- } else {
- sendMessage(prefix + Formatting.translateChars('&', message))
- }
-}
-
-const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:"
+package io.dico.parcels2.util.ext
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.logger
+import org.bukkit.OfflinePlayer
+import org.bukkit.entity.Player
+import org.bukkit.permissions.Permissible
+import org.bukkit.plugin.java.JavaPlugin
+
+inline val OfflinePlayer.uuid get() = uniqueId
+
+@Suppress("UsePropertyAccessSyntax")
+inline val OfflinePlayer.isValid
+ get() = isOnline() || hasPlayedBefore()
+
+const val PERM_BAN_BYPASS = "parcels.admin.bypass.ban"
+const val PERM_BUILD_ANYWHERE = "parcels.admin.bypass.build"
+const val PERM_ADMIN_MANAGE = "parcels.admin.manage"
+
+inline val Permissible.hasPermBanBypass get() = hasPermission(PERM_BAN_BYPASS)
+inline val Permissible.hasPermGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode")
+inline val Permissible.hasPermBuildAnywhere get() = hasPermission(PERM_BUILD_ANYWHERE)
+inline val Permissible.hasPermAdminManage get() = hasPermission(PERM_ADMIN_MANAGE)
+inline val Permissible.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others")
+inline val Permissible.hasPermRandomSpecific get() = hasPermission("parcels.command.random.specific")
+val Player.parcelLimit: Int
+ get() {
+ for (info in effectivePermissions) {
+ val perm = info.permission
+ if (perm.startsWith("parcels.limit.")) {
+ val limitString = perm.substring("parcels.limit.".length)
+ if (limitString == "*") {
+ return Int.MAX_VALUE
+ }
+ return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also {
+ logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).")
+ }
+ }
+ }
+ return DEFAULT_LIMIT
+ }
+
+private const val DEFAULT_LIMIT = 1
+private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a")
+
+fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) {
+ if (except) {
+ sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message))
+ } else if (nopermit) {
+ sendMessage(prefix + Formatting.RED + Formatting.translateChars('&', message))
+ } else {
+ sendMessage(prefix + Formatting.translateChars('&', message))
+ }
+}
+
+const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:"
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt b/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt
index cf67148..5b16860 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt
@@ -1,19 +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]
- }
+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]
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Math.kt b/src/main/kotlin/io/dico/parcels2/util/math/Math.kt
index 12c3e9f..5f8deef 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Math.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Math.kt
@@ -1,42 +1,42 @@
-package io.dico.parcels2.util.math
-
-fun Double.floor(): Int {
- val down = toInt()
- if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) {
- return down - 1
- }
- return down
-}
-
-infix fun Int.umod(divisor: Int): Int {
- val out = this % divisor
- if (out < 0) {
- return out + divisor
- }
- return out
-}
-
-val Int.even: Boolean get() = and(1) == 0
-
-fun IntRange.clamp(min: Int, max: Int): IntRange {
- if (first < min) {
- if (last > max) {
- return IntRange(min, max)
- }
- return IntRange(min, last)
- }
- if (last > max) {
- return IntRange(first, max)
- }
- return this
-}
-
-// 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 {
- return -Math.floorDiv(-this, divisor)
+package io.dico.parcels2.util.math
+
+fun Double.floor(): Int {
+ val down = toInt()
+ if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) {
+ return down - 1
+ }
+ return down
+}
+
+infix fun Int.umod(divisor: Int): Int {
+ val out = this % divisor
+ if (out < 0) {
+ return out + divisor
+ }
+ return out
+}
+
+val Int.even: Boolean get() = and(1) == 0
+
+fun IntRange.clamp(min: Int, max: Int): IntRange {
+ if (first < min) {
+ if (last > max) {
+ return IntRange(min, max)
+ }
+ return IntRange(min, last)
+ }
+ if (last > max) {
+ return IntRange(first, max)
+ }
+ return this
+}
+
+// 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 {
+ return -Math.floorDiv(-this, divisor)
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Region.kt b/src/main/kotlin/io/dico/parcels2/util/math/Region.kt
index cdbd497..cdcbe0e 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Region.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Region.kt
@@ -1,37 +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
- }
- }
-
+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
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt
index 5945120..3b25526 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt
@@ -1,9 +1,9 @@
-package io.dico.parcels2.util.math
-
-data class Vec2i(
- val x: Int,
- val z: Int
-) {
- fun add(ox: Int, oz: Int) = Vec2i(x + ox, z + oz)
- fun toChunk() = Vec2i(x shr 4, z shr 4)
-}
+package io.dico.parcels2.util.math
+
+data class Vec2i(
+ val x: Int,
+ val z: Int
+) {
+ fun add(ox: Int, oz: Int) = Vec2i(x + ox, z + oz)
+ fun toChunk() = Vec2i(x shr 4, z shr 4)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt
index 787f46c..2c3512f 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt
@@ -11,6 +11,8 @@ data class Vec3d(
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 plus(o: Vec3i) = Vec3d(x + o.x, y + o.y, z + o.z)
+ operator fun minus(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)
@@ -50,4 +52,10 @@ data class Vec3d(
Dimension.Y -> addY(value)
Dimension.Z -> addZ(value)
}
+
+ fun copyInto(loc: Location) {
+ loc.x = x
+ loc.y = y
+ loc.z = z
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
index b25764e..b3ba169 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
@@ -15,7 +15,9 @@ data class Vec3i(
fun toVec2i() = Vec2i(x, z)
operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z)
+ operator fun plus(o: Vec3d) = Vec3d(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: Vec3d) = Vec3d(x - o.x, y - o.y, z - o.z)
infix fun addX(o: Int) = Vec3i(x + o, y, z)
infix fun addY(o: Int) = Vec3i(x, y + o, z)
infix fun addZ(o: Int) = Vec3i(x, y, z + o)
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index eff524c..c2e272e 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -1,17 +1,17 @@
-<configuration>
- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg\n</pattern>-->
- <pattern>%magenta(%-16.-16(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern>
- </encoder>
- </appender>
-
- <root level="debug">
- <appender-ref ref="STDOUT" />
- </root>
-
- <logger name="com.zaxxer.hikari.pool.HikariPool" level="info"/>
- <logger name="com.zaxxer.hikari.pool.PoolBase" level="info"/>
- <logger name="com.zaxxer.hikari.HikariConfig" level="info"/>
- <logger name="Exposed" level="info"/>
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg\n</pattern>-->
+ <pattern>%magenta(%-16.-16(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern>
+ </encoder>
+ </appender>
+
+ <root level="debug">
+ <appender-ref ref="STDOUT" />
+ </root>
+
+ <logger name="com.zaxxer.hikari.pool.HikariPool" level="info"/>
+ <logger name="com.zaxxer.hikari.pool.PoolBase" level="info"/>
+ <logger name="com.zaxxer.hikari.HikariConfig" level="info"/>
+ <logger name="Exposed" level="info"/>
</configuration> \ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 19c68da..8830377 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,6 +1,6 @@
-name: Parcels
-author: Dico
-main: io.dico.parcels2.ParcelsPlugin
-version: 0.1
-api-version: 1.13
+name: Parcels
+author: Dico
+main: io.dico.parcels2.ParcelsPlugin
+version: 0.1
+api-version: 1.13
load: STARTUP \ No newline at end of file