mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-09 01:54:04 +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.toLittle
|
||||||
import net.torvald.terrarum.serialise.toLittleLong
|
import net.torvald.terrarum.serialise.toLittleLong
|
||||||
@@ -6,41 +6,178 @@ import org.apache.commons.codec.digest.DigestUtils
|
|||||||
import java.util.Random
|
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
|
private val DOUBLE_MASK = (1L shl 53) - 1
|
||||||
var s1: Long; private set
|
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() {
|
var state0: Long = 0L; private set
|
||||||
this.s0 = s0
|
var state1: Long = 0L; private set
|
||||||
this.s1 = s1
|
|
||||||
|
/**
|
||||||
|
* 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)
|
* Constructs this XoRoRNG by dispersing the bits of seed using [.setSeed] across the two parts of state
|
||||||
throw IllegalArgumentException("Invalid seed: cannot be zero")
|
* 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()
|
public override fun next(bits: Int): Int {
|
||||||
s1 = hash.copyOfRange(8, 16).toLittleLong()
|
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 {
|
override fun nextLong(): Long {
|
||||||
var x = s0
|
val s0 = state0
|
||||||
val y = s1
|
var s1 = state1
|
||||||
s0 = y
|
val result = s0 + s1
|
||||||
x = x xor (x shl 23)
|
|
||||||
s1 = x xor y xor (x ushr 17) xor (y ushr 26)
|
s1 = s1 xor s0
|
||||||
return s1 + y
|
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()
|
/**
|
||||||
|
* Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive
|
||||||
fun reseed(s0: Long, s1: Long) {
|
* result.
|
||||||
this.s0 = s0
|
* @param bound the outer exclusive bound; may be positive or negative
|
||||||
this.s1 = s1
|
* @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
|
val RNG: HQRNG
|
||||||
|
|
||||||
fun loadFromSave(s0: Long, s1: Long) {
|
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 oldJUMPPOWERBUFF = -1.0 // init
|
||||||
private var oldScale = -1.0
|
private var oldScale = -1.0
|
||||||
private var oldDragCoefficient = -1.0
|
private var oldDragCoefficient = -1.0
|
||||||
val jumpAirTime: Double = -1.0
|
var jumpAirTime: Double = -1.0
|
||||||
get() {
|
get() {
|
||||||
// compare all the affecting variables
|
// compare all the affecting variables
|
||||||
if (oldMAX_JUMP_LENGTH == MAX_JUMP_LENGTH &&
|
if (oldMAX_JUMP_LENGTH == MAX_JUMP_LENGTH &&
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ typealias time_t = Long
|
|||||||
* https://en.wikipedia.org/wiki/World_Calendar
|
* https://en.wikipedia.org/wiki/World_Calendar
|
||||||
* http://dwarffortresswiki.org/index.php/DF2014:Calendar
|
* http://dwarffortresswiki.org/index.php/DF2014:Calendar
|
||||||
*
|
*
|
||||||
* And there is no AM/PM concept, 22-hour clock is forced; no leap years.
|
* And there is no AM/PM concept, 24-hour clock is forced; no leap years.
|
||||||
* (AM 12 is still 00h in this system, again, to reduce confusion)
|
* An ingame day should last 22 real-life minutes.
|
||||||
*
|
*
|
||||||
|
* // TODO 4-month year? like Stardew Valley
|
||||||
*
|
*
|
||||||
* Calendar
|
* Calendar
|
||||||
*
|
*
|
||||||
@@ -110,13 +111,13 @@ class WorldTime(initTime: Long = 0L) {
|
|||||||
"Mala", "Gale", "Lime", "Sand", "Timb", "Moon")
|
"Mala", "Gale", "Lime", "Sand", "Timb", "Moon")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** Each day is 22-hour long */
|
/** Each day is displayed as 24 hours, but in real-life clock it's 22 mins long */
|
||||||
val DAY_LENGTH = 79200 //must be the multiple of 3600
|
val DAY_LENGTH = 86400 //must be the multiple of 3600
|
||||||
|
|
||||||
val HOUR_SEC: Int = 3600
|
val HOUR_SEC: Int = 3600
|
||||||
val MINUTE_SEC: Int = 60
|
val MINUTE_SEC: Int = 60
|
||||||
val HOUR_MIN: 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 HOURS_PER_DAY = DAY_LENGTH / HOUR_SEC
|
||||||
|
|
||||||
val YEAR_DAYS: Int = 365
|
val YEAR_DAYS: Int = 365
|
||||||
@@ -164,8 +165,8 @@ class WorldTime(initTime: Long = 0L) {
|
|||||||
val dayName: String
|
val dayName: String
|
||||||
get() = DAY_NAMES[dayOfWeek]
|
get() = DAY_NAMES[dayOfWeek]
|
||||||
|
|
||||||
inline fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt()
|
fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt()
|
||||||
inline fun Long.abs() = Math.abs(this)
|
fun Long.abs() = Math.abs(this)
|
||||||
|
|
||||||
/** Format: "%A, %d %B %Y %X" */
|
/** Format: "%A, %d %B %Y %X" */
|
||||||
fun getFormattedTime() = "${getDayNameShort()}, " +
|
fun getFormattedTime() = "${getDayNameShort()}, " +
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ object ReadWorldInfo {
|
|||||||
throw IllegalArgumentException("File not a Save Meta")
|
throw IllegalArgumentException("File not a Save Meta")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val descVersion = fis.read(1) // 0-127
|
||||||
|
val numberOfHashes = fis.read() // 0-127
|
||||||
|
|
||||||
|
|
||||||
var byteRead = fis.read()
|
var byteRead = fis.read()
|
||||||
while (byteRead != 0) {
|
while (byteRead != 0) {
|
||||||
if (byteRead == -1)
|
if (byteRead == -1)
|
||||||
@@ -38,14 +43,14 @@ object ReadWorldInfo {
|
|||||||
fis.read(8).toLittleLong(), // rng s1
|
fis.read(8).toLittleLong(), // rng s1
|
||||||
fis.read(8).toLittleLong(), // weather s0
|
fis.read(8).toLittleLong(), // weather s0
|
||||||
fis.read(8).toLittleLong(), // weather s1
|
fis.read(8).toLittleLong(), // weather s1
|
||||||
fis.read(32),
|
|
||||||
fis.read(32),
|
|
||||||
fis.read(32),
|
|
||||||
fis.read(4).toLittleInt(), // player id
|
fis.read(4).toLittleInt(), // player id
|
||||||
fis.read(8).toLittleLong(), // world TIME_T
|
fis.read(8).toLittleLong(), // world TIME_T
|
||||||
fis.read(6).toLittleLong(), // creation time
|
fis.read(6).toLittleLong(), // creation time
|
||||||
fis.read(6).toLittleLong(), // last play 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 rngS1: Long,
|
||||||
val weatherS0: Long,
|
val weatherS0: Long,
|
||||||
val weatherS1: Long,
|
val weatherS1: Long,
|
||||||
val worldinfo1Hash: ByteArray,
|
|
||||||
val worldInfo2Hash: ByteArray,
|
|
||||||
val worldInfo3Hash: ByteArray,
|
|
||||||
val playerID: Int,
|
val playerID: Int,
|
||||||
val timeNow: Long,
|
val timeNow: Long,
|
||||||
val creationTime: Long,
|
val creationTime: Long,
|
||||||
val lastPlayTime: 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 META_MAGIC = "TESV".toByteArray(Charsets.UTF_8)
|
||||||
val NULL = 0.toByte()
|
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.
|
* 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>()
|
val outFiles = ArrayList<File>()
|
||||||
outFiles.add(metaFile)
|
outFiles.add(metaFile)
|
||||||
|
|
||||||
val worldInfoHash = ArrayList<ByteArray>() // hash of worldinfo1-3
|
val worldInfoHash = ArrayList<ByteArray>() // hash of worldinfo1-3
|
||||||
// try to write 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())
|
val outFile = File(path + filenum.toString())
|
||||||
if (outFile.exists()) outFile.delete()
|
if (outFile.exists()) outFile.delete()
|
||||||
outFile.createNewFile()
|
outFile.createNewFile()
|
||||||
@@ -65,11 +69,13 @@ object WriteWorldInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// compose save meta
|
// compose save meta (actual writing part)
|
||||||
val metaOut = BufferedOutputStream(FileOutputStream(metaFile), 256)
|
val metaOut = BufferedOutputStream(FileOutputStream(metaFile), 256)
|
||||||
|
|
||||||
|
|
||||||
metaOut.write(META_MAGIC)
|
metaOut.write(META_MAGIC)
|
||||||
|
metaOut.write(VERSION)
|
||||||
|
metaOut.write(HASHED_FILES_COUNT)
|
||||||
|
|
||||||
// world name
|
// world name
|
||||||
val worldNameBytes = world.worldName.toByteArray(Charsets.UTF_8)
|
val worldNameBytes = world.worldName.toByteArray(Charsets.UTF_8)
|
||||||
@@ -80,28 +86,23 @@ object WriteWorldInfo {
|
|||||||
metaOut.write(world.generatorSeed.toLittle())
|
metaOut.write(world.generatorSeed.toLittle())
|
||||||
|
|
||||||
// randomiser seed
|
// randomiser seed
|
||||||
metaOut.write(RoguelikeRandomiser.RNG.s0.toLittle())
|
metaOut.write(RoguelikeRandomiser.RNG.state0.toLittle())
|
||||||
metaOut.write(RoguelikeRandomiser.RNG.s1.toLittle())
|
metaOut.write(RoguelikeRandomiser.RNG.state1.toLittle())
|
||||||
|
|
||||||
// weather seed
|
// weather seed
|
||||||
metaOut.write(WeatherMixer.RNG.s0.toLittle())
|
metaOut.write(WeatherMixer.RNG.state0.toLittle())
|
||||||
metaOut.write(WeatherMixer.RNG.s1.toLittle())
|
metaOut.write(WeatherMixer.RNG.state1.toLittle())
|
||||||
|
|
||||||
// SHA256SUM of worldinfo1-3
|
|
||||||
worldInfoHash.forEach {
|
|
||||||
metaOut.write(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// reference ID of the player
|
// reference ID of the player
|
||||||
metaOut.write(Terrarum.PLAYER_REF_ID.toLittle())
|
metaOut.write(Terrarum.PLAYER_REF_ID.toLittle())
|
||||||
|
|
||||||
// time_t
|
// ingame time_t
|
||||||
metaOut.write((world as GameWorldExtension).time.TIME_T.toLittle())
|
metaOut.write((world as GameWorldExtension).time.TIME_T.toLittle())
|
||||||
|
|
||||||
// creation time (real world time)
|
// creation time (real world time)
|
||||||
metaOut.write(world.creationTime.toLittle48())
|
metaOut.write(world.creationTime.toLittle48())
|
||||||
|
|
||||||
// time at save
|
// time at save (real world time)
|
||||||
val timeNow = System.currentTimeMillis() / 1000L
|
val timeNow = System.currentTimeMillis() / 1000L
|
||||||
metaOut.write(timeNow.toLittle48())
|
metaOut.write(timeNow.toLittle48())
|
||||||
|
|
||||||
@@ -111,8 +112,14 @@ object WriteWorldInfo {
|
|||||||
world.lastPlayTime = timeNow
|
world.lastPlayTime = timeNow
|
||||||
world.totalPlayTime += timeToAdd
|
world.totalPlayTime += timeToAdd
|
||||||
|
|
||||||
|
// SHA256SUM of worldinfo1-3
|
||||||
|
worldInfoHash.forEach {
|
||||||
|
metaOut.write(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// more data goes here //
|
||||||
|
|
||||||
|
|
||||||
metaOut.flush()
|
metaOut.flush()
|
||||||
metaOut.close()
|
metaOut.close()
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ 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, 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
|
... 00 String terminator
|
||||||
|
|
||||||
... Terrain seed (8 bytes)
|
... Terrain seed (8 bytes)
|
||||||
@@ -18,10 +22,6 @@ Ord Hex Description
|
|||||||
... Weather s0 (8 bytes)
|
... Weather s0 (8 bytes)
|
||||||
... Weather s1 (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)
|
... 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)
|
||||||
|
|
||||||
@@ -29,7 +29,9 @@ Ord Hex Description
|
|||||||
... Last play time in time_t (6 bytes)
|
... Last play time in time_t (6 bytes)
|
||||||
... Total playtime in time_t (4 bytes) // will record 136.1 years of playtime
|
... 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