diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 93497fbd..e3879492 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -101,30 +101,40 @@ fun main() { val ent = PlayerEntity(client.world!!) Starbound.onInitialize { + var find = 0L + var set = 0L + var parse = 0L + for (chunkX in 0 .. 61) { for (chunkY in 0 .. 61) { + var t = System.currentTimeMillis() val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte())) + find += System.currentTimeMillis() - t if (data != null) { val chunk = client.world!!.computeIfAbsent(ChunkPos(chunkX, chunkY)) val inflater = Inflater() inflater.setInput(data) + t = System.currentTimeMillis() val output = ByteArray(64_000) val actual = inflater.inflate(output) val reader = DataInputStream(ByteArrayInputStream(output)) + parse += System.currentTimeMillis() - t reader.skipBytes(3) var hitTile = false + t = System.currentTimeMillis() + for (y in 0 .. 31) { for (x in 0 .. 31) { val materialID = reader.readShort() val getMat = Starbound.tilesAccessID[materialID.toInt()] if (getMat != null) { - chunk.chunk.foreground[x, y] = getMat + chunk.foreground[x, y] = getMat hitTile = true } @@ -134,7 +144,7 @@ fun main() { val getMat2 = Starbound.tilesAccessID[materialID2.toInt()] if (getMat2 != null) { - chunk.chunk.background[x, y] = getMat2 + chunk.background[x, y] = getMat2 hitTile = true } @@ -142,6 +152,8 @@ fun main() { } } + set += System.currentTimeMillis() - t + if (hitTile) { //println(chunk.chunk.posVector2d) // ent.position = chunk.chunk.posVector2d + Vector2d(16.0, 34.0) @@ -149,6 +161,8 @@ fun main() { } } } + + println("$find $set $parse") } //ent.position += Vector2d(y = 14.0, x = -10.0) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt index 5dec9b68..3837041d 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt @@ -119,9 +119,9 @@ class ClientWorld(val client: StarboundClient, seed: Long = 0L) : World() - for (chunk in collectInternal(size.encasingChunkPosAABB())) { - determineRenderers.add(chunk.chunk) - chunk.chunk.bake() + for (chunk in collect(size.encasingChunkPosAABB())) { + determineRenderers.add(chunk) + chunk.bake() } renderLayeredList(client.gl.matrixStack, determineRenderers) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt index 6d9397f3..97e949eb 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt @@ -163,26 +163,50 @@ const val CHUNK_SIZE_FF = CHUNK_SIZE - 1 const val CHUNK_SIZEf = CHUNK_SIZE.toFloat() const val CHUNK_SIZEd = CHUNK_SIZE.toDouble() +/** + * Сетка чанков идёт как и сетка тайлов. + * + * * Вправо у нас положительный X + * * Влево у нас отрицательный X + * * Вверх у нас положительный Y + * * Вниз у нас отрицательный Y + */ class ChunkPos(val x: Int, val y: Int) : Comparable { constructor(pos: IStruct2i) : this(pos.component1(), pos.component2()) val firstBlock get() = Vector2i(x shl CHUNK_SHIFT, y shl CHUNK_SHIFT) val lastBlock get() = Vector2i(((x + 1) shl CHUNK_SHIFT) - 1, ((y + 1) shl CHUNK_SHIFT) - 1) - fun up(): ChunkPos { + val top: ChunkPos get() { return ChunkPos(x, y + 1) } - fun down(): ChunkPos { + val bottom: ChunkPos get() { return ChunkPos(x, y - 1) } - fun left(): ChunkPos { - return ChunkPos(x + 1, y) + val left: ChunkPos get() { + return ChunkPos(x - 1, y) } - fun right(): ChunkPos { - return ChunkPos(x - 1, y) + val topLeft: ChunkPos get() { + return ChunkPos(x - 1, y + 1) + } + + val topRight: ChunkPos get() { + return ChunkPos(x + 1, y + 1) + } + + val bottomLeft: ChunkPos get() { + return ChunkPos(x - 1, y - 1) + } + + val bottomRight: ChunkPos get() { + return ChunkPos(x + 1, y - 1) + } + + val right: ChunkPos get() { + return ChunkPos(x + 1, y) } override fun equals(other: Any?): Boolean { @@ -258,7 +282,7 @@ class ChunkPos(val x: Int, val y: Int) : Comparable { * и при этом координаты проверяются относительно чанка и могут спокойно выйти за его пределы, * с желанием получить тайл из соседнего чанка */ -open class TileChunkView( +open class TileView( open val center: ITileChunk, open val right: ITileChunk?, @@ -311,7 +335,7 @@ open class TileChunkView( get() = center.pos } -class MutableTileChunkView( +class MutableTileView( override val center: IMutableTileChunk, override val right: IMutableTileChunk?, @@ -323,7 +347,7 @@ class MutableTileChunkView( override val bottom: IMutableTileChunk?, override val bottomLeft: IMutableTileChunk?, override val bottomRight: IMutableTileChunk?, -) : TileChunkView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk { +) : TileView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk { override fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? { if (x in 0 .. CHUNK_SIZE_FF) { if (y in 0 .. CHUNK_SIZE_FF) { @@ -765,6 +789,15 @@ abstract class Chunk, This : Chunk, This : Chunk, ChunkType : val left: IWorldChunkTuple? val right: IWorldChunkTuple? val bottom: IWorldChunkTuple? + + val topLeft: IWorldChunkTuple? + val topRight: IWorldChunkTuple? + val bottomLeft: IWorldChunkTuple? + val bottomRight: IWorldChunkTuple? } -interface IMutableWorldChunkTuple, ChunkType : Chunk> : IWorldChunkTuple { - override var top: IMutableWorldChunkTuple? - override var left: IMutableWorldChunkTuple? - override var right: IMutableWorldChunkTuple? - override var bottom: IMutableWorldChunkTuple? -} - -class WorldChunkTuple, ChunkType : Chunk>( +class ProxiedWorldChunkTuple, ChunkType : Chunk>( private val parent: IWorldChunkTuple ) : IWorldChunkTuple { override val world get() = parent.world override val chunk get() = parent.chunk - override val top: IWorldChunkTuple? get() { - val getValue = parent.top - if (getValue != null) { - return WorldChunkTuple(getValue) - } - - return null - } - - override val left: IWorldChunkTuple? get() { - val getValue = parent.left - - if (getValue != null) { - return WorldChunkTuple(getValue) - } - - return null - } - - override val right: IWorldChunkTuple? get() { - val getValue = parent.right - - if (getValue != null) { - return WorldChunkTuple(getValue) - } - - return null - } - - override val bottom: IWorldChunkTuple? get() { - val getValue = parent.bottom - - if (getValue != null) { - return WorldChunkTuple(getValue) - } - - return null - } + override val top: IWorldChunkTuple? get() = parent.top?.let(::ProxiedWorldChunkTuple) + override val left: IWorldChunkTuple? get() = parent.left?.let(::ProxiedWorldChunkTuple) + override val right: IWorldChunkTuple? get() = parent.right?.let(::ProxiedWorldChunkTuple) + override val bottom: IWorldChunkTuple? get() = parent.bottom?.let(::ProxiedWorldChunkTuple) + override val topLeft: IWorldChunkTuple? get() = parent.topLeft?.let(::ProxiedWorldChunkTuple) + override val topRight: IWorldChunkTuple? get() = parent.topRight?.let(::ProxiedWorldChunkTuple) + override val bottomLeft: IWorldChunkTuple? get() = parent.bottomLeft?.let(::ProxiedWorldChunkTuple) + override val bottomRight: IWorldChunkTuple? get() = parent.bottomRight?.let(::ProxiedWorldChunkTuple) } -open class MutableWorldChunkTuple, ChunkType : Chunk>( +class InstantWorldChunkTuple, ChunkType : Chunk>( override val world: WorldType, - override val chunk: ChunkType, - override var top: IMutableWorldChunkTuple?, - override var left: IMutableWorldChunkTuple?, - override var right: IMutableWorldChunkTuple?, - override var bottom: IMutableWorldChunkTuple?, -) : IMutableWorldChunkTuple + override val chunk: ChunkType +) : IWorldChunkTuple { + + private val _top = world[chunk.top] + private val _left = world[chunk.left] + private val _right = world[chunk.right] + private val _bottom = world[chunk.bottom] + private val _topLeft = world[chunk.topLeft] + private val _topRight = world[chunk.topRight] + private val _bottomLeft = world[chunk.bottomLeft] + private val _bottomRight = world[chunk.bottomRight] + + override val top: IWorldChunkTuple? by lazy { _top?.let { InstantWorldChunkTuple(world, it) } } + override val left: IWorldChunkTuple? by lazy { _left?.let { InstantWorldChunkTuple(world, it) } } + override val right: IWorldChunkTuple? by lazy { _right?.let { InstantWorldChunkTuple(world, it) } } + override val bottom: IWorldChunkTuple? by lazy { _bottom?.let { InstantWorldChunkTuple(world, it) } } + override val topLeft: IWorldChunkTuple? by lazy { _topLeft?.let { InstantWorldChunkTuple(world, it) } } + override val topRight: IWorldChunkTuple? by lazy { _topRight?.let { InstantWorldChunkTuple(world, it) } } + override val bottomLeft: IWorldChunkTuple? by lazy { _bottomLeft?.let { InstantWorldChunkTuple(world, it) } } + override val bottomRight: IWorldChunkTuple? by lazy { _bottomRight?.let { InstantWorldChunkTuple(world, it) } } +} const val EARTH_FREEFALL_ACCELERATION = 9.8312 / METRES_IN_STARBOUND_UNIT @@ -142,7 +124,7 @@ class Timer(val period: Double, val executionTimes: Int, val func: (Timer) -> Un } abstract class World, ChunkType : Chunk>(val seed: Long = 0L) { - protected val chunkMap = Object2ObjectAVLTreeMap> cmp@{ a, b -> + protected val chunkMap = Object2ObjectAVLTreeMap cmp@{ a, b -> return@cmp a.compareTo(b) } @@ -151,7 +133,7 @@ abstract class World, ChunkType : Chunk() - protected var lastAccessedChunk: IMutableWorldChunkTuple? = null + protected var lastAccessedChunk: ChunkType? = null val physics = B2World(Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION)) @@ -317,51 +299,37 @@ abstract class World, ChunkType : Chunk? { - if (lastAccessedChunk?.chunk?.pos == pos) { + /** + * Возвращает чанк на указанной позиции если он существует + */ + open operator fun get(pos: ChunkPos): ChunkType? { + val lastAccessedChunk = lastAccessedChunk + + if (lastAccessedChunk?.pos == pos) { return lastAccessedChunk } return chunkMap[pos] } - open fun getChunk(pos: ChunkPos): IWorldChunkTuple? { - val getTuple = getChunkInternal(pos) - - if (getTuple != null) - return WorldChunkTuple(getTuple) - - return null + open fun getInstantTuple(pos: ChunkPos): IWorldChunkTuple? { + return this[pos]?.let { InstantWorldChunkTuple(this as This, it) } } - protected open fun computeIfAbsentInternal(pos: ChunkPos): IWorldChunkTuple { - if (lastAccessedChunk?.chunk?.pos == pos) { - return lastAccessedChunk!! + /** + * Возвращает чанк на заданной позиции, создаёт его если он не существует + */ + open fun computeIfAbsent(pos: ChunkPos): ChunkType { + val _lastAccessedChunk = lastAccessedChunk + + if (_lastAccessedChunk?.pos == pos) { + return _lastAccessedChunk } return chunkMap.computeIfAbsent(pos, Object2ObjectFunction { val chunk = chunkFactory(pos) - val top = getChunkInternal(pos.up()) - val left = getChunkInternal(pos.left()) - val right = getChunkInternal(pos.right()) - val bottom = getChunkInternal(pos.down()) - - val tuple = MutableWorldChunkTuple( - world = this as This, - chunk = chunk, - top = top, - left = left, - right = right, - bottom = bottom, - ) - - top?.bottom = tuple - left?.right = tuple - right?.left = tuple - bottom?.top = tuple - - lastAccessedChunk = tuple + lastAccessedChunk = chunk val orphanedInThisChunk = ArrayList() @@ -377,88 +345,76 @@ abstract class World, ChunkType : Chunk { - return WorldChunkTuple(computeIfAbsentInternal(pos)) - } + open fun getForegroundView(pos: ChunkPos): TileView? { + val get = get(pos) ?: return null + val tuple = InstantWorldChunkTuple(this as This, get) - open fun getForegroundView(pos: ChunkPos): TileChunkView? { - val get = getChunkInternal(pos) ?: return null - - return TileChunkView( - center = get.chunk.foreground, - left = get.left?.chunk?.foreground, - top = get.top?.chunk?.foreground, - topLeft = getChunkInternal(pos.up().left())?.chunk?.foreground, - topRight = getChunkInternal(pos.up().right())?.chunk?.foreground, - right = get.right?.chunk?.foreground, - bottom = get.bottom?.chunk?.foreground, - bottomLeft = getChunkInternal(pos.down().left())?.chunk?.foreground, - bottomRight = getChunkInternal(pos.down().right())?.chunk?.foreground, + return TileView( + center = tuple.chunk.foreground, + left = tuple.left?.chunk?.foreground, + top = tuple.top?.chunk?.foreground, + topLeft = tuple.topLeft?.chunk?.foreground, + topRight = tuple.topRight?.chunk?.foreground, + right = tuple.right?.chunk?.foreground, + bottom = tuple.bottom?.chunk?.foreground, + bottomLeft = tuple.bottomLeft?.chunk?.foreground, + bottomRight = tuple.bottomRight?.chunk?.foreground, ) } - open fun getBackgroundView(pos: ChunkPos): TileChunkView? { - val get = getChunkInternal(pos) ?: return null + open fun getBackgroundView(pos: ChunkPos): TileView? { + val get = get(pos) ?: return null + val tuple = InstantWorldChunkTuple(this as This, get) - return TileChunkView( - center = get.chunk.background, - left = get.left?.chunk?.background, - top = get.top?.chunk?.background, - topLeft = getChunkInternal(pos.up().left())?.chunk?.background, - topRight = getChunkInternal(pos.up().right())?.chunk?.background, - right = get.right?.chunk?.background, - bottom = get.bottom?.chunk?.background, - bottomLeft = getChunkInternal(pos.down().left())?.chunk?.background, - bottomRight = getChunkInternal(pos.down().right())?.chunk?.background, + return TileView( + center = tuple.chunk.background, + left = tuple.left?.chunk?.background, + top = tuple.top?.chunk?.background, + topLeft = tuple.topLeft?.chunk?.background, + topRight = tuple.topRight?.chunk?.background, + right = tuple.right?.chunk?.background, + bottom = tuple.bottom?.chunk?.background, + bottomLeft = tuple.bottomLeft?.chunk?.background, + bottomRight = tuple.bottomRight?.chunk?.background, ) } fun getTile(pos: Vector2i): ChunkTile? { - return getChunkInternal(ChunkPos.fromTilePosition(pos))?.chunk?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)) + return get(ChunkPos.fromTilePosition(pos))?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)) } - fun setTile(pos: Vector2i, tile: TileDefinition?): IWorldChunkTuple { - val chunk = computeIfAbsentInternal(ChunkPos.fromTilePosition(pos)) - chunk.chunk.foreground[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile + fun setTile(pos: Vector2i, tile: TileDefinition?): ChunkType { + val chunk = computeIfAbsent(ChunkPos.fromTilePosition(pos)) + chunk.foreground[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile return chunk } fun getBackgroundTile(pos: Vector2i): ChunkTile? { - return getChunkInternal(ChunkPos.fromTilePosition(pos))?.chunk?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)) + return get(ChunkPos.fromTilePosition(pos))?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)) } - fun setBackgroundTile(pos: Vector2i, tile: TileDefinition?): IWorldChunkTuple { - val chunk = computeIfAbsentInternal(ChunkPos.fromTilePosition(pos)) - chunk.chunk.background[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile + fun setBackgroundTile(pos: Vector2i, tile: TileDefinition?): ChunkType { + val chunk = computeIfAbsent(ChunkPos.fromTilePosition(pos)) + chunk.background[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile return chunk } - protected open fun collectInternal(boundingBox: AABBi): List> { - val output = ArrayList>() - - for (pos in boundingBox.chunkPositions) { - val chunk = getChunkInternal(pos) - - if (chunk != null) { - output.add(chunk) - } - } - - return output - } - /** * Возвращает все чанки, которые пересекаются с заданным [boundingBox] */ - open fun collect(boundingBox: AABBi): List> { - val output = ArrayList>() + open fun collect(boundingBox: AABBi): List { + val output = ArrayList() - for (chunk in collectInternal(boundingBox)) { - output.add(WorldChunkTuple(chunk)) + for (pos in boundingBox.chunkPositions) { + val chunk = get(pos) + + if (chunk != null) { + output.add(chunk) + } } return output @@ -475,11 +431,11 @@ abstract class World, ChunkType : Chunk val a = o1.distance(worldaabb) @@ -630,8 +586,8 @@ abstract class World, ChunkType : Chunk val a = o1.distance(worldaabb) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt index b49ace9b..d9205f9d 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt @@ -130,7 +130,7 @@ abstract class Entity(override val world: World<*, *>) : IEntity { val newChunkPos = ChunkPos.fromTilePosition(value) if (oldChunkPos != newChunkPos) { - chunk = world.getChunk(newChunkPos)?.chunk + chunk = world[newChunkPos] } } } @@ -155,7 +155,7 @@ abstract class Entity(override val world: World<*, *>) : IEntity { isSpawned = true world.entities.add(this) - chunk = world.getChunk(ChunkPos.fromTilePosition(position))?.chunk + chunk = world[ChunkPos.fromTilePosition(position)] if (chunk == null) { world.orphanedEntities.add(this)