diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 0924a91a..9a9ce81c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -78,7 +78,7 @@ fun main() { var reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(data), Inflater()))) reader.skipBytes(3) - val chunk = client.world!!.chunkMap.compute(chunkX - 2, chunkY) + val chunk = client.world!!.chunkMap.compute(chunkX, chunkY) if (chunk != null) { for (y in 0 .. 31) { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientWorld.kt index e0771b53..b98b5b45 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientWorld.kt @@ -32,7 +32,7 @@ import java.util.concurrent.Future class ClientWorld( val client: StarboundClient, seed: Long, - size: Vector2i? = null, + size: Vector2i, loopX: Boolean = false, loopY: Boolean = false ) : World<ClientWorld, ClientChunk>(seed, size, loopX, loopY) { @@ -49,13 +49,13 @@ class ClientWorld( override val isClient: Boolean get() = true - val renderRegionWidth = if (size == null) 16 else determineChunkSize(size.x) - val renderRegionHeight = if (size == null) 16 else determineChunkSize(size.y) - val renderRegionsX = if (size == null) 0 else size.x / renderRegionWidth - val renderRegionsY = if (size == null) 0 else size.y / renderRegionHeight + val renderRegionWidth = determineChunkSize(size.x) + val renderRegionHeight = determineChunkSize(size.y) + val renderRegionsX = size.x / renderRegionWidth + val renderRegionsY = size.y / renderRegionHeight fun isValidRenderRegionX(value: Int): Boolean { - if (size == null || loopX) { + if (loopX) { return true } else { return value in 0 .. renderRegionsX @@ -63,7 +63,7 @@ class ClientWorld( } fun isValidRenderRegionY(value: Int): Boolean { - if (size == null || loopY) { + if (loopY) { return true } else { return value in 0 .. renderRegionsY diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/CoordinateMapper.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/CoordinateMapper.kt index b5d0af82..6e9c2b07 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/CoordinateMapper.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/CoordinateMapper.kt @@ -37,23 +37,6 @@ abstract class CoordinateMapper { open fun isValidCellIndex(value: Int): Boolean = inBoundsCell(value) open fun isValidChunkIndex(value: Int): Boolean = inBoundsChunk(value) - object Infinite : CoordinateMapper() { - override val chunks: Int - get() = Int.MAX_VALUE - - override fun cell(value: Int): Int = value - override fun cell(value: Double): Double = value - override fun cell(value: Float): Float = value - override fun chunk(value: Int): Int = value - - override fun chunkFromCell(value: Int): Int { - return value shr CHUNK_SIZE_BITS - } - - override fun inBoundsCell(value: Int) = true - override fun inBoundsChunk(value: Int) = true - } - class Wrapper(private val cells: Int) : CoordinateMapper() { override val chunks = divideUp(cells, CHUNK_SIZE) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt index cc89824f..5490c17a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt @@ -26,15 +26,14 @@ import java.util.concurrent.ForkJoinPool import java.util.concurrent.locks.ReentrantLock import java.util.random.RandomGenerator -@Suppress("UNCHECKED_CAST") abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, ChunkType>>( val seed: Long, - val size: Vector2i?, + val size: Vector2i, val loopX: Boolean, val loopY: Boolean ) : ICellAccess { - val x: CoordinateMapper = if (size == null) CoordinateMapper.Infinite else if (loopX) CoordinateMapper.Wrapper(size.x) else CoordinateMapper.Clamper(size.x) - val y: CoordinateMapper = if (size == null) CoordinateMapper.Infinite else if (loopY) CoordinateMapper.Wrapper(size.y) else CoordinateMapper.Clamper(size.y) + val x: CoordinateMapper = if (loopX) CoordinateMapper.Wrapper(size.x) else CoordinateMapper.Clamper(size.x) + val y: CoordinateMapper = if (loopY) CoordinateMapper.Wrapper(size.y) else CoordinateMapper.Clamper(size.y) // whenever provided cell position is within actual world borders, ignoring wrapping fun inBounds(x: Int, y: Int) = this.x.inBoundsCell(x) && this.y.inBoundsCell(y) @@ -96,8 +95,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun } } - // hash chunk map is around 30% slower than rectangular one - inner class HashChunkMap : ChunkMap() { + inner class SparseChunkMap : ChunkMap() { private val map = Long2ObjectOpenHashMap<ChunkType>() override fun getCell(x: Int, y: Int): AbstractCell { @@ -107,13 +105,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun return this[this@World.x.chunkFromCell(ix), this@World.y.chunkFromCell(iy)]?.getCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK) ?: AbstractCell.NULL } - @Suppress("NAME_SHADOWING") override fun get(x: Int, y: Int): ChunkType? { - if (!this@World.x.isValidChunkIndex(x) || !this@World.y.isValidChunkIndex(y)) return null - - val x = this@World.x.chunk(x) - val y = this@World.y.chunk(y) - + if (!this@World.x.inBoundsChunk(x) || !this@World.y.inBoundsChunk(y)) return null return map[ChunkPos.toLong(x, y)] } @@ -137,10 +130,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun } inner class ArrayChunkMap : ChunkMap() { - val width = size!!.x - val height = size!!.y - - private val map = Object2DArray.nulls<ChunkType>(divideUp(width, CHUNK_SIZE), divideUp(height, CHUNK_SIZE)) + private val map = Object2DArray.nulls<ChunkType>(divideUp(size.x, CHUNK_SIZE), divideUp(size.y, CHUNK_SIZE)) private fun getRaw(x: Int, y: Int): ChunkType { return map[x, y] ?: create(x, y).also { map[x, y] = it } @@ -166,8 +156,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun } override fun get(x: Int, y: Int): ChunkType? { - if (!this@World.x.isValidChunkIndex(x) || !this@World.y.isValidChunkIndex(y)) return null - return getRaw(this@World.x.chunk(x), this@World.y.chunk(y)) + if (!this@World.x.inBoundsChunk(x) || !this@World.y.inBoundsChunk(y)) return null + return getRaw(x, y) } override fun remove(x: Int, y: Int) { @@ -175,7 +165,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun } } - val chunkMap: ChunkMap = if (size != null) ArrayChunkMap() else HashChunkMap() + val chunkMap: ChunkMap = if (size.x <= 32000 && size.y <= 32000) ArrayChunkMap() else SparseChunkMap() val random: RandomGenerator = RandomGenerator.of("Xoroshiro128PlusPlus") var gravity = Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION)