mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
new RNG for everything; Joise update
This commit is contained in:
Binary file not shown.
Binary file not shown.
192
src/net/torvald/random/HQRNG.java
Normal file
192
src/net/torvald/random/HQRNG.java
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}*/
|
||||
@@ -8,7 +8,7 @@ internal interface RNGConsumer {
|
||||
val RNG: HQRNG
|
||||
|
||||
fun loadFromSave(s0: Long, s1: Long) {
|
||||
RNG.reseed(s0, s1)
|
||||
RNG.setSeed(s0, s1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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()}, " +
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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<File>()
|
||||
outFiles.add(metaFile)
|
||||
|
||||
val worldInfoHash = ArrayList<ByteArray>() // 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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user