diff options
Diffstat (limited to 'src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt')
-rw-r--r-- | src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt | 122 |
1 files changed, 95 insertions, 27 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) } } } |