mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-12 14:51:51 +09:00
worldinfo writer
This commit is contained in:
@@ -10,8 +10,8 @@ import java.util.Random
|
|||||||
*/
|
*/
|
||||||
class HQRNG @JvmOverloads constructor(seed: Long = System.nanoTime()) : Random() {
|
class HQRNG @JvmOverloads constructor(seed: Long = System.nanoTime()) : Random() {
|
||||||
|
|
||||||
private var s0: Long
|
var s0: Long; private set
|
||||||
private var s1: Long
|
var s1: Long; private set
|
||||||
|
|
||||||
constructor(s0: Long, s1: Long) : this() {
|
constructor(s0: Long, s1: Long) : this() {
|
||||||
this.s0 = s0
|
this.s0 = s0
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ object ModMgr {
|
|||||||
}
|
}
|
||||||
const val modDir = "./assets/mods"
|
const val modDir = "./assets/mods"
|
||||||
|
|
||||||
|
/** Module name (directory name), ModuleMetadata */
|
||||||
val moduleInfo = HashMap<String, ModuleMetadata>()
|
val moduleInfo = HashMap<String, ModuleMetadata>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -156,6 +157,7 @@ object ModMgr {
|
|||||||
checkExistence(module)
|
checkExistence(module)
|
||||||
return "$modDir/$module/${path.sanitisePath()}"
|
return "$modDir/$module/${path.sanitisePath()}"
|
||||||
}
|
}
|
||||||
|
/** Returning files are read-only */
|
||||||
fun getGdxFile(module: String, path: String): FileHandle {
|
fun getGdxFile(module: String, path: String): FileHandle {
|
||||||
return Gdx.files.internal(getPath(module, path))
|
return Gdx.files.internal(getPath(module, path))
|
||||||
}
|
}
|
||||||
@@ -174,6 +176,40 @@ object ModMgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get a common file from all the installed mods. Files are guaranteed to exist. If a mod does not
|
||||||
|
* contain the file, the mod will be skipped. */
|
||||||
|
fun getFilesFromEveryMod(path: String): List<File> {
|
||||||
|
val path = path.sanitisePath()
|
||||||
|
val moduleNames = moduleInfo.keys.toList()
|
||||||
|
|
||||||
|
val filesList = ArrayList<File>()
|
||||||
|
moduleNames.forEach {
|
||||||
|
val file = File(getPath(it, path))
|
||||||
|
|
||||||
|
if (file.exists()) filesList.add(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesList.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a common file from all the installed mods. Files are guaranteed to exist. If a mod does not
|
||||||
|
* contain the file, the mod will be skipped.
|
||||||
|
*
|
||||||
|
* Returning files are read-only. */
|
||||||
|
fun getGdxFilesFromEveryMod(path: String): List<FileHandle> {
|
||||||
|
val path = path.sanitisePath()
|
||||||
|
val moduleNames = moduleInfo.keys.toList()
|
||||||
|
|
||||||
|
val filesList = ArrayList<FileHandle>()
|
||||||
|
moduleNames.forEach {
|
||||||
|
val file = Gdx.files.internal(getPath(it, path))
|
||||||
|
|
||||||
|
if (file.exists()) filesList.add(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesList.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
object GameBlockLoader {
|
object GameBlockLoader {
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ object BlockCodex {
|
|||||||
blockProps = Array<BlockProp>(TILE_UNIQUE_MAX * 2, { BlockProp() })
|
blockProps = Array<BlockProp>(TILE_UNIQUE_MAX * 2, { BlockProp() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Later entry (possible from other modules) will replace older ones
|
||||||
|
*/
|
||||||
operator fun invoke(module: String, path: String) {
|
operator fun invoke(module: String, path: String) {
|
||||||
try {
|
try {
|
||||||
val records = CSVFetcher.readFromModule(module, path)
|
val records = CSVFetcher.readFromModule(module, path)
|
||||||
|
|||||||
@@ -12,10 +12,17 @@ typealias BlockDamage = Float
|
|||||||
|
|
||||||
open class GameWorld {
|
open class GameWorld {
|
||||||
|
|
||||||
|
var worldName: String = "New World"
|
||||||
var worldIndex: Int
|
var worldIndex: Int
|
||||||
val width: Int
|
val width: Int
|
||||||
val height: Int
|
val height: Int
|
||||||
|
|
||||||
|
val creationTime: Long
|
||||||
|
var lastPlayTime: Long
|
||||||
|
internal set // there's a case of save-and-continue-playing
|
||||||
|
|
||||||
|
/** Used to calculate play time */
|
||||||
|
val loadTime: Long = System.currentTimeMillis() / 1000L
|
||||||
|
|
||||||
//layers
|
//layers
|
||||||
val layerWall: MapLayer
|
val layerWall: MapLayer
|
||||||
@@ -49,7 +56,7 @@ open class GameWorld {
|
|||||||
internal set
|
internal set
|
||||||
|
|
||||||
|
|
||||||
constructor(worldIndex: Int, width: Int, height: Int) {
|
constructor(worldIndex: Int, width: Int, height: Int, creationTIME_T: Long, lastPlayTIME_T: Long = creationTIME_T) {
|
||||||
this.worldIndex = worldIndex
|
this.worldIndex = worldIndex
|
||||||
this.width = width
|
this.width = width
|
||||||
this.height = height
|
this.height = height
|
||||||
@@ -71,9 +78,13 @@ open class GameWorld {
|
|||||||
|
|
||||||
// air pressure layer: 4 * 8 is one cell
|
// air pressure layer: 4 * 8 is one cell
|
||||||
//layerAirPressure = MapLayerHalfFloat(width / 4, height / 8, 13f) // 1013 mBar
|
//layerAirPressure = MapLayerHalfFloat(width / 4, height / 8, 13f) // 1013 mBar
|
||||||
|
|
||||||
|
|
||||||
|
creationTime = creationTIME_T
|
||||||
|
lastPlayTime = lastPlayTIME_T
|
||||||
}
|
}
|
||||||
|
|
||||||
internal constructor(worldIndex: Int, layerData: ReadLayerDataLzma.LayerData) {
|
internal constructor(worldIndex: Int, layerData: ReadLayerDataLzma.LayerData, creationTIME_T: Long, lastPlayTIME_T: Long = creationTIME_T) {
|
||||||
this.worldIndex = worldIndex
|
this.worldIndex = worldIndex
|
||||||
|
|
||||||
layerTerrain = layerData.layerTerrain
|
layerTerrain = layerData.layerTerrain
|
||||||
@@ -90,6 +101,10 @@ open class GameWorld {
|
|||||||
|
|
||||||
width = layerTerrain.width
|
width = layerTerrain.width
|
||||||
height = layerTerrain.height
|
height = layerTerrain.height
|
||||||
|
|
||||||
|
|
||||||
|
creationTime = creationTIME_T
|
||||||
|
lastPlayTime = lastPlayTIME_T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
126
src/net/torvald/terrarum/serialise/WriteWorldInfo.kt
Normal file
126
src/net/torvald/terrarum/serialise/WriteWorldInfo.kt
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package net.torvald.terrarum.serialise
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx
|
||||||
|
import net.torvald.terrarum.ModMgr
|
||||||
|
import net.torvald.terrarum.Terrarum
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.PlayerBuilder
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
|
||||||
|
import net.torvald.terrarum.modulebasegame.weather.WeatherMixer
|
||||||
|
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
|
object WriteWorldInfo {
|
||||||
|
|
||||||
|
// FIXME UNTESTED
|
||||||
|
|
||||||
|
val META_MAGIC = "TESV".toByteArray(Charsets.UTF_8)
|
||||||
|
val NULL = 0.toByte()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO currently it'll dump the temporary file (tmp_worldinfo1) onto the disk and will return the temp file.
|
||||||
|
*
|
||||||
|
* @return File on success; `null` on failure
|
||||||
|
*/
|
||||||
|
internal operator fun invoke(): List<File>? {
|
||||||
|
val world = (Terrarum.ingame!!.world)
|
||||||
|
|
||||||
|
val path = "${Terrarum.defaultSaveDir}/tmp_worldinfo"
|
||||||
|
|
||||||
|
val infileList = arrayOf(
|
||||||
|
ModMgr.getGdxFilesFromEveryMod("blocks/blocks.csv"),
|
||||||
|
ModMgr.getGdxFilesFromEveryMod("items/items.csv"),
|
||||||
|
ModMgr.getGdxFilesFromEveryMod("materials/materials.csv")
|
||||||
|
)
|
||||||
|
|
||||||
|
val metaFile = File(path + "0")
|
||||||
|
|
||||||
|
val outFiles = ArrayList<File>()
|
||||||
|
outFiles.add(metaFile)
|
||||||
|
val worldInfoHash = ArrayList<ByteArray>() // hash of worldinfo1-3
|
||||||
|
// try to write worldinfo1-3
|
||||||
|
|
||||||
|
for (filenum in 1..3) {
|
||||||
|
val outFile = File(path + filenum.toString())
|
||||||
|
if (outFile.exists()) outFile.delete()
|
||||||
|
outFile.createNewFile()
|
||||||
|
|
||||||
|
val outputStream = BufferedOutputStream(FileOutputStream(outFile), 256)
|
||||||
|
val infile = infileList[filenum - 1]
|
||||||
|
|
||||||
|
infile.forEach {
|
||||||
|
val readBytes = it.readBytes()
|
||||||
|
outputStream.write(readBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.flush()
|
||||||
|
outputStream.close()
|
||||||
|
|
||||||
|
|
||||||
|
outFiles.add(outFile)
|
||||||
|
|
||||||
|
|
||||||
|
worldInfoHash.add(DigestUtils.sha256(FileInputStream(outFile)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// compose save meta
|
||||||
|
val metaOut = BufferedOutputStream(FileOutputStream(metaFile), 256)
|
||||||
|
|
||||||
|
|
||||||
|
metaOut.write(META_MAGIC)
|
||||||
|
|
||||||
|
// world name
|
||||||
|
val worldNameBytes = world.worldName.toByteArray(Charsets.UTF_8)
|
||||||
|
metaOut.write(worldNameBytes)
|
||||||
|
if (worldNameBytes.last() != NULL) metaOut.write(NULL.toInt())
|
||||||
|
|
||||||
|
// terrain seed
|
||||||
|
metaOut.write(world.generatorSeed.toLittle())
|
||||||
|
|
||||||
|
// randomiser seed
|
||||||
|
metaOut.write(RoguelikeRandomiser.RNG.s0.toLittle())
|
||||||
|
metaOut.write(RoguelikeRandomiser.RNG.s1.toLittle())
|
||||||
|
|
||||||
|
// weather seed
|
||||||
|
metaOut.write(WeatherMixer.RNG.s0.toLittle())
|
||||||
|
metaOut.write(WeatherMixer.RNG.s1.toLittle())
|
||||||
|
|
||||||
|
// SHA256SUM of worldinfo1-3
|
||||||
|
worldInfoHash.forEach {
|
||||||
|
metaOut.write(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reference ID of the player
|
||||||
|
metaOut.write(Terrarum.PLAYER_REF_ID.toLittle())
|
||||||
|
|
||||||
|
// time_t
|
||||||
|
metaOut.write((world as GameWorldExtension).time.TIME_T.toLittle())
|
||||||
|
|
||||||
|
// creation time (real world time)
|
||||||
|
metaOut.write(world.creationTime.toLittle48())
|
||||||
|
|
||||||
|
// time at save
|
||||||
|
val timeNow = System.currentTimeMillis() / 1000L
|
||||||
|
metaOut.write(timeNow.toLittle48())
|
||||||
|
|
||||||
|
// get playtime and save it
|
||||||
|
val timeToAdd = timeNow - world.loadTime
|
||||||
|
metaOut.write(world.lastPlayTime.plus(timeToAdd).toInt().toLittle())
|
||||||
|
world.lastPlayTime = timeNow
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
metaOut.flush()
|
||||||
|
metaOut.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return outFiles.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ Ord Hex Description
|
|||||||
02 4D S
|
02 4D S
|
||||||
03 44 V
|
03 44 V
|
||||||
|
|
||||||
04 Name of the world in UTF-8 (arbitrary length)
|
04 Name of the world in UTF-8 (arbitrary length, must not contain NULL)
|
||||||
... 00 String terminator
|
... 00 String terminator
|
||||||
|
|
||||||
... Terrain seed (8 bytes)
|
... Terrain seed (8 bytes)
|
||||||
@@ -21,7 +21,6 @@ Ord Hex Description
|
|||||||
... SHA-256 hash of worldinfo1 (32 bytes)
|
... SHA-256 hash of worldinfo1 (32 bytes)
|
||||||
... SHA-256 hash of worldinfo2 (32 bytes)
|
... SHA-256 hash of worldinfo2 (32 bytes)
|
||||||
... SHA-256 hash of worldinfo3 (32 bytes)
|
... SHA-256 hash of worldinfo3 (32 bytes)
|
||||||
... SHA-256 hash of worldinfo4 (32 bytes)
|
|
||||||
|
|
||||||
... ReferenceID of the player (4 bytes, a fixed value of 91A7E2)
|
... ReferenceID of the player (4 bytes, a fixed value of 91A7E2)
|
||||||
... Current world's time_t (the ingame time, 8 bytes)
|
... Current world's time_t (the ingame time, 8 bytes)
|
||||||
|
|||||||
Reference in New Issue
Block a user