From 0eccc4dbea0501436756592d761b44811582cc84 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sun, 10 Nov 2024 22:30:01 +0900 Subject: [PATCH] huge wippie --- src/net/torvald/terrarum/App.java | 3 +- src/net/torvald/terrarum/IngameInstance.kt | 4 +- .../terrarum/blockstats/MinimapComposer.kt | 13 +- src/net/torvald/terrarum/console/GetGR.kt | 3 +- .../terrarum/gameactors/ActorWithBody.kt | 1 + .../torvald/terrarum/gameworld/BlockLayer.kt | 47 +- .../gameworld/BlockLayerFluidI16F16.kt | 34 +- .../gameworld/BlockLayerGenericI16.kt | 33 +- .../gameworld/BlockLayerInMemoryI16.kt | 133 +++ .../gameworld/BlockLayerInMemoryI16F16.kt | 145 +++ .../gameworld/BlockLayerInMemoryI16I8.kt | 151 +++ .../terrarum/gameworld/BlockLayerOresI16I8.kt | 39 +- .../torvald/terrarum/gameworld/ChunkPool.kt | 14 +- .../torvald/terrarum/gameworld/GameWorld.kt | 964 +++++++++--------- .../terrarum/modulebasegame/BuildingMaker.kt | 24 +- .../modulebasegame/ExplosionManager.kt | 27 +- .../terrarum/modulebasegame/IngameRenderer.kt | 21 +- .../terrarum/modulebasegame/TerrarumIngame.kt | 17 +- .../terrarum/modulebasegame/TitleScreen.kt | 17 +- .../terrarum/modulebasegame/WorldSimulator.kt | 1 + .../modulebasegame/WorldgenLoadScreen.kt | 3 +- .../modulebasegame/console/ExportWorld.kt | 8 +- .../modulebasegame/console/ImportWorld.kt | 4 +- .../modulebasegame/gameitems/BlockBase.kt | 1 + .../modulebasegame/serialise/LoadSavegame.kt | 20 +- .../serialise/QuickSaveThread.kt | 3 +- .../serialise/WorldSavingThread.kt | 2 +- .../modulebasegame/serialise/WriteWorld.kt | 7 +- .../worldgenerator/OregenAutotiling.kt | 1 - .../modulebasegame/worldgenerator/Worldgen.kt | 7 +- src/net/torvald/terrarum/serialise/Common.kt | 17 +- .../terrarum/serialise/PointOfInterest.kt | 7 +- ...leWorld.kt => ReadTitlescreenGameWorld.kt} | 33 +- .../terrarum/ui/BasicDebugInfoWindow.kt | 3 +- src/net/torvald/terrarum/utils/HashArray.kt | 2 - .../terrarum/worlddrawer/BlocksDrawer.kt | 18 +- .../terrarum/worlddrawer/FeaturesDrawer.kt | 6 +- .../terrarum/worlddrawer/LightmapRenderer.kt | 9 +- 38 files changed, 1196 insertions(+), 646 deletions(-) create mode 100644 src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16.kt create mode 100644 src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16F16.kt create mode 100644 src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16I8.kt rename src/net/torvald/terrarum/serialise/{ReadSimpleWorld.kt => ReadTitlescreenGameWorld.kt} (55%) diff --git a/src/net/torvald/terrarum/App.java b/src/net/torvald/terrarum/App.java index aa63d26b1..c59e4cda1 100644 --- a/src/net/torvald/terrarum/App.java +++ b/src/net/torvald/terrarum/App.java @@ -31,6 +31,7 @@ import net.torvald.terrarum.gamecontroller.KeyToggler; import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent; import net.torvald.terrarum.gameitems.GameItem; import net.torvald.terrarum.gameworld.GameWorld; +import net.torvald.terrarum.gameworld.TheGameWorld; import net.torvald.terrarum.imagefont.BigAlphNum; import net.torvald.terrarum.imagefont.TinyAlphNum; import net.torvald.terrarum.langpack.Lang; @@ -1016,7 +1017,7 @@ public class App implements ApplicationListener { ModMgr.INSTANCE.disposeMods(); - GameWorld.Companion.makeNullWorld().dispose(); + TheGameWorld.Companion.makeNullWorld().dispose(); Terrarum.INSTANCE.dispose(); diff --git a/src/net/torvald/terrarum/IngameInstance.kt b/src/net/torvald/terrarum/IngameInstance.kt index c40ad3480..398c3df68 100644 --- a/src/net/torvald/terrarum/IngameInstance.kt +++ b/src/net/torvald/terrarum/IngameInstance.kt @@ -12,6 +12,8 @@ import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld +import net.torvald.terrarum.gameworld.TitlescreenGameWorld import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer @@ -105,7 +107,7 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo printStackTrace(this) } - open var world: GameWorld = GameWorld.makeNullWorld() + open var world: GameWorld = TheGameWorld.makeNullWorld() set(value) { val oldWorld = field newWorldLoadedLatch = true diff --git a/src/net/torvald/terrarum/blockstats/MinimapComposer.kt b/src/net/torvald/terrarum/blockstats/MinimapComposer.kt index c2eb1c2ea..b16fc3cdd 100644 --- a/src/net/torvald/terrarum/blockstats/MinimapComposer.kt +++ b/src/net/torvald/terrarum/blockstats/MinimapComposer.kt @@ -2,26 +2,17 @@ package net.torvald.terrarum.blockstats import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Pixmap -import com.badlogic.gdx.graphics.Texture -import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.utils.Disposable -import com.badlogic.gdx.utils.GdxRuntimeException -import com.badlogic.gdx.utils.Queue import net.torvald.terrarum.App -import net.torvald.terrarum.App.printdbg -import net.torvald.terrarum.abs import net.torvald.terrarum.concurrent.ThreadExecutor import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.modulebasegame.ui.UIInventoryMinimap.Companion.MINIMAP_HEIGHT import net.torvald.terrarum.modulebasegame.ui.UIInventoryMinimap.Companion.MINIMAP_WIDTH -import net.torvald.terrarum.sqr import net.torvald.terrarum.toInt import net.torvald.terrarum.worlddrawer.CreateTileAtlas.Companion.WALL_OVERLAY_COLOUR import java.util.concurrent.Callable -import java.util.concurrent.atomic.AtomicInteger -import kotlin.math.absoluteValue import kotlin.math.max -import kotlin.math.roundToInt object MinimapComposer : Disposable { @@ -32,7 +23,7 @@ object MinimapComposer : Disposable { val MINIMAP_TILE_WIDTH = (MINIMAP_WIDTH.toInt() * 3) / SQUARE_SIZE + 4 val MINIMAP_TILE_HEIGHT = (MINIMAP_HEIGHT.toInt() * 3) / SQUARE_SIZE + 4 - private var world: GameWorld = GameWorld.makeNullWorld() + private var world: GameWorld = TheGameWorld.makeNullWorld() fun setWorld(world: GameWorld) { try { diff --git a/src/net/torvald/terrarum/console/GetGR.kt b/src/net/torvald/terrarum/console/GetGR.kt index ed5d5071f..988b8d2e7 100644 --- a/src/net/torvald/terrarum/console/GetGR.kt +++ b/src/net/torvald/terrarum/console/GetGR.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.console import net.torvald.terrarum.* +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.modulebasegame.console.GetAV.isNum /** @@ -8,7 +9,7 @@ import net.torvald.terrarum.modulebasegame.console.GetAV.isNum */ internal object GetGR : ConsoleCommand { override fun execute(args: Array) { - val gameRules = INGAME.world.gameRules + val gameRules = (INGAME.world as TheGameWorld).gameRules // check if args[1] is number or not if (args.size > 1 && !args[1].isNum()) { // args[1] is Gamerule name diff --git a/src/net/torvald/terrarum/gameactors/ActorWithBody.kt b/src/net/torvald/terrarum/gameactors/ActorWithBody.kt index 53204f1c7..665d547f7 100644 --- a/src/net/torvald/terrarum/gameactors/ActorWithBody.kt +++ b/src/net/torvald/terrarum/gameactors/ActorWithBody.kt @@ -18,6 +18,7 @@ import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameparticles.createRandomBlockParticle import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.itemproperties.Calculate import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid diff --git a/src/net/torvald/terrarum/gameworld/BlockLayer.kt b/src/net/torvald/terrarum/gameworld/BlockLayer.kt index 7bd639685..d4bb88d4a 100644 --- a/src/net/torvald/terrarum/gameworld/BlockLayer.kt +++ b/src/net/torvald/terrarum/gameworld/BlockLayer.kt @@ -5,15 +5,46 @@ import com.badlogic.gdx.utils.Disposable /** * Created by minjaesong on 2023-10-10. */ -abstract class BlockLayer : Disposable { - abstract val chunkPool: ChunkPool - abstract val width: Int - abstract val height: Int - abstract val bytesPerBlock: Long - abstract fun unsafeToBytes(x: Int, y: Int): ByteArray - abstract fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) - abstract fun unsafeGetTile(x: Int, y: Int): Int +open class BlockLayer() : Disposable { +// abstract val chunkPool: ChunkPool + open val width: Int = 0 + open val height: Int = 0 + open val bytesPerBlock: Long = 0 + open fun unsafeToBytes(x: Int, y: Int): ByteArray = byteArrayOf(0,0,0,0) + open fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) { } + // for I16; other layer must throw UnsupportedOperationException + open fun unsafeSetTile(x: Int, y: Int, tile: Int) { } + // for I16F16; other layer must throw UnsupportedOperationException + open fun unsafeSetTile(x: Int, y: Int, tile: Int, fill: Float) { } + // for I16I8; other layer must throw UnsupportedOperationException + open fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) { } + open fun unsafeGetTile(x: Int, y: Int): Int = 0 + + open fun getOffset(x: Int, y: Int): Long { + return this.bytesPerBlock * (y * this.width + x) + } + + open val disposed: Boolean = false + override fun dispose() { + } + + open fun unsafeGetTileI16F16(x: Int, y: Int): Pair = 0 to 0f + open fun unsafeGetTileI16I8(x: Int, y: Int): Pair = 0 to 0 + + open fun unsafeSetTileKeepOrePlacement(x: Int, y: Int, tile: Int) { } + +} + +abstract class BlockLayerWithChunkPool : BlockLayer() { + abstract val chunkPool: ChunkPool + + /** + * Unsupported for BlockLayerWithChunkPool + */ + override fun getOffset(x: Int, y: Int): Long { + throw UnsupportedOperationException() + } } /*inline fun BlockLayer.getOffset(x: Int, y: Int): Long { diff --git a/src/net/torvald/terrarum/gameworld/BlockLayerFluidI16F16.kt b/src/net/torvald/terrarum/gameworld/BlockLayerFluidI16F16.kt index d5035d020..634ecc820 100644 --- a/src/net/torvald/terrarum/gameworld/BlockLayerFluidI16F16.kt +++ b/src/net/torvald/terrarum/gameworld/BlockLayerFluidI16F16.kt @@ -1,14 +1,8 @@ package net.torvald.terrarum.gameworld -import net.torvald.terrarum.App -import net.torvald.terrarum.gameworld.BlockLayerGenericI16.Companion -import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN -import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM import net.torvald.terrarum.savegame.DiskSkimmer import net.torvald.terrarum.serialise.toUint -import net.torvald.unsafe.UnsafeHelper -import net.torvald.unsafe.UnsafePtr import net.torvald.util.Float16 const val FLUID_MIN_MASS = 1f / 1024f //Ignore cells that are almost dry (smaller than epsilon of float16) @@ -20,9 +14,10 @@ const val FLUID_MIN_MASS = 1f / 1024f //Ignore cells that are almost dry (smalle * * ``` * * where a_n is a fluid number, f_n is a fluid fill * - * Created by minjaesong on 2023-10-10. + * Unsafe version Created by minjaesong on 2023-10-10. + * Chunkpool version Created by minjaesong on 2024-10-22. */ -class BlockLayerFluidI16F16 : BlockLayer { +class BlockLayerFluidI16F16 : BlockLayerWithChunkPool { override val width: Int override val height: Int @@ -33,7 +28,7 @@ class BlockLayerFluidI16F16 : BlockLayer { height: Int, disk: ClusteredFormatDOM, layerNum: Int, - world: GameWorld + world: TheGameWorld ) { this.width = width this.height = height @@ -46,7 +41,7 @@ class BlockLayerFluidI16F16 : BlockLayer { height: Int, disk: DiskSkimmer, layerNum: Int, - world: GameWorld + world: TheGameWorld ) { this.width = width this.height = height @@ -58,16 +53,16 @@ class BlockLayerFluidI16F16 : BlockLayer { override val bytesPerBlock = BYTES_PER_BLOCK override fun unsafeGetTile(x: Int, y: Int): Int { - return unsafeGetTile1(x, y).first + return unsafeGetTileI16F16(x, y).first } - internal fun unsafeGetTile1(x: Int, y: Int): Pair { + override fun unsafeGetTileI16F16(x: Int, y: Int): Pair { val (chunk, ox, oy) = chunkPool.worldXYChunkNumAndOffset(x, y) return chunkPool.getTileI16F16(chunk, ox, oy) } override fun unsafeToBytes(x: Int, y: Int): ByteArray { - val (tile, fill0) = unsafeGetTile1(x, y) + val (tile, fill0) = unsafeGetTileI16F16(x, y) val fill = Float16.fromFloat(fill0).toUint() return byteArrayOf( ((tile ushr 8) and 255).toByte(), @@ -76,7 +71,15 @@ class BlockLayerFluidI16F16 : BlockLayer { (fill and 255).toByte(), ) } - internal fun unsafeSetTile(x: Int, y: Int, tile0: Int, fill: Float) { + override fun unsafeSetTile(x: Int, y: Int, tile: Int) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile0: Int, fill: Float) { val (chunk, ox, oy) = chunkPool.worldXYChunkNumAndOffset(x, y) val fill = Float16.fromFloat(fill).toUint() chunkPool.setTileRaw(chunk, ox, oy, tile0.and(65535) or fill.shl(16)) @@ -103,8 +106,11 @@ class BlockLayerFluidI16F16 : BlockLayer { fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height) + override var disposed: Boolean = false + override fun dispose() { chunkPool.dispose() + disposed = true } override fun toString(): String = "BlockLayerI16F16 (${width}x$height)" diff --git a/src/net/torvald/terrarum/gameworld/BlockLayerGenericI16.kt b/src/net/torvald/terrarum/gameworld/BlockLayerGenericI16.kt index d6d2e99b6..f34e92628 100644 --- a/src/net/torvald/terrarum/gameworld/BlockLayerGenericI16.kt +++ b/src/net/torvald/terrarum/gameworld/BlockLayerGenericI16.kt @@ -1,13 +1,10 @@ package net.torvald.terrarum.gameworld -import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM import net.torvald.terrarum.savegame.DiskSkimmer import net.torvald.terrarum.serialise.toUint -import net.torvald.unsafe.UnsafeHelper -import net.torvald.unsafe.UnsafePtr /** * Memory layout: @@ -18,21 +15,24 @@ import net.torvald.unsafe.UnsafePtr * * Original version Created by minjaesong on 2016-01-17. * Unsafe version Created by minjaesong on 2019-06-08. + * Chunkpool version Created by minjaesong on 2024-10-22. * * Note to self: refrain from using shorts--just do away with two bytes: different system have different endianness */ -class BlockLayerGenericI16: BlockLayer { +class BlockLayerGenericI16: BlockLayerWithChunkPool { override val width: Int override val height: Int override val chunkPool: ChunkPool + private var _hashcode = 0 + constructor( width: Int, height: Int, disk: ClusteredFormatDOM, layerNum: Int, - world: GameWorld + world: TheGameWorld ) { this.width = width this.height = height @@ -42,6 +42,8 @@ class BlockLayerGenericI16: BlockLayer { WALL -> ChunkPool.getRenameFunTerrain(world) else -> throw IllegalArgumentException("Unknown layer number for I16: $layerNum") }) + + _hashcode = disk.uuid.hashCode() } constructor( @@ -49,7 +51,7 @@ class BlockLayerGenericI16: BlockLayer { height: Int, disk: DiskSkimmer, layerNum: Int, - world: GameWorld + world: TheGameWorld ) { this.width = width this.height = height @@ -59,8 +61,12 @@ class BlockLayerGenericI16: BlockLayer { WALL -> ChunkPool.getRenameFunTerrain(world) else -> throw IllegalArgumentException("Unknown layer number for I16: $layerNum") }) + + _hashcode = disk.diskFile.hashCode() } + override fun hashCode() = _hashcode + override val bytesPerBlock = BYTES_PER_BLOCK override fun unsafeGetTile(x: Int, y: Int): Int { @@ -76,13 +82,21 @@ class BlockLayerGenericI16: BlockLayer { ) } - internal fun unsafeSetTile(x: Int, y: Int, tile: Int) { + override fun unsafeSetTile(x: Int, y: Int, tile: Int) { val (chunk, ox, oy) = chunkPool.worldXYChunkNumAndOffset(x, y) chunkPool.setTileRaw(chunk, ox, oy, tile) } + override fun unsafeSetTile(x: Int, y: Int, tile: Int, fill: Float) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) { + throw UnsupportedOperationException() + } + override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) { - val tile = (0..3).fold(0) { acc, i -> acc or (bytes[i].toUint()).shl(8*i) } + val tile = (0..1).fold(0) { acc, i -> acc or (bytes[i].toUint()).shl(8*i) } unsafeSetTile(x, y, tile) } @@ -101,8 +115,11 @@ class BlockLayerGenericI16: BlockLayer { fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height) + override var disposed: Boolean = false + override fun dispose() { chunkPool.dispose() + disposed = true } override fun toString(): String = "BlockLayerI16 (${width}x$height)" diff --git a/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16.kt b/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16.kt new file mode 100644 index 000000000..55699a12f --- /dev/null +++ b/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16.kt @@ -0,0 +1,133 @@ +package net.torvald.terrarum.gameworld + +import com.badlogic.gdx.utils.Disposable +import net.torvald.terrarum.App.printdbg +import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN +import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL +import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM +import net.torvald.terrarum.serialise.toUint +import net.torvald.unsafe.UnsafeHelper +import net.torvald.unsafe.UnsafePtr + +/** + * Memory layout: + * ``` + * a7 a6 a5 a4 a3 a2 a1 a0 | aF aE aD aC aB aA a9 a8 || + * ``` + * where a_n is a tile number + * + * Original version Created by minjaesong on 2016-01-17. + * Unsafe version Created by minjaesong on 2019-06-08. + * + * Note to self: refrain from using shorts--just do away with two bytes: different system have different endianness + */ +class BlockLayerInMemoryI16( + override val width: Int, + override val height: Int, +): BlockLayer() { + + private constructor() : this(0, 0) + + override val bytesPerBlock = BYTES_PER_BLOCK + + // using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90 + internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * bytesPerBlock) + + val ptrDestroyed: Boolean + get() = ptr.destroyed + + init { + ptr.fillWith(-1) + } + + /** + * Returns an iterator over stored bytes. + * + * @return an Iterator. + */ + fun bytesIterator(): Iterator { + return object : Iterator { + private var iteratorCount = 0L + override fun hasNext(): Boolean { + return iteratorCount < width * height * bytesPerBlock + } + override fun next(): Byte { + iteratorCount += 1 + return ptr[iteratorCount - 1] + } + } + } + + override fun unsafeGetTile(x: Int, y: Int): Int { + val offset = getOffset(x, y) + val lsb = ptr[offset] + val msb = ptr[offset + 1] + + return lsb.toUint() or msb.toUint().shl(8) + } + + override fun unsafeToBytes(x: Int, y: Int): ByteArray { + val offset = getOffset(x, y) + return byteArrayOf(ptr[offset + 1], ptr[offset + 0]) + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, fill: Float) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int) { + val offset = getOffset(x, y) + + val lsb = tile.and(0xff).toByte() + val msb = tile.ushr(8).and(0xff).toByte() + + +// try { + ptr[offset] = lsb + ptr[offset + 1] = msb +// } +// catch (e: IndexOutOfBoundsException) { +// printdbgerr(this, "IndexOutOfBoundsException: x = $x, y = $y; offset = $offset") +// throw e +// } + } + + override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) { + val offset = getOffset(x, y) + ptr[offset] = bytes[1] + ptr[offset + 1] = bytes[0] + } + + /** + * @param blockOffset Offset in blocks. BlockOffset of 0x100 is equal to ```layerPtr + 0x200``` + */ + /*internal fun unsafeSetTile(blockOffset: Long, tile: Int) { + val offset = BYTES_PER_BLOCK * blockOffset + + val lsb = tile.and(0xff).toByte() + val msb = tile.ushr(8).and(0xff).toByte() + + unsafe.putByte(layerPtr + offset, lsb) + unsafe.putByte(layerPtr + offset + 1, msb) + }*/ + + fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height) + + override val disposed: Boolean + get() = ptr.destroyed + + override fun dispose() { + ptr.destroy() + printdbg(this, "BlockLayerI16 with ptr ($ptr) successfully freed") + } + + override fun toString(): String = ptr.toString("BlockLayerI16") + + companion object { + @Transient val BYTES_PER_BLOCK = 2L + } +} diff --git a/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16F16.kt b/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16F16.kt new file mode 100644 index 000000000..c5f2e72ae --- /dev/null +++ b/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16F16.kt @@ -0,0 +1,145 @@ +package net.torvald.terrarum.gameworld + +import net.torvald.terrarum.App +import net.torvald.terrarum.serialise.toUint +import net.torvald.unsafe.UnsafeHelper +import net.torvald.unsafe.UnsafePtr +import net.torvald.util.Float16 + +/** + * * Memory layout: + * * ``` + * * a7 a6 a5 a4 a3 a2 a1 a0 | aF aE aD aC aB aA a9 a8 | f7 f6 f5 f4 f3 f2 f1 f0 | fF fE fD fC fB fA f9 f8 || + * * ``` + * * where a_n is a fluid number, f_n is a fluid fill + * + * Created by minjaesong on 2023-10-10. + */ +class BlockLayerInMemoryI16F16(override val width: Int, override val height: Int) : BlockLayer() { + override val bytesPerBlock = BYTES_PER_BLOCK + + // for some reason, all the efforts of saving the memory space were futile. + + // using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90 + internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * bytesPerBlock) + + val ptrDestroyed: Boolean + get() = ptr.destroyed + + init { + ptr.fillWith(-1) + } + + /** + * @param data Byte array representation of the layer + */ + constructor(width: Int, height: Int, data: ByteArray) : this(width, height) { + TODO() + data.forEachIndexed { index, byte -> UnsafeHelper.unsafe.putByte(ptr.ptr + index, byte) } + } + + /** + * Returns an iterator over stored bytes. + * + * @return an Iterator. + */ + fun bytesIterator(): Iterator { + return object : Iterator { + private var iteratorCount = 0L + override fun hasNext(): Boolean { + return iteratorCount < width * height * bytesPerBlock + } + override fun next(): Byte { + iteratorCount += 1 + return ptr[iteratorCount - 1] + } + } + } + + override fun unsafeGetTile(x: Int, y: Int): Int { + val offset = getOffset(x, y) + val lsb = ptr[offset] + val msb = ptr[offset + 1] + + return lsb.toUint() or msb.toUint().shl(8) + } + + internal fun unsafeGetTile1(x: Int, y: Int): Pair { + val offset = getOffset(x, y) + val lsb = ptr[offset] + val msb = ptr[offset + 1] + val hbits = (ptr[offset + 2].toUint() or ptr[offset + 3].toUint().shl(8)).toShort() + val fill = Float16.toFloat(hbits) + + return lsb.toUint() or msb.toUint().shl(8) to fill + } + + override fun unsafeToBytes(x: Int, y: Int): ByteArray { + val offset = getOffset(x, y) + return byteArrayOf(ptr[offset + 1], ptr[offset + 0], ptr[offset + 3], ptr[offset + 2]) + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile0: Int, fill: Float) { + val offset = getOffset(x, y) + val hbits = Float16.fromFloat(fill).toInt().and(0xFFFF) + + val tile = if (fill < FLUID_MIN_MASS) 0 else tile0 + + val lsb = tile.and(0xff).toByte() + val msb = tile.ushr(8).and(0xff).toByte() + + val hlsb = hbits.and(0xff).toByte() + val hmsb = hbits.ushr(8).and(0xff).toByte() + + ptr[offset] = lsb + ptr[offset + 1] = msb + ptr[offset + 2] = hlsb + ptr[offset + 3] = hmsb + + } + + override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) { + val offset = getOffset(x, y) + ptr[offset] = bytes[1] + ptr[offset + 1] = bytes[0] + ptr[offset + 2] = bytes[3] + ptr[offset + 3] = bytes[2] + } + + /** + * @param blockOffset Offset in blocks. BlockOffset of 0x100 is equal to ```layerPtr + 0x200``` + */ + /*internal fun unsafeSetTile(blockOffset: Long, tile: Int) { + val offset = BYTES_PER_BLOCK * blockOffset + + val lsb = tile.and(0xff).toByte() + val msb = tile.ushr(8).and(0xff).toByte() + + unsafe.putByte(layerPtr + offset, lsb) + unsafe.putByte(layerPtr + offset + 1, msb) + }*/ + + fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height) + + override val disposed: Boolean + get() = ptr.destroyed + + override fun dispose() { + ptr.destroy() + App.printdbg(this, "BlockLayerI16F16 with ptr ($ptr) successfully freed") + } + + override fun toString(): String = ptr.toString("BlockLayerI16F16") + + companion object { + @Transient val BYTES_PER_BLOCK = 4L + } +} diff --git a/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16I8.kt b/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16I8.kt new file mode 100644 index 000000000..adf17a7eb --- /dev/null +++ b/src/net/torvald/terrarum/gameworld/BlockLayerInMemoryI16I8.kt @@ -0,0 +1,151 @@ +package net.torvald.terrarum.gameworld + +import net.torvald.terrarum.App +import net.torvald.terrarum.serialise.toUint +import net.torvald.unsafe.UnsafeHelper +import net.torvald.unsafe.UnsafePtr + +/** + * Memory layout: + * ``` + * a7 a6 a5 a4 a3 a2 a1 a0 | aF aE aD aC aB aA a9 a8 | p7 p6 p5 p4 p3 p2 p1 p0 || + * ``` + * where a_n is a tile number, p_n is a placement index + * Created by minjaesong on 2023-10-10. + */ +class BlockLayerInMemoryI16I8 (override val width: Int, override val height: Int) : BlockLayer() { + override val bytesPerBlock = BYTES_PER_BLOCK + + // for some reason, all the efforts of saving the memory space were futile. + + // using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90 + internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * bytesPerBlock) + + val ptrDestroyed: Boolean + get() = ptr.destroyed + + init { + ptr.fillWith(0) // there is no NOT-GENERATED for ores, keep it as 0 + } + + /** + * @param data Byte array representation of the layer + */ + constructor(width: Int, height: Int, data: ByteArray) : this(width, height) { + TODO() + data.forEachIndexed { index, byte -> UnsafeHelper.unsafe.putByte(ptr.ptr + index, byte) } + } + + /** + * Returns an iterator over stored bytes. + * + * @return an Iterator. + */ + fun bytesIterator(): Iterator { + return object : Iterator { + private var iteratorCount = 0L + override fun hasNext(): Boolean { + return iteratorCount < width * height * bytesPerBlock + } + override fun next(): Byte { + iteratorCount += 1 + return ptr[iteratorCount - 1] + } + } + } + + override fun unsafeGetTile(x: Int, y: Int): Int { + val offset = getOffset(x, y) + val lsb = ptr[offset] + val msb = ptr[offset + 1] + val placement = ptr[offset + 2] + + return lsb.toUint() + msb.toUint().shl(8) + } + + internal fun unsafeGetTile1(x: Int, y: Int): Pair { + val offset = getOffset(x, y) + val lsb = ptr[offset] + val msb = ptr[offset + 1] + val placement = ptr[offset + 2] + + return lsb.toUint() or msb.toUint().shl(8) to placement.toUint() + } + + override fun unsafeToBytes(x: Int, y: Int): ByteArray { + val offset = getOffset(x, y) + return byteArrayOf(ptr[offset + 1], ptr[offset + 0], ptr[offset + 2]) + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, fill: Float) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) { + val offset = getOffset(x, y) + + val lsb = tile.and(0xff).toByte() + val msb = tile.ushr(8).and(0xff).toByte() + + +// try { + ptr[offset] = lsb + ptr[offset + 1] = msb + ptr[offset + 2] = placement.toByte() +// } +// catch (e: IndexOutOfBoundsException) { +// printdbgerr(this, "IndexOutOfBoundsException: x = $x, y = $y; offset = $offset") +// throw e +// } + } + + override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) { + val offset = getOffset(x, y) + ptr[offset] = bytes[1] + ptr[offset + 1] = bytes[0] + ptr[offset + 2] = bytes[2] + } + + override fun unsafeSetTileKeepOrePlacement(x: Int, y: Int, tile: Int) { + val offset = getOffset(x, y) + + val lsb = tile.and(0xff).toByte() + val msb = tile.ushr(8).and(0xff).toByte() + + ptr[offset] = lsb + ptr[offset + 1] = msb + } + + /** + * @param blockOffset Offset in blocks. BlockOffset of 0x100 is equal to ```layerPtr + 0x200``` + */ + /*internal fun unsafeSetTile(blockOffset: Long, tile: Int) { + val offset = BYTES_PER_BLOCK * blockOffset + + val lsb = tile.and(0xff).toByte() + val msb = tile.ushr(8).and(0xff).toByte() + + unsafe.putByte(layerPtr + offset, lsb) + unsafe.putByte(layerPtr + offset + 1, msb) + }*/ + + fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height) + + override val disposed: Boolean + get() = ptr.destroyed + + override fun dispose() { + ptr.destroy() + App.printdbg(this, "BlockLayerI16I8 with ptr ($ptr) successfully freed") + } + + override fun toString(): String = ptr.toString("BlockLayerI16I8") + + companion object { + @Transient val BYTES_PER_BLOCK = 3L + } +} diff --git a/src/net/torvald/terrarum/gameworld/BlockLayerOresI16I8.kt b/src/net/torvald/terrarum/gameworld/BlockLayerOresI16I8.kt index bb15b2aed..b078c581a 100644 --- a/src/net/torvald/terrarum/gameworld/BlockLayerOresI16I8.kt +++ b/src/net/torvald/terrarum/gameworld/BlockLayerOresI16I8.kt @@ -1,14 +1,8 @@ package net.torvald.terrarum.gameworld -import net.torvald.terrarum.App -import net.torvald.terrarum.gameworld.BlockLayerFluidI16F16.Companion -import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN -import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM import net.torvald.terrarum.savegame.DiskSkimmer import net.torvald.terrarum.serialise.toUint -import net.torvald.unsafe.UnsafeHelper -import net.torvald.unsafe.UnsafePtr /** * Memory layout: @@ -16,9 +10,11 @@ import net.torvald.unsafe.UnsafePtr * a7 a6 a5 a4 a3 a2 a1 a0 | aF aE aD aC aB aA a9 a8 | p7 p6 p5 p4 p3 p2 p1 p0 || * ``` * where a_n is a tile number, p_n is a placement index - * Created by minjaesong on 2023-10-10. + * + * Unsafe version Created by minjaesong on 2023-10-10. + * Chunkpool version Created by minjaesong on 2024-10-22. */ -class BlockLayerOresI16I8 : BlockLayer { +class BlockLayerOresI16I8 : BlockLayerWithChunkPool { override val width: Int override val height: Int @@ -29,7 +25,7 @@ class BlockLayerOresI16I8 : BlockLayer { height: Int, disk: ClusteredFormatDOM, layerNum: Int, - world: GameWorld + world: TheGameWorld ) { this.width = width this.height = height @@ -42,7 +38,7 @@ class BlockLayerOresI16I8 : BlockLayer { height: Int, disk: DiskSkimmer, layerNum: Int, - world: GameWorld + world: TheGameWorld ) { this.width = width this.height = height @@ -54,16 +50,16 @@ class BlockLayerOresI16I8 : BlockLayer { override val bytesPerBlock = BYTES_PER_BLOCK override fun unsafeGetTile(x: Int, y: Int): Int { - return unsafeGetTile1(x, y).first + return unsafeGetTileI16I8(x, y).first } - internal fun unsafeGetTile1(x: Int, y: Int): Pair { + override fun unsafeGetTileI16I8(x: Int, y: Int): Pair { val (chunk, ox, oy) = chunkPool.worldXYChunkNumAndOffset(x, y) return chunkPool.getTileI16I8(chunk, ox, oy) } override fun unsafeToBytes(x: Int, y: Int): ByteArray { - val (tile, fill) = unsafeGetTile1(x, y) + val (tile, fill) = unsafeGetTileI16I8(x, y) return byteArrayOf( ((tile ushr 8) and 255).toByte(), (tile and 255).toByte(), @@ -71,7 +67,15 @@ class BlockLayerOresI16I8 : BlockLayer { ) } - internal fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) { + override fun unsafeSetTile(x: Int, y: Int, tile: Int) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, fill: Float) { + throw UnsupportedOperationException() + } + + override fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) { val (chunk, ox, oy) = chunkPool.worldXYChunkNumAndOffset(x, y) chunkPool.setTileRaw(chunk, ox, oy, tile or placement.shl(16)) } @@ -82,8 +86,8 @@ class BlockLayerOresI16I8 : BlockLayer { unsafeSetTile(x, y, tile, placement) } - internal fun unsafeSetTileKeepPlacement(x: Int, y: Int, tile: Int) { - val oldPlacement = unsafeGetTile1(x, y).second + override fun unsafeSetTileKeepOrePlacement(x: Int, y: Int, tile: Int) { + val oldPlacement = unsafeGetTileI16I8(x, y).second unsafeSetTile(x, y, tile, oldPlacement) } @@ -102,8 +106,11 @@ class BlockLayerOresI16I8 : BlockLayer { fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height) + override var disposed: Boolean = false + override fun dispose() { chunkPool.dispose() + disposed = true } override fun toString(): String = "BlockLayerI16I8 (${width}x$height)" diff --git a/src/net/torvald/terrarum/gameworld/ChunkPool.kt b/src/net/torvald/terrarum/gameworld/ChunkPool.kt index a9cd1a9d7..9e360814b 100644 --- a/src/net/torvald/terrarum/gameworld/ChunkPool.kt +++ b/src/net/torvald/terrarum/gameworld/ChunkPool.kt @@ -35,6 +35,8 @@ enum class ChunkAllocClass { } /** + * FIXME: loading a chunk from disk will attempt to create a chunk because the chunk-to-be-loaded is not on the pointers map, and this operation will want to create a new chunk file but the file already exists + * * Single layer gets single Chunk Pool. * * Created by minjaesong on 2024-09-07. @@ -45,7 +47,7 @@ open class ChunkPool { private val disk: Any private val layerIndex: Int private val wordSizeInBytes: Long - private val world: GameWorld + private val world: TheGameWorld private val initialValue: Int // bytes to fill the new chunk private val renumberFun: (Int) -> Int @@ -60,7 +62,7 @@ open class ChunkPool { disk: DiskSkimmer, layerIndex: Int, wordSizeInBytes: Long, - world: GameWorld, + world: TheGameWorld, initialValue: Int, renumberFun: (Int) -> Int, ) { @@ -79,7 +81,7 @@ open class ChunkPool { disk: ClusteredFormatDOM, layerIndex: Int, wordSizeInBytes: Long, - world: GameWorld, + world: TheGameWorld, initialValue: Int, renumberFun: (Int) -> Int, ) { @@ -393,7 +395,7 @@ open class ChunkPool { private fun Int.get2LSB() = this.get3MSB() private fun Int.getLSB() = this.get4MSB() - fun getRenameFunTerrain(world: GameWorld): (Int) -> Int { + fun getRenameFunTerrain(world: TheGameWorld): (Int) -> Int { // word size: 2 return { oldTileNum -> val oldOreName = world.oldTileNumberToNameMap[oldTileNum.toLong()] @@ -402,7 +404,7 @@ open class ChunkPool { } } - fun getRenameFunOres(world: GameWorld): (Int) -> Int { + fun getRenameFunOres(world: TheGameWorld): (Int) -> Int { // word size: 3 return { oldTileNumRaw -> val oldOreNum = oldTileNumRaw and 0x0000FFFF @@ -413,7 +415,7 @@ open class ChunkPool { } } - fun getRenameFunFluids(world: GameWorld): (Int) -> Int { + fun getRenameFunFluids(world: TheGameWorld): (Int) -> Int { // word size: 4 return { oldTileNumRaw -> val oldFluidNum = oldTileNumRaw and 0x0000FFFF diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index 32f8f2c85..e794469f7 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -1,7 +1,6 @@ package net.torvald.terrarum.gameworld -import com.badlogic.gdx.utils.Disposable import net.torvald.gdx.graphics.Cvec import net.torvald.terrarum.* import net.torvald.terrarum.App.printdbg @@ -17,6 +16,7 @@ import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil.CHUNK_H import net.torvald.terrarum.realestate.LandUtil.CHUNK_W +import net.torvald.terrarum.savegame.DiskSkimmer import net.torvald.terrarum.utils.* import net.torvald.terrarum.weather.WeatherMixer import net.torvald.terrarum.weather.Weatherbox @@ -38,32 +38,13 @@ class PhysicalStatus() { } } -/** - * Special version of GameWorld where everything, including layer data, are saved in a single JSON file (i.e. not chunked) - */ -class SimpleGameWorld(width: Int, height: Int) : GameWorld(width, height) { - override lateinit var layerWall: BlockLayerGenericI16 - override lateinit var layerTerrain: BlockLayerGenericI16 - constructor() : this(0, 0) - override fun dispose() { - layerWall.dispose() - layerTerrain.dispose() - } -} - -open class GameWorld( - val worldIndex: UUID // should not be immutable as JSON loader will want to overwrite it -) : Disposable { - - constructor() : this(UUID.randomUUID()) - constructor(width: Int, height: Int) : this(UUID.randomUUID()) { - this.width = width - this.height = height - } +abstract class GameWorld( + val worldIndex: UUID, // should not be immutable as JSON loader will want to overwrite it + var width: Int, + var height: Int +) { var worldCreator: UUID = UUID(0L,0L) // TODO record a value to this - var width: Int = 0; private set - var height: Int = 0; private set var playersLastStatus = PlayersLastStatus() // only gets used when the game saves and loads @@ -84,22 +65,38 @@ open class GameWorld( val gameRules = KVHashMap() // spawn points, creation/lastplay/totalplaytimes are NOT stored to gameRules - init { - creationTime = App.getTIME_T() - } - - //layers - @Transient open lateinit var layerWall: BlockLayerGenericI16 - @Transient open lateinit var layerTerrain: BlockLayerGenericI16 - @Transient open lateinit var layerOres: BlockLayerOresI16I8 // damage to the block follows `terrainDamages` - @Transient open lateinit var layerFluids: BlockLayerFluidI16F16 + abstract var layerWall: BlockLayer + abstract var layerTerrain: BlockLayer + abstract var layerOres: BlockLayer + abstract var layerFluids: BlockLayer val wallDamages = HashArray() val terrainDamages = HashArray() + /** + * Single block can have multiple conduits, different types of conduits are stored separately. + */ + public val wirings = HashedWirings() + protected val wiringGraph = HashedWiringGraph() + + + open var averageTemperature = 288f // 15 deg celsius; simulates global warming + + val extraFields = HashMap() + + + @Deprecated("This value is only used for savegames; DO NOT USE THIS", ReplaceWith("INGAME.actorContainerActive", "net.torvald.terrarum.INGAME")) + internal val actors = ArrayList() // only filled up on save and load; DO NOT USE THIS + + + @Transient private val WIRE_POS_MAP = intArrayOf(1,2,4,8) + @Transient private val WIRE_ANTIPOS_MAP = intArrayOf(4,8,1,2) + @Transient open lateinit var chunkFlags: Array - //val layerThermal: MapLayerHalfFloat // in Kelvins - //val layerFluidPressure: MapLayerHalfFloat // (milibar - 1000) + var portalPoint: Point2i? = null + + /** 0.0..1.0+ */ + open var globalLight = Cvec(0.8f, 0.8f, 0.8f, 0.1f) /** Tilewise spawn point */ var spawnX: Int = 0 @@ -112,50 +109,29 @@ open class GameWorld( spawnX = value.x spawnY = value.y } - var portalPoint: Point2i? = null - - - - /** - * Single block can have multiple conduits, different types of conduits are stored separately. - */ - public val wirings = HashedWirings() - private val wiringGraph = HashedWiringGraph() - - @Transient private val WIRE_POS_MAP = intArrayOf(1,2,4,8) - @Transient private val WIRE_ANTIPOS_MAP = intArrayOf(4,8,1,2) - - /** - * Used by the renderer. When wirings are updated, `wirings` and this properties must be synchronised. - */ - //private val wiringBlocks: HashArray //public World physWorld = new World( new Vec2(0, -Terrarum.game.gravitationalAccel) ); //physics /** Meter per second squared. Currently only the downward gravity is supported. No reverse gravity :p */ open var gravitation: Vector2 = DEFAULT_GRAVITATION - /** 0.0..1.0+ */ - open var globalLight = Cvec(0f, 0f, 0f, 0f) - open var averageTemperature = 288f // 15 deg celsius; simulates global warming - open var generatorSeed: Long = 0 internal set @Transient var disposed = false - private set + protected set val worldTime: WorldTime = WorldTime( // Year EPOCH (125), Month 1, Day 1 is implied - 7 * WorldTime.HOUR_SEC + - 30L * WorldTime.MINUTE_SEC + 7 * WorldTime.HOUR_SEC + + 30L * WorldTime.MINUTE_SEC ) // Terrain, ores and fluids all use the same number space - @Transient private val forcedTileNumberToNames = hashSetOf( + @Transient protected val forcedTileNumberToNames = hashSetOf( Block.AIR, Block.UPDATE, Block.NOT_GENERATED ) - /*@Transient private val forcedFluidNumberToTiles = hashSetOf( + /*@Transient protected val forcedFluidNumberToTiles = hashSetOf( Fluid.NULL )*/ val tileNumberToNameMap = HashArray().also { @@ -178,16 +154,13 @@ open class GameWorld( it[Fluid.NULL] = 0 }*/ - val extraFields = HashMap() - // NOTE: genver was here but removed: genver will be written by manually editing the serialising JSON. Reason: the 'genver' string must be found on a fixed offset on the file. internal var comp = -1 // only gets used when the game saves and loads + internal val dynamicItemInventory = ItemTable() internal val dynamicToStaticTable = ItemRemapTable() - @Deprecated("This value is only used for savegames; DO NOT USE THIS", ReplaceWith("INGAME.actorContainerActive", "net.torvald.terrarum.INGAME")) - internal val actors = ArrayList() // only filled up on save and load; DO NOT USE THIS var weatherbox = Weatherbox() @@ -217,176 +190,7 @@ open class GameWorld( } - /** - * Create new world - */ - constructor(width: Int, height: Int, creationTIME_T: Long, lastPlayTIME_T: Long): this() { - if (width <= 0 || height <= 0) throw IllegalArgumentException("Non-positive width/height: ($width, $height)") - this.width = width - this.height = height - - // preliminary spawn points - this.spawnX = width / 2 - this.spawnY = 150 - - layerTerrain = BlockLayerGenericI16(width, height) - layerWall = BlockLayerGenericI16(width, height) - layerOres = BlockLayerOresI16I8(width, height) - layerFluids = BlockLayerFluidI16F16(width, height) - chunkFlags = Array(height / CHUNK_H) { ByteArray(width / CHUNK_W) } - - // temperature layer: 2x2 is one cell - //layerThermal = MapLayerHalfFloat(width, height, averageTemperature) - - // fluid pressure layer: 4 * 8 is one cell - //layerFluidPressure = MapLayerHalfFloat(width, height, 13f) // 1013 mBar - - - creationTime = creationTIME_T - lastPlayTime = lastPlayTIME_T - - - if (App.tileMaker != null) { - App.tileMaker.tags.forEach { - if (!forcedTileNumberToNames.contains(it.key)) { - printdbg(this, "newworld tileNumber ${it.value.tileNumber} <-> tileName ${it.key}") - - tileNumberToNameMap[it.value.tileNumber.toLong()] = it.key - tileNameToNumberMap[it.key] = it.value.tileNumber - } - } - /*Terrarum.fluidCodex.fluidProps.entries.forEach { - if (!forcedFluidNumberToTiles.contains(it.key)) { - fluidNumberToNameMap[it.value.numericID.toLong()] = it.key - fluidNameToNumberMap[it.key] = it.value.numericID - } - }*/ - } - } - - fun coordInWorld(x: Int, y: Int) = y in 0 until height // ROUNDWORLD implementation - fun coordInWorldStrict(x: Int, y: Int) = x in 0 until width && y in 0 until height // ROUNDWORLD implementation - - fun renumberTilesAfterLoad() { - printdbg(this, "renumberTilesAfterLoad()") - - // patch the "old"map - tileNumberToNameMap[0] = Block.AIR - tileNumberToNameMap[1] = Block.UPDATE - tileNumberToNameMap[65535] = Block.NOT_GENERATED - // before the renaming, update the name maps - oldTileNumberToNameMap = tileNumberToNameMap.toMap() - - tileNumberToNameMap.forEach { l, s -> - printdbg(this, " afterload oldMapping tileNumber $l <-> $s") - } - - printdbg(this, "") - - tileNumberToNameMap.clear() - tileNameToNumberMap.clear() - App.tileMaker.tags.forEach { - printdbg(this, " afterload tileMaker tileNumber ${it.value.tileNumber} <-> ${it.key}") - - tileNumberToNameMap[it.value.tileNumber.toLong()] = it.key - tileNameToNumberMap[it.key] = it.value.tileNumber - } - /*Terrarum.fluidCodex.fluidProps.entries.forEach { - fluidNumberToNameMap[it.value.numericID.toLong()] = it.key - fluidNameToNumberMap[it.key] = it.value.numericID - }*/ - - // force this rule to the old saves - tileNumberToNameMap[0] = Block.AIR - tileNumberToNameMap[1] = Block.UPDATE - tileNumberToNameMap[65535] = Block.NOT_GENERATED - tileNameToNumberMap[Block.AIR] = 0 - tileNameToNumberMap[Block.UPDATE] = 1 - tileNameToNumberMap[Block.NOT_GENERATED] = 65535 -// fluidNumberToNameMap[0] = Fluid.NULL -// fluidNumberToNameMap[65535] = Fluid.NULL -// fluidNameToNumberMap[Fluid.NULL] = 0 - - - BlocksDrawer.rebuildInternalPrecalculations() - - // perform renaming of tile layers - /*for (y in 0 until layerTerrain.height) { - for (x in 0 until layerTerrain.width) { - // renumber terrain and wall - layerTerrain.unsafeSetTile(x, y, tileNameToNumberMap[oldTileNumberToNameMap[layerTerrain.unsafeGetTile(x, y).toLong()]]!!) - layerWall.unsafeSetTile(x, y, tileNameToNumberMap[oldTileNumberToNameMap[layerWall.unsafeGetTile(x, y).toLong()]]!!) - - // renumber ores - val oldOreNum = layerOres.unsafeGetTile(x, y).toLong() - val oldOreName = oldTileNumberToNameMap[oldOreNum] - layerOres.unsafeSetTileKeepPlacement(x, y, oldOreName.let { tileNameToNumberMap[it] ?: throw NullPointerException("Unknown tile name: $oldOreName (<- $oldOreNum)") }) - - // renumber fluids - val (oldFluidNum, oldFluidFill) = layerFluids.unsafeGetTile1(x, y) - val oldFluidName = oldTileNumberToNameMap[oldFluidNum.toLong()] - layerFluids.unsafeSetTile(x, y, oldFluidName.let { tileNameToNumberMap[it] ?: throw NullPointerException("Unknown tile name: $oldFluidName (<- $oldFluidNum)") }, oldFluidFill) - } - }*/ - // will use as much threads you have on the system - printdbg(this, "starting renumbering thread") - try { - val te = ThreadExecutor() - te.renew() - te.submitAll1( - (0 until layerTerrain.width step CHUNK_W).map { xorigin -> - callable { - for (y in 0 until layerTerrain.height) { - for (x in xorigin until (xorigin + CHUNK_W).coerceAtMost(layerTerrain.width)) { - // renumber terrain and wall - layerTerrain.unsafeSetTile( - x, y, - tileNameToNumberMap[oldTileNumberToNameMap[layerTerrain.unsafeGetTile(x, y) - .toLong()]]!! - ) - layerWall.unsafeSetTile( - x, y, - tileNameToNumberMap[oldTileNumberToNameMap[layerWall.unsafeGetTile(x, y) - .toLong()]]!! - ) - - // renumber ores - val oldOreNum = layerOres.unsafeGetTile(x, y).toLong() - val oldOreName = oldTileNumberToNameMap[oldOreNum] - layerOres.unsafeSetTileKeepPlacement(x, y, - oldOreName.let { - tileNameToNumberMap[it] - ?: throw NullPointerException("Unknown tile name: $oldOreName (<- $oldOreNum)") - }) - - // renumber fluids - val (oldFluidNum, oldFluidFill) = layerFluids.unsafeGetTile1(x, y) - val oldFluidName = oldTileNumberToNameMap[oldFluidNum.toLong()] - layerFluids.unsafeSetTile( - x, y, - oldFluidName.let { - tileNameToNumberMap[it] - ?: throw NullPointerException("Unknown tile name: $oldFluidName (<- $oldFluidNum)") - }, - oldFluidFill - ) - } - } - } - } - ) - te.join() - } - catch (e: Throwable) { - e.printStackTrace() - throw e - } - printdbg(this, "renumbering thread finished") - - printdbg(this, "renumberTilesAfterLoad done!") - } - /** * Get 2d array data of wire * @return byte[][] wire layer @@ -394,51 +198,6 @@ open class GameWorld( //val wireArray: ByteArray // get() = layerWire.data - fun getLayer(index: Int) = when(index) { - TERRAIN -> layerTerrain - WALL -> layerWall - ORES -> layerOres - FLUID -> layerFluids - else -> null//throw IllegalArgumentException("Unknown layer index: $index") - } - - fun coerceXY(x: Int, y: Int) = (x fmod width) to (y.coerceIn(0, height - 1)) - fun coerceXY(xy: Pair) = (xy.first fmod width) to (xy.second.coerceIn(0, height - 1)) - - /** - * @return ItemID, WITHOUT wall tag - */ - fun getTileFromWall(rawX: Int, rawY: Int): ItemID { - val (x, y) = coerceXY(rawX, rawY) - return tileNumberToNameMap[layerWall.unsafeGetTile(x, y).toLong()] ?: Block.UPDATE//throw NoSuchElementException("No tile name mapping for wall ${layerWall.unsafeGetTile(x, y)} in ($x, $y) from $layerWall") - } - - /** - * @return ItemID - */ - fun getTileFromTerrain(rawX: Int, rawY: Int): ItemID { - val (x, y) = coerceXY(rawX, rawY) - return tileNumberToNameMap[layerTerrain.unsafeGetTile(x, y).toLong()] ?: Block.UPDATE//throw NoSuchElementException("No tile name mapping for terrain ${layerTerrain.unsafeGetTile(x, y)} in ($x, $y) from $layerTerrain") - } - - /** - * @return Int - */ - fun getTileNumFromWall(rawX: Int, rawY: Int): Int { - val (x, y) = coerceXY(rawX, rawY) - return layerWall.unsafeGetTile(x, y) - } - - /** - * @return Int - */ - fun getTileNumFromTerrain(rawX: Int, rawY: Int): Int { - val (x, y) = coerceXY(rawX, rawY) - return layerTerrain.unsafeGetTile(x, y) - } - - fun getTileFromWallRaw(coercedX: Int, coercedY: Int) = layerWall.unsafeGetTile(coercedX, coercedY) - fun getTileFromTerrainRaw(coercedX: Int, coercedY: Int) = layerTerrain.unsafeGetTile(coercedX, coercedY) /** * Set the tile of wall as specified, with damage value of zero. @@ -505,6 +264,197 @@ open class GameWorld( } } + /** + * @return ItemID + */ + open fun getTileFromTerrain(x: Int, y: Int): ItemID { + val (x, y) = coerceXY(x, y) + return tileNumberToNameMap[layerTerrain.unsafeGetTile(x, y).toLong()] ?: Block.UPDATE//throw NoSuchElementException("No tile name mapping for terrain ${layerTerrain.unsafeGetTile(x, y)} in ($x, $y) from $layerTerrain") + } + /** + * @return ItemID, WITHOUT wall tag + */ + open fun getTileFromWall(x: Int, y: Int): ItemID { + val (x, y) = coerceXY(x, y) + return tileNumberToNameMap[layerWall.unsafeGetTile(x, y).toLong()] ?: Block.UPDATE//throw NoSuchElementException("No tile name mapping for wall ${layerWall.unsafeGetTile(x, y)} in ($x, $y) from $layerWall") + } + open fun getTileFromWallRaw(coercedX: Int, coercedY: Int) = layerWall.unsafeGetTile(coercedX, coercedY) + open fun getTileFromTerrainRaw(coercedX: Int, coercedY: Int) = layerTerrain.unsafeGetTile(coercedX, coercedY) + + open fun getTileFrom(mode: Int, x: Int, y: Int): ItemID { + if (mode == TERRAIN) { + return getTileFromTerrain(x, y) + } + else if (mode == WALL) { + return getTileFromWall(x, y) + } + else + throw IllegalArgumentException("illegal mode input: $mode") + } + + fun getFluid(x: Int, y: Int): FluidInfo { + val (x, y) = coerceXY(x, y) + val (type, fill) = layerFluids.unsafeGetTileI16F16(x, y) + var fluidID = tileNumberToNameMap[type.toLong()] ?: throw NullPointerException("No such fluid: $type") + + if (fluidID == Block.NULL || fluidID == Block.NOT_GENERATED) + fluidID = Fluid.NULL + + return FluidInfo(fluidID, if (fill.isNaN()) 0f else fill) // hex FFFFFFFF (magic number for ungenerated tiles) is interpreted as Float.NaN + } + + data class FluidInfo(val type: ItemID = Fluid.NULL, val amount: Float = 0f) { + /** test if this fluid should be considered as one */ + fun isFluid() = type != Fluid.NULL && amount >= FLUID_MIN_MASS + fun getProp() = FluidCodex[type] + override fun toString() = "Fluid type: ${type}, amount: $amount" + } + + fun getLayer(index: Int) = when(index) { + TERRAIN -> layerTerrain + WALL -> layerWall + ORES -> layerOres + FLUID -> layerFluids + else -> null//throw IllegalArgumentException("Unknown layer index: $index") + } + + /** + * @return ItemID of the broken block AND ore if the block is broken, `null` otherwise + */ + open fun inflictTerrainDamage(x: Int, y: Int, damage: Double, bypassEvent: Boolean): Pair { + if (damage.isNaN()) throw IllegalArgumentException("Cannot inflict NaN amount of damage at($x, $y)") + + val damage = damage.toFloat() + val addr = LandUtil.getBlockAddr(this, x, y) + + //println("[GameWorld] ($x, $y) Damage: $damage") + + if (terrainDamages[addr] == null) { // add new + terrainDamages[addr] = damage + } + else if (terrainDamages[addr]!! + damage <= 0) { // tile is (somehow) fully healed + terrainDamages.remove(addr) + } + else { // normal situation + terrainDamages[addr] = terrainDamages[addr]!! + damage + } + + if (!bypassEvent) { + Terrarum.ingame?.modified(LandUtil.LAYER_TERR, x, y) + } + + //println("[GameWorld] accumulated damage: ${terrainDamages[addr]}") + + // remove tile from the world + if ((terrainDamages[addr] ?: 0f) >= BlockCodex[getTileFromTerrain(x, y)].strength) { + val tileBroke = getTileFromTerrain(x, y) + val oreBroke = getTileFromOre(x, y) + setTileTerrain(x, y, Block.AIR, false) + terrainDamages.remove(addr) + return tileBroke.let { if (it == Block.AIR) null else it } to oreBroke.item.let { if (it == Block.AIR) null else it } + } + + return null to null + } + + fun setTileOnLayerUnsafe(layer: Int, x: Int, y: Int, tile: Int) { + (getLayer(layer) ?: throw IllegalArgumentException("Unknown layer index: $layer")).let { + if (it !is BlockLayerGenericI16) throw IllegalArgumentException("Block layers other than BlockLayer16 is not supported yet)") + it.unsafeSetTile(x, y, tile) + } + } + + /** + * Will return (Block.AIR, 0) if there is no ore + */ + fun getTileFromOre(rawX: Int, rawY: Int): OrePlacement { + val (x, y) = coerceXY(rawX, rawY) + val (tileNum, placement) = layerOres.unsafeGetTileI16I8(x, y) + val tileName = tileNumberToNameMap[tileNum.toLong()] + return OrePlacement(tileName ?: Block.UPDATE, placement) + } + + fun setTileOre(rawX: Int, rawY: Int, ore: ItemID, placement: Int) { + val (x, y) = coerceXY(rawX, rawY) + layerOres.unsafeSetTile(x, y, tileNameToNumberMap[ore]!!, placement) + } + + open fun getTerrainDamage(x: Int, y: Int): Float = + terrainDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f + + /** + * @return true if block is broken + */ + open fun inflictWallDamage(x: Int, y: Int, damage: Double, bypassEvent: Boolean): ItemID? { + if (damage.isNaN()) throw IllegalArgumentException("Cannot inflict NaN amount of damage at($x, $y)") + + val damage = damage.toFloat() + val addr = LandUtil.getBlockAddr(this, x, y) + + if (wallDamages[addr] == null) { // add new + wallDamages[addr] = damage + } + else if (wallDamages[addr]!! + damage <= 0) { // tile is (somehow) fully healed + wallDamages.remove(addr) + } + else { // normal situation + wallDamages[addr] = wallDamages[addr]!! + damage + } + + if (!bypassEvent) { + Terrarum.ingame?.modified(LandUtil.LAYER_TERR, x, y) + } + + // remove tile from the world + if (wallDamages[addr]!! >= BlockCodex[getTileFromWall(x, y)].strength) { + val tileBroke = getTileFromWall(x, y) + setTileWall(x, y, Block.AIR, false) + wallDamages.remove(addr) + return tileBroke + } + + return null + } + open fun getWallDamage(x: Int, y: Int): Float = + wallDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f + + fun setFluid(x: Int, y: Int, fluidType: ItemID, fill: Float) { + val (x, y) = coerceXY(x, y) + + if (!fluidType.isFluid() && fluidType != Block.AIR) throw IllegalArgumentException("Fluid type is not actually fluid: $fluidType") + + /*if (x == 60 && y == 256) { + printdbg(this, "Setting fluid $fill at ($x,$y)") + }*/ + + + if (fluidType == Fluid.NULL && fill != 0f) { + throw Error("Illegal fluid fill at ($x,$y): ${FluidInfo(fluidType, fill)}") + } + + +// val addr = LandUtil.getBlockAddr(this, x, y) + + val fluidNumber = tileNameToNumberMap[fluidType] ?: throw NullPointerException("No such fluid: $fluidType") + + if (fill > FLUID_MIN_MASS) { + //setTileTerrain(x, y, fluidTypeToBlock(fluidType)) + layerFluids.unsafeSetTile(x, y, fluidNumber, fill) + } + else { + layerFluids.unsafeSetTile(x, y, tileNameToNumberMap[Fluid.NULL]!!, 0f) + } + + + /*if (x == 60 && y == 256) { + printdbg(this, "TileTerrain: ${getTileFromTerrain(x, y)}") + printdbg(this, "fluidTypes[$addr] = ${fluidTypes[addr]} (should be ${fluidType.value})") + printdbg(this, "fluidFills[$addr] = ${fluidFills[addr]} (should be $fill)") + }*/ + } + + + fun setTileWire(x: Int, y: Int, tile: ItemID, bypassEvent: Boolean, connection: Int) { val (x, y) = coerceXY(x, y) val blockAddr = LandUtil.getBlockAddr(this, x, y) @@ -542,13 +492,6 @@ open class GameWorld( setWireGraphOfUnsafe(blockAddr, tile, connection) } - fun setTileOnLayerUnsafe(layer: Int, x: Int, y: Int, tile: Int) { - (getLayer(layer) ?: throw IllegalArgumentException("Unknown layer index: $layer")).let { - if (it !is BlockLayerGenericI16) throw IllegalArgumentException("Block layers other than BlockLayer16 is not supported yet)") - it.unsafeSetTile(x, y, tile) - } - } - fun removeTileWire(x: Int, y: Int, tile: ItemID, bypassEvent: Boolean) { val (x, y) = coerceXY(x, y) val blockAddr = LandUtil.getBlockAddr(this, x, y) @@ -704,32 +647,6 @@ open class GameWorld( return wirings[blockAddr]?.ws to wiringGraph[blockAddr] } - fun getTileFrom(mode: Int, x: Int, y: Int): ItemID { - if (mode == TERRAIN) { - return getTileFromTerrain(x, y) - } - else if (mode == WALL) { - return getTileFromWall(x, y) - } - else - throw IllegalArgumentException("illegal mode input: $mode") - } - - /** - * Will return (Block.AIR, 0) if there is no ore - */ - fun getTileFromOre(rawX: Int, rawY: Int): OrePlacement { - val (x, y) = coerceXY(rawX, rawY) - val (tileNum, placement) = layerOres.unsafeGetTile1(x, y) - val tileName = tileNumberToNameMap[tileNum.toLong()] - return OrePlacement(tileName ?: Block.UPDATE, placement) - } - - fun setTileOre(rawX: Int, rawY: Int, ore: ItemID, placement: Int) { - val (x, y) = coerceXY(rawX, rawY) - layerOres.unsafeSetTile(x, y, tileNameToNumberMap[ore]!!, placement) - } - fun terrainIterator(): Iterator> { return object : Iterator> { @@ -757,7 +674,7 @@ open class GameWorld( private var iteratorCount = 0 override fun hasNext(): Boolean = - iteratorCount < width * height + iteratorCount < width * height override fun next(): ItemID { val y = iteratorCount / width @@ -771,128 +688,7 @@ open class GameWorld( } } - /** - * @return ItemID of the broken block AND ore if the block is broken, `null` otherwise - */ - fun inflictTerrainDamage(x: Int, y: Int, damage: Double, bypassEvent: Boolean): Pair { - if (damage.isNaN()) throw IllegalArgumentException("Cannot inflict NaN amount of damage at($x, $y)") - val damage = damage.toFloat() - val addr = LandUtil.getBlockAddr(this, x, y) - - //println("[GameWorld] ($x, $y) Damage: $damage") - - if (terrainDamages[addr] == null) { // add new - terrainDamages[addr] = damage - } - else if (terrainDamages[addr]!! + damage <= 0) { // tile is (somehow) fully healed - terrainDamages.remove(addr) - } - else { // normal situation - terrainDamages[addr] = terrainDamages[addr]!! + damage - } - - if (!bypassEvent) { - Terrarum.ingame?.modified(LandUtil.LAYER_TERR, x, y) - } - - //println("[GameWorld] accumulated damage: ${terrainDamages[addr]}") - - // remove tile from the world - if ((terrainDamages[addr] ?: 0f) >= BlockCodex[getTileFromTerrain(x, y)].strength) { - val tileBroke = getTileFromTerrain(x, y) - val oreBroke = getTileFromOre(x, y) - setTileTerrain(x, y, Block.AIR, false) - terrainDamages.remove(addr) - return tileBroke.let { if (it == Block.AIR) null else it } to oreBroke.item.let { if (it == Block.AIR) null else it } - } - - return null to null - } - fun getTerrainDamage(x: Int, y: Int): Float = - terrainDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f - - /** - * @return true if block is broken - */ - fun inflictWallDamage(x: Int, y: Int, damage: Double, bypassEvent: Boolean): ItemID? { - if (damage.isNaN()) throw IllegalArgumentException("Cannot inflict NaN amount of damage at($x, $y)") - - val damage = damage.toFloat() - val addr = LandUtil.getBlockAddr(this, x, y) - - if (wallDamages[addr] == null) { // add new - wallDamages[addr] = damage - } - else if (wallDamages[addr]!! + damage <= 0) { // tile is (somehow) fully healed - wallDamages.remove(addr) - } - else { // normal situation - wallDamages[addr] = wallDamages[addr]!! + damage - } - - if (!bypassEvent) { - Terrarum.ingame?.modified(LandUtil.LAYER_TERR, x, y) - } - - // remove tile from the world - if (wallDamages[addr]!! >= BlockCodex[getTileFromWall(x, y)].strength) { - val tileBroke = getTileFromWall(x, y) - setTileWall(x, y, Block.AIR, false) - wallDamages.remove(addr) - return tileBroke - } - - return null - } - fun getWallDamage(x: Int, y: Int): Float = - wallDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f - - fun setFluid(x: Int, y: Int, fluidType: ItemID, fill: Float) { - val (x, y) = coerceXY(x, y) - - if (!fluidType.isFluid() && fluidType != Block.AIR) throw IllegalArgumentException("Fluid type is not actually fluid: $fluidType") - - /*if (x == 60 && y == 256) { - printdbg(this, "Setting fluid $fill at ($x,$y)") - }*/ - - - if (fluidType == Fluid.NULL && fill != 0f) { - throw Error("Illegal fluid fill at ($x,$y): ${FluidInfo(fluidType, fill)}") - } - - -// val addr = LandUtil.getBlockAddr(this, x, y) - - val fluidNumber = tileNameToNumberMap[fluidType] ?: throw NullPointerException("No such fluid: $fluidType") - - if (fill > FLUID_MIN_MASS) { - //setTileTerrain(x, y, fluidTypeToBlock(fluidType)) - layerFluids.unsafeSetTile(x, y, fluidNumber, fill) - } - else { - layerFluids.unsafeSetTile(x, y, tileNameToNumberMap[Fluid.NULL]!!, 0f) - } - - - /*if (x == 60 && y == 256) { - printdbg(this, "TileTerrain: ${getTileFromTerrain(x, y)}") - printdbg(this, "fluidTypes[$addr] = ${fluidTypes[addr]} (should be ${fluidType.value})") - printdbg(this, "fluidFills[$addr] = ${fluidFills[addr]} (should be $fill)") - }*/ - } - - fun getFluid(x: Int, y: Int): FluidInfo { - val (x, y) = coerceXY(x, y) - val (type, fill) = layerFluids.unsafeGetTile1(x, y) - var fluidID = tileNumberToNameMap[type.toLong()] ?: throw NullPointerException("No such fluid: $type") - - if (fluidID == Block.NULL || fluidID == Block.NOT_GENERATED) - fluidID = Fluid.NULL - - return FluidInfo(fluidID, if (fill.isNaN()) 0f else fill) // hex FFFFFFFF (magic number for ungenerated tiles) is interpreted as Float.NaN - } /*private fun fluidTypeToBlock(type: FluidType) = when (type.abs()) { Fluid.NULL.value -> Block.AIR @@ -900,12 +696,7 @@ open class GameWorld( else -> throw IllegalArgumentException("Unsupported fluid type: $type") }*/ - data class FluidInfo(val type: ItemID = Fluid.NULL, val amount: Float = 0f) { - /** test if this fluid should be considered as one */ - fun isFluid() = type != Fluid.NULL && amount >= FLUID_MIN_MASS - fun getProp() = FluidCodex[type] - override fun toString() = "Fluid type: ${type}, amount: $amount" - } + /** * Connection rules: connect to all nearby, except: @@ -914,22 +705,22 @@ open class GameWorld( * If the wire does not allow them (e.g. wire bridge, thicknet), connect top-bottom and left-right nodes. */ data class WiringNode( - val ws: SortedArrayList = SortedArrayList() // what could possibly go wrong bloating up the RAM footprint when it's practically infinite these days? + val ws: SortedArrayList = SortedArrayList() // what could possibly go wrong bloating up the RAM footprint when it's practically infinite these days? ) data class WireReceptionState( - var dist: Int = -1, // how many tiles it took to traverse - var src: Point2i = Point2i(0,0) // xy position - // to get the state, use the src to get the state of the source emitter directly, then use dist to apply attenuation + var dist: Int = -1, // how many tiles it took to traverse + var src: Point2i = Point2i(0,0) // xy position + // to get the state, use the src to get the state of the source emitter directly, then use dist to apply attenuation ) /** * These values must be updated by none other than [WorldSimulator]() */ data class WiringSimCell( - var cnx: Int = 0, // connections. [1, 2, 4, 8] = [RIGHT, DOWN, LEFT, UP] - val emt: Vector2 = Vector2(0.0, 0.0), // i'm emitting this much power - val rcp: ArrayList = ArrayList() // how far away are the power sources + var cnx: Int = 0, // connections. [1, 2, 4, 8] = [RIGHT, DOWN, LEFT, UP] + val emt: Vector2 = Vector2(0.0, 0.0), // i'm emitting this much power + val rcp: ArrayList = ArrayList() // how far away are the power sources ) fun getTemperature(worldTileX: Int, worldTileY: Int): Float? { @@ -940,17 +731,143 @@ open class GameWorld( return null } - override fun dispose() { - layerWall.dispose() - layerTerrain.dispose() - layerOres.dispose() - layerFluids.dispose() - //nullWorldInstance?.dispose() // must be called ONLY ONCE; preferably when the app exits - disposed = true + abstract fun dispose() + + constructor() : this(UUID.randomUUID(), 0, 0) + + fun coerceXY(x: Int, y: Int) = (x fmod width) to (y.coerceIn(0, height - 1)) + fun coerceXY(xy: Pair) = (xy.first fmod width) to (xy.second.coerceIn(0, height - 1)) + + + open fun renumberTilesAfterLoad() { + printdbg(this, "renumberTilesAfterLoad()") + + // patch the "old"map + tileNumberToNameMap[0] = Block.AIR + tileNumberToNameMap[1] = Block.UPDATE + tileNumberToNameMap[65535] = Block.NOT_GENERATED + // before the renaming, update the name maps + oldTileNumberToNameMap = tileNumberToNameMap.toMap() + + tileNumberToNameMap.forEach { l, s -> + printdbg(this, " afterload oldMapping tileNumber $l <-> $s") + } + + printdbg(this, "") + + tileNumberToNameMap.clear() + tileNameToNumberMap.clear() + App.tileMaker.tags.forEach { + printdbg(this, " afterload tileMaker tileNumber ${it.value.tileNumber} <-> ${it.key}") + + tileNumberToNameMap[it.value.tileNumber.toLong()] = it.key + tileNameToNumberMap[it.key] = it.value.tileNumber + } + /*Terrarum.fluidCodex.fluidProps.entries.forEach { + fluidNumberToNameMap[it.value.numericID.toLong()] = it.key + fluidNameToNumberMap[it.key] = it.value.numericID + }*/ + + // force this rule to the old saves + tileNumberToNameMap[0] = Block.AIR + tileNumberToNameMap[1] = Block.UPDATE + tileNumberToNameMap[65535] = Block.NOT_GENERATED + tileNameToNumberMap[Block.AIR] = 0 + tileNameToNumberMap[Block.UPDATE] = 1 + tileNameToNumberMap[Block.NOT_GENERATED] = 65535 +// fluidNumberToNameMap[0] = Fluid.NULL +// fluidNumberToNameMap[65535] = Fluid.NULL +// fluidNameToNumberMap[Fluid.NULL] = 0 + + + BlocksDrawer.rebuildInternalPrecalculations() + + // perform renaming of tile layers + /*for (y in 0 until layerTerrain.height) { + for (x in 0 until layerTerrain.width) { + // renumber terrain and wall + layerTerrain.unsafeSetTile(x, y, tileNameToNumberMap[oldTileNumberToNameMap[layerTerrain.unsafeGetTile(x, y).toLong()]]!!) + layerWall.unsafeSetTile(x, y, tileNameToNumberMap[oldTileNumberToNameMap[layerWall.unsafeGetTile(x, y).toLong()]]!!) + + // renumber ores + val oldOreNum = layerOres.unsafeGetTile(x, y).toLong() + val oldOreName = oldTileNumberToNameMap[oldOreNum] + layerOres.unsafeSetTileKeepOrePlacement(x, y, oldOreName.let { tileNameToNumberMap[it] ?: throw NullPointerException("Unknown tile name: $oldOreName (<- $oldOreNum)") }) + + // renumber fluids + val (oldFluidNum, oldFluidFill) = layerFluids.unsafeGetTile1(x, y) + val oldFluidName = oldTileNumberToNameMap[oldFluidNum.toLong()] + layerFluids.unsafeSetTile(x, y, oldFluidName.let { tileNameToNumberMap[it] ?: throw NullPointerException("Unknown tile name: $oldFluidName (<- $oldFluidNum)") }, oldFluidFill) + } + }*/ + // will use as much threads you have on the system + printdbg(this, "starting renumbering thread") + try { + val te = ThreadExecutor() + te.renew() + te.submitAll1( + (0 until layerTerrain.width step CHUNK_W).map { xorigin -> + callable { + for (y in 0 until layerTerrain.height) { + for (x in xorigin until (xorigin + CHUNK_W).coerceAtMost(layerTerrain.width)) { + // renumber terrain and wall + layerTerrain.unsafeSetTile( + x, y, + tileNameToNumberMap[oldTileNumberToNameMap[layerTerrain.unsafeGetTile(x, y) + .toLong()]]!! + ) + layerWall.unsafeSetTile( + x, y, + tileNameToNumberMap[oldTileNumberToNameMap[layerWall.unsafeGetTile(x, y) + .toLong()]]!! + ) + + // renumber ores + val oldOreNum = layerOres.unsafeGetTile(x, y).toLong() + val oldOreName = oldTileNumberToNameMap[oldOreNum] + layerOres.unsafeSetTileKeepOrePlacement(x, y, + oldOreName.let { + tileNameToNumberMap[it] + ?: throw NullPointerException("Unknown tile name: $oldOreName (<- $oldOreNum)") + }) + + // renumber fluids + val (oldFluidNum, oldFluidFill) = layerFluids.unsafeGetTileI16F16(x, y) + val oldFluidName = oldTileNumberToNameMap[oldFluidNum.toLong()] + layerFluids.unsafeSetTile( + x, y, + oldFluidName.let { + tileNameToNumberMap[it] + ?: throw NullPointerException("Unknown tile name: $oldFluidName (<- $oldFluidNum)") + }, + oldFluidFill + ) + } + } + } + } + ) + te.join() + } + catch (e: Throwable) { + e.printStackTrace() + throw e + } + printdbg(this, "renumbering thread finished") + + printdbg(this, "renumberTilesAfterLoad done!") + } + + open fun updateWorldTime(delta: Float) { + worldTime.update(delta) + } + + + init { + creationTime = App.getTIME_T() } - override fun equals(other: Any?) = layerTerrain.ptr == (other as GameWorld).layerTerrain.ptr companion object { @Transient const val TERRAIN = 0 @@ -958,29 +875,146 @@ open class GameWorld( @Transient const val ORES = 2 @Transient const val FLUID = 3 + @Transient val DEFAULT_GRAVITATION = Vector2(0.0, 9.8) + } +} + +/** + * Special version of GameWorld where everything, including layer data, are saved in a single JSON file (i.e. not chunked) + */ +class TitlescreenGameWorld(width: Int, height: Int) : GameWorld(UUID.randomUUID(), width, height) { + override var layerWall: BlockLayer = BlockLayerInMemoryI16(width, height) + override var layerTerrain: BlockLayer = BlockLayerInMemoryI16(width, height) + override var layerOres: BlockLayer = BlockLayerInMemoryI16I8(width, height) + override var layerFluids: BlockLayer = BlockLayerInMemoryI16F16(width, height) + + constructor() : this(0, 0) + + override fun dispose() { + layerWall.dispose() + layerTerrain.dispose() + layerOres.disposed + layerFluids.disposed + } + + companion object { + fun makeNullWorld(): TitlescreenGameWorld { + TODO("Not yet implemented") + } + } +} + +open class TheGameWorld( + worldIndex: UUID, // should not be immutable as JSON loader will want to overwrite it + width: Int, + height: Int +) : GameWorld(worldIndex, width, height) { + + constructor() : this(UUID.randomUUID(), 0, 0) + constructor(width: Int, height: Int) : this(UUID.randomUUID(), width, height) + + //layers + @Transient override lateinit var layerWall: BlockLayer//BlockLayerGenericI16 + @Transient override lateinit var layerTerrain: BlockLayer//BlockLayerGenericI16 + @Transient override lateinit var layerOres: BlockLayer//BlockLayerOresI16I8 // damage to the block follows `terrainDamages` + @Transient override lateinit var layerFluids: BlockLayer//BlockLayerFluidI16F16 + + //val layerThermal: MapLayerHalfFloat // in Kelvins + //val layerFluidPressure: MapLayerHalfFloat // (milibar - 1000) + + + @Transient private val WIRE_POS_MAP = intArrayOf(1,2,4,8) + @Transient private val WIRE_ANTIPOS_MAP = intArrayOf(4,8,1,2) + + /** + * Used by the renderer. When wirings are updated, `wirings` and this properties must be synchronised. + */ + //private val wiringBlocks: HashArray + + + + /** + * Create new world + */ + constructor(width: Int, height: Int, diskSkimmer: DiskSkimmer, creationTIME_T: Long, lastPlayTIME_T: Long): this() { + if (width <= 0 || height <= 0) throw IllegalArgumentException("Non-positive width/height: ($width, $height)") + + this.width = width + this.height = height + + // preliminary spawn points + this.spawnX = width / 2 + this.spawnY = 150 + + layerTerrain = BlockLayerGenericI16(width, height, diskSkimmer, TERRAIN, this) + layerWall = BlockLayerGenericI16(width, height, diskSkimmer, WALL, this) + layerOres = BlockLayerOresI16I8(width, height, diskSkimmer, ORES, this) + layerFluids = BlockLayerFluidI16F16(width, height, diskSkimmer, FLUID, this) + chunkFlags = Array(height / CHUNK_H) { ByteArray(width / CHUNK_W) } + + // temperature layer: 2x2 is one cell + //layerThermal = MapLayerHalfFloat(width, height, averageTemperature) + + // fluid pressure layer: 4 * 8 is one cell + //layerFluidPressure = MapLayerHalfFloat(width, height, 13f) // 1013 mBar + + + creationTime = creationTIME_T + lastPlayTime = lastPlayTIME_T + + + if (App.tileMaker != null) { + App.tileMaker.tags.forEach { + if (!forcedTileNumberToNames.contains(it.key)) { + printdbg(this, "newworld tileNumber ${it.value.tileNumber} <-> tileName ${it.key}") + + tileNumberToNameMap[it.value.tileNumber.toLong()] = it.key + tileNameToNumberMap[it.key] = it.value.tileNumber + } + } + /*Terrarum.fluidCodex.fluidProps.entries.forEach { + if (!forcedFluidNumberToTiles.contains(it.key)) { + fluidNumberToNameMap[it.value.numericID.toLong()] = it.key + fluidNameToNumberMap[it.key] = it.value.numericID + } + }*/ + } + } + + fun coordInWorld(x: Int, y: Int) = y in 0 until height // ROUNDWORLD implementation + fun coordInWorldStrict(x: Int, y: Int) = x in 0 until width && y in 0 until height // ROUNDWORLD implementation + + + override fun dispose() { + if (::layerWall.isInitialized) layerWall.dispose() + if (::layerTerrain.isInitialized) layerTerrain.dispose() + if (::layerOres.isInitialized) layerOres.dispose() + if (::layerFluids.isInitialized) layerFluids.dispose() + //nullWorldInstance?.dispose() // must be called ONLY ONCE; preferably when the app exits + + disposed = true + } + + override fun equals(other: Any?) = layerTerrain.hashCode() == (other as GameWorld).layerTerrain.hashCode() + + companion object { @Transient val TILES_SUPPORTED = ReferencingRanges.TILES.last + 1 //@Transient val SIZEOF: Byte = 2 @Transient const val LAYERS: Byte = 4 // terrain, wall (layerTerrainLowBits + layerWallLowBits), wire - @Transient private var nullWorldInstance: GameWorld? = null + @Transient private var nullWorldInstance: TheGameWorld? = null - fun makeNullWorld(): GameWorld { + fun makeNullWorld(): TheGameWorld { if (nullWorldInstance == null) - nullWorldInstance = GameWorld(1, 1, 0, 0) + nullWorldInstance = TheGameWorld(1, 1) return nullWorldInstance!! } - val DEFAULT_GRAVITATION = Vector2(0.0, 9.8) - @Transient const val CHUNK_NULL = 0x00.toByte() @Transient const val CHUNK_GENERATING = 0x01.toByte() @Transient const val CHUNK_LOADED = 0x02.toByte() } - - open fun updateWorldTime(delta: Float) { - worldTime.update(delta) - } } infix fun Int.fmod(other: Int) = Math.floorMod(this, other) diff --git a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt index c96bca310..affbb6176 100644 --- a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt +++ b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt @@ -3,6 +3,7 @@ package net.torvald.terrarum.modulebasegame import com.badlogic.gdx.Gdx import com.badlogic.gdx.Input.Keys import com.badlogic.gdx.InputAdapter +import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.TextureRegion @@ -14,14 +15,14 @@ import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameparticles.ParticleBase -import net.torvald.terrarum.gameworld.BlockLayerGenericI16 -import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.* import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid -import net.torvald.terrarum.gameworld.WorldTime import net.torvald.terrarum.modulebasegame.ui.UIBuildingMakerBlockChooser import net.torvald.terrarum.modulebasegame.ui.UIBuildingMakerPenMenu import net.torvald.terrarum.modulebasegame.ui.UIPaletteSelector import net.torvald.terrarum.modulebasegame.ui.UIScreenZoom +import net.torvald.terrarum.savegame.DiskSkimmer +import net.torvald.terrarum.savegame.VDUtil import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.serialise.PointOfInterest import net.torvald.terrarum.serialise.POILayer @@ -61,7 +62,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) { - Set… """.trimIndent()) - lateinit var gameWorld: GameWorld + lateinit var gameWorld: TheGameWorld override val musicStreamer = TerrarumMusicAndAmbientStreamer() @@ -419,7 +420,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) { for (x in for_x_start..for_x_end) { if (wiringCounter >= maxRenderableWires) break - val (wires, nodes) = world.getAllWiresFrom(x, y) + val (wires, nodes) = gameWorld.getAllWiresFrom(x, y) wires?.forEach { val wireActor = wireActorsContainer[wiringCounter] @@ -553,7 +554,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) { ) } private fun getNearbyOres8(x: Int, y: Int): List { - return getNearbyTilesPos8(x, y).map { world.getTileFromOre(it.x, it.y) } + return getNearbyTilesPos8(x, y).map { gameWorld.getTileFromOre(it.x, it.y) } } private fun makePenWork(x: Int, y: Int) { @@ -877,8 +878,8 @@ class YamlCommandToolExportTest : YamlInvokable { ui.world.tileNameToNumberMap ) val layer = POILayer(name) - val terr = BlockLayerGenericI16(poi.w, poi.h) - val wall = BlockLayerGenericI16(poi.w, poi.h) + val terr = BlockLayerInMemoryI16(poi.w, poi.h) + val wall = BlockLayerInMemoryI16(poi.w, poi.h) layer.blockLayer = arrayListOf(terr, wall) poi.layers.add(layer) @@ -934,8 +935,13 @@ class YamlCommandNewFlatTerrain : YamlInvokable { println("[BuildingMaker] Generating builder world...") + val tempFile = FileHandle.tempFile("terrarumbuildingmaker").file() + val tempDisk = VDUtil.createNewDisk(1L shl 30, "buildingmaker", Common.CHARSET) + VDUtil.dumpToRealMachine(tempDisk, tempFile) + val tempDiskSkimmer = DiskSkimmer(tempFile) + val timeNow = System.currentTimeMillis() / 1000 - ui.gameWorld = GameWorld(90*12, 90*4, timeNow, timeNow) + ui.gameWorld = TheGameWorld(90*12, 90*4, tempDiskSkimmer, timeNow, timeNow) // remove null tiles for (y in 0 until ui.gameWorld.height) { diff --git a/src/net/torvald/terrarum/modulebasegame/ExplosionManager.kt b/src/net/torvald/terrarum/modulebasegame/ExplosionManager.kt index 96aefcfdf..20b4fad17 100644 --- a/src/net/torvald/terrarum/modulebasegame/ExplosionManager.kt +++ b/src/net/torvald/terrarum/modulebasegame/ExplosionManager.kt @@ -4,9 +4,8 @@ import net.torvald.terrarum.BlockCodex import net.torvald.terrarum.ItemCodex import net.torvald.terrarum.OreCodex import net.torvald.terrarum.ceilToInt -import net.torvald.terrarum.gameworld.BlockLayerGenericI16 +import net.torvald.terrarum.gameworld.BlockLayerInMemoryI16 import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.gameworld.getOffset import net.torvald.terrarum.modulebasegame.gameitems.PickaxeCore import net.torvald.unsafe.UnsafeHelper import java.util.concurrent.* @@ -34,7 +33,7 @@ object ExplosionManager { val lineMax = world.height - ty + CALC_RADIUS // create a copy of the tilemap - val tilemap = BlockLayerGenericI16(CALC_WIDTH, CALC_WIDTH) + val tilemap = BlockLayerInMemoryI16(CALC_WIDTH, CALC_WIDTH) val breakmap = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // fill in the tilemap copy @@ -69,9 +68,9 @@ object ExplosionManager { }.start() } - private fun memcpyFromWorldTiles(CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerGenericI16) { + private fun memcpyFromWorldTiles(CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerInMemoryI16) { // if the bounding box must wrap around - if (xStart > world.width - CALC_RADIUS) { + /*if (xStart > world.width - CALC_RADIUS) { val lenLeft = world.width - xStart val lenRight = CALC_WIDTH - lenLeft @@ -98,12 +97,18 @@ object ExplosionManager { out.getOffset(0, yOff), world.layerTerrain.bytesPerBlock * CALC_WIDTH ) + }*/ + // temporary: copy one by one + for (ox in xStart until xStart + CALC_WIDTH) { + val (x, y) = world.coerceXY(ox, yStart + yOff) + val tileInWorld = world.layerTerrain.unsafeGetTile(x, y) + out.unsafeSetTile(x, y, tileInWorld) } } - private fun memcpyToWorldTiles(CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerGenericI16) { + private fun memcpyToWorldTiles(CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerInMemoryI16) { // if the bounding box must wrap around - if (xStart > world.width - CALC_RADIUS) { + /*if (xStart > world.width - CALC_RADIUS) { val lenLeft = world.width - xStart val lenRight = CALC_WIDTH - lenLeft @@ -130,6 +135,12 @@ object ExplosionManager { world.layerTerrain.getOffset(xStart, yStart + yOff), world.layerTerrain.bytesPerBlock * CALC_WIDTH ) + }*/ + // temporary: copy one by one + for (ox in xStart until xStart + CALC_WIDTH) { + val (x, y) = world.coerceXY(ox, yStart + yOff) + val tileInWorld = out.unsafeGetTile(x, y) + world.layerTerrain.unsafeSetTile(x, y, tileInWorld) } } @@ -155,7 +166,7 @@ object ExplosionManager { CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, breakmap: UnsafeFloatArray, - tilemap: BlockLayerGenericI16, + tilemap: BlockLayerInMemoryI16, tx: Int, ty: Int, power: Float, dropProbNonOre: Float, diff --git a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt index bee7e4783..7d70fe2f9 100644 --- a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt +++ b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt @@ -17,7 +17,6 @@ import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF import net.torvald.terrarum.blockproperties.Block -import net.torvald.terrarum.blockproperties.FluidCodex import net.torvald.terrarum.gameactors.ActorWithBody import net.torvald.terrarum.gameactors.ActorWithBody.Companion.METER import net.torvald.terrarum.gameactors.ActorWithBody.Companion.PHYS_EPSILON_DIST @@ -28,6 +27,7 @@ import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.mouseInInteractableRange import net.torvald.terrarum.gameparticles.ParticleBase import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.modulebasegame.gameactors.Pocketed import net.torvald.terrarum.modulebasegame.gameitems.ItemThrowable @@ -44,8 +44,6 @@ import kotlin.math.min import kotlin.system.exitProcess - - /** * This will be rendered to a postprocessor FBO. * @@ -131,7 +129,7 @@ object IngameRenderer : Disposable { //var renderingParticleCount = 0 // private set - var world: GameWorld = GameWorld.makeNullWorld() + var world: GameWorld = TheGameWorld.makeNullWorld() private set // the grammar "IngameRenderer.world = gameWorld" seemes mundane and this function needs special care! private var newWorldLoadedLatch = false @@ -393,11 +391,16 @@ object IngameRenderer : Disposable { blendNormalStraightAlpha(batch) - val (vo, vg) = world.weatherbox.let { - if (it.currentWeather.identifier == "titlescreen") - 1f to 1f - else - it.currentVibrancy.x to it.currentVibrancy.y + val (vo, vg) = if (world is TheGameWorld) { + (world as TheGameWorld).weatherbox.let { + if (it.currentWeather.identifier == "titlescreen") + 1f to 1f + else + it.currentVibrancy.x to it.currentVibrancy.y + } + } + else { + 1f to 1f } mixedOutTex.texture.bind(0) diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index 7cc7ad13f..07bb3d9aa 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -28,6 +28,7 @@ import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.mouseInInteractableRange import net.torvald.terrarum.gameparticles.ParticleBase import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.modulebasegame.gameactors.* @@ -46,6 +47,7 @@ import net.torvald.terrarum.modulebasegame.worldgenerator.WorldgenParams import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil.CHUNK_H import net.torvald.terrarum.realestate.LandUtil.CHUNK_W +import net.torvald.terrarum.savegame.DiskSkimmer import net.torvald.terrarum.savegame.VDUtil import net.torvald.terrarum.savegame.VirtualDisk import net.torvald.terrarum.serialise.Common @@ -348,7 +350,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { data class Codices( val disk: VirtualDisk, // WORLD disk - val world: GameWorld, + val world: TheGameWorld, // val meta: WriteMeta.WorldMeta, // val block: BlockCodex, // val item: ItemCodex, @@ -383,7 +385,8 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { // feed info to the worldgen - Worldgen.attachMap(world, WorldgenParams.getParamsByVersion(codices.worldGenver, world.generatorSeed)) + if (world is TheGameWorld) + Worldgen.attachMap(world as TheGameWorld, WorldgenParams.getParamsByVersion(codices.worldGenver, world.generatorSeed)) } loadCallback = codices.callbackAfterLoad @@ -443,7 +446,6 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { } } - // try to unstuck the repositioned player codices.player.tryUnstuck() @@ -520,13 +522,18 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { } else { App.getLoadScreen().addMessage("${App.GAME_NAME} ${App.getVERSION_STRING()}") - App.getLoadScreen().addMessage("Creating new world") + val worldFile = getWorldSaveFiledesc(worldSavefileName) + VDUtil.dumpToRealMachine(worldDisk, worldFile) + val skimmer = DiskSkimmer(worldFile) + // init map as chosen size val timeNow = App.getTIME_T() - world = GameWorld(worldParams.width, worldParams.height, timeNow, timeNow) // new game, so the creation time is right now + + world = TheGameWorld(worldParams.width, worldParams.height, skimmer, timeNow, timeNow) // new game, so the creation time is right now + world.generatorSeed = worldParams.worldGenSeed //gameworldIndices.add(world.worldIndex) world.extraFields["basegame.economy"] = GameEconomy() diff --git a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt index feefbf17d..27a0b0ff4 100644 --- a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt +++ b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt @@ -24,18 +24,15 @@ import net.torvald.terrarum.gameactors.ai.ActorAI import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent import net.torvald.terrarum.gameparticles.ParticleBase -import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.gameworld.WorldTime -import net.torvald.terrarum.gameworld.fmod +import net.torvald.terrarum.gameworld.* import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.modulebasegame.ui.UILoadGovernor import net.torvald.terrarum.modulebasegame.ui.UIRemoCon import net.torvald.terrarum.modulebasegame.ui.UITitleRemoConYaml import net.torvald.terrarum.realestate.LandUtil -import net.torvald.terrarum.serialise.ReadSimpleWorld +import net.torvald.terrarum.serialise.ReadTitlescreenGameWorld import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.ui.UICanvas -import net.torvald.terrarum.ui.UIItemTextButton import net.torvald.terrarum.utils.OpenURL import net.torvald.terrarum.weather.WeatherMixer import net.torvald.terrarum.worlddrawer.WorldCamera @@ -69,7 +66,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) { //private var loadDone = false // not required; draw-while-loading is implemented in the AppLoader - private lateinit var demoWorld: GameWorld + private lateinit var demoWorld: TitlescreenGameWorld private lateinit var cameraNodes: FloatArray // camera Y-pos private val cameraNodeWidth = 15 private val lookaheadDist = cameraNodeWidth * TILE_SIZED @@ -165,19 +162,19 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) { val file = ModMgr.getFile("basegame", "demoworld") val reader = java.io.FileReader(file) //ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader) - val world = ReadSimpleWorld(reader, file) + val world = ReadTitlescreenGameWorld(reader, file) demoWorld = world demoWorld.worldTime.timeDelta = 30 printdbg(this, "Demo world loaded") } catch (e: IOException) { - demoWorld = GameWorld(LandUtil.CHUNK_W, LandUtil.CHUNK_H, 0L, 0L) + demoWorld = TitlescreenGameWorld(LandUtil.CHUNK_W, LandUtil.CHUNK_H) demoWorld.worldTime.timeDelta = 30 printdbg(this, "Demo world not found, using empty world") } - demoWorld.renumberTilesAfterLoad() this.world = demoWorld + demoWorld.renumberTilesAfterLoad() // set initial time to summer demoWorld.worldTime.addTime(WorldTime.DAY_LENGTH * 32) @@ -365,7 +362,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) { gdxClearAndEnableBlend(.64f, .754f, .84f, 1f) - if (!demoWorld.layerTerrain.ptr.destroyed) { // FIXME q&d hack to circumvent the dangling pointer issue #26 + if (!demoWorld.disposed) { // FIXME q&d hack to circumvent the dangling pointer issue #26 WorldCamera.update(demoWorld, cameraPlayer) IngameRenderer.invoke( diff --git a/src/net/torvald/terrarum/modulebasegame/WorldSimulator.kt b/src/net/torvald/terrarum/modulebasegame/WorldSimulator.kt index 918ae9e2e..d05c594e9 100644 --- a/src/net/torvald/terrarum/modulebasegame/WorldSimulator.kt +++ b/src/net/torvald/terrarum/modulebasegame/WorldSimulator.kt @@ -12,6 +12,7 @@ import net.torvald.terrarum.gameactors.Controllable import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld.Companion.FLUID +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.modulebasegame.TerrarumIngame.Companion.inUpdateRange import net.torvald.terrarum.modulebasegame.gameactors.* import net.torvald.terrarum.modulebasegame.gameitems.AxeCore diff --git a/src/net/torvald/terrarum/modulebasegame/WorldgenLoadScreen.kt b/src/net/torvald/terrarum/modulebasegame/WorldgenLoadScreen.kt index b5878ce6c..04e721fbd 100644 --- a/src/net/torvald/terrarum/modulebasegame/WorldgenLoadScreen.kt +++ b/src/net/torvald/terrarum/modulebasegame/WorldgenLoadScreen.kt @@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Texture import net.torvald.terrarum.* import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.ui.Toolkit import kotlin.math.roundToInt @@ -96,7 +97,7 @@ class WorldgenLoadScreen(screenToBeLoaded: IngameInstance, private val worldwidt try { // q&d solution for the dangling pointer; i'm doing this only because it's this fucking load screen that's fucking the dead pointer - if (!world.layerTerrain.ptrDestroyed) { + if (!world.layerTerrain.disposed) { val outCol = if (BlockCodex[world.getTileFromTerrain(wx, wy)].isSolid) COL_TERR else if (BlockCodex[world.getTileFromWall(wx, wy)].isSolid) COL_WALLED else COL_AIR diff --git a/src/net/torvald/terrarum/modulebasegame/console/ExportWorld.kt b/src/net/torvald/terrarum/modulebasegame/console/ExportWorld.kt index 3f6e3ea83..40e5814ec 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/ExportWorld.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/ExportWorld.kt @@ -4,9 +4,9 @@ import net.torvald.terrarum.App import net.torvald.terrarum.Terrarum import net.torvald.terrarum.console.ConsoleCommand import net.torvald.terrarum.console.Echo -import net.torvald.terrarum.gameworld.SimpleGameWorld +import net.torvald.terrarum.gameworld.TitlescreenGameWorld import net.torvald.terrarum.modulebasegame.TerrarumIngame -import net.torvald.terrarum.serialise.WriteSimpleWorld +import net.torvald.terrarum.serialise.WriteTitlescreenGameWorld import java.io.File import java.io.IOException @@ -21,12 +21,12 @@ object ExportWorld : ConsoleCommand { try { val ingame = Terrarum.ingame!! as TerrarumIngame val file = File(App.defaultDir + "/Exports/${args[1]}.json") - val simpleworld = SimpleGameWorld(ingame.world.width, ingame.world.height).also { + val simpleworld = TitlescreenGameWorld(ingame.world.width, ingame.world.height).also { it.layerTerrain = ingame.world.layerTerrain it.layerWall = ingame.world.layerWall it.tileNumberToNameMap.putAll(ingame.world.tileNumberToNameMap) } - file.writeText(WriteSimpleWorld(ingame, simpleworld, listOf())) + file.writeText(WriteTitlescreenGameWorld(ingame, simpleworld, listOf())) Echo("Exportworld: exported the world as ${args[1]}.json") } catch (e: IOException) { diff --git a/src/net/torvald/terrarum/modulebasegame/console/ImportWorld.kt b/src/net/torvald/terrarum/modulebasegame/console/ImportWorld.kt index 21c8691c9..81ed31771 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/ImportWorld.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/ImportWorld.kt @@ -5,7 +5,7 @@ import net.torvald.terrarum.Terrarum import net.torvald.terrarum.console.ConsoleCommand import net.torvald.terrarum.console.Echo import net.torvald.terrarum.modulebasegame.TerrarumIngame -import net.torvald.terrarum.serialise.ReadSimpleWorld +import net.torvald.terrarum.serialise.ReadTitlescreenGameWorld import java.io.File import java.io.IOException @@ -20,7 +20,7 @@ object ImportWorld : ConsoleCommand { try { val file = File(App.defaultDir + "/Exports/${args[1]}.json") val reader = java.io.FileReader(file) - ReadSimpleWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader, file) + ReadTitlescreenGameWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader, file) Echo("Importworld: imported a world from ${args[1]}.json") } catch (e: IOException) { diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt index a106ddf0d..6b3d673f8 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt @@ -9,6 +9,7 @@ import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.isWall import net.torvald.terrarum.gameitems.mouseInInteractableRange import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/LoadSavegame.kt b/src/net/torvald/terrarum/modulebasegame/serialise/LoadSavegame.kt index 8fe71f930..3f17db6d0 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/LoadSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/LoadSavegame.kt @@ -2,10 +2,11 @@ package net.torvald.terrarum.modulebasegame.serialise import net.torvald.terrarum.* import net.torvald.terrarum.console.Echo -import net.torvald.terrarum.gameworld.BlockLayerGenericI16 -import net.torvald.terrarum.gameworld.BlockLayerFluidI16F16 -import net.torvald.terrarum.gameworld.BlockLayerOresI16I8 -import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.* +import net.torvald.terrarum.gameworld.GameWorld.Companion.FLUID +import net.torvald.terrarum.gameworld.GameWorld.Companion.ORES +import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN +import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.modulebasegame.FancyWorldReadLoadScreen import net.torvald.terrarum.modulebasegame.TerrarumIngame @@ -60,11 +61,10 @@ object LoadSavegame { val worldDiskSavegameInfo = ByteArray64Reader(worldDisk.getFile(VDFileID.SAVEGAMEINFO)!!.bytes, Common.CHARSET) val world = ReadWorld(worldDiskSavegameInfo, worldDisk.diskFile) - - world.layerTerrain = BlockLayerGenericI16(world.width, world.height) - world.layerWall = BlockLayerGenericI16(world.width, world.height) - world.layerOres = BlockLayerOresI16I8(world.width, world.height) - world.layerFluids = BlockLayerFluidI16F16(world.width, world.height) + world.layerTerrain = BlockLayerGenericI16(world.width, world.height, worldDisk, TERRAIN, world) + world.layerWall = BlockLayerGenericI16(world.width, world.height, worldDisk, WALL, world) + world.layerOres = BlockLayerOresI16I8(world.width, world.height, worldDisk, ORES, world) + world.layerFluids = BlockLayerFluidI16F16(world.width, world.height, worldDisk, FLUID, world) world.chunkFlags = Array(world.height / LandUtil.CHUNK_H) { ByteArray(world.width / LandUtil.CHUNK_W) } newIngame.world = world // must be set before the loadscreen, otherwise the loadscreen will try to read from the NullWorld which is already destroyed @@ -119,7 +119,7 @@ object LoadSavegame { val (cx, cy) = LandUtil.chunkNumToChunkXY(world, chunk) ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, cx, cy) - world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or GameWorld.CHUNK_LOADED + world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or TheGameWorld.CHUNK_LOADED } } loadscreen.progress.getAndAdd(1) diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt index 6abebca3d..39dfa56b8 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt @@ -4,6 +4,7 @@ import net.torvald.gdx.graphics.PixmapIO2 import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.TerrarumAppConfiguration import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer @@ -118,7 +119,7 @@ class QuickSingleplayerWorldSavingThread( // println("Chunk xy from number $chunkNumber -> (${chunkXY.x}, ${chunkXY.y})") - if (chunkFlag and 0x7F == GameWorld.CHUNK_LOADED) { + if (chunkFlag and 0x7F == TheGameWorld.CHUNK_LOADED) { val chunkBytes = WriteWorld.encodeChunk(layer, cx, cy) val entryID = 0x1_0000_0000L or layerNum.toLong().shl(24) or chunkNumber.toLong() diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt index 683b0c440..6e3fc7a82 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt @@ -2,11 +2,11 @@ package net.torvald.terrarum.modulebasegame.serialise import net.torvald.gdx.graphics.PixmapIO2 import net.torvald.terrarum.* -import net.torvald.terrarum.gameworld.GameWorld.Companion.CHUNK_LOADED import net.torvald.terrarum.gameworld.GameWorld.Companion.FLUID import net.torvald.terrarum.gameworld.GameWorld.Companion.ORES import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL +import net.torvald.terrarum.gameworld.TheGameWorld.Companion.CHUNK_LOADED import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WriteWorld.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WriteWorld.kt index 8fd6809f7..23e23d953 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WriteWorld.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WriteWorld.kt @@ -5,6 +5,7 @@ import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.NoSerialise import net.torvald.terrarum.gameworld.BlockLayer import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser @@ -95,12 +96,12 @@ object WriteWorld { */ object ReadWorld { - operator fun invoke(worldDataStream: Reader, origin: File?): GameWorld = - Common.jsoner.fromJson(GameWorld::class.java, worldDataStream).also { + operator fun invoke(worldDataStream: Reader, origin: File?): TheGameWorld = + Common.jsoner.fromJson(TheGameWorld::class.java, worldDataStream).also { fillInDetails(origin, it) } - private fun fillInDetails(origin: File?, world: GameWorld) { + private fun fillInDetails(origin: File?, world: TheGameWorld) { world.tileNumberToNameMap.forEach { l, s -> world.tileNameToNumberMap[s] = l.toInt() } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt index 253b5e66d..f5e305316 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt @@ -15,7 +15,6 @@ import net.torvald.terrarum.serialise.toBig64 import net.torvald.terrarum.toInt import net.torvald.terrarum.utils.OrePlacement import net.torvald.terrarum.worlddrawer.BlocksDrawer -import kotlin.math.max /** * Created by minjaesong on 2023-10-26. diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt index d33e79a95..c97cb2e5f 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt @@ -10,6 +10,7 @@ import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.modulebasegame.TerrarumIngame @@ -163,14 +164,14 @@ object Worldgen { val jobs = getJobs() printdbg(this, "Generating chunk on ($cx, $cy)") Thread { - world.chunkFlags[cy][cx] = GameWorld.CHUNK_GENERATING + world.chunkFlags[cy][cx] = TheGameWorld.CHUNK_GENERATING for (i in jobs.indices) { val it = jobs[i] it.theWork.getChunkDone(cx, cy) } - world.chunkFlags[cy][cx] = GameWorld.CHUNK_LOADED + world.chunkFlags[cy][cx] = TheGameWorld.CHUNK_LOADED callback(cx, cy) }.let { it.priority = 2 @@ -566,7 +567,7 @@ abstract class Gen(val world: GameWorld, val isFinal: Boolean, val seed: Long, v draw(chunkX * LandUtil.CHUNK_W, chunkY * CHUNK_H, localJoise, sampleOffset) loadscreen?.progress?.addAndGet(1L) - world.chunkFlags[chunkY][chunkX] = if (isFinal) GameWorld.CHUNK_LOADED else GameWorld.CHUNK_GENERATING + world.chunkFlags[chunkY][chunkX] = if (isFinal) TheGameWorld.CHUNK_LOADED else TheGameWorld.CHUNK_GENERATING } } } diff --git a/src/net/torvald/terrarum/serialise/Common.kt b/src/net/torvald/terrarum/serialise/Common.kt index b1c8c44eb..9eb45ad72 100644 --- a/src/net/torvald/terrarum/serialise/Common.kt +++ b/src/net/torvald/terrarum/serialise/Common.kt @@ -10,8 +10,9 @@ import io.airlift.compress.zstd.ZstdOutputStream import net.torvald.random.HQRNG import net.torvald.terrarum.* import net.torvald.terrarum.console.EchoError -import net.torvald.terrarum.gameworld.BlockLayerGenericI16 +import net.torvald.terrarum.gameworld.BlockLayerInMemoryI16 import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.gameworld.WorldTime import net.torvald.terrarum.savegame.ByteArray64 import net.torvald.terrarum.savegame.ByteArray64GrowableOutputStream @@ -41,7 +42,7 @@ object Common { val CHARSET = Charsets.UTF_8 /** dispose of the `offendingObject` after rejection! */ - class BlockLayerHashMismatchError(val oldHash: String, val newHash: String, val offendingObject: BlockLayerGenericI16) : Error("Old Hash $oldHash != New Hash $newHash") + class BlockLayerHashMismatchError(val oldHash: String, val newHash: String, val offendingObject: BlockLayerInMemoryI16) : Error("Old Hash $oldHash != New Hash $newHash") private fun Byte.tostr() = this.toInt().and(255).toString(16).padStart(2,'0') private val digester = DigestUtils.getSha256Digest() @@ -74,8 +75,8 @@ object Common { } }) // BlockLayer - it.setSerializer(BlockLayerGenericI16::class.java, object : Json.Serializer { - override fun write(json: Json, obj: BlockLayerGenericI16, knownType: Class<*>?) { + it.setSerializer(BlockLayerInMemoryI16::class.java, object : Json.Serializer { + override fun write(json: Json, obj: BlockLayerInMemoryI16, knownType: Class<*>?) { digester.reset() obj.bytesIterator().forEachRemaining { digester.update(it) } val hash = StringBuilder().let { sb -> digester.digest().forEach { sb.append(it.tostr()) }; sb.toString() } @@ -85,7 +86,7 @@ object Common { json.writeValue(layer) } - override fun read(json: Json, jsonData: JsonValue, type: Class<*>): BlockLayerGenericI16 { + override fun read(json: Json, jsonData: JsonValue, type: Class<*>): BlockLayerInMemoryI16 { // full manual try { return strToBlockLayer(LayerInfo( @@ -435,12 +436,12 @@ object Common { * @param b a BlockLayer * @return Bytes in [b] which are GZip'd then Ascii85-encoded */ - private fun blockLayerToStr(b: BlockLayerGenericI16): String { + private fun blockLayerToStr(b: BlockLayerInMemoryI16): String { return bytesToZipdStr(b.bytesIterator()) } - private fun strToBlockLayer(layerInfo: LayerInfo): BlockLayerGenericI16 { - val layer = BlockLayerGenericI16(layerInfo.x, layerInfo.y) + private fun strToBlockLayer(layerInfo: LayerInfo): BlockLayerInMemoryI16 { + val layer = BlockLayerInMemoryI16(layerInfo.x, layerInfo.y) val unzipdBytes = strToBytes(StringReader(layerInfo.b)) // write to blocklayer and the digester diff --git a/src/net/torvald/terrarum/serialise/PointOfInterest.kt b/src/net/torvald/terrarum/serialise/PointOfInterest.kt index 39fd65f56..9706c7e99 100644 --- a/src/net/torvald/terrarum/serialise/PointOfInterest.kt +++ b/src/net/torvald/terrarum/serialise/PointOfInterest.kt @@ -6,6 +6,7 @@ import net.torvald.terrarum.TerrarumAppConfiguration import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.BlockLayerGenericI16 +import net.torvald.terrarum.gameworld.BlockLayerInMemoryI16 import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL @@ -128,7 +129,7 @@ class POILayer( constructor() : this("undefined") @Transient var name = name - @Transient internal lateinit var blockLayer: ArrayList + @Transient internal lateinit var blockLayer: ArrayList @Transient internal lateinit var dat: Array @Deprecated("Used for debug print", ReplaceWith("name")) @Transient internal var id = "" @@ -179,10 +180,10 @@ class POILayer( if (::blockLayer.isInitialized) { blockLayer.forEach { it.dispose() } } - blockLayer = ArrayList() + blockLayer = ArrayList() dat.forEachIndexed { layerIndex, layer -> - val currentBlockLayer = BlockLayerGenericI16(width, height).also { + val currentBlockLayer = BlockLayerInMemoryI16(width, height).also { blockLayer.add(it) } for (w in 0 until layer.size / byteLength) { diff --git a/src/net/torvald/terrarum/serialise/ReadSimpleWorld.kt b/src/net/torvald/terrarum/serialise/ReadTitlescreenGameWorld.kt similarity index 55% rename from src/net/torvald/terrarum/serialise/ReadSimpleWorld.kt rename to src/net/torvald/terrarum/serialise/ReadTitlescreenGameWorld.kt index 15c83f464..89bb2a3de 100644 --- a/src/net/torvald/terrarum/serialise/ReadSimpleWorld.kt +++ b/src/net/torvald/terrarum/serialise/ReadTitlescreenGameWorld.kt @@ -4,56 +4,53 @@ import net.torvald.terrarum.App import net.torvald.terrarum.IngameInstance import net.torvald.terrarum.ItemCodex import net.torvald.terrarum.gameactors.Actor -import net.torvald.terrarum.gameworld.BlockLayerFluidI16F16 -import net.torvald.terrarum.gameworld.BlockLayerOresI16I8 -import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.gameworld.SimpleGameWorld +import net.torvald.terrarum.gameworld.* +import net.torvald.terrarum.modulebasegame.IngameRenderer import java.io.File import java.io.Reader /** * Created by minjaesong on 2022-09-03. */ -object ReadSimpleWorld { +object ReadTitlescreenGameWorld { - operator fun invoke(worldDataStream: Reader, origin: File?): GameWorld = - Common.jsoner.fromJson(SimpleGameWorld::class.java, worldDataStream).also { + operator fun invoke(worldDataStream: Reader, origin: File?): TitlescreenGameWorld = + Common.jsoner.fromJson(TitlescreenGameWorld::class.java, worldDataStream).also { fillInDetails(origin, it) } - private fun fillInDetails(origin: File?, world: GameWorld) { + private fun fillInDetails(origin: File?, world: TitlescreenGameWorld) { world.tileNumberToNameMap.forEach { l, s -> world.tileNameToNumberMap[s] = l.toInt() } - world.layerOres = BlockLayerOresI16I8(world.width, world.height) - world.layerFluids = BlockLayerFluidI16F16(world.width, world.height) + world.layerOres = BlockLayerInMemoryI16I8(world.width, world.height) + world.layerFluids = BlockLayerInMemoryI16F16(world.width, world.height) ItemCodex.loadFromSave(origin, world.dynamicToStaticTable, world.dynamicItemInventory) } - fun readWorldAndSetNewWorld(ingame: IngameInstance, worldDataStream: Reader, origin: File?): GameWorld { + fun readWorldAndSetNewWorld(ingame: IngameInstance, worldDataStream: Reader, origin: File?): TitlescreenGameWorld { val world = invoke(worldDataStream, origin) ingame.world = world + return world } } -object WriteSimpleWorld { +object WriteTitlescreenGameWorld { - private fun preWrite(ingame: IngameInstance, time_t: Long, world: SimpleGameWorld, actorsList: List) { + private fun preWrite(ingame: IngameInstance, time_t: Long, world: TitlescreenGameWorld, actorsList: List) { val currentPlayTime_t = time_t - ingame.loadedTime_t world.comp = Common.getCompIndex() - world.lastPlayTime = time_t - world.totalPlayTime += currentPlayTime_t - world.actors.clear() - world.actors.addAll(actorsList.map { it.referenceID }.sorted().distinct()) +// world.actors.clear() +// world.actors.addAll(actorsList.map { it.referenceID }.sorted().distinct()) } - operator fun invoke(ingame: IngameInstance, world: SimpleGameWorld, actorsList: List): String { + operator fun invoke(ingame: IngameInstance, world: TitlescreenGameWorld, actorsList: List): String { val time_t = App.getTIME_T() preWrite(ingame, time_t, world, actorsList) val s = Common.jsoner.toJson(world) diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index b0ecb8fbd..7b9bfc3c2 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -18,6 +18,7 @@ import net.torvald.terrarum.audio.AudioMixer.Companion.DS_FLTIDX_PAN import net.torvald.terrarum.audio.dsp.* import net.torvald.terrarum.controller.TerrarumController import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.imagefont.TinyAlphNum import net.torvald.terrarum.modulebasegame.IngameRenderer @@ -315,7 +316,7 @@ class BasicDebugInfoWindow : UICanvas() { val wallNum = it.getTileFromWall(mouseTileX, mouseTileY) val tileNum = it.getTileFromTerrain(mouseTileX, mouseTileY) val (oreNum, orePlacement) = it.getTileFromOre(mouseTileX, mouseTileY) - val wires = it.getAllWiresFrom(mouseTileX, mouseTileY) + val wires = if (it is TheGameWorld) it.getAllWiresFrom(mouseTileX, mouseTileY) else null to null val fluid = it.getFluid(mouseTileX, mouseTileY) val wireCount = wires.first?.size?.toString() ?: "no" val tdmg = it.getTerrainDamage(mouseTileX, mouseTileY).toIntAndFrac(2,2) diff --git a/src/net/torvald/terrarum/utils/HashArray.kt b/src/net/torvald/terrarum/utils/HashArray.kt index 6af439201..e12fbf185 100644 --- a/src/net/torvald/terrarum/utils/HashArray.kt +++ b/src/net/torvald/terrarum/utils/HashArray.kt @@ -1,8 +1,6 @@ package net.torvald.terrarum.utils -import com.badlogic.gdx.utils.Json -import com.badlogic.gdx.utils.JsonValue import net.torvald.terrarum.gameactors.ActorValue import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.BlockAddress diff --git a/src/net/torvald/terrarum/worlddrawer/BlocksDrawer.kt b/src/net/torvald/terrarum/worlddrawer/BlocksDrawer.kt index b14eada01..2a4b6a642 100644 --- a/src/net/torvald/terrarum/worlddrawer/BlocksDrawer.kt +++ b/src/net/torvald/terrarum/worlddrawer/BlocksDrawer.kt @@ -17,9 +17,7 @@ import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gameitems.ItemID -import net.torvald.terrarum.gameworld.FLUID_MIN_MASS -import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.gameworld.fmod +import net.torvald.terrarum.gameworld.* import net.torvald.terrarum.modulebasegame.worldgenerator.shake import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.serialise.toBig64 @@ -43,7 +41,7 @@ import kotlin.math.roundToInt internal object BlocksDrawer { /** World change is managed by IngameRenderer.setWorld() */ - internal var world: GameWorld = GameWorld.makeNullWorld() + internal var world: GameWorld = TheGameWorld.makeNullWorld() /** @@ -448,6 +446,10 @@ internal object BlocksDrawer { */ private fun fillInTileBuffer(mode: Int) { + // don't render fluid and ores for TitlescreenGameWorld (for now) + if (world.layerFluids !is BlockLayerFluidI16F16) return + if (world.layerOres !is BlockLayerOresI16I8) return + // TODO the real fluid rendering must use separate function, but its code should be similar to this. // shader's tileAtlas will be fluid.tga, pixels written to the buffer is in accordance with the new // atlas. IngameRenderer must be modified so that fluid-draw call is separated from drawing tiles. @@ -467,7 +469,7 @@ internal object BlocksDrawer { WALL -> world.layerWall.unsafeGetTile(wx, wy) TERRAIN -> world.layerTerrain.unsafeGetTile(wx, wy) ORES -> world.layerOres.unsafeGetTile(wx, wy)//.also { println(it) } - FLUID -> world.layerFluids.unsafeGetTile1(wx, wy).let { (number, fill) -> + FLUID -> world.layerFluids.unsafeGetTileI16F16(wx, wy).let { (number, fill) -> if (number == 65535 || fill < 1f/30f) 0 else number } @@ -497,7 +499,7 @@ internal object BlocksDrawer { val nearbyFluidType = fluids.asSequence().filter { it.amount >= 0.5f / 16f }.map { it.type }.filter { it.startsWith("fluid@") }.sorted().firstOrNull() val fillThis = - world.layerFluids.unsafeGetTile1(wx, wy).second.let { if (it.isNaN()) 0f else it.coerceAtMost(1f) } + world.layerFluids.unsafeGetTileI16F16(wx, wy).second.let { if (it.isNaN()) 0f else it.coerceAtMost(1f) } val tile = world.getTileFromTerrain(wx, wy) @@ -671,7 +673,7 @@ internal object BlocksDrawer { rawTileNum + nearbyTilesInfo // special case: ores else if (mode == ORES) - rawTileNum + world.layerOres.unsafeGetTile1(wx, wy).second + rawTileNum + world.layerOres.unsafeGetTileI16I8(wx, wy).second // rest of the cases: terrain and walls else rawTileNum + when (renderTag.maskType) { CreateTileAtlas.RenderTag.MASK_NA -> 0 @@ -1188,7 +1190,7 @@ internal object BlocksDrawer { get() { val rate = (((Gdx.graphics.framesPerSecond / 50f) * TILE_SIZEF) / maxOf(WorldCamera.deltaX.abs(), WorldCamera.deltaY.abs()).coerceAtLeast(1)).roundToInt().coerceIn(1, 4) App.debugTimers.put("Renderer.tilemapUpdateDivider", rate.toLong()) - return (!world.layerTerrain.ptrDestroyed && App.GLOBAL_RENDER_TIMER % rate == 0L) + return (!world.layerTerrain.disposed && App.GLOBAL_RENDER_TIMER % rate == 0L) } private var camTransX = 0 diff --git a/src/net/torvald/terrarum/worlddrawer/FeaturesDrawer.kt b/src/net/torvald/terrarum/worlddrawer/FeaturesDrawer.kt index 796380d81..b0ee466b4 100644 --- a/src/net/torvald/terrarum/worlddrawer/FeaturesDrawer.kt +++ b/src/net/torvald/terrarum/worlddrawer/FeaturesDrawer.kt @@ -1,13 +1,11 @@ package net.torvald.terrarum.worlddrawer import com.badlogic.gdx.graphics.g2d.SpriteBatch -import com.jme3.math.FastMath import net.torvald.colourutil.ColourTemp import net.torvald.terrarum.* -import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF -import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockstats.TileSurvey import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.ui.Toolkit import kotlin.math.roundToInt @@ -17,7 +15,7 @@ import kotlin.math.roundToInt object FeaturesDrawer { /** World change is managed by IngameRenderer.setWorld() */ - internal var world: GameWorld = GameWorld.makeNullWorld() + internal var world: GameWorld = TheGameWorld.makeNullWorld() //const val TILE_SIZE = TILE_SIZE diff --git a/src/net/torvald/terrarum/worlddrawer/LightmapRenderer.kt b/src/net/torvald/terrarum/worlddrawer/LightmapRenderer.kt index ae3263236..a5e4d7f26 100644 --- a/src/net/torvald/terrarum/worlddrawer/LightmapRenderer.kt +++ b/src/net/torvald/terrarum/worlddrawer/LightmapRenderer.kt @@ -19,6 +19,7 @@ import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.isBlock import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.TheGameWorld import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.gameactors.Pocketed import net.torvald.terrarum.modulebasegame.ui.abs @@ -43,7 +44,7 @@ import kotlin.math.* object LightmapRenderer { /** World change is managed by IngameRenderer.setWorld() */ - private var world: GameWorld = GameWorld.makeNullWorld() + private var world: GameWorld = TheGameWorld.makeNullWorld() //private lateinit var lightCalcShader: ShaderProgram //private val SHADER_LIGHTING = AppLoader.getConfigBoolean("gpulightcalc") @@ -141,7 +142,7 @@ object LightmapRenderer { } fun recalculate(actorContainer: List) { - if (!world.layerTerrain.ptrDestroyed) _recalculate(actorContainer, lightmap) + if (!world.layerTerrain.disposed) _recalculate(actorContainer, lightmap) } private fun _recalculate(actorContainer: List, lightmap: UnsafeCvecArray) { @@ -153,7 +154,7 @@ object LightmapRenderer { } catch (e: NullPointerException) { System.err.println("[LightmapRendererNew.recalculate] Attempted to refer destroyed unsafe array " + - "(${world.layerTerrain.ptr})") + "(${world.layerTerrain})") e.printStackTrace() return // something's wrong but we'll ignore it like a trustful AK } @@ -656,7 +657,7 @@ object LightmapRenderer { internal fun draw(): Texture { - if (!world.layerTerrain.ptrDestroyed) { + if (!world.layerTerrain.disposed) { // when shader is not used: 0.5 ms on 6700K App.measureDebugTime("Renderer.LightToScreen") {