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.Screen
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.utils.Queue import com.badlogic.gdx.utils.Queue
import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid 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) { 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() { 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.UIRemoCon
import net.torvald.terrarum.modulebasegame.ui.UITitleRemoConYaml import net.torvald.terrarum.modulebasegame.ui.UITitleRemoConYaml
import net.torvald.terrarum.modulebasegame.weather.WeatherMixer import net.torvald.terrarum.modulebasegame.weather.WeatherMixer
import net.torvald.terrarum.serialise.ReadLayerData
import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.worlddrawer.CreateTileAtlas import net.torvald.terrarum.worlddrawer.CreateTileAtlas
import net.torvald.terrarum.worlddrawer.LightmapRenderer import net.torvald.terrarum.worlddrawer.LightmapRenderer
import net.torvald.terrarum.worlddrawer.WorldCamera import net.torvald.terrarum.worlddrawer.WorldCamera
import java.io.FileInputStream
/** /**
* Created by minjaesong on 2017-09-02. * Created by minjaesong on 2017-09-02.
@@ -129,7 +127,10 @@ class TitleScreen(val batch: SpriteBatch) : Screen {
printdbg(this, "Intro pre-load") 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 // set time to summer
demoWorld.time.addTime(WorldTime.DAY_LENGTH * 32) demoWorld.time.addTime(WorldTime.DAY_LENGTH * 32)
@@ -138,7 +139,7 @@ class TitleScreen(val batch: SpriteBatch) : Screen {
cameraNodes = kotlin.FloatArray(nodeCount) { it -> cameraNodes = kotlin.FloatArray(nodeCount) { it ->
val tileXPos = (demoWorld.width.toFloat() * it / nodeCount).floorInt() val tileXPos = (demoWorld.width.toFloat() * it / nodeCount).floorInt()
var travelDownCounter = 0 var travelDownCounter = 0
while (!BlockCodex[demoWorld.getTileFromTerrain(tileXPos, travelDownCounter)].isSolid) { while (travelDownCounter < demoWorld.height && !BlockCodex[demoWorld.getTileFromTerrain(tileXPos, travelDownCounter)].isSolid) {
travelDownCounter += 4 travelDownCounter += 4
} }
travelDownCounter * CreateTileAtlas.TILE_SIZE.toFloat() 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.AppLoader.printmsg
import net.torvald.terrarum.gameworld.FluidType import net.torvald.terrarum.gameworld.FluidType
import net.torvald.terrarum.gameworld.GameWorld 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.utils.CSVFetcher
import net.torvald.terrarum.worlddrawer.LightmapRenderer import net.torvald.terrarum.worlddrawer.LightmapRenderer
import org.apache.commons.csv.CSVRecord import org.apache.commons.csv.CSVRecord
@@ -20,7 +18,7 @@ object BlockCodex {
private var blockProps = HashMap<Int, BlockProp>() private var blockProps = HashMap<Int, BlockProp>()
/** 4096 */ /** 4096 */
const val MAX_TERRAIN_TILES = MapLayer.RANGE * PairedMapLayer.RANGE const val MAX_TERRAIN_TILES = GameWorld.TILES_SUPPORTED
private val nullProp = BlockProp() private val nullProp = BlockProp()

View File

@@ -3,7 +3,6 @@ package net.torvald.terrarum.blockstats
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.MapLayer
import net.torvald.terrarum.modulebasegame.Ingame import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.worlddrawer.BlocksDrawer import net.torvald.terrarum.worlddrawer.BlocksDrawer
import net.torvald.terrarum.worlddrawer.CreateTileAtlas import net.torvald.terrarum.worlddrawer.CreateTileAtlas
@@ -71,11 +70,5 @@ object BlockStats {
return sum 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 package net.torvald.terrarum.gameworld
import com.badlogic.gdx.utils.Disposable
import net.torvald.gdx.graphics.Cvec import net.torvald.gdx.graphics.Cvec
import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
@@ -17,14 +18,14 @@ import kotlin.math.sign
typealias BlockAddress = Long typealias BlockAddress = Long
open class GameWorld { open class GameWorld : Disposable {
var worldName: String = "New World" var worldName: String = "New World"
/** Index start at 1 */ /** Index start at 1 */
var worldIndex: Int var worldIndex: Int
set(value) { set(value) {
if (value <= 0) 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:") printdbg(this, "Creation of new world with index $value, called by:")
Thread.currentThread().stackTrace.forEach { Thread.currentThread().stackTrace.forEach {
@@ -46,17 +47,12 @@ open class GameWorld {
val loadTime: Long = System.currentTimeMillis() / 1000L val loadTime: Long = System.currentTimeMillis() / 1000L
//layers //layers
@TEMzPayload("WALL", TEMzPayload.EIGHT_MSB) @TEMzPayload("WALL", TEMzPayload.TWELVE_BITS_LITTLE)
val layerWall: MapLayer val layerWall: BlockLayer
@TEMzPayload("TERR", TEMzPayload.EIGHT_MSB) @TEMzPayload("TERR", TEMzPayload.EIGHT_MSB)
val layerTerrain: MapLayer val layerTerrain: BlockLayer
//val layerWire: MapLayer //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 layerThermal: MapLayerHalfFloat // in Kelvins
//val layerFluidPressure: MapLayerHalfFloat // (milibar - 1000) //val layerFluidPressure: MapLayerHalfFloat // (milibar - 1000)
@@ -108,11 +104,9 @@ open class GameWorld {
this.spawnX = width / 2 this.spawnX = width / 2
this.spawnY = 200 this.spawnY = 200
layerTerrain = MapLayer(width, height) layerTerrain = BlockLayer(width, height)
layerWall = MapLayer(width, height) layerWall = BlockLayer(width, height)
//layerWire = MapLayer(width, height) //layerWire = MapLayer(width, height)
layerTerrainLowBits = PairedMapLayer(width, height)
layerWallLowBits = PairedMapLayer(width, height)
wallDamages = HashMap() wallDamages = HashMap()
terrainDamages = HashMap() terrainDamages = HashMap()
@@ -140,8 +134,6 @@ open class GameWorld {
layerTerrain = layerData.layerTerrain layerTerrain = layerData.layerTerrain
layerWall = layerData.layerWall layerWall = layerData.layerWall
//layerWire = layerData.layerWire //layerWire = layerData.layerWire
layerTerrainLowBits = layerData.layerTerrainLowBits
layerWallLowBits = layerData.layerWallLowBits
wallDamages = layerData.wallDamages wallDamages = layerData.wallDamages
terrainDamages = layerData.terrainDamages terrainDamages = layerData.terrainDamages
@@ -163,23 +155,6 @@ open class GameWorld {
this.totalPlayTime = totalPlayTime 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 * 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)) 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 (x, y) = coerceXY(x, y)
val wall: Int? = layerWall.getTile(x, y) if (y !in 0 until height) throw Error("Y coord out of world boundary: $y")
val wallDamage: Int? = getWallLowBits(x, y)
return if (wall == null || wallDamage == null) return layerWall.unsafeGetTile(x, y)
null
else
wall * PairedMapLayer.RANGE + wallDamage
} }
fun getTileFromTerrain(x: Int, y: Int): Int? { fun getTileFromTerrain(x: Int, y: Int): Int {
val (x, y) = coerceXY(x, y) val (x, y) = coerceXY(x, y)
val terrain: Int? = layerTerrain.getTile(x, y) if (y !in 0 until height) throw Error("Y coord out of world boundary: $y")
val terrainDamage: Int? = getTerrainLowBits(x, y)
return if (terrain == null || terrainDamage == null)
null
else
terrain * PairedMapLayer.RANGE + terrainDamage
}
private fun getWallLowBits(x: Int, y: Int): Int? { return layerTerrain.unsafeGetTile(x, y)
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)
} }
/** /**
@@ -233,12 +192,10 @@ open class GameWorld {
val tilenum = tilenum % TILES_SUPPORTED // does work without this, but to be safe... val tilenum = tilenum % TILES_SUPPORTED // does work without this, but to be safe...
val oldWall = getTileFromWall(x, y) val oldWall = getTileFromWall(x, y)
layerWall.setTile(x, y, (tilenum / PairedMapLayer.RANGE).toByte()) layerWall.unsafeSetTile(x, y, tilenum)
layerWallLowBits.setData(x, y, tilenum % PairedMapLayer.RANGE)
wallDamages.remove(LandUtil.getBlockAddr(this, x, y)) 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 (x, y) = coerceXY(x, y)
val oldTerrain = getTileFromTerrain(x, y) val oldTerrain = getTileFromTerrain(x, y)
layerTerrain.setTile(x, y, (tilenum / PairedMapLayer.RANGE).toByte()) layerTerrain.unsafeSetTile(x, y, tilenum)
layerTerrainLowBits.setData(x, y, tilenum % PairedMapLayer.RANGE)
val blockAddr = LandUtil.getBlockAddr(this, x, y) val blockAddr = LandUtil.getBlockAddr(this, x, y)
terrainDamages.remove(blockAddr) 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 // 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) { /*fun setTileWire(x: Int, y: Int, tile: Byte) {
@@ -506,16 +461,19 @@ open class GameWorld {
return null return null
} }
override fun dispose() {
layerWall.dispose()
layerTerrain.dispose()
}
companion object { companion object {
@Transient val WALL = 0 @Transient const val WALL = 0
@Transient val TERRAIN = 1 @Transient const val TERRAIN = 1
@Transient val WIRE = 2 @Transient const val WIRE = 2
/** 4096 */ @Transient const val TILES_SUPPORTED = 4096
@Transient val TILES_SUPPORTED = MapLayer.RANGE * PairedMapLayer.RANGE //@Transient val SIZEOF: Byte = 2
@Transient val SIZEOF: Byte = MapLayer.SIZEOF @Transient const val LAYERS: Byte = 4 // terrain, wall (layerTerrainLowBits + layerWallLowBits), wire
@Transient val LAYERS: Byte = 4 // terrain, wall (layerTerrainLowBits + layerWallLowBits), wire
fun makeNullWorld() = GameWorld(1, 1, 1, 0, 0, 0) 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_FLOAT_PAIR = 2
const val INT48_SHORT_PAIR = 3 const val INT48_SHORT_PAIR = 3
const val INT48_INT_PAIR = 4 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. * Created by minjaesong on 2016-02-15.
*/ */
open class PairedMapLayer : Iterable<Byte> { /*open class PairedMapLayer : Iterable<Byte> {
val width: Int; val height: Int val width: Int; val height: Int
@@ -87,4 +87,4 @@ open class PairedMapLayer : Iterable<Byte> {
@Transient const val RANGE = 16 @Transient const val RANGE = 16
@Transient const val SIZEOF: Byte = 1 // 1 for 8-bit, 2 for 16-bit, ... @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.handler.dispose()
it.dispose() it.dispose()
} }
super.dispose()
} }

View File

@@ -1,16 +1,16 @@
package net.torvald.terrarum.modulebasegame.worldgenerator 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.jme3.math.FastMath
import com.sudoplay.joise.Joise import com.sudoplay.joise.Joise
import com.sudoplay.joise.module.* 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.LoadScreen
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.concurrent.ThreadParallel import net.torvald.terrarum.concurrent.ThreadParallel
import net.torvald.terrarum.modulebasegame.RNGConsumer import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.roundInt import net.torvald.terrarum.roundInt
import java.util.* import java.util.*
@@ -105,7 +105,7 @@ object WorldGenerator {
*/ */
fun generateMap() { fun generateMap() {
random = HQRNG(SEED) random = HQRNG(SEED)
println("[mapgenerator] Seed: " + SEED) printdbg(this, "Seed: " + SEED)
worldOceanPosition = if (random.nextBoolean()) TYPE_OCEAN_LEFT else TYPE_OCEAN_RIGHT worldOceanPosition = if (random.nextBoolean()) TYPE_OCEAN_LEFT else TYPE_OCEAN_RIGHT
@@ -491,7 +491,7 @@ object WorldGenerator {
val joise = Joise(ground_select) val joise = Joise(ground_select)
// fill the area as Joise map // fill the area as Joise map
println("[mapgenerator] Raising and eroding terrain...") printdbg(this, "Raising and eroding terrain...")
LoadScreen.addMessage("Raising and eroding terrain...") LoadScreen.addMessage("Raising and eroding terrain...")
for (y in 0..(TERRAIN_UNDULATION - 1)) { for (y in 0..(TERRAIN_UNDULATION - 1)) {
for (x in 0..WIDTH) { for (x in 0..WIDTH) {
@@ -549,7 +549,7 @@ object WorldGenerator {
} }
private fun placeGlacierMount(heightMap: IntArray) { private fun placeGlacierMount(heightMap: IntArray) {
println("[mapgenerator] Putting glacier...") printdbg(this, "Putting glacier...")
// raise // raise
for (i in heightMap.indices) { for (i in heightMap.indices) {
@@ -579,7 +579,7 @@ object WorldGenerator {
} }
private fun heightMapToObjectMap(fs: IntArray) { private fun heightMapToObjectMap(fs: IntArray) {
println("[mapgenerator] Shaping world as processed...") printdbg(this, "Shaping world as processed...")
// iterate for heightmap // iterate for heightmap
for (x in 0..WIDTH - 1) { for (x in 0..WIDTH - 1) {
@@ -602,7 +602,7 @@ object WorldGenerator {
} }
private fun fillMapByNoiseMap() { private fun fillMapByNoiseMap() {
println("[mapgenerator] Shaping world...") printdbg(this, "Shaping world...")
LoadScreen.addMessage("Reticulating splines...") // RETICULATING SPLINES LoadScreen.addMessage("Reticulating splines...") // RETICULATING SPLINES
// generate dirt-stone transition line // generate dirt-stone transition line
// use catmull spline // use catmull spline
@@ -686,7 +686,7 @@ object WorldGenerator {
filter: NoiseFilter = NoiseFilterQuadratic, filter: NoiseFilter = NoiseFilterQuadratic,
filterStart: Double = NOISE_GRAD_START, filterStart: Double = NOISE_GRAD_START,
filterEnd: Double = NOISE_GRAD_END) { filterEnd: Double = NOISE_GRAD_END) {
println("[mapgenerator] " + message) printdbg(this, "" + message)
for (y in 0..HEIGHT - 1) { for (y in 0..HEIGHT - 1) {
for (x in 0..WIDTH - 1) { for (x in 0..WIDTH - 1) {
@@ -716,7 +716,7 @@ object WorldGenerator {
filter: NoiseFilter = NoiseFilterQuadratic, filter: NoiseFilter = NoiseFilterQuadratic,
filterStart: Double = NOISE_GRAD_START, filterStart: Double = NOISE_GRAD_START,
filterEnd: Double = NOISE_GRAD_END) { filterEnd: Double = NOISE_GRAD_END) {
println("[mapgenerator] " + message) printdbg(this, "" + message)
for (y in 0..HEIGHT - 1) { for (y in 0..HEIGHT - 1) {
for (x in 0..WIDTH - 1) { for (x in 0..WIDTH - 1) {
@@ -747,7 +747,7 @@ object WorldGenerator {
filter: NoiseFilter = NoiseFilterQuadratic, filter: NoiseFilter = NoiseFilterQuadratic,
filterStart: Double = NOISE_GRAD_START, filterStart: Double = NOISE_GRAD_START,
filterEnd: Double = NOISE_GRAD_END) { filterEnd: Double = NOISE_GRAD_END) {
println("[mapgenerator] " + message) printdbg(this, "" + message)
for (y in 0..HEIGHT - 1) { for (y in 0..HEIGHT - 1) {
for (x in 0..WIDTH - 1) { for (x in 0..WIDTH - 1) {
@@ -797,7 +797,7 @@ object WorldGenerator {
private val islandSpacing = 1024 private val islandSpacing = 1024
private fun generateFloatingIslands() { private fun generateFloatingIslands() {
println("[mapgenerator] Placing floating islands...") printdbg(this, "Placing floating islands...")
LoadScreen.addMessage("Placing floating islands...") LoadScreen.addMessage("Placing floating islands...")
val nIslandsMax = Math.round(world.width * 6f / 8192f) val nIslandsMax = Math.round(world.width * 6f / 8192f)
@@ -830,7 +830,7 @@ object WorldGenerator {
/* Flood */ /* Flood */
private fun floodBottomLava() { private fun floodBottomLava() {
/*println("[mapgenerator] Flooding with lava...") /*printdbg(this, "Flooding with lava...")
LoadScreen.addMessage("Flooding with lava...") LoadScreen.addMessage("Flooding with lava...")
for (i in HEIGHT * 14 / 15..HEIGHT - 1) { for (i in HEIGHT * 14 / 15..HEIGHT - 1) {
for (j in 0..WIDTH - 1) { for (j in 0..WIDTH - 1) {
@@ -844,7 +844,7 @@ object WorldGenerator {
/* Plant */ /* Plant */
private fun plantGrass() { private fun plantGrass() {
println("[mapgenerator] Planting grass...") printdbg(this, "Planting grass...")
LoadScreen.addMessage("Planting grass...") LoadScreen.addMessage("Planting grass...")
/* TODO composing dirt and stone /* TODO composing dirt and stone
@@ -910,7 +910,7 @@ object WorldGenerator {
"yellow" "yellow"
else else
"white" "white"
println("[mapgenerator] Beach sand type: $thisSandStr") printdbg(this, "Beach sand type: $thisSandStr")
var ix = 0 var ix = 0
while (ix < OCEAN_WIDTH * 1.5) { while (ix < OCEAN_WIDTH * 1.5) {

View File

@@ -1,10 +1,5 @@
package net.torvald.terrarum.serialise 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 * 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") @Deprecated("TEMD is deprecated format; use TEMz which does compression")
internal object ReadLayerData { 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 magicBytes = ByteArray(4)
val layerSizeBytes = ByteArray(1) val layerSizeBytes = ByteArray(1)
val layerCountBytes = ByteArray(1) val layerCountBytes = ByteArray(1)
@@ -109,7 +107,7 @@ internal object ReadLayerData {
} }
return i return i
} }*/
} }
fun Int.toLittle() = byteArrayOf( fun Int.toLittle() = byteArrayOf(

View File

@@ -3,9 +3,8 @@ package net.torvald.terrarum.serialise
import com.badlogic.gdx.utils.compression.Lzma import com.badlogic.gdx.utils.compression.Lzma
import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.BlockAddress
import net.torvald.terrarum.gameworld.BlockLayer
import net.torvald.terrarum.gameworld.FluidType 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.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import java.io.* import java.io.*
@@ -143,14 +142,7 @@ internal object ReadLayerDataLzma {
val inflatedFile = inflatedOS.toByteArray() val inflatedFile = inflatedOS.toByteArray()
// deal with (MSB ++ LSB) payloadBytes[t] = inflatedFile
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
}
} }
val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress) val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress)
@@ -184,13 +176,10 @@ internal object ReadLayerDataLzma {
// TODO parse fluid(Types|Fills) // TODO parse fluid(Types|Fills)
return ReadLayerDataZip.LayerData( return ReadLayerDataZip.LayerData(
MapLayer(width, height, payloadBytes["WALL_MSB"]!!), BlockLayer(width, height, payloadBytes["WALL"]!!),
MapLayer(width, height, payloadBytes["TERR_MSB"]!!), BlockLayer(width, height, payloadBytes["TERR"]!!),
MapLayer(width, height, payloadBytes["WIRE"]!!),
PairedMapLayer(width, height, payloadBytes["WALL_LSB"]!!),
PairedMapLayer(width, height, payloadBytes["TERR_LSB"]!!),
spawnPoint.first, spawnPoint.second, 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.AppLoader.printdbg
import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.BlockAddress
import net.torvald.terrarum.gameworld.BlockLayer
import net.torvald.terrarum.gameworld.FluidType 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.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import java.io.File import java.io.File
@@ -144,14 +143,7 @@ internal object ReadLayerDataZip {
if (uncompLen.toLong() != u.uncompressedSize) if (uncompLen.toLong() != u.uncompressedSize)
throw InternalError("Payload $t DEFLATE size mismatch -- expected ${u.uncompressedSize}, got $uncompLen") throw InternalError("Payload $t DEFLATE size mismatch -- expected ${u.uncompressedSize}, got $uncompLen")
// deal with (MSB ++ LSB) payloadBytes[t] = inflatedFile
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
}
} }
val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress) val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress)
@@ -187,11 +179,8 @@ internal object ReadLayerDataZip {
return LayerData( return LayerData(
MapLayer(width, height, payloadBytes["WALL_MSB"]!!), BlockLayer(width, height, payloadBytes["WALL"]!!),
MapLayer(width, height, payloadBytes["TERR_MSB"]!!), BlockLayer(width, height, payloadBytes["TERR"]!!),
MapLayer(width, height, payloadBytes["WIRE"]!!),
PairedMapLayer(width, height, payloadBytes["WALL_LSB"]!!),
PairedMapLayer(width, height, payloadBytes["TERR_LSB"]!!),
spawnPoint.first, spawnPoint.second, spawnPoint.first, spawnPoint.second,
@@ -203,16 +192,15 @@ internal object ReadLayerDataZip {
* Immediately deployable, a part of the gameworld * Immediately deployable, a part of the gameworld
*/ */
internal data class LayerData( internal data class LayerData(
val layerWall: MapLayer, val layerWall: BlockLayer,
val layerTerrain: MapLayer, val layerTerrain: BlockLayer,
val layerWire: MapLayer,
val layerWallLowBits: PairedMapLayer,
val layerTerrainLowBits: PairedMapLayer,
//val layerThermal: MapLayerHalfFloat, // in Kelvins //val layerThermal: MapLayerHalfFloat, // in Kelvins
//val layerAirPressure: MapLayerHalfFloat, // (milibar - 1000) //val layerAirPressure: MapLayerHalfFloat, // (milibar - 1000)
val spawnX: Int, val spawnX: Int,
val spawnY: Int, val spawnY: Int,
//val wirings: HashMap<BlockAddress, SortedArrayList<GameWorld.WiringNode>>,
val wallDamages: HashMap<BlockAddress, Float>, val wallDamages: HashMap<BlockAddress, Float>,
val terrainDamages: HashMap<BlockAddress, Float>, val terrainDamages: HashMap<BlockAddress, Float>,
val fluidTypes: HashMap<BlockAddress, FluidType>, val fluidTypes: HashMap<BlockAddress, FluidType>,

View File

@@ -1,27 +1,21 @@
package net.torvald.terrarum.serialise 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 * TODO this one does not use TerranVirtualDisk
* *
* Created by minjaesong on 2016-03-18. * Created by minjaesong on 2016-03-18.
*/ */
// internal for everything: prevent malicious module from messing up the savedata // 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 { internal object WriteLayerData {
init {
throw Error("TEMD is old and removed format; use TEMz which does compression")
}
val LAYERS_FILENAME = "worldinfo1" 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 val BYTE_NULL: Byte = 0
@@ -83,7 +77,7 @@ internal object WriteLayerData {
} }
return false return false
} }*/
} }

View File

@@ -101,15 +101,23 @@ internal object WriteLayerDataLzma {
wb(PAYLOAD_HEADER); wb("TERR".toByteArray()) wb(PAYLOAD_HEADER); wb("TERR".toByteArray())
wi48(world.width * world.height * 3L / 2) wi48(world.width * world.height * 3L / 2)
Lzma.compress(ByteArrayInputStream(world.terrainArray), outputStream) world.layerTerrain.bytesIterator().forEach {
Lzma.compress(ByteArrayInputStream(world.layerTerrainLowBits.data), outputStream) val tempByteArray = ByteArray(1)
tempByteArray[0] = it
val tempByteArrayStream = ByteArrayInputStream(tempByteArray)
Lzma.compress(tempByteArrayStream, outputStream)
}
wb(PAYLOAD_FOOTER) wb(PAYLOAD_FOOTER)
// WALL payload // WALL payload
wb(PAYLOAD_HEADER); wb("WALL".toByteArray()) wb(PAYLOAD_HEADER); wb("WALL".toByteArray())
wi48(world.width * world.height * 3L / 2) wi48(world.width * world.height * 3L / 2)
Lzma.compress(ByteArrayInputStream(world.wallArray), outputStream) world.layerWall.bytesIterator().forEach {
Lzma.compress(ByteArrayInputStream(world.layerWallLowBits.data), outputStream) val tempByteArray = ByteArray(1)
tempByteArray[0] = it
val tempByteArrayStream = ByteArrayInputStream(tempByteArray)
Lzma.compress(tempByteArrayStream, outputStream)
}
wb(PAYLOAD_FOOTER) wb(PAYLOAD_FOOTER)
// WIRE payload // WIRE payload

View File

@@ -113,8 +113,7 @@ internal object WriteLayerDataZip {
wb(PAYLOAD_HEADER); wb("TERR".toByteArray()) wb(PAYLOAD_HEADER); wb("TERR".toByteArray())
wi48(world.width * world.height * 3L / 2) wi48(world.width * world.height * 3L / 2)
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false) deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
deflater.write(world.terrainArray) world.layerTerrain.bytesIterator().forEach { deflater.write(it.toInt()) }
deflater.write(world.layerTerrainLowBits.data)
deflater.flush(); deflater.finish() deflater.flush(); deflater.finish()
wb(PAYLOAD_FOOTER) wb(PAYLOAD_FOOTER)
@@ -122,8 +121,7 @@ internal object WriteLayerDataZip {
wb(PAYLOAD_HEADER); wb("WALL".toByteArray()) wb(PAYLOAD_HEADER); wb("WALL".toByteArray())
wi48(world.width * world.height * 3L / 2) wi48(world.width * world.height * 3L / 2)
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false) deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
deflater.write(world.wallArray) world.layerWall.bytesIterator().forEach { deflater.write(it.toInt()) }
deflater.write(world.layerWallLowBits.data)
deflater.flush(); deflater.finish() deflater.flush(); deflater.finish()
wb(PAYLOAD_FOOTER) wb(PAYLOAD_FOOTER)

View File

@@ -4,13 +4,11 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.math.Matrix4 import com.badlogic.gdx.math.Matrix4
import com.jme3.math.FastMath import com.jme3.math.FastMath
import com.badlogic.gdx.graphics.Color
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.PairedMapLayer
import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator
@@ -59,7 +57,6 @@ internal object BlocksDrawer {
val wallOverlayColour = Color(5f / 9f, 5f / 9f, 5f / 9f, 1f) val wallOverlayColour = Color(5f / 9f, 5f / 9f, 5f / 9f, 1f)
const val BREAKAGE_STEPS = 10 const val BREAKAGE_STEPS = 10
const val TILES_PER_BLOCK = PairedMapLayer.RANGE
val WALL = GameWorld.WALL val WALL = GameWorld.WALL
val TERRAIN = GameWorld.TERRAIN val TERRAIN = GameWorld.TERRAIN
@@ -257,7 +254,7 @@ internal object BlocksDrawer {
val fluidNum = this.type.abs() val fluidNum = this.type.abs()
if (this.amount >= WorldSimulator.FLUID_MIN_MASS) { 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 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] # "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) Payload "TERR" -- world terrain data in Uint16
Uncompressed size will be 1.5x of (width * height) 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) Payload "WALL" -- world walls data in Unit16
Uncompressed size will be 1.5x of (width * height) Uncompressed size will be 2x of (width * height)
Payload "WIRE" -- world wires data
Uncompressed size will be as same as (width * height)
Payload "TdMG" -- world terrain damage data, array of: (Int48 tileAddress, Float32 damage) Payload "TdMG" -- world terrain damage data, array of: (Int48 tileAddress, Float32 damage)
Uncompressed size will be arbitrary (multiple of tens) 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) Payload "FlFL" -- world fluid fills, array of: (Int48 tileAddress, Float32 amount)
Uncompressed size will be arbitrary (multiple of tens) Uncompressed size will be arbitrary (multiple of tens)
If the 'amount' < 0.0001f (WorldSimulator.FLUID_MIN_MASS), the entry must be discarded 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) Payload "CtYP" -- conduit types, array of: (Int48 tileAddress, Uint32 bitarray)
can hold 32 different wires simultaneously can hold 32 different wires simultaneously
Payload "CfL0" -- conduit fills, aka size of liquid/gas packet, array of: (Int48 tileAddress, Float32 fill) 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. CfL0..CfL9, CfLa..CfLf are available to store values for 16 different things.