From 4f8c3591c2edadeb639ce12c825c49aa4f48f76b Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 8 Jun 2019 03:00:47 +0900 Subject: [PATCH] 80 fps with unsafe access --- assets/mods/basegame/demoworld.gz | 3 - src/net/torvald/terrarum/IngameInstance.kt | 9 ++ src/net/torvald/terrarum/TitleScreen.kt | 9 +- .../terrarum/blockproperties/BlockCodex.kt | 4 +- .../torvald/terrarum/blockstats/BlockStats.kt | 7 - .../torvald/terrarum/gameworld/BlockLayer.kt | 147 ++++++++++++++++++ .../torvald/terrarum/gameworld/GameWorld.kt | 101 ++++-------- .../torvald/terrarum/gameworld/MapLayer.kt | 67 -------- .../terrarum/gameworld/PairedMapLayer.kt | 4 +- .../torvald/terrarum/modulebasegame/Ingame.kt | 2 + .../worldgenerator/WorldGenerator.kt | 32 ++-- .../terrarum/serialise/ReadLayerData.kt | 12 +- .../terrarum/serialise/ReadLayerDataLzma.kt | 21 +-- .../terrarum/serialise/ReadLayerDataZip.kt | 28 +--- .../terrarum/serialise/WriteLayerData.kt | 20 +-- .../terrarum/serialise/WriteLayerDataLzma.kt | 16 +- .../terrarum/serialise/WriteLayerDataZip.kt | 6 +- .../terrarum/worlddrawer/BlocksDrawerNew.kt | 5 +- work_files/DataFormats/Map data format.txt | 15 +- 19 files changed, 260 insertions(+), 248 deletions(-) delete mode 100644 assets/mods/basegame/demoworld.gz create mode 100644 src/net/torvald/terrarum/gameworld/BlockLayer.kt delete mode 100644 src/net/torvald/terrarum/gameworld/MapLayer.kt diff --git a/assets/mods/basegame/demoworld.gz b/assets/mods/basegame/demoworld.gz deleted file mode 100644 index 6ff0488d8..000000000 --- a/assets/mods/basegame/demoworld.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:852e6dc309ce324a780b1e3a2aa1d98439faa708819a680ca26a2b13aefdaa53 -size 84599 diff --git a/src/net/torvald/terrarum/IngameInstance.kt b/src/net/torvald/terrarum/IngameInstance.kt index 966c08247..c2be28a04 100644 --- a/src/net/torvald/terrarum/IngameInstance.kt +++ b/src/net/torvald/terrarum/IngameInstance.kt @@ -3,6 +3,7 @@ package net.torvald.terrarum import com.badlogic.gdx.Screen import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.utils.Queue +import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid @@ -74,7 +75,15 @@ open class IngameInstance(val batch: SpriteBatch) : Screen { override fun resize(width: Int, height: Int) { } + /** + * You ABSOLUTELY must call this in your child classes (```super.dispose()```) and the AppLoader to properly + * dispose of the world, which uses unsafe memory allocation. + * Failing to do this will result to a memory leak! + */ override fun dispose() { + printdbg(this, "Thank you for properly disposing the world!") + + world.dispose() } //////////// diff --git a/src/net/torvald/terrarum/TitleScreen.kt b/src/net/torvald/terrarum/TitleScreen.kt index 683db6f44..dab98b5ad 100644 --- a/src/net/torvald/terrarum/TitleScreen.kt +++ b/src/net/torvald/terrarum/TitleScreen.kt @@ -27,12 +27,10 @@ import net.torvald.terrarum.modulebasegame.gameworld.WorldTime import net.torvald.terrarum.modulebasegame.ui.UIRemoCon import net.torvald.terrarum.modulebasegame.ui.UITitleRemoConYaml import net.torvald.terrarum.modulebasegame.weather.WeatherMixer -import net.torvald.terrarum.serialise.ReadLayerData import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.worlddrawer.CreateTileAtlas import net.torvald.terrarum.worlddrawer.LightmapRenderer import net.torvald.terrarum.worlddrawer.WorldCamera -import java.io.FileInputStream /** * Created by minjaesong on 2017-09-02. @@ -129,7 +127,10 @@ class TitleScreen(val batch: SpriteBatch) : Screen { printdbg(this, "Intro pre-load") - demoWorld = ReadLayerData(FileInputStream(ModMgr.getFile("basegame", "demoworld"))) + demoWorld = GameWorldExtension(1, 64, 64, 0L, 0L, 0) + + printdbg(this, "Demo world gen complete") + // set time to summer demoWorld.time.addTime(WorldTime.DAY_LENGTH * 32) @@ -138,7 +139,7 @@ class TitleScreen(val batch: SpriteBatch) : Screen { cameraNodes = kotlin.FloatArray(nodeCount) { it -> val tileXPos = (demoWorld.width.toFloat() * it / nodeCount).floorInt() var travelDownCounter = 0 - while (!BlockCodex[demoWorld.getTileFromTerrain(tileXPos, travelDownCounter)].isSolid) { + while (travelDownCounter < demoWorld.height && !BlockCodex[demoWorld.getTileFromTerrain(tileXPos, travelDownCounter)].isSolid) { travelDownCounter += 4 } travelDownCounter * CreateTileAtlas.TILE_SIZE.toFloat() diff --git a/src/net/torvald/terrarum/blockproperties/BlockCodex.kt b/src/net/torvald/terrarum/blockproperties/BlockCodex.kt index 64827daf7..5ce904ea0 100644 --- a/src/net/torvald/terrarum/blockproperties/BlockCodex.kt +++ b/src/net/torvald/terrarum/blockproperties/BlockCodex.kt @@ -5,8 +5,6 @@ import net.torvald.terrarum.AppLoader import net.torvald.terrarum.AppLoader.printmsg import net.torvald.terrarum.gameworld.FluidType import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.gameworld.MapLayer -import net.torvald.terrarum.gameworld.PairedMapLayer import net.torvald.terrarum.utils.CSVFetcher import net.torvald.terrarum.worlddrawer.LightmapRenderer import org.apache.commons.csv.CSVRecord @@ -20,7 +18,7 @@ object BlockCodex { private var blockProps = HashMap() /** 4096 */ - const val MAX_TERRAIN_TILES = MapLayer.RANGE * PairedMapLayer.RANGE + const val MAX_TERRAIN_TILES = GameWorld.TILES_SUPPORTED private val nullProp = BlockProp() diff --git a/src/net/torvald/terrarum/blockstats/BlockStats.kt b/src/net/torvald/terrarum/blockstats/BlockStats.kt index 55e1cacba..4823f431a 100644 --- a/src/net/torvald/terrarum/blockstats/BlockStats.kt +++ b/src/net/torvald/terrarum/blockstats/BlockStats.kt @@ -3,7 +3,6 @@ package net.torvald.terrarum.blockstats import com.jme3.math.FastMath import net.torvald.terrarum.Terrarum import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.gameworld.MapLayer import net.torvald.terrarum.modulebasegame.Ingame import net.torvald.terrarum.worlddrawer.BlocksDrawer import net.torvald.terrarum.worlddrawer.CreateTileAtlas @@ -71,11 +70,5 @@ object BlockStats { return sum } - /** - - * @return copy of the stat data - */ - val statCopy: ShortArray - get() = Arrays.copyOf(tilestat, MapLayer.RANGE) } diff --git a/src/net/torvald/terrarum/gameworld/BlockLayer.kt b/src/net/torvald/terrarum/gameworld/BlockLayer.kt new file mode 100644 index 000000000..9ca518ff7 --- /dev/null +++ b/src/net/torvald/terrarum/gameworld/BlockLayer.kt @@ -0,0 +1,147 @@ +package net.torvald.terrarum.gameworld + +import com.badlogic.gdx.utils.Disposable +import net.torvald.terrarum.AppLoader.printdbg +import sun.misc.Unsafe + +/** + * Created by minjaesong on 2016-01-17. + */ +open class BlockLayer(val width: Int, val height: Int) : Disposable { + + private val unsafe: Unsafe + init { + val unsafeConstructor = Unsafe::class.java.getDeclaredConstructor() + unsafeConstructor.isAccessible = true + unsafe = unsafeConstructor.newInstance() + } + private var unsafeArrayInitialised = false + + private var layerPtr = unsafe.allocateMemory(width * height * BYTES_PER_BLOCK.toLong()) + + /** + * @param data Byte array representation of the layer, where: + * - every 2n-th byte is lowermost 8 bits of the tile number + * - every (2n+1)th byte is uppermost 4 (4096 blocks) or 8 (65536 blocks) bits of the tile number. + * + * When 4096-block mode is being used, every (2n+1)th byte is filled in this format: + * ``` + * (MSB) 0 0 0 0 a b c d (LSB) + * ``` + * + * In other words, the valid range for the every (2n+1)th byte is 0..15. + * + * TL;DR: LITTLE ENDIAN PLEASE + */ + constructor(width: Int, height: Int, data: ByteArray) : this(width, height) { + unsafe.allocateMemory(width * height * BYTES_PER_BLOCK.toLong()) + data.forEachIndexed { index, byte -> unsafe.putByte(layerPtr + index, byte) } + unsafeArrayInitialised = true + } + + init { + if (!unsafeArrayInitialised) { + unsafe.setMemory(layerPtr, width * height * BYTES_PER_BLOCK.toLong(), 0) + unsafeArrayInitialised = true + } + } + + /** + * Returns an iterator over blocks of type `Int`. + * + * @return an Iterator. + */ + fun blocksIterator(): Iterator { + return object : Iterator { + + private var iteratorCount = 0 + + override fun hasNext(): Boolean { + return iteratorCount < width * height + } + + override fun next(): Int { + val y = iteratorCount / width + val x = iteratorCount % width + // advance counter + iteratorCount += 2 + + val offset = 2 * (y * width + x) + val lsb = unsafe.getByte(layerPtr + offset) + val msb = unsafe.getByte(layerPtr + offset + 1) + + //return data[y * width + x] + return lsb.toUint() + msb.toUint().shl(8) + } + } + } + + /** + * Returns an iterator over stored bytes. + * + * @return an Iterator. + */ + fun bytesIterator(): Iterator { + return object : Iterator { + + private var iteratorCount = 0 + + override fun hasNext(): Boolean { + return iteratorCount < width * height + } + + override fun next(): Byte { + val y = iteratorCount / width + val x = iteratorCount % width + // advance counter + iteratorCount += 1 + + return unsafe.getByte(layerPtr + 1) + } + } + } + + internal fun unsafeGetTile(x: Int, y: Int): Int { + val offset = BYTES_PER_BLOCK * (y * width + x) + val lsb = unsafe.getByte(layerPtr + offset) + val msb = unsafe.getByte(layerPtr + offset + 1) + + return lsb.toUint() + msb.toUint().shl(8) + } + + internal fun unsafeSetTile(x: Int, y: Int, tile: Int) { + val offset = BYTES_PER_BLOCK * (y * width + x) + + 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) + } + + /** + * @param blockOffset Offset in blocks. BlockOffset of 0x100 is equal to ```layerPtr + 0x200``` + */ + internal fun unsafeSetTile(blockOffset: Long, tile: Int) { + val offset = 2 * 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 fun dispose() { + unsafe.freeMemory(layerPtr) + printdbg(this, "BlockLayer successfully freed") + } + + companion object { + @Transient val BYTES_PER_BLOCK = 2 + } +} + +fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this) diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index b760a6050..619adb799 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.gameworld +import com.badlogic.gdx.utils.Disposable import net.torvald.gdx.graphics.Cvec import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.Terrarum @@ -17,14 +18,14 @@ import kotlin.math.sign typealias BlockAddress = Long -open class GameWorld { +open class GameWorld : Disposable { var worldName: String = "New World" /** Index start at 1 */ var worldIndex: Int set(value) { if (value <= 0) - throw Error("World index start at 1; you entered $value") + throw Error("World index start at 1; you've entered $value") printdbg(this, "Creation of new world with index $value, called by:") Thread.currentThread().stackTrace.forEach { @@ -46,17 +47,12 @@ open class GameWorld { val loadTime: Long = System.currentTimeMillis() / 1000L //layers - @TEMzPayload("WALL", TEMzPayload.EIGHT_MSB) - val layerWall: MapLayer + @TEMzPayload("WALL", TEMzPayload.TWELVE_BITS_LITTLE) + val layerWall: BlockLayer @TEMzPayload("TERR", TEMzPayload.EIGHT_MSB) - val layerTerrain: MapLayer + val layerTerrain: BlockLayer //val layerWire: MapLayer - @TEMzPayload("WALL", TEMzPayload.FOUR_LSB) - val layerWallLowBits: PairedMapLayer - @TEMzPayload("TERR", TEMzPayload.FOUR_LSB) - val layerTerrainLowBits: PairedMapLayer - //val layerThermal: MapLayerHalfFloat // in Kelvins //val layerFluidPressure: MapLayerHalfFloat // (milibar - 1000) @@ -108,11 +104,9 @@ open class GameWorld { this.spawnX = width / 2 this.spawnY = 200 - layerTerrain = MapLayer(width, height) - layerWall = MapLayer(width, height) + layerTerrain = BlockLayer(width, height) + layerWall = BlockLayer(width, height) //layerWire = MapLayer(width, height) - layerTerrainLowBits = PairedMapLayer(width, height) - layerWallLowBits = PairedMapLayer(width, height) wallDamages = HashMap() terrainDamages = HashMap() @@ -140,8 +134,6 @@ open class GameWorld { layerTerrain = layerData.layerTerrain layerWall = layerData.layerWall //layerWire = layerData.layerWire - layerTerrainLowBits = layerData.layerTerrainLowBits - layerWallLowBits = layerData.layerWallLowBits wallDamages = layerData.wallDamages terrainDamages = layerData.terrainDamages @@ -163,23 +155,6 @@ open class GameWorld { this.totalPlayTime = totalPlayTime } - - /** - * Get 2d array data of terrain - - * @return byte[][] terrain layer - */ - val terrainArray: ByteArray - get() = layerTerrain.data - - /** - * Get 2d array data of wall - - * @return byte[][] wall layer - */ - val wallArray: ByteArray - get() = layerWall.data - /** * Get 2d array data of wire @@ -190,34 +165,18 @@ open class GameWorld { private fun coerceXY(x: Int, y: Int) = (x fmod width) to (y.coerceIn(0, height - 1)) - fun getTileFromWall(x: Int, y: Int): Int? { + fun getTileFromWall(x: Int, y: Int): Int { val (x, y) = coerceXY(x, y) - val wall: Int? = layerWall.getTile(x, y) - val wallDamage: Int? = getWallLowBits(x, y) - return if (wall == null || wallDamage == null) - null - else - wall * PairedMapLayer.RANGE + wallDamage + if (y !in 0 until height) throw Error("Y coord out of world boundary: $y") + + return layerWall.unsafeGetTile(x, y) } - fun getTileFromTerrain(x: Int, y: Int): Int? { + fun getTileFromTerrain(x: Int, y: Int): Int { val (x, y) = coerceXY(x, y) - val terrain: Int? = layerTerrain.getTile(x, y) - val terrainDamage: Int? = getTerrainLowBits(x, y) - return if (terrain == null || terrainDamage == null) - null - else - terrain * PairedMapLayer.RANGE + terrainDamage - } + if (y !in 0 until height) throw Error("Y coord out of world boundary: $y") - private fun getWallLowBits(x: Int, y: Int): Int? { - val (x, y) = coerceXY(x, y) - return layerWallLowBits.getData(x, y) - } - - private fun getTerrainLowBits(x: Int, y: Int): Int? { - val (x, y) = coerceXY(x, y) - return layerTerrainLowBits.getData(x, y) + return layerTerrain.unsafeGetTile(x, y) } /** @@ -233,12 +192,10 @@ open class GameWorld { val tilenum = tilenum % TILES_SUPPORTED // does work without this, but to be safe... val oldWall = getTileFromWall(x, y) - layerWall.setTile(x, y, (tilenum / PairedMapLayer.RANGE).toByte()) - layerWallLowBits.setData(x, y, tilenum % PairedMapLayer.RANGE) + layerWall.unsafeSetTile(x, y, tilenum) wallDamages.remove(LandUtil.getBlockAddr(this, x, y)) - if (oldWall != null) - Terrarum.ingame?.queueWallChangedEvent(oldWall, tilenum, LandUtil.getBlockAddr(this, x, y)) + Terrarum.ingame?.queueWallChangedEvent(oldWall, tilenum, LandUtil.getBlockAddr(this, x, y)) } /** @@ -256,8 +213,7 @@ open class GameWorld { val (x, y) = coerceXY(x, y) val oldTerrain = getTileFromTerrain(x, y) - layerTerrain.setTile(x, y, (tilenum / PairedMapLayer.RANGE).toByte()) - layerTerrainLowBits.setData(x, y, tilenum % PairedMapLayer.RANGE) + layerTerrain.unsafeSetTile(x, y, tilenum) val blockAddr = LandUtil.getBlockAddr(this, x, y) terrainDamages.remove(blockAddr) @@ -267,8 +223,7 @@ open class GameWorld { } // fluid tiles-item should be modified so that they will also place fluid onto their respective map - if (oldTerrain != null) - Terrarum.ingame?.queueTerrainChangedEvent(oldTerrain, tilenum, LandUtil.getBlockAddr(this, x, y)) + Terrarum.ingame?.queueTerrainChangedEvent(oldTerrain, tilenum, LandUtil.getBlockAddr(this, x, y)) } /*fun setTileWire(x: Int, y: Int, tile: Byte) { @@ -506,16 +461,19 @@ open class GameWorld { return null } + override fun dispose() { + layerWall.dispose() + layerTerrain.dispose() + } companion object { - @Transient val WALL = 0 - @Transient val TERRAIN = 1 - @Transient val WIRE = 2 + @Transient const val WALL = 0 + @Transient const val TERRAIN = 1 + @Transient const val WIRE = 2 - /** 4096 */ - @Transient val TILES_SUPPORTED = MapLayer.RANGE * PairedMapLayer.RANGE - @Transient val SIZEOF: Byte = MapLayer.SIZEOF - @Transient val LAYERS: Byte = 4 // terrain, wall (layerTerrainLowBits + layerWallLowBits), wire + @Transient const val TILES_SUPPORTED = 4096 + //@Transient val SIZEOF: Byte = 2 + @Transient const val LAYERS: Byte = 4 // terrain, wall (layerTerrainLowBits + layerWallLowBits), wire fun makeNullWorld() = GameWorld(1, 1, 1, 0, 0, 0) } @@ -547,5 +505,6 @@ annotation class TEMzPayload(val payloadName: String, val arg: Int) { const val INT48_FLOAT_PAIR = 2 const val INT48_SHORT_PAIR = 3 const val INT48_INT_PAIR = 4 + const val TWELVE_BITS_LITTLE = 5 } } diff --git a/src/net/torvald/terrarum/gameworld/MapLayer.kt b/src/net/torvald/terrarum/gameworld/MapLayer.kt deleted file mode 100644 index aeb662a65..000000000 --- a/src/net/torvald/terrarum/gameworld/MapLayer.kt +++ /dev/null @@ -1,67 +0,0 @@ -package net.torvald.terrarum.gameworld - -/** - * Created by minjaesong on 2016-01-17. - */ -open class MapLayer : Iterable { - - val width: Int; val height: Int - internal @Volatile var data: ByteArray // in parallel programming: do not trust your register; always read freshly from RAM! - - constructor(width: Int, height: Int) { - this.width = width - this.height = height - data = ByteArray(width * height) - } - - constructor(width: Int, height: Int, data: ByteArray) { - this.data = data - this.width = width - this.height = height - } - - /** - * Returns an iterator over elements of type `T`. - - * @return an Iterator. - */ - override fun iterator(): Iterator { - return object : Iterator { - - private var iteratorCount = 0 - - override fun hasNext(): Boolean { - return iteratorCount < width * height - } - - override fun next(): Byte { - val y = iteratorCount / width - val x = iteratorCount % width - // advance counter - iteratorCount += 1 - - return data[y * width + x] - } - } - } - - internal fun getTile(x: Int, y: Int): Int? { - return if (x !in 0..width - 1 || y !in 0..height - 1) - null - else - data[y * width + x].toUint() - } - - internal fun setTile(x: Int, y: Int, tile: Byte) { - data[y * width + x] = tile - } - - fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height) - - companion object { - @Transient const val RANGE = 256 - @Transient const val SIZEOF: Byte = 1 // 1 for 8-bit, 2 for 16-bit, ... - } -} - -fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this) diff --git a/src/net/torvald/terrarum/gameworld/PairedMapLayer.kt b/src/net/torvald/terrarum/gameworld/PairedMapLayer.kt index 58574d5af..3f3e54060 100644 --- a/src/net/torvald/terrarum/gameworld/PairedMapLayer.kt +++ b/src/net/torvald/terrarum/gameworld/PairedMapLayer.kt @@ -3,7 +3,7 @@ package net.torvald.terrarum.gameworld /** * Created by minjaesong on 2016-02-15. */ -open class PairedMapLayer : Iterable { +/*open class PairedMapLayer : Iterable { val width: Int; val height: Int @@ -87,4 +87,4 @@ open class PairedMapLayer : Iterable { @Transient const val RANGE = 16 @Transient const val SIZEOF: Byte = 1 // 1 for 8-bit, 2 for 16-bit, ... } -} +}*/ \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/Ingame.kt b/src/net/torvald/terrarum/modulebasegame/Ingame.kt index 7c15588ff..2d0185adb 100644 --- a/src/net/torvald/terrarum/modulebasegame/Ingame.kt +++ b/src/net/torvald/terrarum/modulebasegame/Ingame.kt @@ -1010,6 +1010,8 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) { it.handler.dispose() it.dispose() } + + super.dispose() } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/WorldGenerator.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/WorldGenerator.kt index 12ce6567e..ae5db8e87 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/WorldGenerator.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/WorldGenerator.kt @@ -1,16 +1,16 @@ package net.torvald.terrarum.modulebasegame.worldgenerator -import net.torvald.random.HQRNG -import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.blockproperties.Block import com.jme3.math.FastMath import com.sudoplay.joise.Joise import com.sudoplay.joise.module.* +import net.torvald.random.HQRNG +import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.LoadScreen import net.torvald.terrarum.Terrarum +import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.concurrent.ThreadParallel -import net.torvald.terrarum.modulebasegame.RNGConsumer +import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.roundInt import java.util.* @@ -105,7 +105,7 @@ object WorldGenerator { */ fun generateMap() { random = HQRNG(SEED) - println("[mapgenerator] Seed: " + SEED) + printdbg(this, "Seed: " + SEED) worldOceanPosition = if (random.nextBoolean()) TYPE_OCEAN_LEFT else TYPE_OCEAN_RIGHT @@ -491,7 +491,7 @@ object WorldGenerator { val joise = Joise(ground_select) // fill the area as Joise map - println("[mapgenerator] Raising and eroding terrain...") + printdbg(this, "Raising and eroding terrain...") LoadScreen.addMessage("Raising and eroding terrain...") for (y in 0..(TERRAIN_UNDULATION - 1)) { for (x in 0..WIDTH) { @@ -549,7 +549,7 @@ object WorldGenerator { } private fun placeGlacierMount(heightMap: IntArray) { - println("[mapgenerator] Putting glacier...") + printdbg(this, "Putting glacier...") // raise for (i in heightMap.indices) { @@ -579,7 +579,7 @@ object WorldGenerator { } private fun heightMapToObjectMap(fs: IntArray) { - println("[mapgenerator] Shaping world as processed...") + printdbg(this, "Shaping world as processed...") // iterate for heightmap for (x in 0..WIDTH - 1) { @@ -602,7 +602,7 @@ object WorldGenerator { } private fun fillMapByNoiseMap() { - println("[mapgenerator] Shaping world...") + printdbg(this, "Shaping world...") LoadScreen.addMessage("Reticulating splines...") // RETICULATING SPLINES // generate dirt-stone transition line // use catmull spline @@ -686,7 +686,7 @@ object WorldGenerator { filter: NoiseFilter = NoiseFilterQuadratic, filterStart: Double = NOISE_GRAD_START, filterEnd: Double = NOISE_GRAD_END) { - println("[mapgenerator] " + message) + printdbg(this, "" + message) for (y in 0..HEIGHT - 1) { for (x in 0..WIDTH - 1) { @@ -716,7 +716,7 @@ object WorldGenerator { filter: NoiseFilter = NoiseFilterQuadratic, filterStart: Double = NOISE_GRAD_START, filterEnd: Double = NOISE_GRAD_END) { - println("[mapgenerator] " + message) + printdbg(this, "" + message) for (y in 0..HEIGHT - 1) { for (x in 0..WIDTH - 1) { @@ -747,7 +747,7 @@ object WorldGenerator { filter: NoiseFilter = NoiseFilterQuadratic, filterStart: Double = NOISE_GRAD_START, filterEnd: Double = NOISE_GRAD_END) { - println("[mapgenerator] " + message) + printdbg(this, "" + message) for (y in 0..HEIGHT - 1) { for (x in 0..WIDTH - 1) { @@ -797,7 +797,7 @@ object WorldGenerator { private val islandSpacing = 1024 private fun generateFloatingIslands() { - println("[mapgenerator] Placing floating islands...") + printdbg(this, "Placing floating islands...") LoadScreen.addMessage("Placing floating islands...") val nIslandsMax = Math.round(world.width * 6f / 8192f) @@ -830,7 +830,7 @@ object WorldGenerator { /* Flood */ private fun floodBottomLava() { - /*println("[mapgenerator] Flooding with lava...") + /*printdbg(this, "Flooding with lava...") LoadScreen.addMessage("Flooding with lava...") for (i in HEIGHT * 14 / 15..HEIGHT - 1) { for (j in 0..WIDTH - 1) { @@ -844,7 +844,7 @@ object WorldGenerator { /* Plant */ private fun plantGrass() { - println("[mapgenerator] Planting grass...") + printdbg(this, "Planting grass...") LoadScreen.addMessage("Planting grass...") /* TODO composing dirt and stone @@ -910,7 +910,7 @@ object WorldGenerator { "yellow" else "white" - println("[mapgenerator] Beach sand type: $thisSandStr") + printdbg(this, "Beach sand type: $thisSandStr") var ix = 0 while (ix < OCEAN_WIDTH * 1.5) { diff --git a/src/net/torvald/terrarum/serialise/ReadLayerData.kt b/src/net/torvald/terrarum/serialise/ReadLayerData.kt index c0d3b58d0..fd475c2e4 100644 --- a/src/net/torvald/terrarum/serialise/ReadLayerData.kt +++ b/src/net/torvald/terrarum/serialise/ReadLayerData.kt @@ -1,10 +1,5 @@ package net.torvald.terrarum.serialise -import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension -import java.io.IOException -import java.io.InputStream -import java.util.* - /** * Only being used by the title screen and the demoworld. This object may get deleted at any update * @@ -14,8 +9,11 @@ import java.util.* @Deprecated("TEMD is deprecated format; use TEMz which does compression") internal object ReadLayerData { + init { + throw Error("TEMD is old and removed format; use TEMz which does compression") + } - internal operator fun invoke(inputStream: InputStream, inWorld: GameWorldExtension? = null): GameWorldExtension { + /*internal operator fun invoke(inputStream: InputStream, inWorld: GameWorldExtension? = null): GameWorldExtension { val magicBytes = ByteArray(4) val layerSizeBytes = ByteArray(1) val layerCountBytes = ByteArray(1) @@ -109,7 +107,7 @@ internal object ReadLayerData { } return i - } + }*/ } fun Int.toLittle() = byteArrayOf( diff --git a/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt b/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt index 4ff1d1cba..f1ca95c62 100644 --- a/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt +++ b/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt @@ -3,9 +3,8 @@ package net.torvald.terrarum.serialise import com.badlogic.gdx.utils.compression.Lzma import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.gameworld.BlockAddress +import net.torvald.terrarum.gameworld.BlockLayer import net.torvald.terrarum.gameworld.FluidType -import net.torvald.terrarum.gameworld.MapLayer -import net.torvald.terrarum.gameworld.PairedMapLayer import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read import net.torvald.terrarum.realestate.LandUtil import java.io.* @@ -143,14 +142,7 @@ internal object ReadLayerDataLzma { val inflatedFile = inflatedOS.toByteArray() - // deal with (MSB ++ LSB) - if (t == "TERR" || t == "WALL") { - payloadBytes["${t}_MSB"] = inflatedFile.sliceArray(0 until worldSize.toInt()) // FIXME deflated stream cannot be larger than 2 GB - payloadBytes["${t}_LSB"] = inflatedFile.sliceArray(worldSize.toInt() until u.uncompressedSize.toInt()) // FIXME deflated stream cannot be larger than 2 GB - } - else { - payloadBytes[t] = inflatedFile - } + payloadBytes[t] = inflatedFile } val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress) @@ -184,13 +176,10 @@ internal object ReadLayerDataLzma { // TODO parse fluid(Types|Fills) - + return ReadLayerDataZip.LayerData( - MapLayer(width, height, payloadBytes["WALL_MSB"]!!), - MapLayer(width, height, payloadBytes["TERR_MSB"]!!), - MapLayer(width, height, payloadBytes["WIRE"]!!), - PairedMapLayer(width, height, payloadBytes["WALL_LSB"]!!), - PairedMapLayer(width, height, payloadBytes["TERR_LSB"]!!), + BlockLayer(width, height, payloadBytes["WALL"]!!), + BlockLayer(width, height, payloadBytes["TERR"]!!), spawnPoint.first, spawnPoint.second, diff --git a/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt b/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt index 6c132646a..afa8147bf 100644 --- a/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt +++ b/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt @@ -2,9 +2,8 @@ package net.torvald.terrarum.serialise import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.gameworld.BlockAddress +import net.torvald.terrarum.gameworld.BlockLayer import net.torvald.terrarum.gameworld.FluidType -import net.torvald.terrarum.gameworld.MapLayer -import net.torvald.terrarum.gameworld.PairedMapLayer import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read import net.torvald.terrarum.realestate.LandUtil import java.io.File @@ -144,14 +143,7 @@ internal object ReadLayerDataZip { if (uncompLen.toLong() != u.uncompressedSize) throw InternalError("Payload $t DEFLATE size mismatch -- expected ${u.uncompressedSize}, got $uncompLen") - // deal with (MSB ++ LSB) - if (t == "TERR" || t == "WALL") { - payloadBytes["${t}_MSB"] = inflatedFile.sliceArray(0 until worldSize.toInt()) // FIXME deflated stream cannot be larger than 2 GB - payloadBytes["${t}_LSB"] = inflatedFile.sliceArray(worldSize.toInt() until uncompLen) // FIXME deflated stream cannot be larger than 2 GB - } - else { - payloadBytes[t] = inflatedFile - } + payloadBytes[t] = inflatedFile } val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress) @@ -187,11 +179,8 @@ internal object ReadLayerDataZip { return LayerData( - MapLayer(width, height, payloadBytes["WALL_MSB"]!!), - MapLayer(width, height, payloadBytes["TERR_MSB"]!!), - MapLayer(width, height, payloadBytes["WIRE"]!!), - PairedMapLayer(width, height, payloadBytes["WALL_LSB"]!!), - PairedMapLayer(width, height, payloadBytes["TERR_LSB"]!!), + BlockLayer(width, height, payloadBytes["WALL"]!!), + BlockLayer(width, height, payloadBytes["TERR"]!!), spawnPoint.first, spawnPoint.second, @@ -203,16 +192,15 @@ internal object ReadLayerDataZip { * Immediately deployable, a part of the gameworld */ internal data class LayerData( - val layerWall: MapLayer, - val layerTerrain: MapLayer, - val layerWire: MapLayer, - val layerWallLowBits: PairedMapLayer, - val layerTerrainLowBits: PairedMapLayer, + val layerWall: BlockLayer, + val layerTerrain: BlockLayer, //val layerThermal: MapLayerHalfFloat, // in Kelvins //val layerAirPressure: MapLayerHalfFloat, // (milibar - 1000) val spawnX: Int, val spawnY: Int, + + //val wirings: HashMap>, val wallDamages: HashMap, val terrainDamages: HashMap, val fluidTypes: HashMap, diff --git a/src/net/torvald/terrarum/serialise/WriteLayerData.kt b/src/net/torvald/terrarum/serialise/WriteLayerData.kt index 73dadf13a..5bbfd8863 100644 --- a/src/net/torvald/terrarum/serialise/WriteLayerData.kt +++ b/src/net/torvald/terrarum/serialise/WriteLayerData.kt @@ -1,27 +1,21 @@ package net.torvald.terrarum.serialise -import net.torvald.terrarum.AppLoader -import net.torvald.terrarum.Terrarum -import net.torvald.terrarum.console.EchoError -import net.torvald.terrarum.gameworld.GameWorld -import java.io.File -import java.io.FileOutputStream -import java.io.IOException -import java.nio.charset.Charset -import java.util.zip.GZIPOutputStream - /** * TODO this one does not use TerranVirtualDisk * * Created by minjaesong on 2016-03-18. */ // internal for everything: prevent malicious module from messing up the savedata -@Deprecated("TEMD is deprecated format; use TEMz which does compression") +@Deprecated("TEMD is old and removed format; use TEMz which does compression") internal object WriteLayerData { + init { + throw Error("TEMD is old and removed format; use TEMz which does compression") + } + val LAYERS_FILENAME = "worldinfo1" - val MAGIC = "TEMD".toByteArray(charset = Charset.forName("US-ASCII")) + /*val MAGIC = "TEMD".toByteArray(charset = Charset.forName("US-ASCII")) val BYTE_NULL: Byte = 0 @@ -83,7 +77,7 @@ internal object WriteLayerData { } return false - } + }*/ } diff --git a/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt b/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt index 7bfcbcad7..3f99b9b63 100644 --- a/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt +++ b/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt @@ -101,15 +101,23 @@ internal object WriteLayerDataLzma { wb(PAYLOAD_HEADER); wb("TERR".toByteArray()) wi48(world.width * world.height * 3L / 2) - Lzma.compress(ByteArrayInputStream(world.terrainArray), outputStream) - Lzma.compress(ByteArrayInputStream(world.layerTerrainLowBits.data), outputStream) + world.layerTerrain.bytesIterator().forEach { + val tempByteArray = ByteArray(1) + tempByteArray[0] = it + val tempByteArrayStream = ByteArrayInputStream(tempByteArray) + Lzma.compress(tempByteArrayStream, outputStream) + } wb(PAYLOAD_FOOTER) // WALL payload wb(PAYLOAD_HEADER); wb("WALL".toByteArray()) wi48(world.width * world.height * 3L / 2) - Lzma.compress(ByteArrayInputStream(world.wallArray), outputStream) - Lzma.compress(ByteArrayInputStream(world.layerWallLowBits.data), outputStream) + world.layerWall.bytesIterator().forEach { + val tempByteArray = ByteArray(1) + tempByteArray[0] = it + val tempByteArrayStream = ByteArrayInputStream(tempByteArray) + Lzma.compress(tempByteArrayStream, outputStream) + } wb(PAYLOAD_FOOTER) // WIRE payload diff --git a/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt b/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt index 9eec29e66..04e57a2a9 100644 --- a/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt +++ b/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt @@ -113,8 +113,7 @@ internal object WriteLayerDataZip { wb(PAYLOAD_HEADER); wb("TERR".toByteArray()) wi48(world.width * world.height * 3L / 2) deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false) - deflater.write(world.terrainArray) - deflater.write(world.layerTerrainLowBits.data) + world.layerTerrain.bytesIterator().forEach { deflater.write(it.toInt()) } deflater.flush(); deflater.finish() wb(PAYLOAD_FOOTER) @@ -122,8 +121,7 @@ internal object WriteLayerDataZip { wb(PAYLOAD_HEADER); wb("WALL".toByteArray()) wi48(world.width * world.height * 3L / 2) deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false) - deflater.write(world.wallArray) - deflater.write(world.layerWallLowBits.data) + world.layerWall.bytesIterator().forEach { deflater.write(it.toInt()) } deflater.flush(); deflater.finish() wb(PAYLOAD_FOOTER) diff --git a/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt b/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt index ec9ba90cf..b7d7efe0f 100644 --- a/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt +++ b/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt @@ -4,13 +4,11 @@ import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.* import com.badlogic.gdx.math.Matrix4 import com.jme3.math.FastMath -import com.badlogic.gdx.graphics.Color import net.torvald.terrarum.* import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.gameworld.PairedMapLayer import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator @@ -59,7 +57,6 @@ internal object BlocksDrawer { val wallOverlayColour = Color(5f / 9f, 5f / 9f, 5f / 9f, 1f) const val BREAKAGE_STEPS = 10 - const val TILES_PER_BLOCK = PairedMapLayer.RANGE val WALL = GameWorld.WALL val TERRAIN = GameWorld.TERRAIN @@ -257,7 +254,7 @@ internal object BlocksDrawer { val fluidNum = this.type.abs() if (this.amount >= WorldSimulator.FLUID_MIN_MASS) { - val fluidLevel = this.amount.coerceIn(0f,1f).times(PairedMapLayer.RANGE - 1).roundToInt() + val fluidLevel = this.amount.coerceIn(0f,1f).times(15).roundToInt() return fluidLevel * 16 + fluidNum } diff --git a/work_files/DataFormats/Map data format.txt b/work_files/DataFormats/Map data format.txt index 11e123cd2..3d370d573 100644 --- a/work_files/DataFormats/Map data format.txt +++ b/work_files/DataFormats/Map data format.txt @@ -50,14 +50,11 @@ Ord Hex Description # "EndPYLd\xFF" Payload footer [45, 6E, 64, 50, 59, 4C, 64, FF] -Payload "TERR" -- world terrain data, concatenation of MSB and LSB arrays. In Haskell style: Deflate(MSB ++ LSB) - Uncompressed size will be 1.5x of (width * height) +Payload "TERR" -- world terrain data in Uint16 + Uncompressed size will be 2x of (width * height) -Payload "WALL" -- world walls data, concatenation of MSB and LSB arrays. In Haskell style: Deflate(MSB ++ LSB) - Uncompressed size will be 1.5x of (width * height) - -Payload "WIRE" -- world wires data - Uncompressed size will be as same as (width * height) +Payload "WALL" -- world walls data in Unit16 + Uncompressed size will be 2x of (width * height) Payload "TdMG" -- world terrain damage data, array of: (Int48 tileAddress, Float32 damage) Uncompressed size will be arbitrary (multiple of tens) @@ -71,8 +68,12 @@ Payload "FlTP" -- world fluid types, array of: (Int48 tileAddress, Signed Int16 Payload "FlFL" -- world fluid fills, array of: (Int48 tileAddress, Float32 amount) Uncompressed size will be arbitrary (multiple of tens) If the 'amount' < 0.0001f (WorldSimulator.FLUID_MIN_MASS), the entry must be discarded + +Payload "WiNt" -- wiring nodes, in JSON format + Payload "CtYP" -- conduit types, array of: (Int48 tileAddress, Uint32 bitarray) can hold 32 different wires simultaneously + Payload "CfL0" -- conduit fills, aka size of liquid/gas packet, array of: (Int48 tileAddress, Float32 fill) CfL0..CfL9, CfLa..CfLf are available to store values for 16 different things.