80 fps with unsafe access

This commit is contained in:
minjaesong
2019-06-08 03:00:47 +09:00
parent a1d51d4028
commit 4f8c3591c2
19 changed files with 260 additions and 248 deletions

Binary file not shown.

View File

@@ -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()
}
////////////

View File

@@ -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()

View File

@@ -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<Int, BlockProp>()
/** 4096 */
const val MAX_TERRAIN_TILES = MapLayer.RANGE * PairedMapLayer.RANGE
const val MAX_TERRAIN_TILES = GameWorld.TILES_SUPPORTED
private val nullProp = BlockProp()

View File

@@ -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)
}

View File

@@ -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<Int> {
return object : Iterator<Int> {
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<Byte> {
return object : Iterator<Byte> {
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)

View File

@@ -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
}
}

View File

@@ -1,67 +0,0 @@
package net.torvald.terrarum.gameworld
/**
* Created by minjaesong on 2016-01-17.
*/
open class MapLayer : Iterable<Byte> {
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<Byte> {
return object : Iterator<Byte> {
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)

View File

@@ -3,7 +3,7 @@ package net.torvald.terrarum.gameworld
/**
* Created by minjaesong on 2016-02-15.
*/
open class PairedMapLayer : Iterable<Byte> {
/*open class PairedMapLayer : Iterable<Byte> {
val width: Int; val height: Int
@@ -87,4 +87,4 @@ open class PairedMapLayer : Iterable<Byte> {
@Transient const val RANGE = 16
@Transient const val SIZEOF: Byte = 1 // 1 for 8-bit, 2 for 16-bit, ...
}
}
}*/

View File

@@ -1010,6 +1010,8 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
it.handler.dispose()
it.dispose()
}
super.dispose()
}

View File

@@ -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) {

View File

@@ -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(

View File

@@ -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,

View File

@@ -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<BlockAddress, SortedArrayList<GameWorld.WiringNode>>,
val wallDamages: HashMap<BlockAddress, Float>,
val terrainDamages: HashMap<BlockAddress, Float>,
val fluidTypes: HashMap<BlockAddress, FluidType>,

View File

@@ -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
}
}*/
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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.