Files
SeedCracker-1.14.4/src/main/java/kaptainwutax/seedcracker/magic/PopulationReversal.java
T
2020-01-31 21:38:27 -05:00

123 lines
5.8 KiB
Java

package kaptainwutax.seedcracker.magic;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.Seeds;
import kaptainwutax.seedcracker.util.math.LCG;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class PopulationReversal {
private static final LCG SKIP_2 = Rand.JAVA_LCG.combine(2);
private static final LCG SKIP_4 = Rand.JAVA_LCG.combine(4);
public static ArrayList<Long> getWorldSeeds(long populationSeed, int x, int z) {
populationSeed ^= Rand.JAVA_LCG.multiplier;
ArrayList<Long> worldSeeds = new ArrayList<>();
if (x == 0 && z == 0) {
worldSeeds.add(populationSeed);
return worldSeeds;
}
long c; //a is upper 16 bits, b middle 16 bits, c lower 16 bits of worldseed.
long e = populationSeed & MagicMath.MASK_32; //The algorithm proceeds by solving for worldseed in 16 bit groups
long f = populationSeed & MagicMath.MASK_16; //as such, we need the 16 bit groups of chunkseed for later eqns.
boolean xEven = (x & 1) == 0;
boolean zEven = (z & 1) == 0;
long firstMultiplier = (SKIP_2.multiplier * x + SKIP_4.multiplier * z) & MagicMath.MASK_16;
int multTrailingZeroes = MagicMath.countTrailingZeroes(firstMultiplier); //TODO currently code blows up if this is 16, but you can use it to get bits of seed anyway if it is non zero
long firstMultInv = MagicMath.modInverse(firstMultiplier >> multTrailingZeroes,16);
//TODO We can recover more initial bits when x + z is divisible by a power of 2
if (xEven ^ zEven) { //bottom bit of x*a + z*b is odd so we xor by 1 to get bottom bit of worldseed.
c = (populationSeed & 1) ^ 1;
} else { //bottom bit of x*a + z*b is even so we xor by 0 to get bottom bit of worldseed.
c = (populationSeed & 1);
}
for (; c < (1L << 16); c += 2) { //iterate through all possible lower 16 bits of worldseed.
long target = (c ^ f) & MagicMath.MASK_16; //now that we've guessed 16 bits of worldseed we can undo the mask
//We need to handle the four different cases of the effect the two | 1s have on the seed
long magic = x * ((SKIP_2.multiplier * ((c ^ Rand.JAVA_LCG.multiplier) & MagicMath.MASK_16) + SKIP_2.addend) >>> 16) + z * ((SKIP_4.multiplier * ((c ^ Rand.JAVA_LCG.multiplier) & MagicMath.MASK_16) + SKIP_4.addend) >>> 16);
addWorldSeed(worldSeeds, target - (magic & MagicMath.MASK_16), multTrailingZeroes, firstMultInv, c, e, x, z, populationSeed); //case both nextLongs were odd
addWorldSeed(worldSeeds, target - ((magic + x) & MagicMath.MASK_16), multTrailingZeroes, firstMultInv, c, e, x, z, populationSeed); //case where x nextLong even
addWorldSeed(worldSeeds, target - ((magic + z) & MagicMath.MASK_16), multTrailingZeroes, firstMultInv, c, e, x, z, populationSeed); //case where z nextLong even
addWorldSeed(worldSeeds, target - ((magic + x + z) & MagicMath.MASK_16), multTrailingZeroes, firstMultInv, c, e, x, z, populationSeed); //case where both nextLongs even
}
return worldSeeds;
}
public static long makeSecondAddend(int x, long k, int z) {
return ((x*((((SKIP_2.multiplier * ((k ^ Rand.JAVA_LCG.multiplier) & MagicMath.MASK_32) + SKIP_2.addend) & MagicMath.MASK_48) >>> 16) | 1L) +
z*((((SKIP_4.multiplier * ((k ^ Rand.JAVA_LCG.multiplier) & MagicMath.MASK_32) + SKIP_4.addend) & MagicMath.MASK_48) >>> 16) | 1L)) >>> 16) & MagicMath.MASK_16;
}
public static void addWorldSeed(List<Long> worldSeeds, long firstAddend, int multTrailingZeroes, long firstMultInv, long c, long e, int x, int z, long populationSeed){
if(MagicMath.countTrailingZeroes(firstAddend) >= multTrailingZeroes) { //Does there exist a set of 16 bits which work for bits 17-32
long b = ((((firstMultInv * firstAddend)>>> multTrailingZeroes) ^ (Rand.JAVA_LCG.multiplier >> 16)) & ((1L << (16 - multTrailingZeroes)) - 1));
for(; b < (1L << 16); b += (1L << (16 - multTrailingZeroes))) { //if the previous multiplier had a power of 2 divisor, we get multiple solutions for b
long k = (b << 16) + c;
long target2 = (k ^ e) >> 16; //now that we know b, we can undo more of the mask
long secondAddend = makeSecondAddend(x, k, z);
if (MagicMath.countTrailingZeroes(target2 - secondAddend) >= multTrailingZeroes) { //Does there exist a set of 16 bits which work for bits 33-48
long a = ((((firstMultInv * (target2 - secondAddend)) >>> multTrailingZeroes) ^ (Rand.JAVA_LCG.multiplier >> 32)) & ((1L << (16-multTrailingZeroes)) - 1));
for(; a < (1L << 16); a += (1L << (16 - multTrailingZeroes))) { //if the previous multiplier had a power of 2 divisor, we get multiple solutions for a
if(Seeds.setPopulationSeed(null, (a << 32) + k, x, z) == populationSeed) { //lazy check if the test has succeeded
worldSeeds.add((a << 32) + k);
}
}
}
}
}
}
/*
* Left as reference if I need to test this mess again. :P
* */
public static void main(String[] args) {
long seed;
int x , z;
ArrayList<Long> seeds;
/*long seed = 40820992642153L;
int x = 2;
int z = 4;
ArrayList<Long> seeds = getSeedFromChunkseed(getChunkseed(seed, x, z), x, z);
System.out.println("start");
for (long a : seeds) {
System.out.println(a);
}
System.out.println("done");*/
Random r = new Random();
int failcount = 0;
System.out.println("start");
long start = System.currentTimeMillis();
for(int i = 0; i < 100000; i++){
seed = r.nextLong() & ((1L << 48)-1);
x = r.nextInt(16) - 8;
z = r.nextInt(16) - 8;
seeds = getWorldSeeds(Seeds.setPopulationSeed(null, seed, x, z) ^ Rand.JAVA_LCG.multiplier, x, z);
if (!seeds.contains(seed)) {
System.out.println(seed);
System.out.println(x);
System.out.println(z);
failcount++;
System.out.println();
}
}
System.out.println("End: "+((System.currentTimeMillis()-start)/1000.0));
System.out.println(failcount+" failures.");
}
}