From 93deb62d4b17d4f3a849c4a13ea7034965f428f8 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 19 Oct 2023 00:03:38 +0700 Subject: [PATCH] Immutable chunk cells --- .../kotlin/ru/dbotthepony/kstarbound/Main.kt | 21 +- .../kstarbound/client/StarboundClient.kt | 14 +- .../kstarbound/client/render/RenderLayer.kt | 4 +- .../kstarbound/client/render/TileRenderer.kt | 12 +- .../kstarbound/client/world/ClientChunk.kt | 19 +- .../kstarbound/defs/tile/RenderTemplate.kt | 4 +- .../ru/dbotthepony/kstarbound/world/Chunk.kt | 210 ++++-------------- .../dbotthepony/kstarbound/world/ChunkPos.kt | 4 + .../kstarbound/world/Raycasting.kt | 7 +- .../ru/dbotthepony/kstarbound/world/World.kt | 146 ++++-------- .../kstarbound/world/api/AbstractCell.kt | 29 +++ .../world/api/AbstractLiquidState.kt | 23 ++ .../kstarbound/world/api/AbstractTileState.kt | 26 +++ .../kstarbound/world/api/ICellAccess.kt | 31 +-- .../kstarbound/world/api/IChunkCell.kt | 66 ------ .../kstarbound/world/api/ILiquidState.kt | 26 --- .../kstarbound/world/api/ITileAccess.kt | 26 +-- .../kstarbound/world/api/ImmutableCell.kt | 20 ++ .../world/api/ImmutableLiquidState.kt | 18 ++ .../world/api/ImmutableTileState.kt | 21 ++ .../kstarbound/world/api/MutableCell.kt | 38 ++++ .../world/api/MutableLiquidState.kt | 28 +++ .../{ITileState.kt => MutableTileState.kt} | 43 ++-- .../kstarbound/world/api/OffsetCellAccess.kt | 25 +++ .../kstarbound/world/api/TileView.kt | 23 ++ 25 files changed, 423 insertions(+), 461 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractCell.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractLiquidState.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractTileState.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/IChunkCell.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ILiquidState.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableCell.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableLiquidState.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableTileState.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableCell.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableLiquidState.kt rename src/main/kotlin/ru/dbotthepony/kstarbound/world/api/{ITileState.kt => MutableTileState.kt} (52%) create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/OffsetCellAccess.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/world/api/TileView.kt diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index f3109d61..0924a91a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -1,8 +1,6 @@ package ru.dbotthepony.kstarbound -import com.google.common.collect.ImmutableList -import com.google.gson.reflect.TypeToken import org.apache.logging.log4j.LogManager import org.lwjgl.Version import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose @@ -13,14 +11,13 @@ import ru.dbotthepony.kstarbound.player.Avatar import ru.dbotthepony.kstarbound.player.QuestDescriptor import ru.dbotthepony.kstarbound.player.QuestInstance import ru.dbotthepony.kstarbound.util.JVMTimeSource -import ru.dbotthepony.kstarbound.world.api.IChunkCell +import ru.dbotthepony.kstarbound.world.api.AbstractCell import ru.dbotthepony.kstarbound.world.entities.ItemEntity import ru.dbotthepony.kstarbound.io.json.VersionedJson import ru.dbotthepony.kstarbound.io.readVarInt import ru.dbotthepony.kstarbound.util.AssetPathStack +import ru.dbotthepony.kstarbound.world.api.MutableCell import ru.dbotthepony.kstarbound.world.entities.WorldObject -import ru.dbotthepony.kstarbound.world.physics.Poly -import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.vector.Vector2d import java.io.BufferedInputStream import java.io.ByteArrayInputStream @@ -30,7 +27,6 @@ import java.util.* import java.util.concurrent.TimeUnit import java.util.zip.Inflater import java.util.zip.InflaterInputStream -import kotlin.collections.ArrayList private val LOGGER = LogManager.getLogger() @@ -82,13 +78,12 @@ fun main() { var reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(data), Inflater()))) reader.skipBytes(3) - for (y in 0 .. 31) { - for (x in 0 .. 31) { - val cell = client.world!!.getCellDirect(chunkX * 32 + x, chunkY * 32 + y) - if (cell == null) { - IChunkCell.skip(reader) - } else { - cell.read(reader) + val chunk = client.world!!.chunkMap.compute(chunkX - 2, chunkY) + + if (chunk != null) { + for (y in 0 .. 31) { + for (x in 0 .. 31) { + check(chunk.setCell(x, y, MutableCell().read(reader))) } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt index 2d461087..8f19fc62 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt @@ -54,7 +54,7 @@ import ru.dbotthepony.kstarbound.util.forEachValid import ru.dbotthepony.kstarbound.util.formatBytesShort import ru.dbotthepony.kstarbound.world.LightCalculator import ru.dbotthepony.kstarbound.world.api.ICellAccess -import ru.dbotthepony.kstarbound.world.api.IChunkCell +import ru.dbotthepony.kstarbound.world.api.AbstractCell import ru.dbotthepony.kvector.api.IStruct4f import ru.dbotthepony.kvector.arrays.Matrix3f import ru.dbotthepony.kvector.arrays.Matrix3fStack @@ -662,12 +662,16 @@ class StarboundClient : Closeable { val settings = ClientSettings() val viewportCells: ICellAccess = object : ICellAccess { - override fun getCell(x: Int, y: Int): IChunkCell? { - return world?.getCell(x + viewportCellX, y + viewportCellY) + override fun getCell(x: Int, y: Int): AbstractCell { + return world!!.getCell(x + viewportCellX, y + viewportCellY) } - override fun getCellDirect(x: Int, y: Int): IChunkCell? { - return world?.getCellDirect(x + viewportCellX, y + viewportCellY) + override fun getCellDirect(x: Int, y: Int): AbstractCell { + return world!!.getCellDirect(x + viewportCellX, y + viewportCellY) + } + + override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean { + return world!!.setCell(x + viewportCellX, y + viewportCellY, cell) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/RenderLayer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/RenderLayer.kt index f00c1e3d..5f766386 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/RenderLayer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/RenderLayer.kt @@ -2,7 +2,7 @@ package ru.dbotthepony.kstarbound.client.render import com.google.common.collect.ImmutableMap import org.apache.logging.log4j.LogManager -import ru.dbotthepony.kstarbound.world.api.ITileState +import ru.dbotthepony.kstarbound.world.api.AbstractTileState import ru.dbotthepony.kstarbound.world.api.TileColor enum class RenderLayer { @@ -69,7 +69,7 @@ enum class RenderLayer { } } - fun tileLayer(isBackground: Boolean, isModifier: Boolean, tile: ITileState): Point { + fun tileLayer(isBackground: Boolean, isModifier: Boolean, tile: AbstractTileState): Point { if (isModifier) { return tileLayer(isBackground, true, tile.modifier?.renderParameters?.zLevel ?: 0L, tile.modifier?.modId?.toLong() ?: 0L, tile.modifierHueShift) } else { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt index e88164bf..90ba78e7 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt @@ -13,7 +13,7 @@ import ru.dbotthepony.kstarbound.client.gl.shader.UberShader import ru.dbotthepony.kstarbound.client.gl.vertex.* import ru.dbotthepony.kstarbound.defs.tile.* import ru.dbotthepony.kstarbound.world.api.ITileAccess -import ru.dbotthepony.kstarbound.world.api.ITileState +import ru.dbotthepony.kstarbound.world.api.AbstractTileState import ru.dbotthepony.kstarbound.world.api.TileColor import ru.dbotthepony.kvector.vector.RGBAColor import ru.dbotthepony.kvector.vector.Vector2i @@ -94,13 +94,13 @@ class TileRenderers(val client: StarboundClient) { } private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester { - override fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean { + override fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean { return otherTile?.material == definition && thisTile?.hueShift == otherTile.hueShift } } private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester { - override fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean { + override fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean { return otherTile?.modifier == definition } } @@ -125,7 +125,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) { val bakedBackgroundProgramState = texture?.let { renderers.background(it) } // private var notifiedDepth = false - private fun tesselateAt(self: ITileState, piece: RenderPiece, getter: ITileAccess, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) { + private fun tesselateAt(self: AbstractTileState, piece: RenderPiece, getter: ITileAccess, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) { val fx = pos.x.toFloat() val fy = pos.y.toFloat() @@ -173,7 +173,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) { } private fun tesselatePiece( - self: ITileState, + self: AbstractTileState, matchPiece: RenderMatch, getter: ITileAccess, meshBuilder: LayeredRenderer, @@ -227,7 +227,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) { * * Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf] */ - fun tesselate(self: ITileState, getter: ITileAccess, meshBuilder: LayeredRenderer, pos: Vector2i, isBackground: Boolean = false, isModifier: Boolean = false) { + fun tesselate(self: AbstractTileState, getter: ITileAccess, meshBuilder: LayeredRenderer, pos: Vector2i, isBackground: Boolean = false, isModifier: Boolean = false) { if (texture == null) return // если у нас нет renderTemplate diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientChunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientChunk.kt index afa985c2..cf46a13a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientChunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientChunk.kt @@ -2,28 +2,29 @@ package ru.dbotthepony.kstarbound.client.world import ru.dbotthepony.kstarbound.world.Chunk import ru.dbotthepony.kstarbound.world.ChunkPos +import ru.dbotthepony.kstarbound.world.api.ImmutableCell class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk(world, pos){ - override fun foregroundChanges(cell: Cell) { - super.foregroundChanges(cell) + override fun foregroundChanges(x: Int, y: Int, cell: ImmutableCell) { + super.foregroundChanges(x, y, cell) - world.forEachRenderRegion(cell) { + world.forEachRenderRegion(pos.tile(x, y)) { it.foreground.isDirty = true } } - override fun backgroundChanges(cell: Cell) { - super.backgroundChanges(cell) + override fun backgroundChanges(x: Int, y: Int, cell: ImmutableCell) { + super.backgroundChanges(x, y, cell) - world.forEachRenderRegion(cell) { + world.forEachRenderRegion(pos.tile(x, y)) { it.background.isDirty = true } } - override fun liquidChanges(cell: Cell) { - super.liquidChanges(cell) + override fun liquidChanges(x: Int, y: Int, cell: ImmutableCell) { + super.liquidChanges(x, y, cell) - world.forEachRenderRegion(cell) { + world.forEachRenderRegion(pos.tile(x, y)) { it.liquidIsDirty = true } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/tile/RenderTemplate.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/tile/RenderTemplate.kt index b267e7aa..35bc81c8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/tile/RenderTemplate.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/tile/RenderTemplate.kt @@ -8,7 +8,7 @@ import ru.dbotthepony.kstarbound.defs.image.Image import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.util.WriteOnce import ru.dbotthepony.kstarbound.world.api.ITileAccess -import ru.dbotthepony.kstarbound.world.api.ITileState +import ru.dbotthepony.kstarbound.world.api.AbstractTileState import ru.dbotthepony.kvector.vector.Vector2i @JsonFactory @@ -21,7 +21,7 @@ data class RenderPiece( ) fun interface EqualityRuleTester { - fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean + fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean } @JsonFactory diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt index 61ef8e3f..f5d68009 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt @@ -1,16 +1,10 @@ package ru.dbotthepony.kstarbound.world import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet -import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials -import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition -import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier -import ru.dbotthepony.kstarbound.defs.tile.TileDefinition +import ru.dbotthepony.kstarbound.world.api.AbstractCell import ru.dbotthepony.kstarbound.world.api.ICellAccess -import ru.dbotthepony.kstarbound.world.api.IChunkCell -import ru.dbotthepony.kstarbound.world.api.ILiquidState -import ru.dbotthepony.kstarbound.world.api.ITileState +import ru.dbotthepony.kstarbound.world.api.ImmutableCell import ru.dbotthepony.kstarbound.world.api.OffsetCellAccess -import ru.dbotthepony.kstarbound.world.api.TileColor import ru.dbotthepony.kstarbound.world.api.TileView import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kvector.arrays.Object2DArray @@ -33,10 +27,7 @@ abstract class Chunk, This : Chunk, This : Chunk(CHUNK_SIZE, CHUNK_SIZE) + Object2DArray(CHUNK_SIZE, CHUNK_SIZE, AbstractCell.NULL) } - override fun getCell(x: Int, y: Int): IChunkCell { - var get = cells.value[x, y] + override fun getCell(x: Int, y: Int): AbstractCell { + if (!cells.isInitialized()) + return AbstractCell.NULL - if (get == null) { - get = Cell(x, y) - cells.value[x, y] = get + return cells.value[x, y] + } + + override fun getCellDirect(x: Int, y: Int): AbstractCell { + return getCell(x, y) + } + + override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean { + val ix = x and CHUNK_SIZE_FF + val iy = y and CHUNK_SIZE_FF + + if (ix != x || iy != y) return false + + val old = if (cells.isInitialized()) cells.value[ix, iy] else AbstractCell.NULL + val new = cell.immutable() + + if (old != new) { + cells.value[ix, iy] = new + + if (old.foreground != new.foreground) { + foregroundChanges(ix, iy, new) + } + + if (old.background != new.background) { + backgroundChanges(ix, iy, new) + } + + if (old.liquid != new.liquid) { + liquidChanges(ix, iy, new) + } + + cellChanges(ix, iy, new) } - return get - } - - override fun getCellDirect(x: Int, y: Int): IChunkCell { - return getCell(x, y) + return true } // local cells' tile access @@ -89,24 +97,24 @@ abstract class Chunk, This : Chunk, This : Chunk { val tileY = y shl CHUNK_SIZE_BITS val tile = Vector2i(tileX, tileY) + fun tile(x: Int, y: Int): Vector2i { + return Vector2i(tileX + x, tileY + y) + } + val top: ChunkPos get() { return ChunkPos(x, y + 1) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Raycasting.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Raycasting.kt index aa9e3366..6e4b050e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Raycasting.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Raycasting.kt @@ -1,9 +1,8 @@ package ru.dbotthepony.kstarbound.world -import ru.dbotthepony.kstarbound.world.METRES_IN_STARBOUND_UNIT import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity import ru.dbotthepony.kstarbound.world.api.ICellAccess -import ru.dbotthepony.kstarbound.world.api.IChunkCell +import ru.dbotthepony.kstarbound.world.api.AbstractCell import ru.dbotthepony.kvector.vector.Vector2d import ru.dbotthepony.kvector.vector.Vector2i import kotlin.collections.ArrayList @@ -20,7 +19,7 @@ data class RayCastResult( ) { constructor(startPos: Vector2d, direction: Vector2d) : this(listOf(), null, 0.0, startPos, startPos, direction) - data class HitCell(val pos: Vector2i, val normal: Direction, val borderCross: Vector2d, val cell: IChunkCell) + data class HitCell(val pos: Vector2i, val normal: Direction, val borderCross: Vector2d, val cell: AbstractCell) } enum class RayFilterResult(val hit: Boolean, val write: Boolean) { @@ -44,7 +43,7 @@ fun interface TileRayFilter { /** * [x] and [y] are wrapped around positions */ - fun test(cell: IChunkCell, fraction: Double, x: Int, y: Int, normal: Direction, borderX: Double, borderY: Double): RayFilterResult + fun test(cell: AbstractCell, fraction: Double, x: Int, y: Int, normal: Direction, borderX: Double, borderY: Double): RayFilterResult } val NeverFilter = TileRayFilter { state, fraction, x, y, normal, borderX, borderY -> RayFilterResult.CONTINUE } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt index 9a384175..cc89824f 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt @@ -10,12 +10,11 @@ import ru.dbotthepony.kstarbound.math.* import ru.dbotthepony.kstarbound.util.MailboxExecutorService import ru.dbotthepony.kstarbound.util.ParallelPerform import ru.dbotthepony.kstarbound.world.api.ICellAccess -import ru.dbotthepony.kstarbound.world.api.IChunkCell +import ru.dbotthepony.kstarbound.world.api.AbstractCell import ru.dbotthepony.kstarbound.world.api.TileView import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kstarbound.world.entities.WorldObject import ru.dbotthepony.kstarbound.world.physics.CollisionPoly -import ru.dbotthepony.kstarbound.world.physics.CollisionType import ru.dbotthepony.kstarbound.world.physics.Poly import ru.dbotthepony.kvector.api.IStruct2d import ru.dbotthepony.kvector.api.IStruct2i @@ -23,8 +22,6 @@ import ru.dbotthepony.kvector.arrays.Object2DArray import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.vector.Vector2d import ru.dbotthepony.kvector.vector.Vector2i -import java.lang.ref.ReferenceQueue -import java.lang.ref.WeakReference import java.util.concurrent.ForkJoinPool import java.util.concurrent.locks.ReentrantLock import java.util.random.RandomGenerator @@ -54,33 +51,31 @@ abstract class World, ChunkType : Chunk() operator fun get(pos: ChunkPos) = get(pos.x, pos.y) - protected inner class Ref(chunk: ChunkType) : WeakReference(chunk, queue) { - val pos = chunk.pos - } - protected fun create(x: Int, y: Int): ChunkType { - purge() val pos = ChunkPos(x, y) val chunk = chunkFactory(pos) val orphanedInThisChunk = ArrayList() @@ -103,68 +98,41 @@ abstract class World, ChunkType : Chunk() + private val map = Long2ObjectOpenHashMap() - override fun getCell(x: Int, y: Int): IChunkCell? { - if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return null + override fun getCell(x: Int, y: Int): AbstractCell { + if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return AbstractCell.NULL val ix = this@World.x.cell(x) val iy = this@World.y.cell(y) - return this[this@World.x.chunkFromCell(ix), this@World.y.chunkFromCell(iy)]?.getCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK) + 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) - return map[ChunkPos.toLong(x, y)]?.let { - if (it is World<*, *>.ChunkMap.Ref) { - it.get() as ChunkType? - } else { - it as ChunkType? - } - } ?: create(x, y).also { map[ChunkPos.toLong(x, y)] = Ref(it) } + return map[ChunkPos.toLong(x, y)] } - override fun purge() { - var next = queue.poll() as World<*, *>.ChunkMap.Ref? - - while (next != null) { - val k = ChunkPos.toLong(next.pos.x, next.pos.y) - val get = map[k] - - if (get === next) - map.remove(k) - - next = queue.poll() as World<*, *>.ChunkMap.Ref? - } + override fun compute(x: Int, y: Int): ChunkType? { + if (!this@World.x.inBoundsChunk(x) || !this@World.y.inBoundsChunk(y)) return null + return map[ChunkPos.toLong(x, y)] ?: create(x, y).also { map[ChunkPos.toLong(x, y)] = it } } - override fun promote(self: ChunkType) { - val (x, y) = self.pos - val ref = map[ChunkPos.toLong(x, y)] - - if (ref !is World<*, *>.ChunkMap.Ref) { - throw IllegalStateException("Tried to promote chunk from weak to strong storage at $x, $y; but there is $ref") - } - - if (!(ref as World<*, ChunkType>.ChunkMap.Ref).refersTo(self)) { - throw IllegalStateException("Tried to promote chunk from weak to strong storage at $x, $y; but it doesn't refer to valid object (self: $self, referent: ${ref.get()})") - } - - map[ChunkPos.toLong(x, y)] = self + override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean { + if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return false + val ix = this@World.x.cell(x) + val iy = this@World.y.cell(y) + val cx = this@World.x.chunkFromCell(ix) + val cy = this@World.y.chunkFromCell(iy) + return (map[ChunkPos.toLong(cx, cy)] ?: create(cx, cy).also { map[ChunkPos.toLong(cx, cy)] = it }).setCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK, cell) } override fun remove(x: Int, y: Int) { - val x = this@World.x.chunk(x) - val y = this@World.y.chunk(y) - - val ref = map.remove(ChunkPos.toLong(x, y)) - - if (ref is World<*, *>.ChunkMap.Ref) { - ref.clear() - } + map.remove(ChunkPos.toLong(this@World.x.chunk(x), this@World.y.chunk(y))) } } @@ -172,64 +140,38 @@ abstract class World, ChunkType : Chunk(divideUp(width, CHUNK_SIZE), divideUp(height, CHUNK_SIZE)) + private val map = Object2DArray.nulls(divideUp(width, CHUNK_SIZE), divideUp(height, CHUNK_SIZE)) private fun getRaw(x: Int, y: Int): ChunkType { - return map[x, y]?.let { - if (it is World<*, *>.ChunkMap.Ref) { - it.get() as ChunkType? - } else { - it as ChunkType? - } - } ?: create(x, y).also { - map[x, y] = Ref(it) - } + return map[x, y] ?: create(x, y).also { map[x, y] = it } } - override fun getCell(x: Int, y: Int): IChunkCell? { - if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return null + override fun compute(x: Int, y: Int): ChunkType? { + if (!this@World.x.inBoundsChunk(x) || !this@World.y.inBoundsChunk(y)) return null + return getRaw(x, y) + } + override fun getCell(x: Int, y: Int): AbstractCell { + if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return AbstractCell.NULL val ix = this@World.x.cell(x) val iy = this@World.y.cell(y) - return getRaw(ix ushr CHUNK_SIZE_BITS, iy ushr CHUNK_SIZE_BITS).getCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK) } + override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean { + if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return false + val ix = this@World.x.cell(x) + val iy = this@World.y.cell(y) + return getRaw(ix ushr CHUNK_SIZE_BITS, iy ushr CHUNK_SIZE_BITS).setCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK, cell) + } + 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)) } - override fun purge() { - while (queue.poll() != null) {} - } - - override fun promote(self: ChunkType) { - val (x, y) = self.pos - val ref = map[x, y] - - if (ref !is World<*, *>.ChunkMap.Ref) { - throw IllegalStateException("Tried to promote chunk from weak to strong storage at $x, $y; but there is $ref") - } - - if (!(ref as World<*, ChunkType>.ChunkMap.Ref).refersTo(self)) { - throw IllegalStateException("Tried to promote chunk from weak to strong storage at $x, $y; but it doesn't refer to valid object (self: $self, referent: ${ref.get()})") - } - - map[x, y] = self - } - override fun remove(x: Int, y: Int) { - val x = this@World.x.chunk(x) - val y = this@World.y.chunk(y) - - val old = map[x, y] - - if (old is World<*, *>.ChunkMap.Ref) { - old.clear() - } - - map[x, y] = null + map[this@World.x.chunk(x), this@World.y.chunk(y)] = null } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractCell.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractCell.kt new file mode 100644 index 00000000..6d63a2e4 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractCell.kt @@ -0,0 +1,29 @@ +package ru.dbotthepony.kstarbound.world.api + +import java.io.DataInputStream + +sealed class AbstractCell { + abstract val foreground: AbstractTileState + abstract val background: AbstractTileState + abstract val liquid: AbstractLiquidState + + abstract val dungeonId: Int + abstract val biome: Int + abstract val envBiome: Int + abstract val isIndestructible: Boolean + + abstract fun immutable(): ImmutableCell + abstract fun mutable(): MutableCell + + companion object { + fun skip(stream: DataInputStream) { + AbstractTileState.skip(stream) + AbstractTileState.skip(stream) + AbstractLiquidState.skip(stream) + stream.skipNBytes(1 + 2 + 1 + 1 + 1 + 1) + } + + val EMPTY = ImmutableCell(AbstractTileState.EMPTY, AbstractTileState.EMPTY, AbstractLiquidState.EMPTY, 0, 0, 0, false) + val NULL = ImmutableCell(AbstractTileState.NULL, AbstractTileState.NULL, AbstractLiquidState.EMPTY, 0, 0, 0, false) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractLiquidState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractLiquidState.kt new file mode 100644 index 00000000..5672f0d9 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractLiquidState.kt @@ -0,0 +1,23 @@ +package ru.dbotthepony.kstarbound.world.api + +import ru.dbotthepony.kstarbound.Registries +import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition +import java.io.DataInputStream + +sealed class AbstractLiquidState { + abstract val def: LiquidDefinition? + abstract val level: Float + abstract val pressure: Float + abstract val isInfinite: Boolean + + abstract fun mutable(): MutableLiquidState + abstract fun immutable(): ImmutableLiquidState + + companion object { + fun skip(stream: DataInputStream) { + stream.skipNBytes(1 + 4 + 4 + 1) + } + + val EMPTY = ImmutableLiquidState() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractTileState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractTileState.kt new file mode 100644 index 00000000..e2114c44 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/AbstractTileState.kt @@ -0,0 +1,26 @@ +package ru.dbotthepony.kstarbound.world.api + +import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials +import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier +import ru.dbotthepony.kstarbound.defs.tile.TileDefinition +import java.io.DataInputStream + +sealed class AbstractTileState { + abstract val material: TileDefinition + abstract val modifier: MaterialModifier? + abstract val color: TileColor + abstract val hueShift: Float + abstract val modifierHueShift: Float + + abstract fun immutable(): ImmutableTileState + abstract fun mutable(): MutableTileState + + companion object { + fun skip(stream: DataInputStream) { + stream.skipNBytes(2 + 1 + 1 + 2 + 1) + } + + val EMPTY = ImmutableTileState(BuiltinMetaMaterials.EMPTY) + val NULL = ImmutableTileState(BuiltinMetaMaterials.NULL) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ICellAccess.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ICellAccess.kt index f0dfd5d3..0d78e4cb 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ICellAccess.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ICellAccess.kt @@ -5,19 +5,22 @@ import ru.dbotthepony.kvector.vector.Vector2i interface ICellAccess { /** - * non-null - valid cell (maybe wrapped around) - * null - invalid cell (outside world bounds) + * with wrap-around */ - fun getCell(x: Int, y: Int): IChunkCell? + fun getCell(x: Int, y: Int): AbstractCell fun getCell(pos: IStruct2i) = getCell(pos.component1(), pos.component2()) /** - * non-null - valid cell and not wrapped around - * null - invalid cell (outside world bounds) + * without wrap-around */ - fun getCellDirect(x: Int, y: Int): IChunkCell? + fun getCellDirect(x: Int, y: Int): AbstractCell fun getCellDirect(pos: IStruct2i) = getCellDirect(pos.component1(), pos.component2()) + /** + * whenever cell was set + */ + fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean + /** * Возвращает псевдослучайное Long для заданной позиции * @@ -45,19 +48,3 @@ interface ICellAccess { fun randomDoubleFor(pos: Vector2i) = randomDoubleFor(pos.x, pos.y) } -class OffsetCellAccess(private val parent: ICellAccess, var x: Int, var y: Int) : ICellAccess { - constructor(parent: ICellAccess, offset: IStruct2i) : this(parent, offset.component1(), offset.component2()) - - override fun getCell(x: Int, y: Int): IChunkCell? { - return parent.getCell(x + this.x, y + this.y) - } - - override fun getCellDirect(x: Int, y: Int): IChunkCell? { - return parent.getCellDirect(x + this.x, y + this.y) - } - - override fun randomLongFor(x: Int, y: Int) = parent.randomLongFor(x + this.x, y + this.y) - override fun randomDoubleFor(x: Int, y: Int) = parent.randomDoubleFor(x + this.x, y + this.y) - override fun randomLongFor(pos: Vector2i) = parent.randomLongFor(pos.x + this.x, pos.y + this.y) - override fun randomDoubleFor(pos: Vector2i) = parent.randomDoubleFor(pos.x + this.x, pos.y + this.y) -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/IChunkCell.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/IChunkCell.kt deleted file mode 100644 index 3a48cb7c..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/IChunkCell.kt +++ /dev/null @@ -1,66 +0,0 @@ -package ru.dbotthepony.kstarbound.world.api - -import ru.dbotthepony.kstarbound.RegistryObject -import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition -import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier -import ru.dbotthepony.kstarbound.defs.tile.TileDefinition -import ru.dbotthepony.kstarbound.world.physics.Poly -import ru.dbotthepony.kvector.api.IStruct2i -import ru.dbotthepony.kvector.util2d.AABB -import ru.dbotthepony.kvector.vector.Vector2d -import java.io.DataInputStream - -private val rect = Poly(listOf(Vector2d.ZERO, Vector2d(0.0, 1.0), Vector2d(1.0, 1.0), Vector2d(1.0, 0.0))) - -interface IChunkCell : IStruct2i { - /** - * absolute (in world) - */ - val x: Int - - /** - * absolute (in world) - */ - val y: Int - - override fun component1(): Int { - return x - } - - override fun component2(): Int { - return y - } - - val foreground: ITileState - val background: ITileState - val liquid: ILiquidState - - var dungeonId: Int - var biome: Int - var envBiome: Int - var isIndestructible: Boolean - - fun read(stream: DataInputStream) { - foreground.read(stream) - background.read(stream) - liquid.read(stream) - - stream.skipNBytes(1) // collisionMap - - dungeonId = stream.readUnsignedShort() - biome = stream.readUnsignedByte() - envBiome = stream.readUnsignedByte() - isIndestructible = stream.readBoolean() - - stream.skipNBytes(1) // unknown - } - - companion object { - fun skip(stream: DataInputStream) { - ITileState.skip(stream) - ITileState.skip(stream) - ILiquidState.skip(stream) - stream.skipNBytes(1 + 2 + 1 + 1 + 1 + 1) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ILiquidState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ILiquidState.kt deleted file mode 100644 index 6eb51ca5..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ILiquidState.kt +++ /dev/null @@ -1,26 +0,0 @@ -package ru.dbotthepony.kstarbound.world.api - -import ru.dbotthepony.kstarbound.Registries -import ru.dbotthepony.kstarbound.RegistryObject -import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition -import java.io.DataInputStream - -interface ILiquidState { - var def: LiquidDefinition? - var level: Float - var pressure: Float - var isInfinite: Boolean - - fun read(stream: DataInputStream) { - def = Registries.liquid[stream.readUnsignedByte()]?.value - level = stream.readFloat() - pressure = stream.readFloat() - isInfinite = stream.readBoolean() - } - - companion object { - fun skip(stream: DataInputStream) { - stream.skipNBytes(1 + 4 + 4 + 1) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileAccess.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileAccess.kt index 7411d3dd..1e280077 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileAccess.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileAccess.kt @@ -5,30 +5,8 @@ import ru.dbotthepony.kvector.api.IStruct2i // for getting tiles directly, avoiding manual layer specification interface ITileAccess : ICellAccess { // relative - fun getTile(x: Int, y: Int): ITileState? + fun getTile(x: Int, y: Int): AbstractTileState? fun getTile(pos: IStruct2i) = getTile(pos.component1(), pos.component2()) - fun getTileDirect(x: Int, y: Int): ITileState? + fun getTileDirect(x: Int, y: Int): AbstractTileState? fun getTileDirect(pos: IStruct2i) = getTile(pos.component1(), pos.component2()) } - -sealed class TileView(parent: ICellAccess) : ITileAccess, ICellAccess by parent { - class Foreground(parent: ICellAccess) : TileView(parent) { - override fun getTile(x: Int, y: Int): ITileState? { - return getCell(x, y)?.foreground - } - - override fun getTileDirect(x: Int, y: Int): ITileState? { - return getCellDirect(x, y)?.foreground - } - } - - class Background(parent: ICellAccess) : TileView(parent) { - override fun getTile(x: Int, y: Int): ITileState? { - return getCell(x, y)?.background - } - - override fun getTileDirect(x: Int, y: Int): ITileState? { - return getCellDirect(x, y)?.background - } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableCell.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableCell.kt new file mode 100644 index 00000000..3560398a --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableCell.kt @@ -0,0 +1,20 @@ +package ru.dbotthepony.kstarbound.world.api + +data class ImmutableCell( + override val foreground: ImmutableTileState = AbstractTileState.EMPTY, + override val background: ImmutableTileState = AbstractTileState.EMPTY, + override val liquid: ImmutableLiquidState = AbstractLiquidState.EMPTY, + + override val dungeonId: Int = 0, + override val biome: Int = 0, + override val envBiome: Int = 0, + override val isIndestructible: Boolean = false, +) : AbstractCell() { + override fun immutable(): ImmutableCell { + return this + } + + override fun mutable(): MutableCell { + return MutableCell(foreground.mutable(), background.mutable(), liquid.mutable(), dungeonId, biome, envBiome, isIndestructible) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableLiquidState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableLiquidState.kt new file mode 100644 index 00000000..0a73cfb7 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableLiquidState.kt @@ -0,0 +1,18 @@ +package ru.dbotthepony.kstarbound.world.api + +import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition + +data class ImmutableLiquidState( + override val def: LiquidDefinition? = null, + override val level: Float = 0f, + override val pressure: Float = 0f, + override val isInfinite: Boolean = false, +) : AbstractLiquidState() { + override fun mutable(): MutableLiquidState { + return MutableLiquidState(def, level, pressure, isInfinite) + } + + override fun immutable(): ImmutableLiquidState { + return this + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableTileState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableTileState.kt new file mode 100644 index 00000000..d5328f77 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ImmutableTileState.kt @@ -0,0 +1,21 @@ +package ru.dbotthepony.kstarbound.world.api + +import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials +import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier +import ru.dbotthepony.kstarbound.defs.tile.TileDefinition + +data class ImmutableTileState( + override var material: TileDefinition = BuiltinMetaMaterials.NULL, + override var modifier: MaterialModifier? = null, + override var color: TileColor = TileColor.DEFAULT, + override var hueShift: Float = 0f, + override var modifierHueShift: Float = 0f, +) : AbstractTileState() { + override fun immutable(): ImmutableTileState { + return this + } + + override fun mutable(): MutableTileState { + return MutableTileState(material, modifier, color, hueShift, modifierHueShift) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableCell.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableCell.kt new file mode 100644 index 00000000..04145915 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableCell.kt @@ -0,0 +1,38 @@ +package ru.dbotthepony.kstarbound.world.api + +import java.io.DataInputStream + +data class MutableCell( + override var foreground: MutableTileState = MutableTileState(), + override var background: MutableTileState = MutableTileState(), + override var liquid: MutableLiquidState = MutableLiquidState(), + + override var dungeonId: Int = 0, + override var biome: Int = 0, + override var envBiome: Int = 0, + override var isIndestructible: Boolean = false, +) : AbstractCell() { + fun read(stream: DataInputStream): MutableCell { + foreground.read(stream) + background.read(stream) + liquid.read(stream) + + stream.skipNBytes(1) // collisionMap + + dungeonId = stream.readUnsignedShort() + biome = stream.readUnsignedByte() + envBiome = stream.readUnsignedByte() + isIndestructible = stream.readBoolean() + + stream.skipNBytes(1) // unknown + return this + } + + override fun immutable(): ImmutableCell { + return ImmutableCell(foreground.immutable(), background.immutable(), liquid.immutable(), dungeonId, biome, envBiome, isIndestructible) + } + + override fun mutable(): MutableCell { + return this + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableLiquidState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableLiquidState.kt new file mode 100644 index 00000000..89d3e6b2 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableLiquidState.kt @@ -0,0 +1,28 @@ +package ru.dbotthepony.kstarbound.world.api + +import ru.dbotthepony.kstarbound.Registries +import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition +import java.io.DataInputStream + +data class MutableLiquidState( + override var def: LiquidDefinition? = null, + override var level: Float = 0f, + override var pressure: Float = 0f, + override var isInfinite: Boolean = false, +) : AbstractLiquidState() { + fun read(stream: DataInputStream): MutableLiquidState { + def = Registries.liquid[stream.readUnsignedByte()]?.value + level = stream.readFloat() + pressure = stream.readFloat() + isInfinite = stream.readBoolean() + return this + } + + override fun mutable(): MutableLiquidState { + return this + } + + override fun immutable(): ImmutableLiquidState { + return ImmutableLiquidState(def, level, pressure, isInfinite) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableTileState.kt similarity index 52% rename from src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileState.kt rename to src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableTileState.kt index e0612724..7fdcbe3a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileState.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/MutableTileState.kt @@ -1,20 +1,30 @@ package ru.dbotthepony.kstarbound.world.api import ru.dbotthepony.kstarbound.Registries -import ru.dbotthepony.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import java.io.DataInputStream -interface ITileState { - var material: TileDefinition - var modifier: MaterialModifier? - var color: TileColor - var hueShift: Float - var modifierHueShift: Float +data class MutableTileState( + override var material: TileDefinition = BuiltinMetaMaterials.NULL, + override var modifier: MaterialModifier? = null, + override var color: TileColor = TileColor.DEFAULT, + override var hueShift: Float = 0f, + override var modifierHueShift: Float = 0f, +) : AbstractTileState() { + override fun immutable(): ImmutableTileState { + val result = ImmutableTileState(material, modifier, color, hueShift, modifierHueShift) + if (result == NULL) return NULL + if (result == EMPTY) return EMPTY + return result + } - fun setHueShift(value: Int) { + override fun mutable(): MutableTileState { + return this + } + + fun setHueShift(value: Int): MutableTileState { if (value < 0) { hueShift = 0f } else if (value >= 255) { @@ -22,9 +32,11 @@ interface ITileState { } else { hueShift = value / 255f * 360f } + + return this } - fun setModHueShift(value: Int) { + fun setModHueShift(value: Int): MutableTileState { if (value < 0) { modifierHueShift = 0f } else if (value >= 255) { @@ -32,19 +44,16 @@ interface ITileState { } else { modifierHueShift = value / 255f * 360f } + + return this } - fun read(stream: DataInputStream) { + fun read(stream: DataInputStream): MutableTileState { material = Registries.tiles[stream.readUnsignedShort()]?.value ?: BuiltinMetaMaterials.EMPTY setHueShift(stream.read()) color = TileColor.of(stream.read()) modifier = Registries.tileModifiers[stream.readUnsignedShort()]?.value setModHueShift(stream.read()) + return this } - - companion object { - fun skip(stream: DataInputStream) { - stream.skipNBytes(2 + 1 + 1 + 2 + 1) - } - } -} \ No newline at end of file +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/OffsetCellAccess.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/OffsetCellAccess.kt new file mode 100644 index 00000000..29f66c93 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/OffsetCellAccess.kt @@ -0,0 +1,25 @@ +package ru.dbotthepony.kstarbound.world.api + +import ru.dbotthepony.kvector.api.IStruct2i +import ru.dbotthepony.kvector.vector.Vector2i + +class OffsetCellAccess(private val parent: ICellAccess, var x: Int, var y: Int) : ICellAccess { + constructor(parent: ICellAccess, offset: IStruct2i) : this(parent, offset.component1(), offset.component2()) + + override fun getCell(x: Int, y: Int): AbstractCell { + return parent.getCell(x + this.x, y + this.y) + } + + override fun getCellDirect(x: Int, y: Int): AbstractCell { + return parent.getCellDirect(x + this.x, y + this.y) + } + + override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean { + return parent.setCell(x, y, cell) + } + + override fun randomLongFor(x: Int, y: Int) = parent.randomLongFor(x + this.x, y + this.y) + override fun randomDoubleFor(x: Int, y: Int) = parent.randomDoubleFor(x + this.x, y + this.y) + override fun randomLongFor(pos: Vector2i) = parent.randomLongFor(pos.x + this.x, pos.y + this.y) + override fun randomDoubleFor(pos: Vector2i) = parent.randomDoubleFor(pos.x + this.x, pos.y + this.y) +} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/TileView.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/TileView.kt new file mode 100644 index 00000000..2a65f795 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/TileView.kt @@ -0,0 +1,23 @@ +package ru.dbotthepony.kstarbound.world.api + +sealed class TileView(parent: ICellAccess) : ITileAccess, ICellAccess by parent { + class Foreground(parent: ICellAccess) : TileView(parent) { + override fun getTile(x: Int, y: Int): AbstractTileState? { + return getCell(x, y)?.foreground + } + + override fun getTileDirect(x: Int, y: Int): AbstractTileState? { + return getCellDirect(x, y)?.foreground + } + } + + class Background(parent: ICellAccess) : TileView(parent) { + override fun getTile(x: Int, y: Int): AbstractTileState? { + return getCell(x, y)?.background + } + + override fun getTileDirect(x: Int, y: Int): AbstractTileState? { + return getCellDirect(x, y)?.background + } + } +} \ No newline at end of file