summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico <dico.karssiens@gmail.com>2018-09-28 21:16:14 +0100
committerDico <dico.karssiens@gmail.com>2018-09-28 21:16:14 +0100
commit67cb73e4c713118b266f8d2b25eafe9527af7c73 (patch)
tree8247a282a6ed0464699bb2176e0b0b3771482237
parentbb6ae7d37037180f4cb29a084b1e2ab1a86c747a (diff)
10/10 commit messages btw
-rw-r--r--build.gradle.kts2
-rw-r--r--src/main/kotlin/io/dico/parcels2/JobDispatcher.kt (renamed from src/main/kotlin/io/dico/parcels2/blockvisitor/JobDispatcher.kt)26
-rw-r--r--src/main/kotlin/io/dico/parcels2/Parcel.kt17
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt36
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelId.kt30
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelWorld.kt15
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt139
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt27
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt49
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt9
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt188
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt88
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt89
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt65
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt30
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt3
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/Options.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Backing.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Storage.kt12
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt7
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt14
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt28
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt41
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt5
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt2
31 files changed, 598 insertions, 348 deletions
diff --git a/build.gradle.kts b/build.gradle.kts
index bd94b3f..12c2d97 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -77,7 +77,7 @@ dependencies {
// not on sk89q maven repo yet
compileClasspath(files("$rootDir/debug/plugins/worldedit-bukkit-7.0.0-beta-01.jar"))
- //compileClasspath(files("$rootDir/debug/lib/spigot-1.13.1.jar"))
+ compileClasspath(files("$rootDir/debug/lib/spigot-1.13.1.jar"))
compile("org.jetbrains.exposed:exposed:0.10.5") { isTransitive = false }
compile("joda-time:joda-time:2.10")
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/JobDispatcher.kt b/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt
index c6bcacc..10da0da 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/JobDispatcher.kt
+++ b/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt
@@ -1,7 +1,5 @@
-package io.dico.parcels2.blockvisitor
+package io.dico.parcels2
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.logger
import io.dico.parcels2.util.math.clampMin
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
@@ -23,9 +21,9 @@ data class TickJobtimeOptions(var jobTime: Int, var tickInterval: Int)
interface JobDispatcher {
/**
- * Submit a [task] that should be run synchronously, but limited such that it does not stall the server
+ * Submit a [function] that should be run synchronously, but limited such that it does not stall the server
*/
- fun dispatch(task: JobFunction): Job
+ fun dispatch(function: JobFunction): Job
/**
* Get a list of all jobs
@@ -55,7 +53,7 @@ interface Job : JobAndScopeMembersUnion {
/**
* The coroutine associated with this job
*/
- val job: CoroutineJob
+ val coroutine: CoroutineJob
/**
* true if this job has completed
@@ -147,8 +145,8 @@ class BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJo
private val _jobs = LinkedList<JobInternal>()
override val jobs: List<Job> = _jobs
- override fun dispatch(task: JobFunction): Job {
- val job: JobInternal = JobImpl(plugin, task)
+ override fun dispatch(function: JobFunction): Job {
+ val job: JobInternal = JobImpl(plugin, function)
if (bukkitTask == null) {
val completed = job.resume(options.jobTime.toLong())
@@ -198,7 +196,7 @@ class BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJo
}
private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
- override val job: CoroutineJob = scope.launch(start = LAZY) { task() }
+ override val coroutine: CoroutineJob = scope.launch(start = LAZY) { task() }
private var continuation: Continuation<Unit>? = null
private var nextSuspensionTime: Long = 0L
@@ -207,10 +205,10 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
override val elapsedTime
get() =
- if (job.isCompleted) startTimeOrElapsedTime
+ if (coroutine.isCompleted) startTimeOrElapsedTime
else currentTimeMillis() - startTimeOrElapsedTime
- override val isComplete get() = job.isCompleted
+ override val isComplete get() = coroutine.isCompleted
private var _progress = 0.0
override val progress get() = _progress
@@ -223,7 +221,7 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
private var onCompleted: JobUpdateLister? = null
init {
- job.invokeOnCompletion { exception ->
+ coroutine.invokeOnCompletion { exception ->
// report any error that occurred
completionException = exception?.also {
if (it !is CancellationException)
@@ -306,13 +304,13 @@ private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
isStarted = true
startTimeOrElapsedTime = System.currentTimeMillis()
- job.start()
+ coroutine.start()
return continuation == null
}
override suspend fun awaitCompletion() {
- job.join()
+ coroutine.join()
}
private fun delegateProgress(curPortion: Double, portion: Double): JobScope =
diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt
index 517b88f..4f89fe0 100644
--- a/src/main/kotlin/io/dico/parcels2/Parcel.kt
+++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt
@@ -1,6 +1,7 @@
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
@@ -19,7 +20,7 @@ interface Parcel : ParcelData, Privileges {
val pos: Vec2i
val x: Int
val z: Int
- val data: ParcelData
+ val data: ParcelDataHolder
val infoString: String
val hasBlockVisitors: Boolean
val globalPrivileges: GlobalPrivileges?
@@ -27,21 +28,21 @@ interface Parcel : ParcelData, Privileges {
override val keyOfOwner: PlayerProfile.Real?
get() = owner as? PlayerProfile.Real
- fun copyDataIgnoringDatabase(data: ParcelData)
+ fun copyData(newData: ParcelDataHolder, callerIsDatabase: Boolean = false)
- fun copyData(data: ParcelData)
+ fun dispose() = copyData(ParcelDataHolder())
- fun dispose()
-
- suspend fun withBlockVisitorPermit(block: suspend () -> Unit)
+ fun updateOwnerSign(force: Boolean = false)
val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
}
+
+
interface ParcelData : RawPrivileges {
var owner: PlayerProfile?
val lastClaimTime: DateTime?
- var ownerSignOutdated: Boolean
+ var isOwnerSignOutdated: Boolean
var interactableConfig: InteractableConfiguration
//fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
@@ -59,7 +60,7 @@ class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf())
: ParcelData, PrivilegesHolder(addedMap) {
override var owner: PlayerProfile? = null
override var lastClaimTime: DateTime? = null
- override var ownerSignOutdated = false
+ 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 d45ff83..109c5dc 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
@@ -1,15 +1,18 @@
package io.dico.parcels2
-import io.dico.parcels2.blockvisitor.*
+import io.dico.parcels2.blockvisitor.RegionTraverser
import io.dico.parcels2.util.math.Region
import io.dico.parcels2.util.math.Vec2i
+import io.dico.parcels2.util.math.Vec3i
import io.dico.parcels2.util.math.get
import kotlinx.coroutines.CoroutineScope
import org.bukkit.Chunk
import org.bukkit.Location
+import org.bukkit.Material
import org.bukkit.World
import org.bukkit.block.Biome
import org.bukkit.block.Block
+import org.bukkit.block.BlockFace
import org.bukkit.entity.Entity
import org.bukkit.generator.BlockPopulator
import org.bukkit.generator.ChunkGenerator
@@ -34,10 +37,12 @@ abstract class ParcelGenerator : ChunkGenerator() {
})
}
- abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId,
- container: ParcelContainer,
- coroutineScope: CoroutineScope,
- jobDispatcher: JobDispatcher): Pair<ParcelLocator, ParcelBlockManager>
+ abstract fun makeParcelLocatorAndBlockManager(
+ parcelProvider: ParcelProvider,
+ container: ParcelContainer,
+ coroutineScope: CoroutineScope,
+ jobDispatcher: JobDispatcher
+ ): Pair<ParcelLocator, ParcelBlockManager>
}
interface ParcelBlockManager {
@@ -45,7 +50,7 @@ interface ParcelBlockManager {
val jobDispatcher: JobDispatcher
val parcelTraverser: RegionTraverser
- // fun getBottomBlock(parcel: ParcelId): Vec2i
+ fun getRegionOrigin(parcel: ParcelId) = getRegion(parcel).origin.toVec2i()
fun getHomeLocation(parcel: ParcelId): Location
@@ -53,15 +58,15 @@ interface ParcelBlockManager {
fun getEntities(parcel: ParcelId): Collection<Entity>
- fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?)
+ fun isParcelInfoSectionLoaded(parcel: ParcelId): Boolean
- fun setBiome(parcel: ParcelId, biome: Biome): Job
+ fun updateParcelInfo(parcel: ParcelId, owner: PlayerProfile?)
- fun clearParcel(parcel: ParcelId): Job
+ fun getParcelForInfoBlockInteraction(block: Vec3i, type: Material, face: BlockFace): Parcel?
- fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Job
+ fun setBiome(parcel: ParcelId, biome: Biome): Job?
- fun submitBlockVisitor(vararg parcelIds: ParcelId, task: JobFunction): Job
+ fun clearParcel(parcel: ParcelId): Job?
/**
* Used to update owner blocks in the corner of the parcel
@@ -69,9 +74,12 @@ interface ParcelBlockManager {
fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i>
}
-inline fun ParcelBlockManager.doBlockOperation(parcel: ParcelId,
- traverser: RegionTraverser,
- crossinline operation: suspend JobScope.(Block) -> Unit) = submitBlockVisitor(parcel) {
+inline fun ParcelBlockManager.tryDoBlockOperation(
+ parcelProvider: ParcelProvider,
+ parcel: ParcelId,
+ traverser: RegionTraverser,
+ crossinline operation: suspend JobScope.(Block) -> Unit
+) = parcelProvider.trySubmitBlockVisitor(Permit(), arrayOf(parcel)) {
val region = getRegion(parcel)
val blockCount = region.blockCount.toDouble()
val blocks = traverser.traverseRegion(region)
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelId.kt b/src/main/kotlin/io/dico/parcels2/ParcelId.kt
index 926fc23..eef7129 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelId.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelId.kt
@@ -1,3 +1,5 @@
+@file:Suppress("FunctionName")
+
package io.dico.parcels2
import io.dico.parcels2.util.math.Vec2i
@@ -15,14 +17,12 @@ interface ParcelWorldId {
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) }
-
- companion object {
- operator fun invoke(worldName: String, worldUid: UUID?): ParcelWorldId = ParcelWorldIdImpl(worldName, worldUid)
- operator fun invoke(worldName: String): ParcelWorldId = ParcelWorldIdImpl(worldName, null)
- }
}
-fun ParcelWorldId.toStringExt() = "ParcelWorld($name)"
+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
@@ -35,24 +35,22 @@ interface ParcelId {
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)
-
- companion object {
- operator fun invoke(worldId: ParcelWorldId, pos: Vec2i) = invoke(worldId, pos.x, pos.z)
- operator fun invoke(worldName: String, worldUid: UUID?, pos: Vec2i) = invoke(worldName, worldUid, pos.x, pos.z)
- operator fun invoke(worldName: String, worldUid: UUID?, x: Int, z: Int) = invoke(ParcelWorldId(worldName, worldUid), x, z)
- operator fun invoke(worldId: ParcelWorldId, x: Int, z: Int): ParcelId = ParcelIdImpl(worldId, x, z)
- }
}
-fun ParcelId.toStringExt() = "Parcel(${worldId.name},$idString)"
+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() = toStringExt()
+ override fun toString() = parcelWorldIdToString()
}
private class ParcelIdImpl(override val worldId: ParcelWorldId,
override val x: Int,
override val z: Int) : ParcelId {
- override fun toString() = toStringExt()
+ 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 a3c6c24..c31b11a 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
@@ -9,8 +9,11 @@ import org.bukkit.World
import org.bukkit.block.Block
import org.bukkit.entity.Entity
import org.joda.time.DateTime
+import java.lang.IllegalStateException
import java.util.UUID
+class Permit
+
interface ParcelProvider {
val worlds: Map<String, ParcelWorld>
@@ -43,6 +46,15 @@ interface ParcelProvider {
fun getWorldGenerator(worldName: String): ParcelGenerator?
fun loadWorlds()
+
+ fun acquireBlockVisitorPermit(parcelId: ParcelId, with: Permit): Boolean
+
+ @Throws(IllegalStateException::class)
+ fun releaseBlockVisitorPermit(parcelId: ParcelId, with: Permit)
+
+ fun trySubmitBlockVisitor(permit: Permit, parcelIds: Array<out ParcelId>, function: JobFunction): Job?
+
+ fun swapParcels(parcelId1: ParcelId, parcelId2: ParcelId): Job?
}
interface ParcelLocator {
@@ -69,7 +81,7 @@ interface ParcelContainer {
fun getParcelById(id: Vec2i): Parcel? = getParcelById(id.x, id.z)
- fun getParcelById(id: ParcelId): Parcel? = getParcelById(id.x, id.z)
+ fun getParcelById(id: ParcelId): Parcel?
fun nextEmptyParcel(): Parcel?
@@ -88,5 +100,4 @@ interface ParcelWorld : ParcelLocator, ParcelContainer {
val globalPrivileges: GlobalPrivilegesManager
val creationTime: DateTime?
-
}
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
index b1bf554..a6ebcd8 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
@@ -3,8 +3,6 @@ package io.dico.parcels2
import io.dico.dicore.Registrator
import io.dico.dicore.command.EOverridePolicy
import io.dico.dicore.command.ICommandDispatcher
-import io.dico.parcels2.blockvisitor.BukkitJobDispatcher
-import io.dico.parcels2.blockvisitor.JobDispatcher
import io.dico.parcels2.command.getParcelCommands
import io.dico.parcels2.defaultimpl.GlobalPrivilegesManagerImpl
import io.dico.parcels2.defaultimpl.ParcelProviderImpl
@@ -17,6 +15,7 @@ import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.MainThreadDispatcher
import io.dico.parcels2.util.PluginScheduler
import io.dico.parcels2.util.ext.tryCreate
+import io.dico.parcels2.util.isServerThread
import kotlinx.coroutines.CoroutineScope
import org.bukkit.Bukkit
import org.bukkit.generator.ChunkGenerator
@@ -47,6 +46,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, options.tickJobtime) }
override fun onEnable() {
+ plogger.info("Is server thread: ${isServerThread()}")
plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
plogger.debug(System.getProperty("user.dir"))
if (!init()) {
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
index dc8ac28..b749b36 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
@@ -6,27 +6,89 @@ 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 {
-sealed class RegionTraverser {
- fun traverseRegion(region: Region, worldHeight: Int = 256): Iterable<Vec3i> =
- Iterable { iterator<Vec3i> { build(validify(region, worldHeight)) } }
-
- 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)
+ 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)
}
+ }
+
+}
- if (region.origin.y + region.size.y > worldHeight) {
- val size = region.size.withY(worldHeight - region.origin.y)
- return Region(region.origin, size)
+@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 {
- return region
+ /**
+ * 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 {
- protected abstract suspend fun Scope.build(region: Region)
+ /**
+ * 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))
@@ -34,9 +96,35 @@ sealed class RegionTraverser {
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(
@@ -50,7 +138,7 @@ sealed class RegionTraverser {
}
}
- override suspend fun Scope.build(region: Region) {
+ override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
val order = order
val (primary, secondary, tertiary) = order.toArray()
val (origin, size) = region
@@ -71,6 +159,7 @@ sealed class RegionTraverser {
}
}
+ /*medium.iterationCompleted()*/
}
}
@@ -91,7 +180,7 @@ sealed class RegionTraverser {
return region to null
}
- override suspend fun Scope.build(region: Region) {
+ override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
val (bottom, top) = slice(region, bottomSectionMaxY)
if (bottomFirst) {
@@ -101,15 +190,22 @@ sealed class RegionTraverser {
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 Directional ->
- return cur
+ /*is ParcelTraverser -> cur = cur.delegate*/
+ is Directional -> return cur
is Slicing ->
cur =
if (position.y <= cur.bottomSectionMaxY) cur.bottomTraverser
@@ -118,10 +214,17 @@ sealed class RegionTraverser {
}
}
+ /**
+ * 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
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
index 84931b2..df3cfab 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
@@ -1,5 +1,7 @@
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
diff --git a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
index 41f5d36..bcc7997 100644
--- a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
@@ -1,12 +1,12 @@
package io.dico.parcels2.command
-import io.dico.dicore.command.*
-import io.dico.parcels2.ParcelWorld
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.PlayerProfile
-import io.dico.parcels2.PlayerProfile.*
-import io.dico.parcels2.PrivilegeKey
-import io.dico.parcels2.blockvisitor.Job
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.EMessageType
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.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
@@ -42,15 +42,13 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
else -> throw CommandException()
}
- protected fun areYouSureMessage(context: ExecutionContext) = "Are you sure? You cannot undo this action!\n" +
- "Run \"/${context.route.joinToString(" ")} -sure\" if you want to go through with this."
-
- protected fun ParcelScope.clearWithProgressUpdates(context: ExecutionContext, action: String) {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
- world.blockManager.clearParcel(parcel.id)
+ 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) {
+ 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)
@@ -59,7 +57,6 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
.format(progress * 100, elapsedTime / 1000.0)
)
}
- }
override fun getCoroutineContext() = plugin.coroutineContext
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
index 79028a2..e700d6d 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
@@ -5,11 +5,10 @@ import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.Validate
import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.Flag
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.PlayerProfile
-import io.dico.parcels2.Privilege
+import io.dico.parcels2.*
import io.dico.parcels2.command.ParcelTarget.TargetKind
-import io.dico.parcels2.resolved
+import io.dico.parcels2.defaultimpl.DefaultParcelContainer
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@@ -23,6 +22,33 @@ class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
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? {
@@ -37,15 +63,17 @@ class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
if (!sure) return areYouSureMessage(context)
parcel.dispose()
- world.blockManager.clearParcel(parcel.id).reportProgressUpdates(context, "Reset")
+ 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? {
+ 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)
@@ -53,13 +81,14 @@ class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
?: throw CommandException("Invalid parcel target")
// Validate.isTrue(parcel2.world == world, "Parcel must be in the same world")
- Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in this parcel")
+ Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in that parcel")
val data = parcel.data
parcel.copyData(parcel2.data)
parcel2.copyData(data)
- world.blockManager.swapParcels(parcel.id, parcel2.id).reportProgressUpdates(context, "Swap")
+ 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
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
index c6a4d6a..e0bde7d 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
@@ -1,5 +1,6 @@
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
@@ -54,9 +55,9 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
)
val random = Random()
- world.blockManager.doBlockOperation(parcel.id, traverser = RegionTraverser.upward) { block ->
+ world.blockManager.tryDoBlockOperation(plugin.parcelProvider, parcel.id, traverser = RegionTraverser.upward) { block ->
block.blockData = blockDatas[random.nextInt(7)]
- }.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
+ }?.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
context.sendMessage(
EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
.format(progress * 100, elapsedTime / 1000.0)
@@ -82,7 +83,7 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("jobs")
fun cmdJobs(): Any? {
val workers = plugin.jobDispatcher.jobs
- println(workers.map { it.job }.joinToString(separator = "\n"))
+ println(workers.map { it.coroutine }.joinToString(separator = "\n"))
return "Task count: ${workers.size}"
}
@@ -95,7 +96,7 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@PreprocessArgs
fun cmdMessage(sender: CommandSender, message: String): Any? {
// testing @PreprocessArgs which merges "hello there" into a single argument
- sender.sendMessage(message)
+ sender.sendMessage(Formatting.translate(message))
return null
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
index 7f791cf..c918b80 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
@@ -127,7 +127,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? {
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
if (!sure) return areYouSureMessage(context)
- world.blockManager.clearParcel(parcel.id).reportProgressUpdates(context, "Clear")
+ world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Clear")
return null
}
@@ -135,7 +135,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
@RequireParcelPrivilege(Privilege.OWNER)
fun ParcelScope.cmdSetbiome(context: ExecutionContext, biome: Biome): Any? {
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
- world.blockManager.setBiome(parcel.id, biome).reportProgressUpdates(context, "Biome change")
+ world.blockManager.setBiome(parcel.id, biome)?.reportProgressUpdates(context, "Biome change")
return null
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
index f4d051e..70a8dda 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
@@ -92,8 +92,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget {
var input = buffer.next()
- val worldString = input.substringBefore("->", missingDelimiterValue = "")
- input = input.substringAfter("->")
+ val worldString = input.substringBefore("/", missingDelimiterValue = "")
+ input = input.substringAfter("/")
val world = if (worldString.isEmpty()) {
val player = requirePlayer(sender, parameter, "the world")
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
index 24db275..1193af3 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
@@ -64,7 +64,7 @@ class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer {
}
}
- fun allParcels(): Sequence<Parcel> = sequence {
+ fun getAllParcels(): Iterator<Parcel> = iterator {
for (array in parcels) {
yieldAll(array.iterator())
}
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
index 51671d4..e9ec148 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
@@ -1,22 +1,14 @@
package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
-import io.dico.parcels2.blockvisitor.*
+import io.dico.parcels2.blockvisitor.RegionTraverser
import io.dico.parcels2.options.DefaultGeneratorOptions
-import io.dico.parcels2.util.math.Region
-import io.dico.parcels2.util.math.Vec2i
-import io.dico.parcels2.util.math.Vec3i
-import io.dico.parcels2.util.math.even
-import io.dico.parcels2.util.math.umod
-import io.dico.parcels2.util.math.get
+import io.dico.parcels2.util.math.*
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
-import kotlinx.coroutines.launch
import org.bukkit.*
import org.bukkit.block.Biome
import org.bukkit.block.BlockFace
import org.bukkit.block.Skull
-import org.bukkit.block.data.BlockData
import org.bukkit.block.data.type.Slab
import org.bukkit.block.data.type.WallSign
import java.util.Random
@@ -118,12 +110,13 @@ class DefaultParcelGenerator(
}
override fun makeParcelLocatorAndBlockManager(
- worldId: ParcelWorldId,
+ parcelProvider: ParcelProvider,
container: ParcelContainer,
coroutineScope: CoroutineScope,
jobDispatcher: JobDispatcher
): Pair<ParcelLocator, ParcelBlockManager> {
- return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, jobDispatcher)
+ val impl = ParcelLocatorAndBlockManagerImpl(parcelProvider, container, coroutineScope, jobDispatcher)
+ return impl to impl
}
private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? {
@@ -139,11 +132,25 @@ class DefaultParcelGenerator(
return null
}
- private inner class ParcelLocatorImpl(
- val worldId: ParcelWorldId,
- val container: ParcelContainer
- ) : ParcelLocator {
- override val world: World = this@DefaultParcelGenerator.world
+ @Suppress("DEPRECATION")
+ private inner class ParcelLocatorAndBlockManagerImpl(
+ val parcelProvider: ParcelProvider,
+ val container: ParcelContainer,
+ coroutineScope: CoroutineScope,
+ override val jobDispatcher: JobDispatcher
+ ) : ParcelBlockManagerBase(), ParcelLocator, CoroutineScope by coroutineScope {
+
+ override val world: World get() = this@DefaultParcelGenerator.world
+ val worldId = parcelProvider.getWorld(world)?.id ?: ParcelWorldId(world)
+ override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight)
+
+ private val cornerWallType = when {
+ o.wallType is Slab -> (o.wallType.clone() as Slab).apply { type = Slab.Type.DOUBLE }
+ o.wallType.material.name.endsWith("CARPET") -> {
+ Bukkit.createBlockData(Material.getMaterial(o.wallType.material.name.substringBefore("CARPET") + "WOOL"))
+ }
+ else -> null
+ }
override fun getParcelAt(x: Int, z: Int): Parcel? {
return convertBlockLocationToId(x, z, container::getParcelById)
@@ -152,112 +159,113 @@ class DefaultParcelGenerator(
override fun getParcelIdAt(x: Int, z: Int): ParcelId? {
return convertBlockLocationToId(x, z) { idx, idz -> ParcelId(worldId, idx, idz) }
}
- }
- @Suppress("DEPRECATION")
- private inner class ParcelBlockManagerImpl(
- val worldId: ParcelWorldId,
- coroutineScope: CoroutineScope,
- override val jobDispatcher: JobDispatcher
- ) : ParcelBlockManagerBase(), CoroutineScope by coroutineScope {
- override val world: World = this@DefaultParcelGenerator.world
- override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight)
- /*override*/ fun getBottomBlock(parcel: ParcelId): Vec2i = Vec2i(
- sectionSize * (parcel.x - 1) + pathOffset + o.offsetX,
- sectionSize * (parcel.z - 1) + pathOffset + o.offsetZ
- )
+ private fun checkParcelId(parcel: ParcelId): ParcelId {
+ if (!parcel.worldId.equals(worldId)) {
+ throw IllegalArgumentException()
+ }
+ return parcel
+ }
- override fun getHomeLocation(parcel: ParcelId): Location {
- val bottom = getBottomBlock(parcel)
- val x = bottom.x + (o.parcelSize - 1) / 2.0
- val z = bottom.z - 2
- return Location(world, x + 0.5, o.floorHeight + 1.0, z + 0.5, 0F, 0F)
+ override fun getRegionOrigin(parcel: ParcelId): Vec2i {
+ checkParcelId(parcel)
+ return Vec2i(
+ sectionSize * (parcel.x - 1) + pathOffset + o.offsetX,
+ sectionSize * (parcel.z - 1) + pathOffset + o.offsetZ
+ )
}
override fun getRegion(parcel: ParcelId): Region {
- val bottom = getBottomBlock(parcel)
+ val origin = getRegionOrigin(parcel)
return Region(
- Vec3i(bottom.x, 0, bottom.z),
+ Vec3i(origin.x, 0, origin.z),
Vec3i(o.parcelSize, maxHeight, o.parcelSize)
)
}
- private fun getRegionConsideringWorld(parcel: ParcelId): Region {
- if (parcel.worldId != worldId) {
- (parcel.worldId as? ParcelWorld)?.let {
- return it.blockManager.getRegion(parcel)
+ override fun getHomeLocation(parcel: ParcelId): Location {
+ val origin = getRegionOrigin(parcel)
+ val x = origin.x + (o.parcelSize - 1) / 2.0
+ val z = origin.z - 2
+ return Location(world, x + 0.5, o.floorHeight + 1.0, z + 0.5, 0F, 0F)
+ }
+
+ override fun getParcelForInfoBlockInteraction(block: Vec3i, type: Material, face: BlockFace): Parcel? {
+ if (block.y != o.floorHeight + 1) return null
+
+ val expectedParcelOrigin = when (type) {
+ Material.WALL_SIGN -> Vec2i(block.x + 1, block.z + 2)
+ o.wallType.material, cornerWallType?.material -> {
+ if (face != BlockFace.NORTH || world[block + Vec3i.convert(BlockFace.NORTH)].type == Material.WALL_SIGN) {
+ return null
+ }
+
+ Vec2i(block.x + 1, block.z + 1)
}
- throw IllegalArgumentException()
+ else -> return null
}
- return getRegion(parcel)
+
+ return getParcelAt(expectedParcelOrigin.x, expectedParcelOrigin.z)
+ ?.takeIf { expectedParcelOrigin == getRegionOrigin(it.id) }
+ ?.also { parcel ->
+ if (type != Material.WALL_SIGN && parcel.owner != null) {
+ updateParcelInfo(parcel.id, parcel.owner)
+ parcel.isOwnerSignOutdated = false
+ }
+ }
}
- override fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?) {
- val b = getBottomBlock(parcel)
+ override fun isParcelInfoSectionLoaded(parcel: ParcelId): Boolean {
+ val wallBlockChunk = getRegionOrigin(parcel).add(-1, -1).toChunk()
+ return world.isChunkLoaded(wallBlockChunk.x, wallBlockChunk.z)
+ }
+
+ override fun updateParcelInfo(parcel: ParcelId, owner: PlayerProfile?) {
+ val b = getRegionOrigin(parcel)
val wallBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 1)
- val signBlock = world.getBlockAt(b.x - 2, o.floorHeight + 1, b.z - 1)
+ val signBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 2)
val skullBlock = world.getBlockAt(b.x - 1, o.floorHeight + 2, b.z - 1)
if (owner == null) {
wallBlock.blockData = o.wallType
signBlock.type = Material.AIR
skullBlock.type = Material.AIR
- } else {
- val wallBlockType: BlockData = if (o.wallType is Slab)
- (o.wallType.clone() as Slab).apply { type = Slab.Type.DOUBLE }
- else
- o.wallType
-
- wallBlock.blockData = wallBlockType
+ } else {
+ cornerWallType?.let { wallBlock.blockData = it }
signBlock.blockData = (Bukkit.createBlockData(Material.WALL_SIGN) as WallSign).apply { facing = BlockFace.NORTH }
val sign = signBlock.state as org.bukkit.block.Sign
sign.setLine(0, "${parcel.x},${parcel.z}")
- sign.setLine(2, owner.name)
+ sign.setLine(2, owner.name ?: "")
sign.update()
+ skullBlock.type = Material.AIR
skullBlock.type = Material.PLAYER_HEAD
val skull = skullBlock.state as Skull
if (owner is PlayerProfile.Real) {
skull.owningPlayer = Bukkit.getOfflinePlayer(owner.uuid)
- } else {
- skull.owner = owner.name
+
+ } else if (!skull.setOwner(owner.name)) {
+ skullBlock.type = Material.AIR
+ return
}
- skull.rotation = BlockFace.WEST
+
+ skull.rotation = BlockFace.SOUTH
skull.update()
}
}
- private fun getParcel(parcelId: ParcelId): Parcel? {
- // todo dont rely on this cast
- val world = worldId as? ParcelWorld ?: return null
- return world.getParcelById(parcelId)
+ private fun trySubmitBlockVisitor(vararg parcels: ParcelId, function: JobFunction): Job? {
+ parcels.forEach { checkParcelId(it) }
+ return parcelProvider.trySubmitBlockVisitor(Permit(), parcels, function)
}
- override fun submitBlockVisitor(vararg parcelIds: ParcelId, task: JobFunction): Job {
- val parcels = parcelIds.mapNotNull { getParcel(it) }
- if (parcels.isEmpty()) return jobDispatcher.dispatch(task)
- if (parcels.any { it.hasBlockVisitors }) throw IllegalArgumentException("This parcel already has a block visitor")
-
- val worker = jobDispatcher.dispatch(task)
-
- for (parcel in parcels) {
- launch(start = UNDISPATCHED) {
- parcel.withBlockVisitorPermit {
- worker.awaitCompletion()
- }
- }
- }
-
- return worker
- }
-
- override fun setBiome(parcel: ParcelId, biome: Biome): Job = submitBlockVisitor(parcel) {
+ override fun setBiome(parcel: ParcelId, biome: Biome) = trySubmitBlockVisitor(checkParcelId(parcel)) {
val world = world
- val b = getBottomBlock(parcel)
+ val b = getRegionOrigin(parcel)
val parcelSize = o.parcelSize
for (x in b.x until b.x + parcelSize) {
for (z in b.z until b.z + parcelSize) {
@@ -267,7 +275,7 @@ class DefaultParcelGenerator(
}
}
- override fun clearParcel(parcel: ParcelId): Job = submitBlockVisitor(parcel) {
+ override fun clearParcel(parcel: ParcelId) = trySubmitBlockVisitor(checkParcelId(parcel)) {
val region = getRegion(parcel)
val blocks = parcelTraverser.traverseRegion(region)
val blockCount = region.blockCount.toDouble()
@@ -291,22 +299,6 @@ class DefaultParcelGenerator(
}
}
- override fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Job = submitBlockVisitor(parcel1, parcel2) {
- var region1 = getRegionConsideringWorld(parcel1)
- var region2 = getRegionConsideringWorld(parcel2)
-
- val size = region1.size.clampMax(region2.size)
- if (size != region1.size) {
- region1 = region1.withSize(size)
- region2 = region2.withSize(size)
- }
-
- val schematicOf1 = delegateWork(0.25) { Schematic().apply { load(world, region1) } }
- val schematicOf2 = delegateWork(0.25) { Schematic().apply { load(world, region2) } }
- delegateWork(0.25) { with(schematicOf1) { paste(world, region2.origin) } }
- delegateWork(0.25) { with(schematicOf2) { paste(world, region1.origin) } }
- }
-
override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> {
/*
* Get the offsets for the world out of the way
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
index 16136e9..db5d193 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
@@ -3,59 +3,84 @@ package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
import io.dico.parcels2.Privilege.*
import io.dico.parcels2.util.ext.alsoIfTrue
+import io.dico.parcels2.util.isServerThread
import io.dico.parcels2.util.math.Vec2i
import org.bukkit.Material
import org.joda.time.DateTime
-import java.util.concurrent.atomic.AtomicInteger
+import java.lang.IllegalStateException
-class ParcelImpl(
+class ParcelImpl (
override val world: ParcelWorld,
override val x: Int,
override val z: Int
) : Parcel, ParcelId {
- override val id: ParcelId = this
+ override val id: ParcelId get() = this
override val pos get() = Vec2i(x, z)
- override var data: ParcelDataHolder = ParcelDataHolder(); private set
- override val hasBlockVisitors get() = blockVisitors.get() > 0
+ override var data = ParcelDataHolder(); private set
override val worldId: ParcelWorldId get() = world.id
- override fun copyDataIgnoringDatabase(data: ParcelData) {
- this.data = ((data as? Parcel)?.data ?: data) as ParcelDataHolder
- }
+ override fun copyData(newData: ParcelDataHolder, callerIsDatabase: Boolean) {
+ if (callerIsDatabase) {
+ data = newData
+ return
+ }
- override fun copyData(data: ParcelData) {
- copyDataIgnoringDatabase(data)
- world.storage.setParcelData(this, data)
- }
+ val ownerChanged = owner != newData.owner
+ if (ownerChanged) {
+ updateOwnerSign(true, false, true)
+ }
- override fun dispose() {
- copyDataIgnoringDatabase(ParcelDataHolder())
- world.storage.setParcelData(this, null)
- }
+ val ownerSignWasOutdated = if (callerIsDatabase) newData.isOwnerSignOutdated else isOwnerSignOutdated
+ val ownerChanged = owner != newData.owner
+
+ data = newData
+
+ if (ownerChanged && isServerThread()) {
+ updateOwnerSign(true, false, updateDatabase = callerIsDatabase)
+ } else {
+ newData.isOwnerSignOutdated = ownerChanged || ownerSignWasOutdated
+ }
+
+ world.storage.setParcelData(this, data)
+ }
override var owner: PlayerProfile?
get() = data.owner
set(value) {
if (data.owner != value) {
world.storage.setParcelOwner(this, value)
- world.blockManager.setOwnerBlock(this, value)
data.owner = value
+ updateOwnerSign(true, false, true)
}
}
override val lastClaimTime: DateTime?
get() = data.lastClaimTime
- override var ownerSignOutdated: Boolean
- get() = data.ownerSignOutdated
+ override var isOwnerSignOutdated: Boolean
+ get() = data.isOwnerSignOutdated
set(value) {
- if (data.ownerSignOutdated != value) {
+ if (data.isOwnerSignOutdated != value) {
world.storage.setParcelOwnerSignOutdated(this, value)
- data.ownerSignOutdated = value
+ data.isOwnerSignOutdated = value
}
}
+ override fun updateOwnerSign(force: Boolean) {
+ updateOwnerSign(false, force, true)
+ }
+
+ private fun updateOwnerSign(ownerChanged: Boolean, force: Boolean, updateDatabase: Boolean) {
+ if (!ownerChanged && !isOwnerSignOutdated && !force) return
+
+ val update = force || world.blockManager.isParcelInfoSectionLoaded(this)
+ if (update) world.blockManager.updateParcelInfo(this, owner)
+
+ if (updateDatabase) isOwnerSignOutdated = !update
+ else data.isOwnerSignOutdated = !update
+ }
+
override val privilegeMap: PrivilegeMap
get() = data.privilegeMap
@@ -106,7 +131,24 @@ class ParcelImpl(
}
}
+ override val hasBlockVisitors: Boolean
+ get() = permit != null
+ private var permit: Permit? = null
+
+ fun acquireBlockVisitorPermit(with: Permit): Boolean {
+ if (permit === with) return true
+ if (permit != null) return false
+ permit = with
+ return true
+ }
+
+ fun releaseBlockVisitorPermit(with: Permit) {
+ if (permit !== with) throw IllegalStateException()
+ permit = null
+ }
+
+ /*
private var blockVisitors = AtomicInteger(0)
override suspend fun withBlockVisitorPermit(block: suspend () -> Unit) {
@@ -116,9 +158,9 @@ class ParcelImpl(
} finally {
blockVisitors.getAndDecrement()
}
- }
+ }*/
- override fun toString() = toStringExt()
+ override fun toString() = parcelIdToString()
override val infoString: String
get() = getInfoString()
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
index 7a2534f..48a7fee 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
@@ -1,8 +1,10 @@
package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
+import io.dico.parcels2.blockvisitor.Schematic
import io.dico.parcels2.util.schedule
-import kotlinx.coroutines.Unconfined
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.bukkit.Bukkit
import org.bukkit.WorldCreator
@@ -44,10 +46,11 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
private fun loadWorlds0() {
if (Bukkit.getWorlds().isEmpty()) {
plugin.schedule(::loadWorlds0)
- plugin.logger.warning("Scheduling to load worlds in the next tick, \nbecause no bukkit worlds are loaded yet")
+ plugin.logger.warning("Scheduling to load worlds in the next tick because no bukkit worlds are loaded yet")
return
}
+ val newlyCreatedWorlds = mutableListOf<ParcelWorld>()
for ((worldName, worldOptions) in options.worlds.entries) {
var parcelWorld = _worlds[worldName]
if (parcelWorld != null) continue
@@ -56,19 +59,20 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
val worldExists = Bukkit.getWorld(worldName) != null
val bukkitWorld =
if (worldExists) Bukkit.getWorld(worldName)!!
- else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") }
+ else {
+ logger.info("Creating world $worldName")
+ WorldCreator(worldName).generator(generator).createWorld()
+ }
- parcelWorld = ParcelWorldImpl(
- bukkitWorld, generator, worldOptions.runtime, plugin.storage,
- plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.jobDispatcher
- )
+ parcelWorld = ParcelWorldImpl(plugin, bukkitWorld, generator, worldOptions.runtime,::DefaultParcelContainer)
if (!worldExists) {
val time = DateTime.now()
plugin.storage.setWorldCreationTime(parcelWorld.id, time)
parcelWorld.creationTime = time
+ newlyCreatedWorlds.add(parcelWorld)
} else {
- launch(context = Unconfined) {
+ GlobalScope.launch(context = Dispatchers.Unconfined) {
parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: DateTime.now()
}
}
@@ -76,11 +80,11 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
_worlds[worldName] = parcelWorld
}
- loadStoredData()
+ loadStoredData(newlyCreatedWorlds.toSet())
}
- private fun loadStoredData() {
- plugin.launch {
+ private fun loadStoredData(newlyCreatedWorlds: Collection<ParcelWorld> = emptyList()) {
+ plugin.launch(Dispatchers.Default) {
val migration = plugin.options.migration
if (migration.enabled) {
migration.instance?.newInstance()?.apply {
@@ -96,11 +100,14 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
}
logger.info("Loading all parcel data...")
- val channel = plugin.storage.transmitAllParcelData()
- while (true) {
- val (id, data) = channel.receiveOrNull() ?: break
- val parcel = getParcelById(id) ?: continue
- data?.let { parcel.copyDataIgnoringDatabase(it) }
+
+ val job1 = launch {
+ val channel = plugin.storage.transmitAllParcelData()
+ while (true) {
+ val (id, data) = channel.receiveOrNull() ?: break
+ val parcel = getParcelById(id) ?: continue
+ data?.let { parcel.copyData(it, callerIsDatabase = true) }
+ }
}
val channel2 = plugin.storage.transmitAllGlobalPrivileges()
@@ -113,11 +120,61 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
(plugin.globalPrivileges[profile] as PrivilegesHolder).copyPrivilegesFrom(data)
}
+ job1.join()
+
logger.info("Loading data completed")
_dataIsLoaded = true
}
}
+ override fun acquireBlockVisitorPermit(parcelId: ParcelId, with: Permit): Boolean {
+ val parcel = getParcelById(parcelId) as? ParcelImpl ?: return true
+ return parcel.acquireBlockVisitorPermit(with)
+ }
+
+ override fun releaseBlockVisitorPermit(parcelId: ParcelId, with: Permit) {
+ val parcel = getParcelById(parcelId) as? ParcelImpl ?: return
+ parcel.releaseBlockVisitorPermit(with)
+ }
+
+ override fun trySubmitBlockVisitor(permit: Permit, vararg parcelIds: ParcelId, function: JobFunction): Job? {
+ val withPermit = parcelIds.filter { acquireBlockVisitorPermit(it, permit) }
+ if (withPermit.size != parcelIds.size) {
+ withPermit.forEach { releaseBlockVisitorPermit(it, permit) }
+ return null
+ }
+
+ val job = plugin.jobDispatcher.dispatch(function)
+
+ plugin.launch {
+ job.awaitCompletion()
+ withPermit.forEach { releaseBlockVisitorPermit(it, permit) }
+ }
+
+ return job
+ }
+
+ override fun swapParcels(parcelId1: ParcelId, parcelId2: ParcelId): Job? {
+ val blockManager1 = getWorldById(parcelId1.worldId)?.blockManager ?: return null
+ val blockManager2 = getWorldById(parcelId2.worldId)?.blockManager ?: return null
+
+ return trySubmitBlockVisitor(Permit(), parcelId1, parcelId2) {
+ var region1 = blockManager1.getRegion(parcelId1)
+ var region2 = blockManager2.getRegion(parcelId2)
+
+ val size = region1.size.clampMax(region2.size)
+ if (size != region1.size) {
+ region1 = region1.withSize(size)
+ 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) } }
+ }
+ }
+
/*
fun loadWorlds(options: Options) {
for ((worldName, worldOptions) in options.worlds.entries) {
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
index 78257c3..531a25f 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
@@ -3,30 +3,27 @@
package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
-import io.dico.parcels2.blockvisitor.JobDispatcher
import io.dico.parcels2.options.RuntimeWorldOptions
import io.dico.parcels2.storage.Storage
import kotlinx.coroutines.CoroutineScope
+import org.bukkit.GameRule
import org.bukkit.World
import org.joda.time.DateTime
import java.util.UUID
-class ParcelWorldImpl(override val world: World,
- override val generator: ParcelGenerator,
- override var options: RuntimeWorldOptions,
- override val storage: Storage,
- override val globalPrivileges: GlobalPrivilegesManager,
- containerFactory: ParcelContainerFactory,
- coroutineScope: CoroutineScope,
- jobDispatcher: JobDispatcher)
- : ParcelWorld,
- ParcelWorldId,
- ParcelContainer, /* missing delegation */
- ParcelLocator /* missing delegation */ {
-
+class ParcelWorldImpl(
+ val plugin: ParcelsPlugin,
+ override val world: World,
+ override val generator: ParcelGenerator,
+ override var options: RuntimeWorldOptions,
+ containerFactory: ParcelContainerFactory
+) : ParcelWorld, ParcelWorldId, ParcelContainer, ParcelLocator {
override val id: ParcelWorldId get() = this
override val uid: UUID? get() = world.uid
+ override val storage get() = plugin.storage
+ override val globalPrivileges get() = plugin.globalPrivileges
+
init {
if (generator.world != world) {
throw IllegalArgumentException()
@@ -39,52 +36,40 @@ class ParcelWorldImpl(override val world: World,
override val blockManager: ParcelBlockManager
init {
- val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, jobDispatcher)
- locator = pair.first
- blockManager = pair.second
-
+ val (locator, blockManager) = generator.makeParcelLocatorAndBlockManager(plugin.parcelProvider, container, plugin, plugin.jobDispatcher)
+ this.locator = locator
+ this.blockManager = blockManager
enforceOptions()
}
fun enforceOptions() {
if (options.dayTime) {
- world.setGameRuleValue("doDaylightCycle", "false")
+ world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
world.setTime(6000)
}
if (options.noWeather) {
world.setStorm(false)
world.setThundering(false)
- world.weatherDuration = Integer.MAX_VALUE
+ world.weatherDuration = Int.MAX_VALUE
}
- world.setGameRuleValue("doTileDrops", "${options.doTileDrops}")
+ world.setGameRule(GameRule.DO_TILE_DROPS, options.doTileDrops)
}
- // Updated by ParcelProviderImpl
+ // Accessed by ParcelProviderImpl
override var creationTime: DateTime? = null
- /*
- Interface delegation needs to be implemented manually because JetBrains has yet to fix it.
- */
- // ParcelLocator interface
- override fun getParcelAt(x: Int, z: Int): Parcel? {
- return locator.getParcelAt(x, z)
- }
+ override fun getParcelAt(x: Int, z: Int): Parcel? = locator.getParcelAt(x, z)
- override fun getParcelIdAt(x: Int, z: Int): ParcelId? {
- return locator.getParcelIdAt(x, z)
- }
+ override fun getParcelIdAt(x: Int, z: Int): ParcelId? = locator.getParcelIdAt(x, z)
- // ParcelContainer interface
- override fun getParcelById(x: Int, z: Int): Parcel? {
- return container.getParcelById(x, z)
- }
+ override fun getParcelById(x: Int, z: Int): Parcel? = container.getParcelById(x, z)
- override fun nextEmptyParcel(): Parcel? {
- return container.nextEmptyParcel()
- }
+ override fun getParcelById(id: ParcelId): Parcel? = container.getParcelById(id)
+
+ override fun nextEmptyParcel(): Parcel? = container.nextEmptyParcel()
- override fun toString() = toStringExt()
+ override fun toString() = parcelWorldIdToString()
}
diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
index 6c3b355..04941b4 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
@@ -2,16 +2,13 @@ package io.dico.parcels2.listener
import gnu.trove.TLongCollection
import gnu.trove.set.hash.TLongHashSet
+import io.dico.dicore.Formatting
import io.dico.dicore.ListenerMarker
import io.dico.dicore.RegistratorListener
import io.dico.parcels2.*
import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.ext.*
-import io.dico.parcels2.util.math.Dimension
-import io.dico.parcels2.util.math.Vec3d
-import io.dico.parcels2.util.math.Vec3i
-import io.dico.parcels2.util.math.clampMax
-import io.dico.parcels2.util.math.clampMin
+import io.dico.parcels2.util.math.*
import org.bukkit.Location
import org.bukkit.Material.*
import org.bukkit.World
@@ -128,6 +125,8 @@ class ParcelListeners(
if (!canBuildOnArea(event.player, area)) {
event.isCancelled = true
}
+
+ area?.updateOwnerSign()
}
/*
@@ -253,10 +252,15 @@ class ParcelListeners(
}
}
- onPlayerInteractEvent_RightClick(event, world, parcel)
+ onPlayerRightClick(event, world, parcel)
+
+ if (!event.isCancelled && parcel == null) {
+ world.blockManager.getParcelForInfoBlockInteraction(Vec3i(clickedBlock), type, event.blockFace)
+ ?.apply { user.sendMessage(Formatting.GREEN + infoString) }
+ }
}
- Action.RIGHT_CLICK_AIR -> onPlayerInteractEvent_RightClick(event, world, parcel)
+ 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
@@ -265,7 +269,7 @@ class ParcelListeners(
}
@Suppress("NON_EXHAUSTIVE_WHEN")
- private fun onPlayerInteractEvent_RightClick(event: PlayerInteractEvent, world: ParcelWorld, parcel: Parcel?) {
+ private fun onPlayerRightClick(event: PlayerInteractEvent, world: ParcelWorld, parcel: Parcel?) {
if (event.hasItem()) {
val item = event.item.type
if (world.options.blockedItems.contains(item)) {
@@ -275,7 +279,9 @@ class ParcelListeners(
if (!canBuildOnArea(event.player, parcel)) {
when (item) {
- LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL -> event.isCancelled = true
+ LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL -> {
+ event.isCancelled = true
+ }
}
}
}
@@ -614,9 +620,9 @@ class ParcelListeners(
if (parcels.isEmpty()) return@l
parcels.forEach { id ->
- val parcel = world.getParcelById(id)?.takeIf { it.ownerSignOutdated } ?: return@forEach
- world.blockManager.setOwnerBlock(parcel.id, parcel.owner)
- parcel.ownerSignOutdated = false
+ val parcel = world.getParcelById(id)?.takeIf { it.isOwnerSignOutdated } ?: return@forEach
+ world.blockManager.updateParcelInfo(parcel.id, parcel.owner)
+ parcel.isOwnerSignOutdated = false
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt b/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
index 2ad10e9..5e36099 100644
--- a/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
@@ -18,4 +18,5 @@ private class PlotmeMigrationFactory : PolymorphicOptionsFactory<Migration> {
}
class PlotmeMigrationOptions(val worldsFromTo: Map<String, String> = mapOf("plotworld" to "parcels"),
- val storage: StorageOptions = StorageOptions(options = DataConnectionOptions(database = "plotme"))) \ No newline at end of file
+ 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 dc80db9..35d48ba 100644
--- a/src/main/kotlin/io/dico/parcels2/options/Options.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/Options.kt
@@ -1,6 +1,6 @@
package io.dico.parcels2.options
-import io.dico.parcels2.blockvisitor.TickJobtimeOptions
+import io.dico.parcels2.TickJobtimeOptions
import org.bukkit.GameMode
import org.bukkit.Material
import java.io.Reader
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
index 2e0a8ee..12d5bc7 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
@@ -43,14 +43,14 @@ interface Backing {
fun transmitAllParcelData(channel: SendChannel<DataPair>)
- fun readParcelData(parcel: ParcelId): ParcelData?
+ fun readParcelData(parcel: ParcelId): ParcelDataHolder?
fun getOwnedParcels(user: PlayerProfile): List<ParcelId>
fun getNumParcels(user: PlayerProfile): Int = getOwnedParcels(user).size
- fun setParcelData(parcel: ParcelId, data: ParcelData?)
+ fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?)
fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
index abd9f41..d718f20 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
@@ -13,7 +13,7 @@ import org.joda.time.DateTime
import java.util.UUID
import kotlin.coroutines.CoroutineContext
-typealias DataPair = Pair<ParcelId, ParcelData?>
+typealias DataPair = Pair<ParcelId, ParcelDataHolder?>
typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder>
interface Storage {
@@ -33,7 +33,7 @@ interface Storage {
fun updatePlayerName(uuid: UUID, name: String): Job
- fun readParcelData(parcel: ParcelId): Deferred<ParcelData?>
+ fun readParcelData(parcel: ParcelId): Deferred<ParcelDataHolder?>
fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
@@ -44,7 +44,7 @@ interface Storage {
fun getNumParcels(user: PlayerProfile): Deferred<Int>
- fun setParcelData(parcel: ParcelId, data: ParcelData?): Job
+ fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?): Job
fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
@@ -62,7 +62,7 @@ interface Storage {
fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job
- fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelData>>
+ fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>>
}
class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope {
@@ -93,7 +93,7 @@ class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineSco
override fun getNumParcels(user: PlayerProfile) = b.launchFuture { b.getNumParcels(user) }
- override fun setParcelData(parcel: ParcelId, data: ParcelData?) = b.launchJob { b.setParcelData(parcel, data) }
+ 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) }
@@ -110,5 +110,5 @@ class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineSco
override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setGlobalPrivilege(owner, player, privilege) }
- override fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelData>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) }
+ 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 fa68338..e49be79 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
@@ -9,6 +9,7 @@ import io.dico.parcels2.storage.*
import io.dico.parcels2.util.math.clampMax
import io.dico.parcels2.util.ext.synchronized
import kotlinx.coroutines.*
+import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.ArrayChannel
import kotlinx.coroutines.channels.LinkedListChannel
import kotlinx.coroutines.channels.ReceiveChannel
@@ -152,7 +153,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
channel.close()
}
- override fun readParcelData(parcel: ParcelId): ParcelData? {
+ override fun readParcelData(parcel: ParcelId): ParcelDataHolder? {
val row = ParcelsT.getRow(parcel) ?: return null
return rowToParcelData(row)
}
@@ -165,7 +166,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
.toList()
}
- override fun setParcelData(parcel: ParcelId, data: ParcelData?) {
+ override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) {
if (data == null) {
transaction {
ParcelsT.getId(parcel)?.let { id ->
@@ -262,7 +263,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
owner = row[ParcelsT.owner_id]?.let { ProfilesT.getItem(it) }
lastClaimTime = row[ParcelsT.claim_time]
- ownerSignOutdated = row[ParcelsT.sign_oudated]
+ isOwnerSignOutdated = row[ParcelsT.sign_oudated]
val id = row[ParcelsT.id]
ParcelOptionsT.select { ParcelOptionsT.parcel_id eq id }.firstOrNull()?.let { optrow ->
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 6ebac6d..0245625 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
@@ -5,8 +5,7 @@ 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) {
+class UpsertStatement<Key : Any>(table: Table, conflictColumn: Column<*>? = null, conflictIndex: Index? = null) : InsertStatement<Key>(table, false) {
val indexName: String
val indexColumns: List<Column<*>>
@@ -66,9 +65,10 @@ 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>> SqlExpressionBuilder.greater(col1: ExpressionWithColumnType<T>, col2: ExpressionWithColumnType<T>): Expression<T> {
- return case(col1)
- .When(col1.greater(col2), col1)
- .Else(col2)
-}
+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/migration/plotme/PlotmeMigration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
index a79d315..831fe42 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
@@ -7,8 +7,10 @@ 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.greater
+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
@@ -24,6 +26,7 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
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)
@@ -51,9 +54,9 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
isShutdown = true
}
- suspend fun doWork(target: Storage) {
+ suspend fun doWork(target: Storage) = with (tables) {
val exit = transaction {
- (!PlotmePlotsT.exists()).also {
+ (!PlotmePlots.exists()).also {
if (it) mlogger.warn("Plotme tables don't appear to exist. Exiting.")
}
}
@@ -75,29 +78,32 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
}
mlogger.info("Transmitting data from plotmeplots table")
+ var count = 0
transaction {
- PlotmePlotsT.selectAll()
- .orderBy(PlotmePlotsT.world_name)
- .orderBy(with(SqlExpressionBuilder) { greater(PlotmePlotsT.px.abs(), PlotmePlotsT.pz.abs()) })
+
+ PlotmePlots.selectAll()
+ .orderBy(PlotmePlots.world_name)
+ .orderBy(greaterOf(PlotmePlots.px.abs(), PlotmePlots.pz.abs()))
.forEach { row ->
- val parcel = getParcelId(PlotmePlotsT, row) ?: return@forEach
- val owner = PlayerProfile.safe(row[PlotmePlotsT.owner_uuid]?.toUUID(), row[PlotmePlotsT.owner_name])
+ 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 {
- PlotmeAllowedT.transmitPlotmeAddedTable(Privilege.CAN_BUILD)
+ PlotmeAllowed.transmitPlotmeAddedTable(Privilege.CAN_BUILD)
}
mlogger.info("Transmitting data from plotmedenied table")
transaction {
- PlotmeDeniedT.transmitPlotmeAddedTable(Privilege.BANNED)
+ PlotmeDenied.transmitPlotmeAddedTable(Privilege.BANNED)
}
- mlogger.warn("Data has been **transmitted**.")
+ 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.")
}
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 8564ad3..dc788c8 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
@@ -2,25 +2,30 @@ package io.dico.parcels2.storage.migration.plotme
import org.jetbrains.exposed.sql.Table
-const val uppercase: Boolean = false
-@Suppress("ConstantConditionIf")
-fun String.toCorrectCase() = if (uppercase) this else toLowerCase()
-
-sealed class PlotmeTable(name: String) : Table(name) {
- val px = integer("idX").primaryKey()
- val pz = integer("idZ").primaryKey()
- val world_name = varchar("world", 32).primaryKey()
-}
+class PlotmeTables(val uppercase: Boolean) {
+ fun String.toCorrectCase() = if (uppercase) this else toLowerCase()
-object PlotmePlotsT : PlotmeTable("plotmePlots".toCorrectCase()) {
- val owner_name = varchar("owner", 32)
- val owner_uuid = blob("ownerid").nullable()
-}
+ 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()
+ }
-sealed class PlotmePlotPlayerMap(name: String) : PlotmeTable(name) {
- val player_name = varchar("player", 32)
- val player_uuid = blob("playerid").nullable()
+ inner class PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase())
+ inner class PlotmeDeniedT : PlotmePlotPlayerMap("plotmeDenied".toCorrectCase())
}
-object PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase())
-object 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 d6837bd..618eaed 100644
--- a/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt
@@ -10,3 +10,5 @@ fun getPlayerName(uuid: UUID): String? = getOfflinePlayer(uuid)?.name
fun getOfflinePlayer(uuid: UUID): OfflinePlayer? = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }
fun getOfflinePlayer(name: String): OfflinePlayer? = Bukkit.getOfflinePlayer(name).takeIf { it.isValid }
+
+fun isServerThread(): Boolean = Thread.currentThread().name == "Server thread"
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 9385535..5945120 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt
@@ -3,4 +3,7 @@ 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/Vec3i.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
index a721afa..b25764e 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
@@ -11,7 +11,9 @@ data class Vec3i(
val z: Int
) {
constructor(loc: Location) : this(loc.blockX, loc.blockY, loc.blockZ)
+ constructor(block: Block) : this(block.x, block.y, block.z)
+ fun toVec2i() = Vec2i(x, z)
operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z)
operator fun minus(o: Vec3i) = Vec3i(x - o.x, y - o.y, z - o.z)
infix fun addX(o: Int) = Vec3i(x + o, y, z)