Generalised BlockLayer

This commit is contained in:
minjaesong
2023-10-10 03:56:02 +09:00
parent 8f1ca485f6
commit e76ff58b3e
12 changed files with 330 additions and 148 deletions

View File

@@ -423,3 +423,4 @@ fun ItemID.isVirtual() = this.startsWith("$PREFIX_VIRTUALTILE@")
fun ItemID.isBlock() = !this.contains('@') && !this.isDynamic() fun ItemID.isBlock() = !this.contains('@') && !this.isDynamic()
fun ItemID.isWall() = this.startsWith("wall@") fun ItemID.isWall() = this.startsWith("wall@")
fun ItemID.isFluid() = this.startsWith("fluid@") fun ItemID.isFluid() = this.startsWith("fluid@")
fun ItemID.isOre() = this.startsWith("ores@")

View File

@@ -1,110 +1,14 @@
package net.torvald.terrarum.gameworld package net.torvald.terrarum.gameworld
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.serialise.toUint
import net.torvald.unsafe.UnsafeHelper
import net.torvald.unsafe.UnsafePtr
/** /**
* Memory layout: * Created by minjaesong on 2023-10-10.
* ```
* a7 a6 a5 a4 a3 a2 a1 a0 | aF aE aD aC aB aA a9 a8 ||
* ```
* where a_n is a tile number
*
* Original version Created by minjaesong on 2016-01-17.
* Unsafe version Created by minjaesong on 2019-06-08.
*
* Note to self: refrain from using shorts--just do away with two bytes: different system have different endianness
*/ */
open class BlockLayer(val width: Int, val height: Int) : Disposable { interface BlockLayer : Disposable {
// for some reason, all the efforts of saving the memory space were futile.
// using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90 val bytesPerBlock: Long
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * BYTES_PER_BLOCK) fun unsafeToBytes(x: Int, y: Int): ByteArray
fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray)
val ptrDestroyed: Boolean
get() = ptr.destroyed
init {
ptr.fillWith(0)
}
/**
* @param data Byte array representation of the layer
*/
constructor(width: Int, height: Int, data: ByteArray) : this(width, height) {
TODO()
data.forEachIndexed { index, byte -> UnsafeHelper.unsafe.putByte(ptr.ptr + index, byte) }
}
/**
* Returns an iterator over stored bytes.
*
* @return an Iterator.
*/
fun bytesIterator(): Iterator<Byte> {
return object : Iterator<Byte> {
private var iteratorCount = 0L
override fun hasNext(): Boolean {
return iteratorCount < width * height * BYTES_PER_BLOCK
}
override fun next(): Byte {
iteratorCount += 1
return ptr[iteratorCount - 1]
}
}
}
internal fun unsafeGetTile(x: Int, y: Int): Int {
val offset = BYTES_PER_BLOCK * (y * width + x)
val lsb = ptr[offset]
val msb = ptr[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()
// try {
ptr[offset] = lsb
ptr[offset + 1] = msb
// }
// catch (e: IndexOutOfBoundsException) {
// printdbgerr(this, "IndexOutOfBoundsException: x = $x, y = $y; offset = $offset")
// throw e
// }
}
/**
* @param blockOffset Offset in blocks. BlockOffset of 0x100 is equal to ```layerPtr + 0x200```
*/
/*internal fun unsafeSetTile(blockOffset: Long, tile: Int) {
val offset = BYTES_PER_BLOCK * blockOffset
val lsb = tile.and(0xff).toByte()
val msb = tile.ushr(8).and(0xff).toByte()
unsafe.putByte(layerPtr + offset, lsb)
unsafe.putByte(layerPtr + offset + 1, msb)
}*/
fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height)
override fun dispose() {
ptr.destroy()
printdbg(this, "BlockLayer with ptr ($ptr) successfully freed")
}
override fun toString(): String = ptr.toString()
companion object {
@Transient val BYTES_PER_BLOCK = 2L
}
} }

View File

@@ -0,0 +1,126 @@
package net.torvald.terrarum.gameworld
import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.serialise.toUint
import net.torvald.unsafe.UnsafeHelper
import net.torvald.unsafe.UnsafePtr
/**
* Memory layout:
* ```
* a7 a6 a5 a4 a3 a2 a1 a0 | aF aE aD aC aB aA a9 a8 ||
* ```
* where a_n is a tile number
*
* Original version Created by minjaesong on 2016-01-17.
* Unsafe version Created by minjaesong on 2019-06-08.
*
* Note to self: refrain from using shorts--just do away with two bytes: different system have different endianness
*/
open class BlockLayerI16(val width: Int, val height: Int) : BlockLayer {
override val bytesPerBlock = BYTES_PER_BLOCK
// for some reason, all the efforts of saving the memory space were futile.
// using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * BYTES_PER_BLOCK)
val ptrDestroyed: Boolean
get() = ptr.destroyed
init {
ptr.fillWith(0)
}
/**
* @param data Byte array representation of the layer
*/
constructor(width: Int, height: Int, data: ByteArray) : this(width, height) {
TODO()
data.forEachIndexed { index, byte -> UnsafeHelper.unsafe.putByte(ptr.ptr + index, byte) }
}
/**
* Returns an iterator over stored bytes.
*
* @return an Iterator.
*/
fun bytesIterator(): Iterator<Byte> {
return object : Iterator<Byte> {
private var iteratorCount = 0L
override fun hasNext(): Boolean {
return iteratorCount < width * height * BYTES_PER_BLOCK
}
override fun next(): Byte {
iteratorCount += 1
return ptr[iteratorCount - 1]
}
}
}
internal fun unsafeGetTile(x: Int, y: Int): Int {
val offset = BYTES_PER_BLOCK * (y * width + x)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
return lsb.toUint() + msb.toUint().shl(8)
}
override fun unsafeToBytes(x: Int, y: Int): ByteArray {
val offset = BYTES_PER_BLOCK * (y * width + x)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
return byteArrayOf(msb, lsb)
}
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()
// try {
ptr[offset] = lsb
ptr[offset + 1] = msb
// }
// catch (e: IndexOutOfBoundsException) {
// printdbgerr(this, "IndexOutOfBoundsException: x = $x, y = $y; offset = $offset")
// throw e
// }
}
override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) {
val offset = BYTES_PER_BLOCK * (y * width + x)
ptr[offset] = bytes[1]
ptr[offset + 1] = bytes[0]
}
/**
* @param blockOffset Offset in blocks. BlockOffset of 0x100 is equal to ```layerPtr + 0x200```
*/
/*internal fun unsafeSetTile(blockOffset: Long, tile: Int) {
val offset = BYTES_PER_BLOCK * blockOffset
val lsb = tile.and(0xff).toByte()
val msb = tile.ushr(8).and(0xff).toByte()
unsafe.putByte(layerPtr + offset, lsb)
unsafe.putByte(layerPtr + offset + 1, msb)
}*/
fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height)
override fun dispose() {
ptr.destroy()
printdbg(this, "BlockLayerI16 with ptr ($ptr) successfully freed")
}
override fun toString(): String = ptr.toString("BlockLayerI16")
companion object {
@Transient val BYTES_PER_BLOCK = 2L
}
}

View File

@@ -0,0 +1,126 @@
package net.torvald.terrarum.gameworld
import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarum.App
import net.torvald.terrarum.serialise.toUint
import net.torvald.unsafe.UnsafeHelper
import net.torvald.unsafe.UnsafePtr
/**
* Memory layout:
* ```
* a7 a6 a5 a4 a3 a2 a1 a0 | aF aE aD aC aB aA a9 a8 | p7 p6 p5 p4 p3 p2 p1 p0 ||
* ```
* where a_n is a tile number, p_n is a placement index
* Created by minjaesong on 2023-10-10.
*/
class BlockLayerI16I8 (val width: Int, val height: Int) : BlockLayer {
override val bytesPerBlock = BYTES_PER_BLOCK
// for some reason, all the efforts of saving the memory space were futile.
// using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * BYTES_PER_BLOCK)
val ptrDestroyed: Boolean
get() = ptr.destroyed
init {
ptr.fillWith(0)
}
/**
* @param data Byte array representation of the layer
*/
constructor(width: Int, height: Int, data: ByteArray) : this(width, height) {
TODO()
data.forEachIndexed { index, byte -> UnsafeHelper.unsafe.putByte(ptr.ptr + index, byte) }
}
/**
* Returns an iterator over stored bytes.
*
* @return an Iterator.
*/
fun bytesIterator(): Iterator<Byte> {
return object : Iterator<Byte> {
private var iteratorCount = 0L
override fun hasNext(): Boolean {
return iteratorCount < width * height * BYTES_PER_BLOCK
}
override fun next(): Byte {
iteratorCount += 1
return ptr[iteratorCount - 1]
}
}
}
internal fun unsafeGetTile(x: Int, y: Int): Pair<Int, Int> {
val offset = BYTES_PER_BLOCK * (y * width + x)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
val placement = ptr[offset + 2]
return lsb.toUint() + msb.toUint().shl(8) to placement.toUint()
}
override fun unsafeToBytes(x: Int, y: Int): ByteArray {
val offset = BYTES_PER_BLOCK * (y * width + x)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
val placement = ptr[offset + 2]
return byteArrayOf(msb, lsb, placement)
}
internal fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) {
val offset = BYTES_PER_BLOCK * (y * width + x)
val lsb = tile.and(0xff).toByte()
val msb = tile.ushr(8).and(0xff).toByte()
// try {
ptr[offset] = lsb
ptr[offset + 1] = msb
ptr[offset + 2] = placement.toByte()
// }
// catch (e: IndexOutOfBoundsException) {
// printdbgerr(this, "IndexOutOfBoundsException: x = $x, y = $y; offset = $offset")
// throw e
// }
}
override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) {
val offset = BYTES_PER_BLOCK * (y * width + x)
ptr[offset] = bytes[1]
ptr[offset + 1] = bytes[0]
ptr[offset + 2] = bytes[2]
}
/**
* @param blockOffset Offset in blocks. BlockOffset of 0x100 is equal to ```layerPtr + 0x200```
*/
/*internal fun unsafeSetTile(blockOffset: Long, tile: Int) {
val offset = BYTES_PER_BLOCK * blockOffset
val lsb = tile.and(0xff).toByte()
val msb = tile.ushr(8).and(0xff).toByte()
unsafe.putByte(layerPtr + offset, lsb)
unsafe.putByte(layerPtr + offset + 1, msb)
}*/
fun isInBound(x: Int, y: Int) = (x >= 0 && y >= 0 && x < width && y < height)
override fun dispose() {
ptr.destroy()
App.printdbg(this, "BlockLayerI16I8 with ptr ($ptr) successfully freed")
}
override fun toString(): String = ptr.toString("BlockLayerI16I8")
companion object {
@Transient val BYTES_PER_BLOCK = 3L
}
}

View File

@@ -7,22 +7,20 @@ import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.Fluid import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.concurrent.ThreadExecutor
import net.torvald.terrarum.gameactors.ActorID import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.gameitems.isFluid import net.torvald.terrarum.gameitems.isFluid
import net.torvald.terrarum.gameitems.isOre
import net.torvald.terrarum.itemproperties.ItemRemapTable import net.torvald.terrarum.itemproperties.ItemRemapTable
import net.torvald.terrarum.itemproperties.ItemTable import net.torvald.terrarum.itemproperties.ItemTable
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.utils.* import net.torvald.terrarum.utils.*
import net.torvald.terrarum.weather.WeatherMixer import net.torvald.terrarum.weather.WeatherMixer
import net.torvald.terrarum.weather.WeatherSchedule
import net.torvald.terrarum.weather.Weatherbox import net.torvald.terrarum.weather.Weatherbox
import net.torvald.util.SortedArrayList import net.torvald.util.SortedArrayList
import org.dyn4j.geometry.Vector2 import org.dyn4j.geometry.Vector2
import java.util.* import java.util.*
import kotlin.math.absoluteValue
typealias BlockAddress = Long typealias BlockAddress = Long
@@ -40,8 +38,8 @@ class PhysicalStatus() {
* Special version of GameWorld where everything, including layer data, are saved in a single JSON file (i.e. not chunked) * Special version of GameWorld where everything, including layer data, are saved in a single JSON file (i.e. not chunked)
*/ */
class SimpleGameWorld(width: Int, height: Int) : GameWorld(width, height) { class SimpleGameWorld(width: Int, height: Int) : GameWorld(width, height) {
override lateinit var layerWall: BlockLayer override lateinit var layerWall: BlockLayerI16
override lateinit var layerTerrain: BlockLayer override lateinit var layerTerrain: BlockLayerI16
constructor() : this(0, 0) constructor() : this(0, 0)
} }
@@ -83,12 +81,12 @@ open class GameWorld(
} }
//layers //layers
@Transient lateinit open var layerWall: BlockLayer @Transient lateinit open var layerWall: BlockLayerI16
@Transient lateinit open var layerTerrain: BlockLayer @Transient lateinit open var layerTerrain: BlockLayerI16
val layerOres = HashedOres() // damage to the block follows `terrainDamages` @Transient lateinit open var layerOres: BlockLayerI16I8 // damage to the block follows `terrainDamages`
val wallDamages = HashArray<Float>() val wallDamages = HashArray<Float>()
val terrainDamages = HashArray<Float>() val terrainDamages = HashArray<Float>()
val layerFluids = HashedFluidTypeAndFills() val layerFluids = HashedFluidTypeAndFills() // TODO: chunk them using BlockLayerI32
@@ -201,8 +199,8 @@ open class GameWorld(
this.spawnX = width / 2 this.spawnX = width / 2
this.spawnY = 150 this.spawnY = 150
layerTerrain = BlockLayer(width, height) layerTerrain = BlockLayerI16(width, height)
layerWall = BlockLayer(width, height) layerWall = BlockLayerI16(width, height)
// temperature layer: 2x2 is one cell // temperature layer: 2x2 is one cell
//layerThermal = MapLayerHalfFloat(width, height, averageTemperature) //layerThermal = MapLayerHalfFloat(width, height, averageTemperature)
@@ -277,6 +275,7 @@ open class GameWorld(
fun getLayer(index: Int) = when(index) { fun getLayer(index: Int) = when(index) {
0 -> layerTerrain 0 -> layerTerrain
1 -> layerWall 1 -> layerWall
2 -> layerOres
else -> null//throw IllegalArgumentException("Unknown layer index: $index") else -> null//throw IllegalArgumentException("Unknown layer index: $index")
} }
@@ -549,6 +548,21 @@ open class GameWorld(
throw IllegalArgumentException("illegal mode input: " + mode.toString()) throw IllegalArgumentException("illegal mode input: " + mode.toString())
} }
fun getTileFromOre(rawX: Int, rawY: Int): OrePlacement? {
val (x, y) = coerceXY(rawX, rawY)
val (tileNum, placement) = layerOres.unsafeGetTile(x, y)
val tileName = tileNumberToNameMap[tileNum.toLong()]
if (tileName == Block.AIR)
return null
else
return OrePlacement(tileName ?: Block.UPDATE, placement)
}
fun setTileOre(rawX: Int, rawY: Int, ore: ItemID, placement: Int) {
val (x, y) = coerceXY(rawX, rawY)
layerOres.unsafeSetTile(x, y, tileNameToNumberMap[ore]!!, placement)
}
fun terrainIterator(): Iterator<ItemID> { fun terrainIterator(): Iterator<ItemID> {
return object : Iterator<ItemID> { return object : Iterator<ItemID> {

View File

@@ -3,6 +3,8 @@ package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.App import net.torvald.terrarum.App
import net.torvald.terrarum.BlockCodex import net.torvald.terrarum.BlockCodex
import net.torvald.terrarum.WireCodex import net.torvald.terrarum.WireCodex
import net.torvald.terrarum.gameitems.isBlock
import net.torvald.terrarum.gameitems.isWall
/** /**
* Created by minjaesong on 2016-02-03. * Created by minjaesong on 2016-02-03.
@@ -74,19 +76,22 @@ object PlayerBuilderSigrid {
fun fillTestInventory(inventory: ActorInventory) { fun fillTestInventory(inventory: ActorInventory) {
App.tileMaker.tags.forEach { (t, _) -> App.tileMaker.tags.forEach { (t, _) ->
val prop = BlockCodex[t] if (t.isBlock() || t.isWall()) {
if (!prop.isActorBlock && !prop.hasTag("AIR") && !prop.hasTag("INTERNAL")) { val prop = BlockCodex[t]
if (!prop.isActorBlock && !prop.hasTag("AIR") && !prop.hasTag("INTERNAL")) {
inventory.add(t, 9995) inventory.add(t, 9995)
try { try {
inventory.add( inventory.add(
"wall@$t", "wall@$t",
9995 9995
) // this code will try to add nonexisting wall items, do not get surprised with NPEs ) // this code will try to add nonexisting wall items, do not get surprised with NPEs
} }
catch (e: NullPointerException) { /* tHiS iS fInE */ } catch (e: NullPointerException) { /* tHiS iS fInE */
catch (e: Throwable) { }
System.err.println("[PlayerBuilder] $e") catch (e: Throwable) {
System.err.println("[PlayerBuilder] $e")
}
} }
} }
} }

View File

@@ -4,19 +4,17 @@ import com.badlogic.gdx.graphics.Pixmap
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.console.Echo import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.gameworld.BlockLayer import net.torvald.terrarum.gameworld.BlockLayerI16
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.ChunkLoadingLoadScreen import net.torvald.terrarum.modulebasegame.ChunkLoadingLoadScreen
import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.SavegameMigrator
import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.savegame.* import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarum.worlddrawer.WorldCamera import net.torvald.terrarum.worlddrawer.WorldCamera
import java.io.File import java.io.File
import java.io.Reader import java.io.Reader
@@ -145,8 +143,8 @@ object LoadSavegame {
val worldDiskSavegameInfo = ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET) val worldDiskSavegameInfo = ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)
val world = ReadWorld(worldDiskSavegameInfo, worldDisk.diskFile) val world = ReadWorld(worldDiskSavegameInfo, worldDisk.diskFile)
world.layerTerrain = BlockLayer(world.width, world.height) world.layerTerrain = BlockLayerI16(world.width, world.height)
world.layerWall = BlockLayer(world.width, world.height) world.layerWall = BlockLayerI16(world.width, world.height)
newIngame.world = world // must be set before the loadscreen, otherwise the loadscreen will try to read from the NullWorld which is already destroyed newIngame.world = world // must be set before the loadscreen, otherwise the loadscreen will try to read from the NullWorld which is already destroyed
newIngame.worldDisk = VDUtil.readDiskArchive(worldDisk.diskFile, Level.INFO) newIngame.worldDisk = VDUtil.readDiskArchive(worldDisk.diskFile, Level.INFO)

View File

@@ -4,6 +4,8 @@ import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.NoSerialise import net.torvald.terrarum.gameactors.NoSerialise
import net.torvald.terrarum.gameworld.BlockLayer import net.torvald.terrarum.gameworld.BlockLayer
import net.torvald.terrarum.gameworld.BlockLayerI16
import net.torvald.terrarum.gameworld.BlockLayerI16I8
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
@@ -82,9 +84,7 @@ object WriteWorld {
val ba = ByteArray64() val ba = ByteArray64()
for (y in cy * LandUtil.CHUNK_H until (cy + 1) * LandUtil.CHUNK_H) { for (y in cy * LandUtil.CHUNK_H until (cy + 1) * LandUtil.CHUNK_H) {
for (x in cx * LandUtil.CHUNK_W until (cx + 1) * LandUtil.CHUNK_W) { for (x in cx * LandUtil.CHUNK_W until (cx + 1) * LandUtil.CHUNK_W) {
val tilenum = layer.unsafeGetTile(x, y) ba.appendBytes(layer.unsafeToBytes(x, y))
ba.appendByte(tilenum.ushr(8).and(255).toByte())
ba.appendByte(tilenum.and(255).toByte())
} }
} }
@@ -116,17 +116,19 @@ object ReadWorld {
fun decodeChunkToLayer(chunk: ByteArray64, targetLayer: BlockLayer, cx: Int, cy: Int) { fun decodeChunkToLayer(chunk: ByteArray64, targetLayer: BlockLayer, cx: Int, cy: Int) {
val bytes = Common.unzip(chunk) val bytes = Common.unzip(chunk)
if (bytes.size != cw * ch * 2L) if (bytes.size != cw * ch * targetLayer.bytesPerBlock)
throw UnsupportedOperationException("Chunk size mismatch: decoded chunk size is ${bytes.size} bytes " + throw UnsupportedOperationException("Chunk size mismatch: decoded chunk size is ${bytes.size} bytes " +
"where ${LandUtil.CHUNK_W * LandUtil.CHUNK_H * 2L} bytes (Int16 of ${LandUtil.CHUNK_W}x${LandUtil.CHUNK_H}) were expected") "where ${LandUtil.CHUNK_W * LandUtil.CHUNK_H * targetLayer.bytesPerBlock} bytes (Int${8 * targetLayer.bytesPerBlock} of ${LandUtil.CHUNK_W}x${LandUtil.CHUNK_H}) were expected")
for (k in 0 until cw * ch) { for (k in 0 until cw * ch) {
val tilenum = bytes[2L*k].toUint().shl(8) or bytes[2L*k + 1].toUint()
val offx = k % cw val offx = k % cw
val offy = k / cw val offy = k / cw
val ba = ByteArray(targetLayer.bytesPerBlock.toInt()) {
bytes[targetLayer.bytesPerBlock * k + it]
}
// try { // try {
targetLayer.unsafeSetTile(cx * cw + offx, cy * ch + offy, tilenum) targetLayer.unsafeSetTile(cx * cw + offx, cy * ch + offy, ba)
// } // }
// catch (e: IndexOutOfBoundsException) { // catch (e: IndexOutOfBoundsException) {
// printdbgerr(this, "IndexOutOfBoundsException, cx = $cx, cy = $cy, k = $k, offx = $offx, offy = $offy") // printdbgerr(this, "IndexOutOfBoundsException, cx = $cx, cy = $cy, k = $k, offx = $offx, offy = $offy")

View File

@@ -111,6 +111,12 @@ class Terragen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
world.setTileTerrain(x, y, newTile, true) world.setTileTerrain(x, y, newTile, true)
world.setTileWall(x, y, newTile, true) world.setTileWall(x, y, newTile, true)
// TODO TEST CODE
if (newTile == Block.DIRT) {
world.setTileOre(x, y, "ores@basegame:1", 0)
}
} }
/* /*

View File

@@ -4,10 +4,9 @@ import com.badlogic.gdx.utils.Json
import com.badlogic.gdx.utils.JsonValue import com.badlogic.gdx.utils.JsonValue
import com.badlogic.gdx.utils.JsonWriter import com.badlogic.gdx.utils.JsonWriter
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration import net.torvald.terrarum.TerrarumAppConfiguration
import net.torvald.terrarum.console.EchoError import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.gameworld.BlockLayer import net.torvald.terrarum.gameworld.BlockLayerI16
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.WorldTime import net.torvald.terrarum.gameworld.WorldTime
import net.torvald.terrarum.savegame.ByteArray64 import net.torvald.terrarum.savegame.ByteArray64
@@ -42,7 +41,7 @@ object Common {
val CHARSET = Charsets.UTF_8 val CHARSET = Charsets.UTF_8
/** dispose of the `offendingObject` after rejection! */ /** dispose of the `offendingObject` after rejection! */
class BlockLayerHashMismatchError(val oldHash: String, val newHash: String, val offendingObject: BlockLayer) : Error("Old Hash $oldHash != New Hash $newHash") class BlockLayerHashMismatchError(val oldHash: String, val newHash: String, val offendingObject: BlockLayerI16) : Error("Old Hash $oldHash != New Hash $newHash")
private fun Byte.tostr() = this.toInt().and(255).toString(16).padStart(2,'0') private fun Byte.tostr() = this.toInt().and(255).toString(16).padStart(2,'0')
private val digester = DigestUtils.getSha256Digest() private val digester = DigestUtils.getSha256Digest()
@@ -76,8 +75,8 @@ object Common {
} }
}) })
// BlockLayer // BlockLayer
jsoner.setSerializer(BlockLayer::class.java, object : Json.Serializer<BlockLayer> { jsoner.setSerializer(BlockLayerI16::class.java, object : Json.Serializer<BlockLayerI16> {
override fun write(json: Json, obj: BlockLayer, knownType: Class<*>?) { override fun write(json: Json, obj: BlockLayerI16, knownType: Class<*>?) {
digester.reset() digester.reset()
obj.bytesIterator().forEachRemaining { digester.update(it) } obj.bytesIterator().forEachRemaining { digester.update(it) }
val hash = StringBuilder().let { sb -> digester.digest().forEach { sb.append(it.tostr()) }; sb.toString() } val hash = StringBuilder().let { sb -> digester.digest().forEach { sb.append(it.tostr()) }; sb.toString() }
@@ -87,7 +86,7 @@ object Common {
json.writeValue(layer) json.writeValue(layer)
} }
override fun read(json: Json, jsonData: JsonValue, type: Class<*>): BlockLayer { override fun read(json: Json, jsonData: JsonValue, type: Class<*>): BlockLayerI16 {
// full manual // full manual
try { try {
return strToBlockLayer(LayerInfo( return strToBlockLayer(LayerInfo(
@@ -437,12 +436,12 @@ object Common {
* @param b a BlockLayer * @param b a BlockLayer
* @return Bytes in [b] which are GZip'd then Ascii85-encoded * @return Bytes in [b] which are GZip'd then Ascii85-encoded
*/ */
private fun blockLayerToStr(b: BlockLayer): String { private fun blockLayerToStr(b: BlockLayerI16): String {
return bytesToZipdStr(b.bytesIterator()) return bytesToZipdStr(b.bytesIterator())
} }
private fun strToBlockLayer(layerInfo: LayerInfo): BlockLayer { private fun strToBlockLayer(layerInfo: LayerInfo): BlockLayerI16 {
val layer = BlockLayer(layerInfo.x, layerInfo.y) val layer = BlockLayerI16(layerInfo.x, layerInfo.y)
val unzipdBytes = strToBytes(StringReader(layerInfo.b)) val unzipdBytes = strToBytes(StringReader(layerInfo.b))
// write to blocklayer and the digester // write to blocklayer and the digester

View File

@@ -162,7 +162,7 @@ class CreateTileAtlas {
if (dirName == "blocks") { if (dirName == "blocks") {
// filter files that do not exist on the blockcodex // filter files that do not exist on the blockcodex
dir.list() dir.list()
.filter { tgaFile -> !tgaFile.isDirectory && (BlockCodex.getOrNull("$modname:${tgaFile.nameWithoutExtension()}") != null) } .filter { tgaFile -> tgaFile.extension() == "tga" && !tgaFile.isDirectory && (BlockCodex.getOrNull("$modname:${tgaFile.nameWithoutExtension()}") != null) }
.sortedBy { it.nameWithoutExtension().toInt() } .sortedBy { it.nameWithoutExtension().toInt() }
.forEach { tgaFile: FileHandle -> // toInt() to sort by the number, not lexicographically .forEach { tgaFile: FileHandle -> // toInt() to sort by the number, not lexicographically
// tgaFile be like: ./assets/mods/basegame/blocks/32.tga (which is not always .tga) // tgaFile be like: ./assets/mods/basegame/blocks/32.tga (which is not always .tga)
@@ -173,7 +173,7 @@ class CreateTileAtlas {
} }
else { else {
// TODO test // TODO test
dir.list().filter { tgaFile -> !tgaFile.isDirectory }.sortedBy { it.nameWithoutExtension().toInt() }.forEach { tgaFile: FileHandle -> dir.list().filter { tgaFile -> tgaFile.extension() == "tga" && !tgaFile.isDirectory }.sortedBy { it.nameWithoutExtension().toInt() }.forEach { tgaFile: FileHandle ->
val newFile = ModMgr.GameRetextureLoader.altFilePaths.getOrDefault(tgaFile.path(), tgaFile) val newFile = ModMgr.GameRetextureLoader.altFilePaths.getOrDefault(tgaFile.path(), tgaFile)
tgaList[dirName]!!.add(modname to newFile) tgaList[dirName]!!.add(modname to newFile)
} }

View File

@@ -198,6 +198,7 @@ internal class UnsafePtr(pointer: Long, allocSize: Long) {
} }
override fun toString() = "0x${ptr.toString(16)} with size $size" override fun toString() = "0x${ptr.toString(16)} with size $size"
fun toString(prefix: String) = "$prefix 0x${ptr.toString(16)} with size $size"
override fun equals(other: Any?) = this.ptr == (other as UnsafePtr).ptr && this.size == other.size override fun equals(other: Any?) = this.ptr == (other as UnsafePtr).ptr && this.size == other.size
} }