10/10 commit messages btw
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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 =
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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?
|
||||
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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)) } }
|
||||
class OccupiedException(parcelId: ParcelId) : Exception("Parcel $parcelId is occupied")
|
||||
|
||||
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)
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
if (region.origin.y + region.size.y > worldHeight) {
|
||||
val size = region.size.withY(worldHeight - region.origin.y)
|
||||
return Region(region.origin, size)
|
||||
}
|
||||
|
||||
return region
|
||||
val region = world.blockManager.getRegion(parcelId)
|
||||
return traverseRegion(region, world.world.maxHeight, medium)
|
||||
}
|
||||
|
||||
protected abstract suspend fun Scope.build(region: Region)
|
||||
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))
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
return 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 setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?) {
|
||||
val b = getBottomBlock(parcel)
|
||||
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)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
|
||||
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 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
|
||||
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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
val ownerChanged = owner != newData.owner
|
||||
if (ownerChanged) {
|
||||
updateOwnerSign(true, false, true)
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
override fun copyData(data: ParcelData) {
|
||||
copyDataIgnoringDatabase(data)
|
||||
world.storage.setParcelData(this, data)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
copyDataIgnoringDatabase(ParcelDataHolder())
|
||||
world.storage.setParcelData(this, null)
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 toString() = toStringExt()
|
||||
override fun nextEmptyParcel(): Parcel? = container.nextEmptyParcel()
|
||||
|
||||
override fun toString() = parcelWorldIdToString()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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")))
|
||||
val storage: StorageOptions = StorageOptions(options = DataConnectionOptions(database = "plotme")),
|
||||
val tableNamesUppercase: Boolean = false)
|
||||
@@ -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
|
||||
|
||||
@@ -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?)
|
||||
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
class PlotmeTables(val uppercase: Boolean) {
|
||||
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()
|
||||
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())
|
||||
}
|
||||
|
||||
object 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()
|
||||
}
|
||||
|
||||
object PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase())
|
||||
object PlotmeDeniedT : PlotmePlotPlayerMap("plotmeDenied".toCorrectCase())
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user