diff --git a/src/main/java/kaptainwutax/seedcracker/SeedCracker.java b/src/main/java/kaptainwutax/seedcracker/SeedCracker.java index fe4478a..5c18988 100644 --- a/src/main/java/kaptainwutax/seedcracker/SeedCracker.java +++ b/src/main/java/kaptainwutax/seedcracker/SeedCracker.java @@ -1,11 +1,9 @@ package kaptainwutax.seedcracker; -import kaptainwutax.seedcracker.cracker.BiomeData; -import kaptainwutax.seedcracker.cracker.PillarData; -import kaptainwutax.seedcracker.cracker.StructureData; -import kaptainwutax.seedcracker.cracker.TimeMachine; +import kaptainwutax.seedcracker.cracker.*; import kaptainwutax.seedcracker.render.RenderQueue; import net.fabricmc.api.ModInitializer; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.biome.layer.BiomeLayerSampler; import net.minecraft.world.biome.layer.BiomeLayers; import net.minecraft.world.biome.source.BiomeSourceType; @@ -16,6 +14,8 @@ import org.apache.logging.log4j.Logger; import java.util.ArrayList; import java.util.List; +import java.util.Random; +import java.util.stream.IntStream; public class SeedCracker implements ModInitializer { @@ -28,6 +28,7 @@ public class SeedCracker implements ModInitializer { private TimeMachine timeMachine = new TimeMachine(); private List structureCache = new ArrayList<>(); + private List populationCache = new ArrayList<>(); private List biomeCache = new ArrayList<>(); @Override @@ -74,7 +75,7 @@ public class SeedCracker implements ModInitializer { added = true; } - if(this.structureSeeds == null && this.pillarSeeds != null && this.structureCache.size() >= 5) { + if(this.structureSeeds == null && this.pillarSeeds != null && this.structureCache.size() >= 3) { this.structureSeeds = new ArrayList<>(); LOG.warn("Looking for structure seeds with " + this.structureCache.size() + " structure features."); @@ -89,6 +90,7 @@ public class SeedCracker implements ModInitializer { } this.structureCache.clear(); + this.onPopulationData(null); this.onBiomeData(null); } else if(this.structureSeeds != null && structureData != null) { this.structureSeeds.removeIf(structureSeed -> { @@ -103,6 +105,48 @@ public class SeedCracker implements ModInitializer { return added; } + public synchronized boolean onPopulationData(PopulationData populationData) { + boolean added = false; + + if(populationData != null && !this.populationCache.contains(populationData)) { + this.populationCache.add(populationData); + added = true; + } + + if(this.worldSeeds == null && this.structureSeeds != null && this.populationCache.size() >= 4) { + this.worldSeeds = new ArrayList<>(); + LOG.warn("Looking for world seeds with " + this.populationCache.size() + " decorators."); + + for(int i = 0; i < this.structureSeeds.size(); i++) { + SeedCracker.LOG.warn("Progress " + (i * 100.0f) / this.structureSeeds.size() + "%..."); + + long structureSeed = this.structureSeeds.get(i); + + for(long j = 0; j < (1L << 16); j++) { + long worldSeed = (j << 48) | structureSeed; + boolean goodSeed = true; + + for(PopulationData data: this.populationCache) { + if(!data.test(worldSeed)) { + goodSeed = false; + break; + } + } + + if(goodSeed) { + this.worldSeeds.add(worldSeed); + } + } + } + + LOG.warn("Finished search with " + this.worldSeeds + (this.worldSeeds.size() == 1 ? " seed." : " seeds.")); + } else if(this.worldSeeds != null && populationData != null) { + this.worldSeeds.removeIf(worldSeed -> !populationData.test(worldSeed)); + } + + return added; + } + public synchronized boolean onBiomeData(BiomeData biomeData) { boolean added = false; @@ -126,7 +170,7 @@ public class SeedCracker implements ModInitializer { BiomeLayerSampler sampler = BiomeLayers.build(worldSeed, LevelGeneratorType.DEFAULT, BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings())[1]; - for (BiomeData data : this.biomeCache) { + for(BiomeData data : this.biomeCache) { if (!data.test(worldSeed, sampler)) { goodSeed = false; break; @@ -150,148 +194,33 @@ public class SeedCracker implements ModInitializer { BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings())[1]; return !biomeData.test(worldSeed, sampler); }); + } else if(this.worldSeeds != null) { + this.worldSeeds.removeIf(worldSeed -> { + for(BiomeData data: this.biomeCache) { + BiomeLayerSampler sampler = BiomeLayers.build(worldSeed, LevelGeneratorType.DEFAULT, + BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings())[1]; + if(!biomeData.test(worldSeed, sampler))return true; + } + + return false; + }); } return added; } - - 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); + Random rand = new Random(1234L); - 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)); - } - }); - });*/ + IntStream.range(0, 8).mapToObj((int_1x) -> { + int int_2 = rand.nextInt(16); + int int_3 = rand.nextInt(256); + int int_4 = rand.nextInt(16); + System.out.println("Created " + int_2 + ", " + int_3 + ", " + int_4); + return new BlockPos(int_2, int_3, int_4); + }).forEach(pos -> { + System.out.println("Populating " + pos); + }); } - - - /* - 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/DungeonData.java b/src/main/java/kaptainwutax/seedcracker/cracker/DungeonData.java new file mode 100644 index 0000000..31b94da --- /dev/null +++ b/src/main/java/kaptainwutax/seedcracker/cracker/DungeonData.java @@ -0,0 +1,49 @@ +package kaptainwutax.seedcracker.cracker; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.ChunkRandom; +import net.minecraft.world.gen.GenerationStep; +import net.minecraft.world.gen.decorator.Decorator; + +import java.util.ArrayList; +import java.util.List; + +public class DungeonData extends PopulationData { + + public static final Feature DUNGEON = new Feature(GenerationStep.Feature.UNDERGROUND_STRUCTURES, Decorator.DUNGEONS) {}; + public static final Integer COBBLESTONE_CALL = 0; + public static final Integer MOSSY_COBBLESTONE_CALL = 1; + + private List starts; + private final List> floorCallsList; + + public DungeonData(ChunkPos chunkPos, Biome biome, List starts, List> floorCallsList) { + super(chunkPos, DUNGEON, biome); + this.starts = starts; + this.floorCallsList = floorCallsList; + } + + @Override + public boolean test(ChunkRandom chunkRandom) { + List idsToCheck = new ArrayList<>(); + + for(int i = 0; i < 8; i++) { + BlockPos pos = new BlockPos(chunkRandom.nextInt(16), chunkRandom.nextInt(256), chunkRandom.nextInt(16)); + int a = this.starts.indexOf(pos); + if(a != -1)idsToCheck.add(a); + + //We assume the dungeon failed, skip 2 calls. + chunkRandom.nextBoolean(); + chunkRandom.nextBoolean(); + } + + if(idsToCheck.size() != this.starts.size())return false; + + //TODO: Implement floor calls. + + return true; + } + +} diff --git a/src/main/java/kaptainwutax/seedcracker/cracker/PopulationData.java b/src/main/java/kaptainwutax/seedcracker/cracker/PopulationData.java new file mode 100644 index 0000000..9cdcd04 --- /dev/null +++ b/src/main/java/kaptainwutax/seedcracker/cracker/PopulationData.java @@ -0,0 +1,77 @@ +package kaptainwutax.seedcracker.cracker; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.ChunkRandom; +import net.minecraft.world.gen.GenerationStep; +import net.minecraft.world.gen.decorator.ConfiguredDecorator; +import net.minecraft.world.gen.decorator.Decorator; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.DecoratedFeatureConfig; + +import java.util.List; + +public abstract class PopulationData { + + private final ChunkPos chunkPos; + private final Feature feature; + private final Biome biome; + + public PopulationData(ChunkPos chunkPos, Feature feature, Biome biome) { + this.chunkPos = chunkPos; + this.feature = feature; + this.biome = biome; + } + + public final boolean test(long worldSeed) { + ChunkRandom chunkRandom = this.feature.buildRand(worldSeed, this.biome, this.chunkPos); + return this.test(chunkRandom); + } + + public abstract boolean test(ChunkRandom chunkRandom); + + @Override + public boolean equals(Object obj) { + if(obj == this)return true; + + if(obj instanceof PopulationData) { + PopulationData populationData = ((PopulationData)obj); + return populationData.chunkPos.equals(this.chunkPos) && populationData.feature == this.feature; + } + + return false; + } + + + public abstract static class Feature { + private GenerationStep.Feature genStep; + private Decorator decorator; + + public Feature(GenerationStep.Feature genStep, Decorator decorator) { + this.genStep = genStep; + this.decorator = decorator; + } + + public ChunkRandom buildRand(long worldSeed, Biome biome, ChunkPos chunkPos) { + List> features = biome.getFeaturesForStep(this.genStep); + + for(int i = 0; i < features.size(); i++) { + ConfiguredFeature feature = features.get(i); + if(!(feature.config instanceof DecoratedFeatureConfig))continue; + ConfiguredDecorator currentDecorator = ((DecoratedFeatureConfig)feature.config).decorator; + + if(currentDecorator.decorator == this.decorator) { + BlockPos pos = new BlockPos(chunkPos.getStartX(), 0, chunkPos.getStartZ()); + ChunkRandom chunkRandom = new ChunkRandom(); + long populationSeed = chunkRandom.setSeed(worldSeed, pos.getX(), pos.getZ()); + chunkRandom.setFeatureSeed(populationSeed, i, this.genStep.ordinal()); + return chunkRandom; + } + } + + return null; + } + } + +} diff --git a/src/main/java/kaptainwutax/seedcracker/feature/decoration/DungeonFeature.java b/src/main/java/kaptainwutax/seedcracker/feature/decoration/DungeonFeature.java index 5adcc59..6b33bf6 100644 --- a/src/main/java/kaptainwutax/seedcracker/feature/decoration/DungeonFeature.java +++ b/src/main/java/kaptainwutax/seedcracker/feature/decoration/DungeonFeature.java @@ -28,7 +28,7 @@ public class DungeonFeature extends DecorationFeature { for(int xo = -dungeonSize.getX(); xo <= dungeonSize.getX(); xo++) { for(int zo = -dungeonSize.getZ(); zo <= dungeonSize.getZ(); zo++) { - Block block = this.world.getBlockState(pos.add(xo, -1, zo)).getBlock(); + Block block = this.world.getBlockState(this.pos.add(xo, -1, zo)).getBlock(); if(block == Blocks.MOSSY_COBBLESTONE) { floorCalls.add(MOSSY_COBBLESTONE_CALL);