From c6a4e86f15959d41689307fcd9fbecac579ce07e Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 11 Nov 2019 19:42:08 -0500 Subject: [PATCH] structure seed cracking --- .../kaptainwutax/seedcracker/SeedCracker.java | 217 ++++++++++++++++++ .../seedcracker/cracker/StructureData.java | 114 +++++++++ 2 files changed, 331 insertions(+) create mode 100644 src/main/java/kaptainwutax/seedcracker/cracker/StructureData.java diff --git a/src/main/java/kaptainwutax/seedcracker/SeedCracker.java b/src/main/java/kaptainwutax/seedcracker/SeedCracker.java index a84bb79..36d5353 100644 --- a/src/main/java/kaptainwutax/seedcracker/SeedCracker.java +++ b/src/main/java/kaptainwutax/seedcracker/SeedCracker.java @@ -1,10 +1,24 @@ package kaptainwutax.seedcracker; +import kaptainwutax.seedcracker.cracker.StructureData; import kaptainwutax.seedcracker.render.RenderQueue; +import kaptainwutax.seedcracker.util.Rand; +import kaptainwutax.seedcracker.util.math.LCG; import net.fabricmc.api.ModInitializer; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.biome.layer.BiomeLayerSampler; +import net.minecraft.world.biome.layer.BiomeLayers; +import net.minecraft.world.biome.source.BiomeSourceType; +import net.minecraft.world.gen.ChunkRandom; +import net.minecraft.world.level.LevelGeneratorType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + public class SeedCracker implements ModInitializer { public static final Logger LOG = LogManager.getLogger("Seed Cracker"); @@ -14,4 +28,207 @@ public class SeedCracker implements ModInitializer { RenderQueue.get().add("hand", FinderQueue.get()::renderFinders); } + public static void main(String[] args) { + //91,94,82,85,88,79,97,76,100,103 + SeedCracker cracker = new SeedCracker(); + List heights = new ArrayList<>(); + heights.add(91); + heights.add(94); + heights.add(82); + heights.add(85); + heights.add(88); + heights.add(79); + heights.add(97); + heights.add(76); + heights.add(100); + heights.add(103); + + List structureDataList = new ArrayList<>(); + structureDataList.add(new StructureData(new ChunkPos(-37, -95), StructureData.END_CITY)); + structureDataList.add(new StructureData(new ChunkPos(-57,-116), StructureData.END_CITY)); + structureDataList.add(new StructureData(new ChunkPos(-18, 204), StructureData.OCEAN_MONUMENT)); + structureDataList.add(new StructureData(new ChunkPos(-44, 109), StructureData.OCEAN_MONUMENT)); + structureDataList.add(new StructureData(new ChunkPos(14, -158), StructureData.DESERT_PYRAMID)); + + cracker.getSpikeSeeds(heights).forEach(seed -> { + System.out.println("Found pillar seed: [" + seed + "]."); + + structureDataList.forEach(s -> { + System.out.println("Structure data: [" + s.getSalt() + ", " + s.getOffsetX() + ", " + s.getOffsetZ() + "]."); + }); + + cracker.getStructureSeeds(seed, structureDataList).forEach(seed1 -> { + System.out.println("Found structure seed: [" + seed1 + "]."); + + for(long i = 0; i < (1L << 16); i++) { + long worldSeed = (i << 48) | seed1; + BiomeLayerSampler[] biomeLayerSamplers = BiomeLayers.build(worldSeed, LevelGeneratorType.DEFAULT, BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings()); + System.out.println(worldSeed + ", " + biomeLayerSamplers[0].sample(0, 0)); + } + }); + }); + } + + public List getStructureSeeds(int pillarSeed, List structureDataList) { + List structureSeeds = new ArrayList<>(); + ChunkRandom chunkRandom = new ChunkRandom(); + + for(long i = 0; i < (1L << 32); i++) { + long structureSeed = this.timeMachine(i, pillarSeed); + //if(structureSeed != 229625539606330L)continue; + boolean goodSeed = true; + + for(StructureData structureData: structureDataList) { + chunkRandom.setStructureSeed(structureSeed, structureData.getRegionX(), + structureData.getRegionZ(), structureData.getSalt()); + + if(!structureData.test(chunkRandom)) { + goodSeed = false; + break; + } + } + + if(goodSeed) { + structureSeeds.add(structureSeed); + } + } + return structureSeeds; + } + + private LCG inverseLCG = Rand.JAVA_LCG.combine(-2); + + public long timeMachine(long partialWorldSeed, int pillarSeed) { + long currentSeed = 0L; + currentSeed |= (partialWorldSeed & 0xFFFF0000L) << 16; + currentSeed |= (long)pillarSeed << 16; + currentSeed |= partialWorldSeed & 0xFFFFL; + + currentSeed = inverseLCG.nextSeed(currentSeed); + currentSeed ^= Rand.JAVA_LCG.multiplier; + return currentSeed; + } + + + public List getSpikeSeeds(List heights) { + List result = new ArrayList<>(); + + for(int spikeSeed = 0; spikeSeed < (1 << 16); spikeSeed++) { + List h = this.getSpikeHeights(spikeSeed); + if(h.equals(heights))result.add(spikeSeed); + } + + return result; + } + + public List getSpikeHeights(int spikeSeed) { + List indices = new ArrayList<>(); + + for (int i = 0; i < 10; i++) { + indices.add(i); + } + + Collections.shuffle(indices, new Random(spikeSeed)); + + List heights = new ArrayList<>(); + + for (Integer index: indices) { + heights.add(76 + index * 3); + } + + return heights; + } + + /* + public List getSpikeHeights(long worldSeed) { + Random rand = new Random(worldSeed); + long pillarSeed = rand.nextLong() & 65535L; + + List indices = new ArrayList<>(); + + for (int i = 0; i < 10; i++) { + indices.add(i); + } + + Collections.shuffle(indices, new Random(pillarSeed)); + + List heights = new ArrayList<>(); + + for (Integer index: indices) { + heights.add(76 + index * 3); + } + + return heights; + } + + + protected ChunkPos getStart(ChunkGenerator chunkGenerator_1, Random rand, int chunkX, int chunkZ) { + int templeDistance = 32; + int templeSeparation = 8; + + chunkX = chunkX < 0 ? chunkX - templeDistance + 1 : chunkX; + chunkZ = chunkZ < 0 ? chunkZ - templeDistance + 1 : chunkZ; + + //Pick out in which region the chunk is. + int regionX = chunkX / templeDistance; + int regionZ = chunkZ / templeDistance; + + ((ChunkRandom)rand).setStructureSeed(chunkGenerator_1.getSeed(), regionX, regionZ, this.getSeedModifier()); + + //Convert it back to a chunk pos. + regionX *= templeDistance; + regionZ *= templeDistance; + + //Add a random chunk offset. + regionX += rand.nextInt(templeDistance - templeSeparation); + regionZ += rand.nextInt(templeDistance - templeSeparation); + + return new ChunkPos(regionX, regionZ); + } + + public void getTempleCalls(ChunkPos chunkPos) { + int templeDistance = 32; + + int chunkX = chunkPos.x; + int chunkZ = chunkPos.z; + + chunkX = chunkX < 0 ? chunkX - templeDistance + 1 : chunkX; + chunkZ = chunkZ < 0 ? chunkZ - templeDistance + 1 : chunkZ; + + //Pick out in which region the chunk is. + int regionX = (chunkX / templeDistance) * templeDistance; + int regionZ = (chunkZ / templeDistance) * templeDistance; + + int offsetX = chunkPos.x - regionX; + int offsetZ = chunkPos.z - regionZ; + } + + public boolean shouldStartAt(ChunkGenerator chunkGen, Random random_1, int chunkX, int chunkZ) { + ChunkPos startPosition = this.getStart(chunkGen, random_1, chunkX, chunkZ); + + if(chunkX == startPosition.x && chunkZ == startPosition.z) { + Biome biome = chunkGen.getBiomeSource().getBiome(new BlockPos(chunkX * 16 + 9, 0, chunkZ * 16 + 9)); + + if(chunkGen.hasStructure(biome, this)) { + return true; + } + } + + return false; + } + + public long setStructureSeed(long worldSeed, int regionX, int regionZ, int salt) { + long seed = (long)regionX * 341873128712L + (long)regionZ * 132897987541L + worldSeed + (long)salt; + seed = (seed ^ Rand.JAVA_LCG.multiplier) & (Rand.JAVA_LCG.modulo - 1); + return seed; + } + + public void getStart(long worldSeed, int regionX, int regionZ, int salt) { + long seed = this.setStructureSeed(worldSeed, regionX, regionZ, salt); + int nextIntSeed = (int)(Rand.JAVA_LCG.nextSeed(seed) >>> (48 - 31)); + int chunkX = nextIntSeed % 24; + nextIntSeed = (int)(Rand.JAVA_LCG.nextSeed(Rand.JAVA_LCG.nextSeed(seed)) >>> (48 - 31)); + int chunkZ = nextIntSeed % 24; + } + */ + } diff --git a/src/main/java/kaptainwutax/seedcracker/cracker/StructureData.java b/src/main/java/kaptainwutax/seedcracker/cracker/StructureData.java new file mode 100644 index 0000000..f482527 --- /dev/null +++ b/src/main/java/kaptainwutax/seedcracker/cracker/StructureData.java @@ -0,0 +1,114 @@ +package kaptainwutax.seedcracker.cracker; + +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.gen.ChunkRandom; + +public class StructureData { + + private int regionX; + private int regionZ; + private int offsetX; + private int offsetZ; + private Feature feature; + + public StructureData(ChunkPos chunkPos, Feature feature) { + this.feature = feature; + this.feature.buildCalls(this, chunkPos); + } + + public int getRegionX() { + return this.regionX; + } + + public int getRegionZ() { + return this.regionZ; + } + + public int getOffsetX() { + return this.offsetX; + } + + public int getOffsetZ() { + return this.offsetZ; + } + + public int getSalt() { + return this.feature.salt; + } + + public boolean test(ChunkRandom rand) { + return this.feature.test(rand, this.offsetX, this.offsetZ); + } + + public abstract static class Feature { + public final int salt; + public final int templeDistance; + + public Feature(int salt, int templeDistance) { + this.salt = salt; + this.templeDistance = templeDistance; + } + + public void buildCalls(StructureData data, ChunkPos chunkPos) { + int chunkX = chunkPos.x; + int chunkZ = chunkPos.z; + + chunkX = chunkX < 0 ? chunkX - this.templeDistance + 1 : chunkX; + chunkZ = chunkZ < 0 ? chunkZ - this.templeDistance + 1 : chunkZ; + + //Pick out in which region the chunk is. + int regionX = (chunkX / this.templeDistance); + int regionZ = (chunkZ / this.templeDistance); + + data.regionX = regionX; + data.regionZ = regionZ; + + regionX *= this.templeDistance; + regionZ *= this.templeDistance; + + data.offsetX = chunkPos.x - regionX; + data.offsetZ = chunkPos.z - regionZ; + } + + public abstract boolean test(ChunkRandom rand, int x, int z); + } + + + public static final Feature SWAMP_HUT = new Feature(14357620, 32) { + @Override + public boolean test(ChunkRandom rand, int x, int z) { + return rand.nextInt(24) == x && rand.nextInt(24) == z; + } + }; + + public static final Feature DESERT_PYRAMID = new Feature(14357617, 32) { + @Override + public boolean test(ChunkRandom rand, int x, int z) { + return rand.nextInt(24) == x && rand.nextInt(24) == z; + } + }; + + public static final Feature JUNGLE_TEMPLE = new Feature(14357619, 32) { + @Override + public boolean test(ChunkRandom rand, int x, int z) { + return rand.nextInt(24) == x && rand.nextInt(24) == z; + } + }; + + public static final Feature END_CITY = new Feature(10387313, 20) { + @Override + public boolean test(ChunkRandom rand, int x, int z) { + return (rand.nextInt(9) + rand.nextInt(9)) / 2 == x + && (rand.nextInt(9) + rand.nextInt(9)) / 2 == z; + } + }; + + public static final Feature OCEAN_MONUMENT = new Feature(10387313, 32) { + @Override + public boolean test(ChunkRandom rand, int x, int z) { + return (rand.nextInt(27) + rand.nextInt(27)) / 2 == x + && (rand.nextInt(27) + rand.nextInt(27)) / 2 == z; + } + }; + +}