diff options
Diffstat (limited to 'src/main/kotlin/io/dico/parcels2/defaultimpl')
4 files changed, 145 insertions, 56 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt index 7e75f52..244d38c 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt @@ -1,14 +1,13 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.* -import io.dico.parcels2.blockvisitor.RegionTraversal +import io.dico.parcels2.blockvisitor.RegionTraverser import io.dico.parcels2.blockvisitor.Worker import io.dico.parcels2.blockvisitor.WorktimeLimiter import io.dico.parcels2.options.DefaultGeneratorOptions import io.dico.parcels2.util.* import org.bukkit.* import org.bukkit.block.Biome -import org.bukkit.block.Block import org.bukkit.block.BlockFace import org.bukkit.block.Skull import org.bukkit.block.data.BlockData @@ -18,11 +17,13 @@ import java.util.Random private val airType = Bukkit.createBlockData(Material.AIR) -class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() { +private const val chunkSize = 16 + +class DefaultParcelGenerator(override val worldName: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() { private var _world: World? = null override val world: World get() { - if (_world == null) _world = Bukkit.getWorld(name)!!.also { + if (_world == null) _world = Bukkit.getWorld(worldName)!!.also { maxHeight = it.maxHeight return it } @@ -103,12 +104,10 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix) } - override fun makeParcelBlockManager(worktimeLimiter: WorktimeLimiter): ParcelBlockManager { - return ParcelBlockManagerImpl(worktimeLimiter) - } - - override fun makeParcelLocator(container: ParcelContainer): ParcelLocator { - return ParcelLocatorImpl(container) + override fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId, + container: ParcelContainer, + worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager> { + return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, worktimeLimiter) } private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? { @@ -124,22 +123,26 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp return null } - private inner class ParcelLocatorImpl(val container: ParcelContainer) : ParcelLocator { + private inner class ParcelLocatorImpl(val worldId: ParcelWorldId, + val container: ParcelContainer) : ParcelLocator { override val world: World = this@DefaultParcelGenerator.world + override fun getParcelAt(x: Int, z: Int): Parcel? { return convertBlockLocationToId(x, z, container::getParcelById) } override fun getParcelIdAt(x: Int, z: Int): ParcelId? { - return convertBlockLocationToId(x, z) { idx, idz -> ParcelId(world.name, world.uid, idx, idz) } + return convertBlockLocationToId(x, z) { idx, idz -> ParcelId(worldId, idx, idz) } } } @Suppress("DEPRECATION") - private inner class ParcelBlockManagerImpl(override val worktimeLimiter: WorktimeLimiter) : ParcelBlockManager { + private inner class ParcelBlockManagerImpl(val worldId: ParcelWorldId, + override val worktimeLimiter: WorktimeLimiter) : ParcelBlockManagerBase() { override val world: World = this@DefaultParcelGenerator.world + override val parcelTraverser: RegionTraverser = RegionTraverser.upToAndDownUntil(o.floorHeight) - override fun getBottomBlock(parcel: ParcelId): Vec2i = Vec2i( + /*override*/ fun getBottomBlock(parcel: ParcelId): Vec2i = Vec2i( sectionSize * (parcel.x - 1) + pathOffset + o.offsetX, sectionSize * (parcel.z - 1) + pathOffset + o.offsetZ ) @@ -151,6 +154,11 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp return Location(world, x + 0.5, o.floorHeight + 1.0, z + 0.5, 0F, 0F) } + override fun getRegion(parcel: ParcelId): Region { + val bottom = getBottomBlock(parcel) + return Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize)) + } + override fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?) { val b = getBottomBlock(parcel) @@ -203,9 +211,8 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp } override fun clearParcel(parcel: ParcelId): Worker = worktimeLimiter.submit { - val bottom = getBottomBlock(parcel) - val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize)) - val blocks = RegionTraversal.DOWNWARD.regionTraverser(region) + val region = getRegion(parcel) + val blocks = parcelTraverser.traverseRegion(region) val blockCount = region.blockCount.toDouble() val world = world @@ -227,17 +234,78 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp } } - override fun doBlockOperation(parcel: ParcelId, direction: RegionTraversal, operation: (Block) -> Unit): Worker = worktimeLimiter.submit { - val bottom = getBottomBlock(parcel) - val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize)) - val blocks = direction.regionTraverser(region) - val blockCount = region.blockCount.toDouble() - val world = world + override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> { + /* + * Get the offsets for the world out of the way + * to simplify the calculation that follows. + */ + + val x = chunk.x.shl(4) - (o.offsetX + pathOffset) + val z = chunk.z.shl(4) - (o.offsetZ + pathOffset) + + /* Locations of wall corners (where owner blocks are placed) are defined as: + * + * x umod sectionSize == sectionSize-1 + * + * This check needs to be made for all 16 slices of the chunk in 2 dimensions + * How to optimize this? + * Let's take the expression + * + * x umod sectionSize + * + * And call it modX + * x can be shifted (chunkSize -1) times to attempt to get a modX of 0. + * This means that if the modX is 1, and sectionSize == (chunkSize-1), there would be a match at the last shift. + * To check that there are any matches, we can see if the following holds: + * + * modX >= ((sectionSize-1) - (chunkSize-1)) + * + * Which can be simplified to: + * modX >= sectionSize - chunkSize + * + * if sectionSize == chunkSize, this expression can be simplified to + * modX >= 0 + * which is always true. This is expected. + * To get the total number of matches on a dimension, we can evaluate the following: + * + * (modX - (sectionSize - chunkSize) + sectionSize) / sectionSize + * + * We add sectionSize to the lhs because, if the other part of the lhs is 0, we need at least 1. + * This can be simplified to: + * + * (modX + chunkSize) / sectionSize + */ + + val sectionSize = sectionSize + + val modX = x umod sectionSize + val matchesOnDimensionX = (modX + chunkSize) / sectionSize + if (matchesOnDimensionX <= 0) return emptyList() + + val modZ = z umod sectionSize + val matchesOnDimensionZ = (modZ + chunkSize) / sectionSize + if (matchesOnDimensionZ <= 0) return emptyList() + + /* + * Now we need to find the first id within the matches, + * and then return the subsequent matches in a rectangle following it. + * + * On each dimension, get the distance to the first match, which is equal to (sectionSize-1 - modX) + * and add it to the coordinate value + */ + val firstX = x + (sectionSize - 1 - modX) + val firstZ = z + (sectionSize - 1 - modZ) + + val firstIdX = (firstX + 1) / sectionSize + 1 + val firstIdZ = (firstZ + 1) / sectionSize + 1 + + if (matchesOnDimensionX == 1 && matchesOnDimensionZ == 1) { + // fast-path optimization + return listOf(Vec2i(firstIdX, firstIdZ)) + } - for ((index, vec) in blocks.withIndex()) { - markSuspensionPoint() - operation(world[vec]) - setProgress((index + 1) / blockCount) + return (0 until matchesOnDimensionX).flatMap { idOffsetX -> + (0 until matchesOnDimensionZ).map { idOffsetZ -> Vec2i(firstIdX + idOffsetX, firstIdZ + idOffsetZ) } } } diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt index c154955..7755363 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt @@ -47,13 +47,23 @@ class ParcelImpl(override val world: ParcelWorld, val globalAddedMap: AddedDataMap? get() = owner?.let { world.globalAddedData[it].addedMap } - override val since: DateTime? get() = data.since + override val lastClaimTime: DateTime? get() = data.lastClaimTime + + override var ownerSignOutdated: Boolean + get() = data.ownerSignOutdated + set(value) { + if (data.ownerSignOutdated != value) { + world.storage.setParcelOwnerSignOutdated(this, value) + data.ownerSignOutdated = value + } + } 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 } } diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt index 70d428d..0c1dadc 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt @@ -1,8 +1,11 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.* +import kotlinx.coroutines.experimental.Unconfined +import kotlinx.coroutines.experimental.launch import org.bukkit.Bukkit import org.bukkit.WorldCreator +import org.joda.time.DateTime class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { inline val options get() = plugin.options @@ -49,9 +52,24 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { if (parcelWorld != null) continue val generator: ParcelGenerator = getWorldGenerator(worldName)!! - val bukkitWorld = Bukkit.getWorld(worldName) ?: WorldCreator(worldName).generator(generator).createWorld() + 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") } + parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage, plugin.globalAddedData, ::DefaultParcelContainer, plugin.worktimeLimiter) + + if (!worldExists) { + val time = DateTime.now() + plugin.storage.setWorldCreationTime(parcelWorld.id, time) + parcelWorld.creationTime = time + } else { + launch(context = Unconfined) { + parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: DateTime.now() + } + } + _worlds[worldName] = parcelWorld } diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt index 4a168f5..a54e519 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt @@ -7,21 +7,21 @@ import io.dico.parcels2.blockvisitor.WorktimeLimiter import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.storage.Storage import org.bukkit.World +import org.joda.time.DateTime import java.util.UUID -class ParcelWorldImpl private -constructor(override val world: World, - override val generator: ParcelGenerator, - override var options: RuntimeWorldOptions, - override val storage: Storage, - override val globalAddedData: GlobalAddedDataManager, - containerFactory: ParcelContainerFactory, - blockManager: ParcelBlockManager) +class ParcelWorldImpl(override val world: World, + override val generator: ParcelGenerator, + override var options: RuntimeWorldOptions, + override val storage: Storage, + override val globalAddedData: GlobalAddedDataManager, + containerFactory: ParcelContainerFactory, + worktimeLimiter: WorktimeLimiter) : ParcelWorld, ParcelWorldId, - ParcelContainer, // missing delegation - ParcelLocator, // missing delegation - ParcelBlockManager by blockManager { + ParcelContainer, /* missing delegation */ + ParcelLocator /* missing delegation */ { + override val id: ParcelWorldId get() = this override val uid: UUID? get() = world.uid @@ -33,10 +33,14 @@ constructor(override val world: World, override val name: String = world.name!! override val container: ParcelContainer = containerFactory(this) - override val locator: ParcelLocator = generator.makeParcelLocator(container) - override val blockManager: ParcelBlockManager = blockManager + override val locator: ParcelLocator + override val blockManager: ParcelBlockManager init { + val pair = generator.makeParcelLocatorAndBlockManager(id, container, worktimeLimiter) + locator = pair.first + blockManager = pair.second + enforceOptions() } @@ -55,24 +59,13 @@ constructor(override val world: World, world.setGameRuleValue("doTileDrops", "${options.doTileDrops}") } + // Updated by ParcelProviderImpl + override var creationTime: DateTime? = null + /* Interface delegation needs to be implemented manually because JetBrains has yet to fix it. */ - companion object { - // Use this to be able to delegate blockManager and assign it to a property too, at least. - operator fun invoke(world: World, - generator: ParcelGenerator, - options: RuntimeWorldOptions, - storage: Storage, - globalAddedData: GlobalAddedDataManager, - containerFactory: ParcelContainerFactory, - worktimeLimiter: WorktimeLimiter): ParcelWorldImpl { - val blockManager = generator.makeParcelBlockManager(worktimeLimiter) - return ParcelWorldImpl(world, generator, options, storage, globalAddedData, containerFactory, blockManager) - } - } - // ParcelLocator interface override fun getParcelAt(x: Int, z: Int): Parcel? { return locator.getParcelAt(x, z) |