From 0e93f6609b82679f31d8f23ce886527c06926708 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 27 Oct 2018 00:03:06 +0900 Subject: [PATCH] new RNG for everything; Joise update --- .../mods/basegame/weathers/generic_skybox.tga | 4 +- lib/Terrarum_Joise.jar | Bin 313013 -> 313692 bytes src/net/torvald/random/HQRNG.java | 192 ++++++++++++++++++ src/net/torvald/random/HQRNG.kt | 189 ++++++++++++++--- .../terrarum/modulebasegame/RNGConsumer.kt | 2 +- .../gameactors/ActorHumanoid.kt | 2 +- .../modulebasegame/gameworld/WorldTime.kt | 15 +- .../terrarum/serialise/ReadWorldInfo.kt | 21 +- .../terrarum/serialise/WriteWorldInfo.kt | 33 +-- work_files/DataFormats/Savegame metadata.txt | 14 +- 10 files changed, 408 insertions(+), 64 deletions(-) create mode 100644 src/net/torvald/random/HQRNG.java diff --git a/assets/mods/basegame/weathers/generic_skybox.tga b/assets/mods/basegame/weathers/generic_skybox.tga index 9b35a1f34..680799220 100644 --- a/assets/mods/basegame/weathers/generic_skybox.tga +++ b/assets/mods/basegame/weathers/generic_skybox.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24c9e5e2eb919f33e326c2f22319d0c769614ab38b01bc9455a4647b667211fa -size 3212 +oid sha256:a89e79942fb0a4b2243215d156b2d44019fa8e731745cd19cfbea30091a43be6 +size 3500 diff --git a/lib/Terrarum_Joise.jar b/lib/Terrarum_Joise.jar index be920faae5acf607290301ef56f14f2616d181f8..f0ed0d0d810d7c6c5d7421827802549ae514194a 100644 GIT binary patch delta 20623 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 Z>~J8oAI3-AiP4F1s0?OEB&ZVO{{Z#r5NiMc delta 20219 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 diff --git a/src/net/torvald/random/HQRNG.java b/src/net/torvald/random/HQRNG.java new file mode 100644 index 000000000..5d8065fc1 --- /dev/null +++ b/src/net/torvald/random/HQRNG.java @@ -0,0 +1,192 @@ +package net.torvald.random; + +import java.util.Random; + +/** + * Xoroshift128 + * + * see https://github.com/SquidPony/SquidLib/blob/master/squidlib-util/src/main/java/squidpony/squidmath/XoRoRNG.java + */ +public class HQRNG extends Random { + + private static final long DOUBLE_MASK = (1L << 53) - 1; + private static final double NORM_53 = 1. / (1L << 53); + private static final long FLOAT_MASK = (1L << 24) - 1; + private static final double NORM_24 = 1. / (1L << 24); + + private static final long serialVersionUID = 1018744536171610262L; + + private long state0, state1; + + public long getState0() { + return state0; + } + + public long getState1() { + return state1; + } + + /** + * Creates a new generator seeded using four calls to Math.random(). + */ + public HQRNG() { + this((long) ((Math.random() - 0.5) * 0x10000000000000L) + ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L), + (long) ((Math.random() - 0.5) * 0x10000000000000L) + ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L)); + } + /** + * Constructs this XoRoRNG by dispersing the bits of seed using {@link #setSeed(long)} across the two parts of state + * this has. + * @param seed a long that won't be used exactly, but will affect both components of state + */ + public HQRNG(final long seed) { + setSeed(seed); + } + /** + * Constructs this XoRoRNG by calling {@link #setSeed(long, long)} on the arguments as given; see that method for + * the specific details (stateA and stateB are kept as-is unless they are both 0). + * @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0 + * @param stateB the number to use as the second part of the state + */ + public HQRNG(final long stateA, final long stateB) { + setSeed(stateA, stateB); + } + + @Override + public final int next(int bits) { + final long s0 = state0; + long s1 = state1; + final int result = (int)(s0 + s1) >>> (32 - bits); + s1 ^= s0; + state0 = (s0 << 55 | s0 >>> 9) ^ s1 ^ (s1 << 14); // a, b + state1 = (s1 << 36 | s1 >>> 28); // c + return result; + } + + @Override + public final long nextLong() { + final long s0 = state0; + long s1 = state1; + final long result = s0 + s1; + + s1 ^= s0; + state0 = (s0 << 55 | s0 >>> 9) ^ s1 ^ (s1 << 14); // a, b + state1 = (s1 << 36 | s1 >>> 28); // c + /* + state0 = Long.rotateLeft(s0, 55) ^ s1 ^ (s1 << 14); // a, b + state1 = Long.rotateLeft(s1, 36); // c + */ + return result; + } + + /** + * Can return any int, positive or negative, of any size permissible in a 32-bit signed integer. + * @return any int, all 32 bits are random + */ + public int nextInt() { + return (int)nextLong(); + } + + /** + * Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive + * result. + * @param bound the outer exclusive bound; may be positive or negative + * @return a random int between 0 (inclusive) and bound (exclusive) + */ + public int nextInt(final int bound) { + return (int) ((bound * (nextLong() >>> 33)) >> 31); + } + /** + * Inclusive lower, exclusive upper. + * @param inner the inner bound, inclusive, can be positive or negative + * @param outer the outer bound, exclusive, should be positive, should usually be greater than inner + * @return a random int that may be equal to inner and will otherwise be between inner and outer + */ + public int nextInt(final int inner, final int outer) { + return inner + nextInt(outer - inner); + } + + /** + * Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive + * result. + * @param bound the outer exclusive bound; may be positive or negative + * @return a random long between 0 (inclusive) and bound (exclusive) + */ + public long nextLong(long bound) { + long rand = nextLong(); + final long randLow = rand & 0xFFFFFFFFL; + final long boundLow = bound & 0xFFFFFFFFL; + rand >>>= 32; + bound >>= 32; + final long z = (randLow * boundLow >> 32); + long t = rand * boundLow + z; + final long tLow = t & 0xFFFFFFFFL; + t >>>= 32; + return rand * bound + t + (tLow + randLow * bound >> 32) - (z >> 63) - (bound >> 63); + } + /** + * Inclusive inner, exclusive outer; both inner and outer can be positive or negative. + * @param inner the inner bound, inclusive, can be positive or negative + * @param outer the outer bound, exclusive, can be positive or negative and may be greater than or less than inner + * @return a random long that may be equal to inner and will otherwise be between inner and outer + */ + public long nextLong(final long inner, final long outer) { + return inner + nextLong(outer - inner); + } + + public double nextDouble() { + return (nextLong() & DOUBLE_MASK) * NORM_53; + } + + public float nextFloat() { + return (float) ((nextLong() & FLOAT_MASK) * NORM_24); + } + + public boolean nextBoolean() { + return nextLong() < 0L; + } + + public void nextBytes(final byte[] bytes) { + int i = bytes.length, n = 0; + while (i != 0) { + n = Math.min(i, 8); + for (long bits = nextLong(); n-- != 0; bits >>>= 8) { + bytes[--i] = (byte) bits; + } + } + } + + /** + * Sets the seed of this generator using one long, running that through LightRNG's algorithm twice to get the state. + * @param seed the number to use as the seed + */ + public void setSeed(final long seed) { + + long state = seed + 0x9E3779B97F4A7C15L, + z = state; + z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; + z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; + state0 = z ^ (z >>> 31); + state += 0x9E3779B97F4A7C15L; + z = state; + z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; + z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; + state1 = z ^ (z >>> 31); + } + + /** + * Sets the seed of this generator using two longs, using them without changes unless both are 0 (then it makes the + * state variable corresponding to stateA 1 instead). + * @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0 + * @param stateB the number to use as the second part of the state + */ + public void setSeed(final long stateA, final long stateB) { + + state0 = stateA; + state1 = stateB; + if((stateA | stateB) == 0L) + state0 = 1L; + } + +} diff --git a/src/net/torvald/random/HQRNG.kt b/src/net/torvald/random/HQRNG.kt index a43398af3..d0f17572c 100644 --- a/src/net/torvald/random/HQRNG.kt +++ b/src/net/torvald/random/HQRNG.kt @@ -1,4 +1,4 @@ -package net.torvald.random +/*package net.torvald.random import net.torvald.terrarum.serialise.toLittle import net.torvald.terrarum.serialise.toLittleLong @@ -6,41 +6,178 @@ import org.apache.commons.codec.digest.DigestUtils import java.util.Random /** - * Xorshift128+ + * Xoroshift128 + * + * @see https://github.com/SquidPony/SquidLib/blob/master/squidlib-util/src/main/java/squidpony/squidmath/XoRoRNG.java */ -class HQRNG @JvmOverloads constructor(seed: Long = System.nanoTime()) : Random() { +class HQRNG() : Random() { - var s0: Long; private set - var s1: Long; private set + private val DOUBLE_MASK = (1L shl 53) - 1 + private val NORM_53 = 1.0 / (1L shl 53) + private val FLOAT_MASK = (1L shl 24) - 1 + private val NORM_24 = 1.0 / (1L shl 24) - constructor(s0: Long, s1: Long) : this() { - this.s0 = s0 - this.s1 = s1 + var state0: Long = 0L; private set + var state1: Long = 0L; private set + + /** + * Creates a new generator seeded using four calls to Math.random(). + */ + init { + reseed(((Math.random() - 0.5) * 0x10000000000000L).toLong() xor ((Math.random() - 0.5) * 2.0 * -0x8000000000000000L).toLong(), + ((Math.random() - 0.5) * 0x10000000000000L).toLong() xor ((Math.random() - 0.5) * 2.0 * -0x8000000000000000L).toLong()) } - init { - if (seed == 0L) - throw IllegalArgumentException("Invalid seed: cannot be zero") + /** + * Constructs this XoRoRNG by dispersing the bits of seed using [.setSeed] across the two parts of state + * this has. + * @param seed a long that won't be used exactly, but will affect both components of state + */ + constructor(seed: Long): this() { + setSeed(seed) + } - val hash = DigestUtils.sha256(seed.toString()) + /** + * Constructs this XoRoRNG by calling [.setSeed] on the arguments as given; see that method for + * the specific details (stateA and stateB are kept as-is unless they are both 0). + * @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0 + * @param stateB the number to use as the second part of the state + */ + constructor(stateA: Long, stateB: Long): this() { + reseed(stateA, stateB) + } - s0 = hash.copyOfRange(0, 8).toLittleLong() - s1 = hash.copyOfRange(8, 16).toLittleLong() + public override fun next(bits: Int): Int { + val s0 = state0 + var s1 = state1 + val result = (s0 + s1).toInt().ushr(32 - bits) + s1 = s1 xor s0 + state0 = s0 shl 55 or s0.ushr(9) xor s1 xor (s1 shl 14) // a, b + state1 = s1 shl 36 or s1.ushr(28) // c + return result } override fun nextLong(): Long { - var x = s0 - val y = s1 - s0 = y - x = x xor (x shl 23) - s1 = x xor y xor (x ushr 17) xor (y ushr 26) - return s1 + y + val s0 = state0 + var s1 = state1 + val result = s0 + s1 + + s1 = s1 xor s0 + state0 = s0 shl 55 or s0.ushr(9) xor s1 xor (s1 shl 14) // a, b + state1 = s1 shl 36 or s1.ushr(28) // c + /* + state0 = Long.rotateLeft(s0, 55) ^ s1 ^ (s1 << 14); // a, b + state1 = Long.rotateLeft(s1, 36); // c + */ + return result } - fun serialize() = s0.toLittle() + s1.toLittle() - - fun reseed(s0: Long, s1: Long) { - this.s0 = s0 - this.s1 = s1 + /** + * Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive + * result. + * @param bound the outer exclusive bound; may be positive or negative + * @return a random int between 0 (inclusive) and bound (exclusive) + */ + override fun nextInt(bound: Int): Int { + return (bound * nextLong().ushr(33) shr 31).toInt() } -} \ No newline at end of file + + /** + * Inclusive lower, exclusive upper. + * @param inner the inner bound, inclusive, can be positive or negative + * @param outer the outer bound, exclusive, should be positive, should usually be greater than inner + * @return a random int that may be equal to inner and will otherwise be between inner and outer + */ + fun nextInt(inner: Int, outer: Int): Int { + return inner + nextInt(outer - inner) + } + + /** + * Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive + * result. + * @param bound the outer exclusive bound; may be positive or negative + * @return a random long between 0 (inclusive) and bound (exclusive) + */ + fun nextLong(bound: Long): Long { + var bound = bound + var rand = nextLong() + val randLow = rand and 0xFFFFFFFFL + val boundLow = bound and 0xFFFFFFFFL + rand = rand ushr 32 + bound = bound shr 32 + val z = randLow * boundLow shr 32 + var t = rand * boundLow + z + val tLow = t and 0xFFFFFFFFL + t = t ushr 32 + return rand * bound + t + (tLow + randLow * bound shr 32) - (z shr 63) - (bound shr 63) + } + + /** + * Inclusive inner, exclusive outer; both inner and outer can be positive or negative. + * @param inner the inner bound, inclusive, can be positive or negative + * @param outer the outer bound, exclusive, can be positive or negative and may be greater than or less than inner + * @return a random long that may be equal to inner and will otherwise be between inner and outer + */ + fun nextLong(inner: Long, outer: Long): Long { + return inner + nextLong(outer - inner) + } + + override fun nextDouble(): Double { + return (nextLong() and DOUBLE_MASK) * NORM_53 + } + + override fun nextFloat(): Float { + return ((nextLong() and FLOAT_MASK) * NORM_24).toFloat() + } + + override fun nextBoolean(): Boolean { + return nextLong() < 0L + } + + override fun nextBytes(bytes: ByteArray) { + var i = bytes.size + var n = 0 + while (i != 0) { + n = Math.min(i, 8) + var bits = nextLong() + while (n-- != 0) { + bytes[--i] = bits.toByte() + bits = bits ushr 8 + } + } + } + + + fun serialize() = state0.toLittle() + state1.toLittle() + + /** + * Sets the seed of this generator using one long, running that through LightRNG's algorithm twice to get the state. + * @param seed the number to use as the seed + */ + override fun setSeed(seed: Long) { + var state = seed + -0x61c8864680b583ebL + var z = state + z = (z xor z.ushr(30)) * -0x40a7b892e31b1a47L + z = (z xor z.ushr(27)) * -0x6b2fb644ecceee15L + state0 = z xor z.ushr(31) + state += -0x61c8864680b583ebL + z = state + z = (z xor z.ushr(30)) * -0x40a7b892e31b1a47L + z = (z xor z.ushr(27)) * -0x6b2fb644ecceee15L + state1 = z xor z.ushr(31) + } + + /** + * Sets the seed of this generator using two longs, using them without changes unless both are 0 (then it makes the + * state variable corresponding to stateA 1 instead). + * @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0 + * @param stateB the number to use as the second part of the state + */ + fun reseed(stateA: Long, stateB: Long) { + + state0 = stateA + state1 = stateB + if (stateA or stateB == 0L) + state0 = 1L + } +}*/ \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/RNGConsumer.kt b/src/net/torvald/terrarum/modulebasegame/RNGConsumer.kt index 1c6f17dff..262470c9c 100644 --- a/src/net/torvald/terrarum/modulebasegame/RNGConsumer.kt +++ b/src/net/torvald/terrarum/modulebasegame/RNGConsumer.kt @@ -8,7 +8,7 @@ internal interface RNGConsumer { val RNG: HQRNG fun loadFromSave(s0: Long, s1: Long) { - RNG.reseed(s0, s1) + RNG.setSeed(s0, s1) } } diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/ActorHumanoid.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/ActorHumanoid.kt index 6a6dffd35..d12b8cb96 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/ActorHumanoid.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/ActorHumanoid.kt @@ -491,7 +491,7 @@ open class ActorHumanoid( private var oldJUMPPOWERBUFF = -1.0 // init private var oldScale = -1.0 private var oldDragCoefficient = -1.0 - val jumpAirTime: Double = -1.0 + var jumpAirTime: Double = -1.0 get() { // compare all the affecting variables if (oldMAX_JUMP_LENGTH == MAX_JUMP_LENGTH && diff --git a/src/net/torvald/terrarum/modulebasegame/gameworld/WorldTime.kt b/src/net/torvald/terrarum/modulebasegame/gameworld/WorldTime.kt index 2b27ebac7..1cfa2b1bb 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameworld/WorldTime.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameworld/WorldTime.kt @@ -13,9 +13,10 @@ typealias time_t = Long * https://en.wikipedia.org/wiki/World_Calendar * http://dwarffortresswiki.org/index.php/DF2014:Calendar * - * And there is no AM/PM concept, 22-hour clock is forced; no leap years. - * (AM 12 is still 00h in this system, again, to reduce confusion) + * And there is no AM/PM concept, 24-hour clock is forced; no leap years. + * An ingame day should last 22 real-life minutes. * + * // TODO 4-month year? like Stardew Valley * * Calendar * @@ -110,13 +111,13 @@ class WorldTime(initTime: Long = 0L) { "Mala", "Gale", "Lime", "Sand", "Timb", "Moon") companion object { - /** Each day is 22-hour long */ - val DAY_LENGTH = 79200 //must be the multiple of 3600 + /** Each day is displayed as 24 hours, but in real-life clock it's 22 mins long */ + val DAY_LENGTH = 86400 //must be the multiple of 3600 val HOUR_SEC: Int = 3600 val MINUTE_SEC: Int = 60 val HOUR_MIN: Int = 60 - val GAME_MIN_TO_REAL_SEC: Float = 60f + val GAME_MIN_TO_REAL_SEC: Float = 720f/11f val HOURS_PER_DAY = DAY_LENGTH / HOUR_SEC val YEAR_DAYS: Int = 365 @@ -164,8 +165,8 @@ class WorldTime(initTime: Long = 0L) { val dayName: String get() = DAY_NAMES[dayOfWeek] - inline fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt() - inline fun Long.abs() = Math.abs(this) + fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt() + fun Long.abs() = Math.abs(this) /** Format: "%A, %d %B %Y %X" */ fun getFormattedTime() = "${getDayNameShort()}, " + diff --git a/src/net/torvald/terrarum/serialise/ReadWorldInfo.kt b/src/net/torvald/terrarum/serialise/ReadWorldInfo.kt index 55a705edb..87945d43e 100644 --- a/src/net/torvald/terrarum/serialise/ReadWorldInfo.kt +++ b/src/net/torvald/terrarum/serialise/ReadWorldInfo.kt @@ -22,6 +22,11 @@ object ReadWorldInfo { throw IllegalArgumentException("File not a Save Meta") } + + val descVersion = fis.read(1) // 0-127 + val numberOfHashes = fis.read() // 0-127 + + var byteRead = fis.read() while (byteRead != 0) { if (byteRead == -1) @@ -38,14 +43,14 @@ object ReadWorldInfo { fis.read(8).toLittleLong(), // rng s1 fis.read(8).toLittleLong(), // weather s0 fis.read(8).toLittleLong(), // weather s1 - fis.read(32), - fis.read(32), - fis.read(32), fis.read(4).toLittleInt(), // player id fis.read(8).toLittleLong(), // world TIME_T fis.read(6).toLittleLong(), // creation time fis.read(6).toLittleLong(), // last play time - fis.read(4).toLittleInt() // total time wasted + fis.read(4).toLittleInt(), // total time wasted + fis.read(32), // sha256sum worldinfo1 + fis.read(32), // sha256sum worldinfo2 + fis.read(32) // sha256sum worldinfo3 ) } @@ -57,13 +62,13 @@ object ReadWorldInfo { val rngS1: Long, val weatherS0: Long, val weatherS1: Long, - val worldinfo1Hash: ByteArray, - val worldInfo2Hash: ByteArray, - val worldInfo3Hash: ByteArray, val playerID: Int, val timeNow: Long, val creationTime: Long, val lastPlayTime: Long, - val totalPlayTime: Int + val totalPlayTime: Int, + val worldinfo1Hash: ByteArray, + val worldInfo2Hash: ByteArray, + val worldInfo3Hash: ByteArray ) } \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/WriteWorldInfo.kt b/src/net/torvald/terrarum/serialise/WriteWorldInfo.kt index f6a2fb115..18ce61b76 100644 --- a/src/net/torvald/terrarum/serialise/WriteWorldInfo.kt +++ b/src/net/torvald/terrarum/serialise/WriteWorldInfo.kt @@ -18,6 +18,9 @@ object WriteWorldInfo { val META_MAGIC = "TESV".toByteArray(Charsets.UTF_8) val NULL = 0.toByte() + val VERSION = 1 + val HASHED_FILES_COUNT = 3 + /** * TODO currently it'll dump the temporary file (tmp_worldinfo1) onto the disk and will return the temp file. * @@ -38,10 +41,11 @@ object WriteWorldInfo { val outFiles = ArrayList() outFiles.add(metaFile) + val worldInfoHash = ArrayList() // hash of worldinfo1-3 // try to write worldinfo1-3 - for (filenum in 1..3) { + for (filenum in 1..HASHED_FILES_COUNT) { val outFile = File(path + filenum.toString()) if (outFile.exists()) outFile.delete() outFile.createNewFile() @@ -65,11 +69,13 @@ object WriteWorldInfo { } - // compose save meta + // compose save meta (actual writing part) val metaOut = BufferedOutputStream(FileOutputStream(metaFile), 256) metaOut.write(META_MAGIC) + metaOut.write(VERSION) + metaOut.write(HASHED_FILES_COUNT) // world name val worldNameBytes = world.worldName.toByteArray(Charsets.UTF_8) @@ -80,28 +86,23 @@ object WriteWorldInfo { metaOut.write(world.generatorSeed.toLittle()) // randomiser seed - metaOut.write(RoguelikeRandomiser.RNG.s0.toLittle()) - metaOut.write(RoguelikeRandomiser.RNG.s1.toLittle()) + metaOut.write(RoguelikeRandomiser.RNG.state0.toLittle()) + metaOut.write(RoguelikeRandomiser.RNG.state1.toLittle()) // weather seed - metaOut.write(WeatherMixer.RNG.s0.toLittle()) - metaOut.write(WeatherMixer.RNG.s1.toLittle()) - - // SHA256SUM of worldinfo1-3 - worldInfoHash.forEach { - metaOut.write(it) - } + metaOut.write(WeatherMixer.RNG.state0.toLittle()) + metaOut.write(WeatherMixer.RNG.state1.toLittle()) // reference ID of the player metaOut.write(Terrarum.PLAYER_REF_ID.toLittle()) - // time_t + // ingame time_t metaOut.write((world as GameWorldExtension).time.TIME_T.toLittle()) // creation time (real world time) metaOut.write(world.creationTime.toLittle48()) - // time at save + // time at save (real world time) val timeNow = System.currentTimeMillis() / 1000L metaOut.write(timeNow.toLittle48()) @@ -111,8 +112,14 @@ object WriteWorldInfo { world.lastPlayTime = timeNow world.totalPlayTime += timeToAdd + // SHA256SUM of worldinfo1-3 + worldInfoHash.forEach { + metaOut.write(it) + } + // more data goes here // + metaOut.flush() metaOut.close() diff --git a/work_files/DataFormats/Savegame metadata.txt b/work_files/DataFormats/Savegame metadata.txt index 011b92404..d7d15d488 100644 --- a/work_files/DataFormats/Savegame metadata.txt +++ b/work_files/DataFormats/Savegame metadata.txt @@ -9,7 +9,11 @@ Ord Hex Description 02 4D S 03 44 V -04 Name of the world in UTF-8 (arbitrary length, must not contain NULL) +04 01 Descriptor version number + +05 03 Number of hashes + +06 Name of the world in UTF-8 (arbitrary length, must not contain NULL) ... 00 String terminator ... Terrain seed (8 bytes) @@ -18,10 +22,6 @@ Ord Hex Description ... Weather s0 (8 bytes) ... Weather s1 (8 bytes) -... SHA-256 hash of worldinfo1 (32 bytes) -... SHA-256 hash of worldinfo2 (32 bytes) -... SHA-256 hash of worldinfo3 (32 bytes) - ... ReferenceID of the player (4 bytes, a fixed value of 91A7E2) ... Current world's time_t (the ingame time, 8 bytes) @@ -29,7 +29,9 @@ Ord Hex Description ... Last play time in time_t (6 bytes) ... Total playtime in time_t (4 bytes) // will record 136.1 years of playtime - +... SHA-256 hash of worldinfo1 (32 bytes) +... SHA-256 hash of worldinfo2 (32 bytes) +... SHA-256 hash of worldinfo3 (32 bytes)