matthew proofed underlying logic

This commit is contained in:
Unknown
2020-01-31 21:38:27 -05:00
parent 6fb15ca0c5
commit f16063774d
47 changed files with 876 additions and 460 deletions
@@ -1,199 +1,213 @@
package kaptainwutax.seedcracker;
import io.netty.util.internal.ConcurrentSet;
import kaptainwutax.seedcracker.cracker.*;
import kaptainwutax.seedcracker.cracker.population.DecoratorData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.finder.FinderQueue;
import kaptainwutax.seedcracker.render.RenderQueue;
import kaptainwutax.seedcracker.util.Log;
import kaptainwutax.seedcracker.util.Rand;
import net.fabricmc.api.ModInitializer;
import net.minecraft.world.level.LevelProperties;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class SeedCracker implements ModInitializer {
private static final SeedCracker INSTANCE = new SeedCracker();
public long hashedWorldSeed = -1;
public List<Long> worldSeeds = null;
public Set<Long> structureSeeds = null;
public List<Integer> pillarSeeds = null;
private TimeMachine timeMachine = new TimeMachine();
private List<StructureData> structureCache = new ArrayList<>();
private List<DecoratorData> decoratorCache = new ArrayList<>();
private List<BiomeData> biomeCache = new ArrayList<>();
private DataStorage dataStorage = new DataStorage();
@Override
public void onInitialize() {
RenderQueue.get().add("hand", FinderQueue.get()::renderFinders);
DecoratorCache.get().initialize();
/*
long ss = 5718603440394L;
LCG lcg = Rand.JAVA_LCG.combine(-5);
for(int i = 0; i < 8; i++) {
System.out.println("n");
PopulationReversal.getWorldSeeds(ss, 31 * 16, 19 * 16).forEach(seed -> {
System.out.println("Structure seed: " + seed);
for(int u = 0; u < 1 << 16; u++) {
long worldSeed = ((long)u << 48) | seed;
if(RandomSeed.isRandomSeed(worldSeed)) {
System.out.println("nextLong() equivalent: " + worldSeed);
}
}
});
ss = lcg.nextSeed(ss);
}*/
/*
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(new File("all_mineral.txt")));
StructureData t = new StructureData(new ChunkPos(0, 0), StructureFeatures.BURIED_TREASURE);
StructureData s = new StructureData(new ChunkPos(0, 0), StructureFeatures.SHIPWRECK);
StructureData v = new StructureData(new ChunkPos(1, 1), StructureFeatures.VILLAGE);
BiomeData b1 = new BiomeData(new BlockPos(9, 0, 9), biome -> biome.hasStructureFeature(Feature.BURIED_TREASURE));
BiomeData b2 = new BiomeData(new BlockPos(9, 0, 9), biome -> biome.hasStructureFeature(Feature.SHIPWRECK));
BiomeData b3 = new BiomeData(new BlockPos(9 + 16, 0, 9 + 16), biome -> biome.hasStructureFeature(Feature.VILLAGE));
SpawnPointData spawn = new SpawnPointData(BlockPos.ORIGIN, 50);
ChestLootData loot = new ChestLootData(
MCLootTables.BURIED_TREASURE_CHEST,
new ChestLootData.Stack(Items.EMERALD, Predicates.MORE_OR_EQUAL_TO, 3),
new ChestLootData.Stack(Items.IRON_INGOT, Predicates.MORE_OR_EQUAL_TO, 4),
new ChestLootData.Stack(Items.GOLD_INGOT, Predicates.MORE_OR_EQUAL_TO, 3),
new ChestLootData.Stack(Items.TNT, Predicates.MORE_OR_EQUAL_TO, 1),
new ChestLootData.Stack(Items.DIAMOND, Predicates.MORE_OR_EQUAL_TO, 1)
);
int count = 0;
Rand rand = new Rand(0L, false);
for(long seed = (1L << 48) - 1; seed >= 0; seed--) {
if(!t.test(seed, rand))continue;
if(!s.test(seed, rand))continue;
if(!v.test(seed, rand))continue;
if(!loot.test(seed + 20002L, rand))continue;
for(long u = 0L; u < 1L << 16; u++) {
long worldSeed = (u << 48) | seed;
FakeBiomeSource source = new FakeBiomeSource(worldSeed);
if(!b1.test(source))continue;
if(!b2.test(source))continue;
if(!b3.test(source))continue;
if(!spawn.test(source))continue;
String s2 = "[" + (++count) + "] " + worldSeed + " with structure seed " + seed + "\n";
System.out.print(s2);
writer.write(s2);
writer.flush();
break;
}
}
} catch(Exception e) {
e.printStackTrace();
}*/
/*
long seed = 1_000_000_000_000_000L;
int distance = 80;
Predicate<Biome>[] trees = new Predicate[] {
biome -> biome instanceof ForestBiome || biome instanceof WoodedHillsBiome, //OAK TREE
biome -> biome instanceof SavannaBiome || biome instanceof SavannaPlateauBiome, //ACACIA TREE
biome -> biome instanceof DarkForestHillsBiome || biome instanceof DarkForestBiome, //DARK OAK TREE
biome -> biome instanceof SnowyTaigaBiome || biome instanceof TaigaBiome, //SPRUCE TREE
biome -> biome instanceof JungleBiome || biome instanceof JungleHillsBiome || biome instanceof ModifiedJungleBiome, //JUNGLE TREE
};
Set<Integer> ids = new HashSet<>();
do {
FakeBiomeSource source = new FakeBiomeSource(seed);
BlockPos spawn = SpawnPointData.getSpawnPoint(source);
int x = spawn.getX();
int z = spawn.getZ();
//X direction
for(int s = -1; s != 1; s = 1) {
for(int d = 0; d < distance; d++) {
Biome biome = BiomeData.sampleBiome(source, x + s * d, 0, z);
for(int j = 0; j < trees.length; j++) {
if(!ids.contains(j) && trees[j].test(biome)) {
ids.add(j);
}
}
}
if(ids.size() >= trees.length - 1) {
System.out.println(seed + ", " + ids);
ids.clear();
break;
}
ids.clear();
//Z direction
for(int d = 0; d < distance; d++) {
Biome biome = BiomeData.sampleBiome(source, x, 0, z + s * d);
for(int j = 0; j < trees.length; j++) {
if(!ids.contains(j) && trees[j].test(biome)) {
ids.add(j);
}
}
}
if(ids.size() >= trees.length - 1) {
System.out.println(seed + ", " + ids);
ids.clear();
break;
}
ids.clear();
}
seed++;
} while(seed != 0L);*/
/*
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(new File("season_7_seeds_negative.txt")));
int count = 0;
long seed = 0L;
BiomeData m1 = new BiomeData(new BlockPos(-200, 0, 0), biome -> biome instanceof MushroomFieldsBiome || biome instanceof MushroomFieldShoreBiome);
BiomeData m2 = new BiomeData(new BlockPos(200, 0, 0), biome -> biome instanceof MushroomFieldsBiome || biome instanceof MushroomFieldShoreBiome);
BiomeData m3 = new BiomeData(new BlockPos(0, 0, -200), biome -> biome instanceof MushroomFieldsBiome || biome instanceof MushroomFieldShoreBiome);
BiomeData m4 = new BiomeData(new BlockPos(0, 0, 200), biome -> biome instanceof MushroomFieldsBiome || biome instanceof MushroomFieldShoreBiome);
do {
FakeBiomeSource source = new FakeBiomeSource(seed);
if(m1.test(source) && m2.test(source) && m3.test(source) && m4.test(source)) {
String s = "[" + (++count) + "] " + seed + "\n";
System.out.println(s);
writer.write(s);
writer.flush();
}
seed--;
} while(seed != 0L);
writer.close();
} catch(Exception e) {
;
}*/
/*
long popSeed = 107038380818082L;
for(int x = 0; x < 10000; x++) {
StructureData b = new StructureData(new ChunkPos(x, 0), StructureFeatures.BURIED_TREASURE);
BiomeData b1 = new BiomeData(new BlockPos(16 * x + 9, 0, 9), Biomes.BEACH);
List<Long> worldSeeds = Magic.getSeedFromChunkseed(popSeed, x * 16, 0);
int finalX = x;
worldSeeds.forEach(structureSeed -> {
if(b.test(structureSeed, new Rand(0L))) {
System.out.println("structure seed " + structureSeed + " at x " + (finalX * 16));
for(long u = 0L; u < 1L << 16; u++) {
long worldSeed = (u << 48) | structureSeed;
FakeBiomeSource source = new FakeBiomeSource(worldSeed);
if(!b1.test(source))continue;
System.out.println(worldSeed + " with structure seed " + structureSeed + " at x " + (finalX * 16));
}
}
});
}*/
}
public static SeedCracker get() {
return INSTANCE;
}
public void clear() {
this.hashedWorldSeed = -1;
this.worldSeeds = null;
this.structureSeeds = null;
this.pillarSeeds = null;
this.structureCache.clear();
this.biomeCache.clear();
this.decoratorCache.clear();
}
public synchronized boolean onPillarData(PillarData pillarData) {
if(pillarData != null && (this.pillarSeeds == null || this.pillarSeeds.isEmpty())) {
Log.warn("Looking for pillar seeds...");
this.pillarSeeds = pillarData.getPillarSeeds();
if(this.pillarSeeds.size() > 0) {
Log.warn("Finished search with " + this.pillarSeeds + (this.pillarSeeds.size() == 1 ? " seed." : " seeds."));
} else {
Log.error("Finished search with no seeds.");
}
this.onStructureData(null);
return true;
}
return false;
}
public synchronized boolean onStructureData(StructureData structureData) {
boolean added = false;
if(structureData != null && !this.structureCache.contains(structureData)) {
this.structureCache.add(structureData);
added = true;
}
if(this.structureSeeds == null && this.pillarSeeds != null && this.structureCache.size() + this.decoratorCache.size() >= 5) {
this.structureSeeds = new ConcurrentSet<>();
Log.warn("Looking for structure seeds with " + this.structureCache.size() + " structure features.");
Log.warn("Looking for structure seeds with " + this.decoratorCache.size() + " decorator features.");
this.pillarSeeds.forEach(pillarSeed -> {
timeMachine.buildStructureSeeds(pillarSeed, this.structureCache, this.decoratorCache, this.structureSeeds);
});
if(this.structureSeeds.size() > 0) {
Log.warn("Finished search with " + this.structureSeeds + (this.structureSeeds.size() == 1 ? " seed." : " seeds."));
} else {
Log.error("Finished search with no seeds.");
}
this.structureCache.clear();
this.onDecoratorData(null);
this.onBiomeData(null);
} else if(this.structureSeeds != null && structureData != null) {
this.structureSeeds.removeIf(structureSeed -> {
Rand rand = new Rand(0L);
return !structureData.test(structureSeed, rand);
});
this.onBiomeData(null);
}
return added;
}
public synchronized boolean onDecoratorData(DecoratorData decoratorData) {
boolean added = false;
if(decoratorData != null && !this.decoratorCache.contains(decoratorData)) {
this.decoratorCache.add(decoratorData);
added = true;
}
this.onStructureData(null);
return added;
}
public synchronized boolean onBiomeData(BiomeData biomeData) {
boolean added = false;
if(biomeData != null && !this.biomeCache.contains(biomeData)) {
this.biomeCache.add(biomeData);
added = true;
}
if(this.worldSeeds == null && this.structureSeeds != null && this.biomeCache.size() >= 5) {
this.worldSeeds = new ArrayList<>();
if(this.hashedWorldSeed != -1) {
Log.warn("SHA2 seed was found.");
Log.warn("Looking for world seeds with hash [" + this.hashedWorldSeed + "].");
for(long structureSeed: this.structureSeeds) {
for(long j = 0; j < (1L << 16); j++) {
long worldSeed = (j << 48) | structureSeed;
long hash = LevelProperties.sha256Hash(worldSeed);
if(hash == this.hashedWorldSeed) {
this.worldSeeds.add(worldSeed);
Log.warn("Finished search with " + this.worldSeeds + (this.worldSeeds.size() == 1 ? " seed." : " seeds."));
return added;
}
}
}
Log.error("Finished search with no seeds, reverting to biomes.");
}
Log.warn("Looking for world seeds with " + this.biomeCache.size() + " biomes.");
this.structureSeeds.forEach(structureSeed -> {
for(long j = 0; j < (1L << 16); j++) {
long worldSeed = (j << 48) | structureSeed;
boolean goodSeed = true;
FakeBiomeSource fakeBiomeSource = new FakeBiomeSource(worldSeed);
for(BiomeData data : this.biomeCache) {
if(!data.test(fakeBiomeSource)) {
goodSeed = false;
break;
}
}
if(goodSeed) {
this.worldSeeds.add(worldSeed);
}
}
});
if(this.worldSeeds.size() > 0) {
Log.warn("Finished search with " + this.worldSeeds + (this.worldSeeds.size() == 1 ? " seed." : " seeds."));
} else {
Log.error("Finished search with no seeds.");
}
} else if(this.worldSeeds != null && biomeData != null) {
this.worldSeeds.removeIf(worldSeed -> {
FakeBiomeSource fakeBiomeSource = new FakeBiomeSource(worldSeed);
return !biomeData.test(fakeBiomeSource);
});
} else if(this.worldSeeds != null) {
this.worldSeeds.removeIf(worldSeed -> {
for(BiomeData data: this.biomeCache) {
FakeBiomeSource fakeBiomeSource = new FakeBiomeSource(worldSeed);
if(!data.test(fakeBiomeSource))return true;
}
return false;
});
}
return added;
public DataStorage getDataStorage() {
return this.dataStorage;
}
}
@@ -1,12 +1,16 @@
package kaptainwutax.seedcracker.cracker;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.cracker.storage.ISeedStorage;
import kaptainwutax.seedcracker.cracker.storage.TimeMachine;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.VoronoiBiomeAccessType;
import java.util.function.Predicate;
public class BiomeData {
public class BiomeData implements ISeedStorage {
private BlockPos pos;
@@ -18,6 +22,12 @@ public class BiomeData {
this.biome = biome;
}
public BiomeData(BlockPos pos, int biomeId) {
this.pos = pos;
this.biome = Registry.BIOME.get(biomeId);
}
public BiomeData(BlockPos pos, Predicate<Biome> biomePredicate) {
this.pos = pos;
this.biomePredicate = biomePredicate;
@@ -40,7 +50,12 @@ public class BiomeData {
}
public static Biome sampleBiome(FakeBiomeSource source, int x, int y, int z) {
return VoronoiBiomeAccessType.INSTANCE.getBiome(source.getHashedSeed(), z, y, x, source);
return VoronoiBiomeAccessType.INSTANCE.getBiome(source.getHashedSeed(), x, y, z, source);
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.BIOMES);
}
@Override
@@ -54,4 +69,12 @@ public class BiomeData {
return false;
}
@Override
public int hashCode() {
if(this.biomePredicate == null) {
return this.biome.getName().asFormattedString().hashCode();
}
return super.hashCode();
}
}
@@ -13,7 +13,7 @@ import net.minecraft.util.math.BlockPos;
import java.util.*;
import java.util.function.BiPredicate;
public class ChestLootData implements ISeedData {
public class ChestLootData {
private LootTable lootTable;
private Map<Item, List<Stack>> stacksMap = new HashMap<>();
@@ -32,9 +32,8 @@ public class ChestLootData implements ISeedData {
}
}
@Override
public boolean test(long seed, Rand rand) {
rand.setSeed(seed, true);
public boolean test(long lootSeed, Rand rand) {
rand.setSeed(lootSeed, true);
LootContext lootContext = new LootBuilder().setRandom(rand.toRandom())
.put(LootContextParameters.POSITION, new BlockPos(0, 0, 0)).build(LootContextTypes.CHEST);
@@ -1,69 +0,0 @@
package kaptainwutax.seedcracker.cracker;
import kaptainwutax.seedcracker.util.Log;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.decorator.Decorator;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.DecoratedFeatureConfig;
import net.minecraft.world.gen.feature.FeatureConfig;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DecoratorCache {
public static int INVALID = -1;
private static DecoratorCache INSTANCE = new DecoratorCache();
private boolean initialized = false;
private Map<Biome, Map<Decorator<?>, Integer>> decoratorSaltMap = new HashMap<>();
public static DecoratorCache get() {
return INSTANCE;
}
public void initialize() {
Registry.BIOME.forEach(biome -> {
this.decoratorSaltMap.put(biome, new HashMap<>());
for(GenerationStep.Feature genStep: GenerationStep.Feature.values()) {
this.initializeBiomeStep(biome, genStep);
}
});
this.initialized = true;
}
private void initializeBiomeStep(Biome biome, GenerationStep.Feature genStep) {
List<ConfiguredFeature<?, ?>> features = biome.getFeaturesForStep(genStep);
for(int i = 0; i < features.size(); i++) {
FeatureConfig config = features.get(i).config;
if(!(config instanceof DecoratedFeatureConfig))continue;
this.decoratorSaltMap.get(biome).put(((DecoratedFeatureConfig)config).decorator.decorator, i + genStep.ordinal() * 10000);
}
}
public int getSalt(Biome biome, Decorator<?> feature, boolean debug) {
if(!this.initialized) {
this.initialize();
}
Integer salt = this.decoratorSaltMap.get(biome).get(feature);
if(salt == null) {
if(debug) {
Log.error(biome.getClass().getSimpleName() + " does not have decorator " + feature + ".");
}
salt = INVALID;
}
return salt;
}
}
@@ -43,7 +43,7 @@ public class FakeBiomeSource extends BiomeSource {
}
static {
BIOMES = ImmutableSet.of(Biomes.OCEAN, Biomes.PLAINS, Biomes.DESERT, Biomes.MOUNTAINS, Biomes.FOREST, Biomes.TAIGA, Biomes.SWAMP, Biomes.RIVER, Biomes.FROZEN_OCEAN, Biomes.FROZEN_RIVER, Biomes.SNOWY_TUNDRA, Biomes.SNOWY_MOUNTAINS, Biomes.MUSHROOM_FIELDS, Biomes.MUSHROOM_FIELD_SHORE, Biomes.BEACH, Biomes.DESERT_HILLS, Biomes.WOODED_HILLS, Biomes.TAIGA_HILLS, Biomes.MOUNTAIN_EDGE, Biomes.JUNGLE, Biomes.JUNGLE_HILLS, Biomes.JUNGLE_EDGE, Biomes.DEEP_OCEAN, Biomes.STONE_SHORE, Biomes.SNOWY_BEACH, Biomes.BIRCH_FOREST, Biomes.BIRCH_FOREST_HILLS, Biomes.DARK_FOREST, Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS, Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS, Biomes.WOODED_MOUNTAINS, Biomes.SAVANNA, Biomes.SAVANNA_PLATEAU, Biomes.BADLANDS, Biomes.WOODED_BADLANDS_PLATEAU, Biomes.BADLANDS_PLATEAU, Biomes.WARM_OCEAN, Biomes.LUKEWARM_OCEAN, Biomes.COLD_OCEAN, Biomes.DEEP_WARM_OCEAN, Biomes.DEEP_LUKEWARM_OCEAN, Biomes.DEEP_COLD_OCEAN, Biomes.DEEP_FROZEN_OCEAN, Biomes.SUNFLOWER_PLAINS, Biomes.DESERT_LAKES, Biomes.GRAVELLY_MOUNTAINS, Biomes.FLOWER_FOREST, Biomes.TAIGA_MOUNTAINS, Biomes.SWAMP_HILLS, Biomes.ICE_SPIKES, Biomes.MODIFIED_JUNGLE, Biomes.MODIFIED_JUNGLE_EDGE, Biomes.TALL_BIRCH_FOREST, Biomes.TALL_BIRCH_HILLS, Biomes.DARK_FOREST_HILLS, Biomes.SNOWY_TAIGA_MOUNTAINS, Biomes.GIANT_SPRUCE_TAIGA, Biomes.GIANT_SPRUCE_TAIGA_HILLS, Biomes.MODIFIED_GRAVELLY_MOUNTAINS, Biomes.SHATTERED_SAVANNA, Biomes.SHATTERED_SAVANNA_PLATEAU, Biomes.ERODED_BADLANDS, Biomes.MODIFIED_WOODED_BADLANDS_PLATEAU, Biomes.MODIFIED_BADLANDS_PLATEAU);
BIOMES = ImmutableSet.of(Biomes.OCEAN, Biomes.PLAINS, Biomes.DESERT, Biomes.MOUNTAINS, Biomes.FOREST, Biomes.TAIGA, new Biome[]{Biomes.SWAMP, Biomes.RIVER, Biomes.FROZEN_OCEAN, Biomes.FROZEN_RIVER, Biomes.SNOWY_TUNDRA, Biomes.SNOWY_MOUNTAINS, Biomes.MUSHROOM_FIELDS, Biomes.MUSHROOM_FIELD_SHORE, Biomes.BEACH, Biomes.DESERT_HILLS, Biomes.WOODED_HILLS, Biomes.TAIGA_HILLS, Biomes.MOUNTAIN_EDGE, Biomes.JUNGLE, Biomes.JUNGLE_HILLS, Biomes.JUNGLE_EDGE, Biomes.DEEP_OCEAN, Biomes.STONE_SHORE, Biomes.SNOWY_BEACH, Biomes.BIRCH_FOREST, Biomes.BIRCH_FOREST_HILLS, Biomes.DARK_FOREST, Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS, Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS, Biomes.WOODED_MOUNTAINS, Biomes.SAVANNA, Biomes.SAVANNA_PLATEAU, Biomes.BADLANDS, Biomes.WOODED_BADLANDS_PLATEAU, Biomes.BADLANDS_PLATEAU, Biomes.WARM_OCEAN, Biomes.LUKEWARM_OCEAN, Biomes.COLD_OCEAN, Biomes.DEEP_WARM_OCEAN, Biomes.DEEP_LUKEWARM_OCEAN, Biomes.DEEP_COLD_OCEAN, Biomes.DEEP_FROZEN_OCEAN, Biomes.SUNFLOWER_PLAINS, Biomes.DESERT_LAKES, Biomes.GRAVELLY_MOUNTAINS, Biomes.FLOWER_FOREST, Biomes.TAIGA_MOUNTAINS, Biomes.SWAMP_HILLS, Biomes.ICE_SPIKES, Biomes.MODIFIED_JUNGLE, Biomes.MODIFIED_JUNGLE_EDGE, Biomes.TALL_BIRCH_FOREST, Biomes.TALL_BIRCH_HILLS, Biomes.DARK_FOREST_HILLS, Biomes.SNOWY_TAIGA_MOUNTAINS, Biomes.GIANT_SPRUCE_TAIGA, Biomes.GIANT_SPRUCE_TAIGA_HILLS, Biomes.MODIFIED_GRAVELLY_MOUNTAINS, Biomes.SHATTERED_SAVANNA, Biomes.SHATTERED_SAVANNA_PLATEAU, Biomes.ERODED_BADLANDS, Biomes.MODIFIED_WOODED_BADLANDS_PLATEAU, Biomes.MODIFIED_BADLANDS_PLATEAU});
}
}
@@ -1,9 +0,0 @@
package kaptainwutax.seedcracker.cracker;
import kaptainwutax.seedcracker.util.Rand;
public interface ISeedData {
boolean test(long seed, Rand rand);
}
@@ -1,11 +1,16 @@
package kaptainwutax.seedcracker.cracker;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.cracker.storage.SeedData;
import kaptainwutax.seedcracker.cracker.storage.TimeMachine;
import kaptainwutax.seedcracker.util.Rand;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class PillarData {
public class PillarData extends SeedData {
private List<Integer> heights;
@@ -13,25 +18,20 @@ public class PillarData {
this.heights = heights;
}
public List<Integer> getPillarSeeds() {
List<Integer> result = new ArrayList<>();
for(int pillarSeed = 0; pillarSeed < (1 << 16); pillarSeed++) {
List<Integer> h = this.getPillarHeights(pillarSeed);
if(h.equals(this.heights)) result.add(pillarSeed);
}
return result;
@Override
public boolean test(long seed, Rand rand) {
List<Integer> h = this.getPillarHeights((int)seed);
return h.equals(this.heights);
}
public List<Integer> getPillarHeights(int spikeSeed) {
public List<Integer> getPillarHeights(int pillarSeed) {
List<Integer> indices = new ArrayList<>();
for(int i = 0; i < 10; i++) {
indices.add(i);
}
Collections.shuffle(indices, new Random(spikeSeed));
Collections.shuffle(indices, new Random(pillarSeed));
List<Integer> heights = new ArrayList<>();
@@ -42,4 +42,14 @@ public class PillarData {
return heights;
}
@Override
public double getBits() {
return 16;
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.PILLARS);
}
}
@@ -1,13 +1,18 @@
package kaptainwutax.seedcracker.cracker;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.cracker.storage.SeedData;
import kaptainwutax.seedcracker.cracker.storage.TimeMachine;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.Seeds;
import net.minecraft.util.math.ChunkPos;
public class SlimeChunkData implements ISeedData {
public class SlimeChunkData extends SeedData {
protected ChunkPos chunkPos;
protected boolean isSlimeChunk;
protected static final double BITS = Math.log(10) / Math.log(2);
protected final ChunkPos chunkPos;
protected final boolean isSlimeChunk;
public SlimeChunkData(ChunkPos chunkPos, boolean isSlimeChunk) {
this.chunkPos = chunkPos;
@@ -20,4 +25,14 @@ public class SlimeChunkData implements ISeedData {
return (rand.nextInt(10) == 0) == this.isSlimeChunk;
}
@Override
public double getBits() {
return BITS;
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.STRUCTURES);
}
}
@@ -1,100 +0,0 @@
package kaptainwutax.seedcracker.cracker;
import kaptainwutax.seedcracker.cracker.population.DecoratorData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.util.Log;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.math.LCG;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class TimeMachine {
private LCG inverseLCG = Rand.JAVA_LCG.combine(-2);
public int THREAD_COUNT = 4;
public ExecutorService SERVICE = Executors.newFixedThreadPool(THREAD_COUNT);
private boolean isRunning = false;
public TimeMachine() {
}
public List<Long> bruteforceRegion(int pillarSeed, int region, long size, List<StructureData> structureDataList, List<DecoratorData> decoratorDataList) {
List<Long> result = new ArrayList<>();
Rand rand = new Rand(0L);
long start = region * size;
long end = start + size;
for(long i = start; i < end; i++) {
long structureSeed = this.timeMachine(i, pillarSeed);
boolean goodSeed = true;
for(StructureData structureData: structureDataList) {
if(!goodSeed)break;
if(!structureData.test(structureSeed, rand))goodSeed = false;
}
for(DecoratorData decoratorData : decoratorDataList) {
if(!goodSeed)break;
if(!decoratorData.test(structureSeed, rand))goodSeed = false;
}
if(goodSeed) {
result.add(structureSeed);
}
}
return result;
}
public Set<Long> buildStructureSeeds(int pillarSeed, List<StructureData> structureDataList, List<DecoratorData> decoratorDataList, Set<Long> structureSeeds) {
if(this.isRunning) {
throw new IllegalStateException("Time Machine is already running");
}
this.isRunning = true;
long size = (long)Math.ceil((double)(1L << 32) / THREAD_COUNT);
AtomicInteger progress = new AtomicInteger();
for(int i = 0; i < THREAD_COUNT; i++) {
int finalI = i;
SERVICE.submit(() -> {
structureSeeds.addAll(this.bruteforceRegion(pillarSeed, finalI, size, structureDataList, decoratorDataList));
Log.warn("Completed thread " + finalI + "!");
progress.getAndIncrement();
});
}
while(progress.get() < THREAD_COUNT) {
try {Thread.sleep(20);}
catch(InterruptedException e) {e.printStackTrace();}
}
this.isRunning = false;
return structureSeeds;
}
public long timeMachine(long partialWorldSeed, int pillarSeed) {
long currentSeed = 0L;
currentSeed |= (partialWorldSeed & 0xFFFF0000L) << 16;
currentSeed |= (long)pillarSeed << 16;
currentSeed |= partialWorldSeed & 0xFFFFL;
currentSeed = this.inverseLCG.nextSeed(currentSeed);
currentSeed ^= Rand.JAVA_LCG.multiplier;
return currentSeed;
}
public void stop() {
this.isRunning = false;
}
}
@@ -1,32 +1,26 @@
package kaptainwutax.seedcracker.cracker.population;
import kaptainwutax.seedcracker.cracker.DecoratorCache;
import kaptainwutax.seedcracker.cracker.ISeedData;
import kaptainwutax.seedcracker.cracker.storage.SeedData;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.Seeds;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.decorator.Decorator;
public abstract class DecoratorData implements ISeedData {
public abstract class DecoratorData extends SeedData {
private final ChunkPos chunkPos;
private final Decorator<?> decorator;
private final int salt;
private final Biome biome;
public DecoratorData(ChunkPos chunkPos, Decorator<?> decorator, Biome biome) {
public DecoratorData(ChunkPos chunkPos, int salt, Biome biome) {
this.chunkPos = chunkPos;
this.decorator = decorator;
this.salt = salt;
this.biome = biome;
}
@Override
public final boolean test(long seed, Rand rand) {
long decoratorSeed = this.getPopulationSeed(seed, this.chunkPos.x << 4, this.chunkPos.z << 4);
int salt = DecoratorCache.get().getSalt(this.biome, this.decorator, true);
if(salt == DecoratorCache.INVALID) {
return false;
}
long decoratorSeed = Seeds.setPopulationSeed(null, seed, this.chunkPos.x << 4, this.chunkPos.z << 4);
decoratorSeed += salt;
decoratorSeed ^= Rand.JAVA_LCG.multiplier;
@@ -35,13 +29,6 @@ public abstract class DecoratorData implements ISeedData {
return this.testDecorator(rand);
}
public long getPopulationSeed(long structureSeed, int x, int z) {
Rand rand = new Rand(structureSeed, true);
long a = rand.nextLong() | 1L;
long b = rand.nextLong() | 1L;
return (long)x * a + (long)z * b ^ structureSeed;
}
public abstract boolean testDecorator(Rand rand);
@Override
@@ -50,10 +37,15 @@ public abstract class DecoratorData implements ISeedData {
if(obj instanceof DecoratorData) {
DecoratorData decoratorData = ((DecoratorData)obj);
return decoratorData.chunkPos.equals(this.chunkPos) && decoratorData.decorator == this.decorator;
return decoratorData.chunkPos.equals(this.chunkPos) && decoratorData.salt == this.salt;
}
return false;
}
@Override
public int hashCode() {
return this.chunkPos.hashCode() * 31 + this.salt;
}
}
@@ -1,17 +1,20 @@
package kaptainwutax.seedcracker.cracker.population;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.cracker.storage.TimeMachine;
import kaptainwutax.seedcracker.util.Rand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.decorator.Decorator;
public class DesertWellData extends DecoratorData {
public static final int SALT = 30010;
private static final double BITS = Math.log(1000 * 16 * 16) / Math.log(2);
private BlockPos pos;
public DesertWellData(ChunkPos chunkPos, Biome biome, BlockPos pos) {
super(chunkPos, Decorator.CHANCE_HEIGHTMAP, biome);
super(chunkPos, SALT, biome);
this.pos = new BlockPos(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
}
@@ -23,4 +26,14 @@ public class DesertWellData extends DecoratorData {
return true;
}
@Override
public double getBits() {
return BITS;
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.STRUCTURES);
}
}
@@ -1,18 +1,22 @@
package kaptainwutax.seedcracker.cracker.population;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.cracker.storage.TimeMachine;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.math.LCG;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.decorator.Decorator;
import java.util.List;
public class DungeonData extends DecoratorData {
public static LCG REVERSE_SKIP = Rand.JAVA_LCG.combine(-1);
public static LCG Y_START_SKIP = Rand.JAVA_LCG.combine(2);
private static final double BITS = Math.log(256 * 16 * 16 * 0.125D) / Math.log(2);
public static final int SALT = 20003;
private static LCG REVERSE_SKIP = Rand.JAVA_LCG.combine(-1);
private static LCG Y_START_SKIP = Rand.JAVA_LCG.combine(2);
public static LCG Y_SKIP = Rand.JAVA_LCG.combine(5);
public static final Integer COBBLESTONE_CALL = 0;
@@ -22,7 +26,7 @@ public class DungeonData extends DecoratorData {
private final List<List<Integer>> floorCallsList;
public DungeonData(ChunkPos chunkPos, Biome biome, List<BlockPos> starts, List<List<Integer>> floorCallsList) {
super(chunkPos, Decorator.DUNGEONS, biome);
super(chunkPos, SALT, biome);
this.starts = starts;
this.floorCallsList = floorCallsList;
}
@@ -50,4 +54,14 @@ public class DungeonData extends DecoratorData {
return false;
}
@Override
public double getBits() {
return BITS;
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.STRUCTURES);
}
}
@@ -1,18 +1,22 @@
package kaptainwutax.seedcracker.cracker.population;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.cracker.storage.TimeMachine;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.math.LCG;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.decorator.Decorator;
import java.util.List;
import java.util.stream.Collectors;
public class EmeraldOreData extends DecoratorData {
public static final LCG[] SKIP = {
private static final double BITS = Math.log(28 * 16 * 16 * 0.5D) / Math.log(2);
public static final int SALT = 40014;
private static final LCG[] SKIP = {
Rand.JAVA_LCG.combine(0),
Rand.JAVA_LCG.combine(1),
Rand.JAVA_LCG.combine(2),
@@ -22,7 +26,7 @@ public class EmeraldOreData extends DecoratorData {
private List<BlockPos> starts;
public EmeraldOreData(ChunkPos chunkPos, Biome biome, List<BlockPos> ores) {
super(chunkPos, Decorator.EMERALD_ORE, biome);
super(chunkPos, SALT, biome);
this.starts = ores.stream().map(pos ->
new BlockPos(pos.getX() & 15, pos.getY(), pos.getZ() & 15)
@@ -51,4 +55,14 @@ public class EmeraldOreData extends DecoratorData {
return false;
}
@Override
public double getBits() {
return BITS;
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.STRUCTURES);
}
}
@@ -1,19 +1,23 @@
package kaptainwutax.seedcracker.cracker.population;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.cracker.storage.TimeMachine;
import kaptainwutax.seedcracker.util.Rand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.decorator.Decorator;
public class EndGatewayData extends DecoratorData {
private static final double BITS = Math.log(700 * 16 * 16 * 7) / Math.log(2);
private static final int SALT = 30000;
private int xOffset;
private int zOffset;
private int height;
public EndGatewayData(ChunkPos chunkPos, Biome biome, BlockPos pos, int height) {
super(chunkPos, Decorator.END_GATEWAY, biome);
super(chunkPos, SALT, biome);
this.xOffset = pos.getX() & 15;
this.zOffset = pos.getZ() & 15;
this.height = height;
@@ -29,4 +33,14 @@ public class EndGatewayData extends DecoratorData {
return true;
}
@Override
public double getBits() {
return BITS;
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.STRUCTURES);
}
}
@@ -0,0 +1,123 @@
package kaptainwutax.seedcracker.cracker.storage;
import io.netty.util.internal.ConcurrentSet;
import kaptainwutax.seedcracker.cracker.BiomeData;
import kaptainwutax.seedcracker.cracker.PillarData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.util.Log;
import java.util.Comparator;
import java.util.Set;
import java.util.function.Consumer;
public class DataStorage {
public static Comparator<SeedData> SEED_DATA_COMPARATOR = (s1, s2) -> {
boolean isStructure1 = s1 instanceof StructureData;
boolean isStructure2 = s2 instanceof StructureData;
if(isStructure1 != isStructure2) {
return isStructure2 ? 1: -1;
}
if(s1.equals(s2)) {
return 0;
}
double diff = s2.getBits() - s1.getBits();
return diff == 0.0D ? 1 : (int)Math.signum(diff);
};
protected TimeMachine timeMachine = new TimeMachine(this);
protected Set<Consumer<DataStorage>> scheduledData = new ConcurrentSet<>();
protected PillarData pillarData = null;
protected ScheduledSet<SeedData> baseSeedData = new ScheduledSet<>(SEED_DATA_COMPARATOR);
protected ScheduledSet<BiomeData> biomeSeedData = new ScheduledSet<>(null);
protected HashedSeedData hashedSeedData = null;
public void tick() {
if(!this.timeMachine.isRunning) {
this.baseSeedData.dump();
this.biomeSeedData.dump();
this.scheduledData.removeIf(c -> {
c.accept(this);
return true;
});
}
}
public synchronized boolean addPillarData(PillarData pillarData) {
boolean isAdded = this.pillarData == null;
if(isAdded && pillarData != null) {
this.pillarData = pillarData;
this.schedule(pillarData::onDataAdded);
}
return isAdded;
}
public synchronized boolean addBaseData(SeedData seedData) {
if(this.baseSeedData.contains(seedData)) {
return false;
}
this.baseSeedData.scheduleAdd(seedData);
this.schedule(seedData::onDataAdded);
return true;
}
public synchronized boolean addBiomeData(BiomeData biomeData) {
if(this.biomeSeedData.contains(biomeData)) {
return false;
}
this.biomeSeedData.scheduleAdd(biomeData);
this.schedule(biomeData::onDataAdded);
return true;
}
public synchronized boolean addHashedSeedData(HashedSeedData hashedSeedData) {
if(this.hashedSeedData == null || this.hashedSeedData.getHashedSeed() != hashedSeedData.getHashedSeed()) {
Log.warn("Fetched hashed world seed [" + hashedSeedData.getHashedSeed() + "].");
this.hashedSeedData = hashedSeedData;
this.schedule(hashedSeedData::onDataAdded);
return true;
}
return false;
}
public void schedule(Consumer<DataStorage> consumer) {
this.scheduledData.add(consumer);
}
public TimeMachine getTimeMachine() {
return this.timeMachine;
}
public double getBaseBits() {
double bits = 0.0D;
for(SeedData baseSeedDatum: this.baseSeedData) {
bits += baseSeedDatum.getBits();
}
return bits;
}
public void clear() {
System.out.println("Clearing data storage.");
this.scheduledData = new ConcurrentSet<>();
this.pillarData = null;
this.baseSeedData = new ScheduledSet<>(SEED_DATA_COMPARATOR);
this.biomeSeedData = new ScheduledSet<>(null);
this.hashedSeedData = null;
this.timeMachine.terminate = true;
this.timeMachine = new TimeMachine(this);
}
}
@@ -0,0 +1,33 @@
package kaptainwutax.seedcracker.cracker.storage;
import kaptainwutax.seedcracker.util.Rand;
import net.minecraft.world.level.LevelProperties;
public class HashedSeedData extends SeedData {
private final long hashedSeed;
public HashedSeedData(long hashedSeed) {
this.hashedSeed = hashedSeed;
}
@Override
public boolean test(long seed, Rand rand) {
return LevelProperties.sha256Hash(seed) == this.hashedSeed;
}
public long getHashedSeed() {
return this.hashedSeed;
}
@Override
public double getBits() {
return 64;
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.BIOMES);
}
}
@@ -0,0 +1,7 @@
package kaptainwutax.seedcracker.cracker.storage;
public interface ISeedStorage {
void onDataAdded(DataStorage dataStorage);
}
@@ -0,0 +1,44 @@
package kaptainwutax.seedcracker.cracker.storage;
import java.util.*;
public class ScheduledSet<T> implements Iterable<T> {
protected final Set<T> baseSet;
protected final Set<T> scheduledSet;
public ScheduledSet(Comparator<T> comparator) {
if(comparator != null) {
this.baseSet = new TreeSet<>(comparator);
} else {
this.baseSet = new HashSet<>();
}
this.scheduledSet = new HashSet<>();
}
public synchronized void scheduleAdd(T e) {
this.scheduledSet.add(e);
}
public synchronized void dump() {
synchronized(this.baseSet) {
this.baseSet.addAll(this.scheduledSet);
this.scheduledSet.clear();
}
}
public synchronized boolean contains(T e) {
return this.baseSet.contains(e) || this.scheduledSet.contains(e);
}
@Override
public synchronized Iterator<T> iterator() {
return this.baseSet.iterator();
}
public synchronized int size() {
return this.baseSet.size();
}
}
@@ -0,0 +1,11 @@
package kaptainwutax.seedcracker.cracker.storage;
import kaptainwutax.seedcracker.util.Rand;
public abstract class SeedData implements ISeedStorage {
public abstract boolean test(long seed, Rand rand);
public abstract double getBits();
}
@@ -0,0 +1,215 @@
package kaptainwutax.seedcracker.cracker.storage;
import kaptainwutax.seedcracker.cracker.BiomeData;
import kaptainwutax.seedcracker.cracker.FakeBiomeSource;
import kaptainwutax.seedcracker.util.Log;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.math.LCG;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TimeMachine {
public static ExecutorService SERVICE = Executors.newFixedThreadPool(1);
private LCG inverseLCG = Rand.JAVA_LCG.combine(-2);
protected DataStorage dataStorage;
public boolean isRunning = false;
public boolean terminate = false;
protected List<Integer> pillarSeeds = null;
protected List<Long> structureSeeds = null;
protected List<Long> worldSeeds = null;
public TimeMachine(DataStorage dataStorage) {
this.dataStorage = dataStorage;
}
public void poke(Phase phase) {
this.isRunning = true;
final Phase[] finalPhase = {phase};
SERVICE.submit(() -> {
while(finalPhase[0] != null && !this.terminate) {
if(finalPhase[0] == Phase.PILLARS) {
if(!this.pokePillars())break;
} else if(finalPhase[0] == Phase.STRUCTURES) {
if(!this.pokeStructures())break;
} else if(finalPhase[0] == Phase.BIOMES) {
if(!this.pokeBiomes())break;
}
finalPhase[0] = finalPhase[0].nextPhase();
}
});
this.isRunning = false;
}
protected boolean pokePillars() {
if(this.pillarSeeds != null || this.dataStorage.pillarData == null)return false;
this.pillarSeeds = new ArrayList<>();
Log.debug("====================================");
Log.warn("Looking for pillar seeds...");
for(int pillarSeed = 0; pillarSeed < 1 << 16 && !this.terminate; pillarSeed++) {
if(this.dataStorage.pillarData.test(pillarSeed, null)) {
Log.warn("Found pillar seed [" + pillarSeed + "].");
this.pillarSeeds.add(pillarSeed);
}
}
if(!this.pillarSeeds.isEmpty()) {
Log.warn("Finished searching for pillar seeds.");
} else {
Log.error("Finished search with no results.");
}
return true;
}
protected boolean pokeStructures() {
if(this.pillarSeeds == null || this.structureSeeds != null || this.dataStorage.getBaseBits() < 54.0D)return false;
this.structureSeeds = new ArrayList<>();
Rand rand = new Rand(0L, false);
Log.debug("====================================");
Log.warn("Looking for structure seeds...");
for(int pillarSeed: this.pillarSeeds) {
for(long partialWorldSeed = 0; partialWorldSeed < 1L << 32; partialWorldSeed++) {
if((partialWorldSeed & ((1 << 29) - 1)) == 0) {
Log.debug("Progress: " + ((100.0D * (double)partialWorldSeed) / (double)(1L << 32)) + "%");
}
long seed = this.timeMachine(partialWorldSeed, pillarSeed);
boolean matches = true;
for(SeedData baseSeedDatum: this.dataStorage.baseSeedData) {
if(!baseSeedDatum.test(seed, rand)) {
matches = false;
break;
}
}
if(matches) {
this.structureSeeds.add(seed);
Log.warn("Found structure seed [" + seed + "].");
}
if(this.terminate) {
return false;
}
}
}
if(!this.structureSeeds.isEmpty()) {
Log.warn("Finished searching for structure seeds.");
} else {
Log.error("Finished search with no results.");
}
return true;
}
protected boolean pokeBiomes() {
if(this.structureSeeds == null || this.worldSeeds != null)return false;
if(this.dataStorage.hashedSeedData == null && this.dataStorage.biomeSeedData.size() < 5)return false;
this.worldSeeds = new ArrayList<>();
Log.debug("====================================");
Log.warn("Looking for world seeds...");
if(this.dataStorage.hashedSeedData != null) {
for(long structureSeed: this.structureSeeds) {
for(long upperBits = 0; upperBits < 1 << 16; upperBits++) {
long worldSeed = (upperBits << 48) | structureSeed;
if(!this.dataStorage.hashedSeedData.test(worldSeed, null)) {
continue;
} else {
this.worldSeeds.add(worldSeed);
Log.warn("Found world seed [" + worldSeed + "]");
}
if(this.terminate) {
return false;
}
}
}
if(!this.worldSeeds.isEmpty()) {
Log.warn("Finished searching for world seeds.");
return true;
} else {
Log.error("Finished search with no results, reverting back to biomes.");
}
}
Log.warn("Looking for world seeds...");
for(long structureSeed: this.structureSeeds) {
for(long upperBits = 0; upperBits < 1 << 16; upperBits++) {
long worldSeed = (upperBits << 48) | structureSeed;
FakeBiomeSource source = new FakeBiomeSource(worldSeed);
boolean matches = true;
for(BiomeData biomeSeedDatum: this.dataStorage.biomeSeedData) {
if(!biomeSeedDatum.test(source)) {
matches = false;
break;
}
}
if(matches) {
this.worldSeeds.add(worldSeed);
Log.warn("Found world seed [" + worldSeed + "].");
}
if(this.terminate) {
return false;
}
}
}
if(!this.worldSeeds.isEmpty()) {
Log.warn("Finished searching for world seeds.");
} else {
Log.error("Finished search with no results.");
}
return true;
}
public long timeMachine(long partialWorldSeed, int pillarSeed) {
long currentSeed = 0L;
currentSeed |= (partialWorldSeed & 0xFFFF0000L) << 16;
currentSeed |= (long)pillarSeed << 16;
currentSeed |= partialWorldSeed & 0xFFFFL;
currentSeed = this.inverseLCG.nextSeed(currentSeed);
currentSeed ^= Rand.JAVA_LCG.multiplier;
return currentSeed;
}
public enum Phase {
BIOMES(null), STRUCTURES(BIOMES), PILLARS(STRUCTURES);
private final Phase nextPhase;
Phase(Phase nextPhase) {
this.nextPhase = nextPhase;
}
public Phase nextPhase() {
return this.nextPhase;
}
}
}
@@ -1,12 +1,14 @@
package kaptainwutax.seedcracker.cracker.structure;
import kaptainwutax.seedcracker.cracker.ISeedData;
import kaptainwutax.seedcracker.cracker.storage.DataStorage;
import kaptainwutax.seedcracker.cracker.storage.SeedData;
import kaptainwutax.seedcracker.cracker.storage.TimeMachine;
import kaptainwutax.seedcracker.cracker.structure.type.FeatureType;
import kaptainwutax.seedcracker.util.Seeds;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.Seeds;
import net.minecraft.util.math.ChunkPos;
public class StructureData implements ISeedData {
public class StructureData extends SeedData {
public int chunkX;
public int chunkZ;
@@ -29,16 +31,31 @@ public class StructureData implements ISeedData {
return this.featureType.test(rand, this, structureSeed);
}
@Override
public double getBits() {
return this.featureType.getBits();
}
@Override
public void onDataAdded(DataStorage dataStorage) {
dataStorage.getTimeMachine().poke(TimeMachine.Phase.STRUCTURES);
}
@Override
public boolean equals(Object obj) {
if(obj == this)return true;
if(obj instanceof StructureData) {
StructureData structureData = ((StructureData)obj);
return structureData.regionX == this.regionX && structureData.regionZ == this.regionZ && structureData.featureType == this.featureType;
return structureData.featureType == this.featureType && structureData.regionX == this.regionX && structureData.regionZ == this.regionZ;
}
return false;
}
@Override
public int hashCode() {
return this.regionX * 961 + this.regionZ * 31 + this.featureType.salt;
}
}
@@ -41,6 +41,8 @@ public class StructureFeatures {
public static final FeatureType<StructureData> BURIED_TREASURE = new RarityType(10387320, 1, 0.01F);
public static final FeatureType<StructureData> NETHER_FORTRESS = new FeatureType<StructureData>(-1, 1) {
protected final double bits = Math.log(3 * 8 * 8) / Math.log(2);
@Override
public boolean test(Rand rand, StructureData data, long structureSeed) {
Seeds.setWeakSeed(rand, structureSeed, data.chunkX, data.chunkZ);
@@ -49,14 +51,26 @@ public class StructureFeatures {
&& data.chunkX == ((data.chunkX >> 4) << 4) + 4 + rand.nextInt(8)
&& data.chunkZ == ((data.chunkZ >> 4) << 4) + 4 + rand.nextInt(8);
}
@Override
public double getBits() {
return this.bits;
}
};
public static final FeatureType<StructureData> MINESHAFT = new FeatureType<StructureData>(-1, 1) {
protected final double bits = Math.log(1.0D / 0.004D) / Math.log(2);
@Override
public boolean test(Rand rand, StructureData data, long structureSeed) {
Seeds.setStructureStartSeed(rand, structureSeed, data.chunkX, data.chunkZ);
return rand.nextDouble() < 0.004D;
}
@Override
public double getBits() {
return this.bits;
}
};
}
@@ -6,10 +6,12 @@ import kaptainwutax.seedcracker.util.Rand;
public class AbstractTempleType extends FeatureType<StructureData> {
protected final int offset;
protected final double bits;
public AbstractTempleType(int salt, int distance, int offset) {
super(salt, distance);
this.offset = offset;
this.bits = Math.log(this.offset * this.offset) / Math.log(2);
}
@Override
@@ -17,4 +19,9 @@ public class AbstractTempleType extends FeatureType<StructureData> {
return rand.nextInt(this.offset) == data.offsetX && rand.nextInt(this.offset) == data.offsetZ;
}
@Override
public double getBits() {
return this.bits;
}
}
@@ -40,4 +40,6 @@ public abstract class FeatureType<T extends StructureData> {
public abstract boolean test(Rand rand, T data, long structureSeed);
public abstract double getBits();
}
@@ -5,11 +5,13 @@ import kaptainwutax.seedcracker.util.Rand;
public class RarityType extends FeatureType<StructureData> {
private float rarity;
private final float rarity;
private final double bits;
public RarityType(int salt, int distance, float rarity) {
super(salt, distance);
this.rarity = rarity;
this.bits = Math.log(1.0D / this.rarity) / Math.log(2);
}
@Override
@@ -17,4 +19,9 @@ public class RarityType extends FeatureType<StructureData> {
return rand.nextFloat() < this.rarity;
}
@Override
public double getBits() {
return this.bits;
}
}
@@ -7,10 +7,12 @@ import kaptainwutax.seedcracker.util.Rand;
public class TriangularType extends FeatureType<StructureData> {
protected final int peak;
protected final double bits;
public TriangularType(int salt, int distance, int peak) {
super(salt, distance);
this.peak = peak;
this.bits = Math.log(this.peak * this.peak) / Math.log(2);
}
@Override
@@ -19,4 +21,12 @@ public class TriangularType extends FeatureType<StructureData> {
&& (rand.nextInt(this.peak) + rand.nextInt(this.peak)) / 2 == data.offsetZ;
}
/*
* For this type specifically, the bits approximation is bad since the distribution is special.
*/
@Override
public double getBits() {
return this.bits;
}
}
@@ -35,7 +35,7 @@ public class BiomeFinder extends Finder {
continue;
}
if(SeedCracker.get().onBiomeData(new BiomeData(blockPos, biome))) {
if(SeedCracker.get().getDataStorage().addBiomeData(new BiomeData(blockPos, biome))) {
blockPos = this.world.getTopPosition(Heightmap.Type.WORLD_SURFACE, blockPos).down();
result.add(blockPos);
}
@@ -27,17 +27,20 @@ public class FinderQueue {
}
public void onChunkData(World world, ChunkPos chunkPos) {
this.finderConfig.getActiveFinderTypes().forEach(type -> {
this.finderConfig.getActiveFinderTypes().forEach(type -> {
SERVICE.submit(() -> {
List<Finder> finders = type.finderBuilder.build(world, chunkPos);
try {
List<Finder> finders = type.finderBuilder.build(world, chunkPos);
finders.forEach(finder -> {
if(finder.isValidDimension(world.dimension.getType())) {
this.finderConfig.addFinder(type, finder);
finder.findInChunk();
this.finderConfig.addFinder(type, finder);
}
});
finders.forEach(finder -> {
if(finder.isValidDimension(world.dimension.getType())) {
finder.findInChunk();
this.finderConfig.addFinder(type, finder);
}
});
} catch(Exception e) {
e.printStackTrace();
}
});
});
}
@@ -48,9 +48,10 @@ public class DesertWellFinder extends PieceFinder {
List<BlockPos> result = super.findInChunk();
result.forEach(pos -> {
this.renderers.add(new Cuboid(pos, SIZE, new Vector4f(0.5f, 0.5f, 1.0f, 1.0f)));
this.renderers.add(new Cube(pos.add(2, 1, 2), new Vector4f(0.5f, 0.5f, 1.0f, 1.0f)));
SeedCracker.get().onDecoratorData(new DesertWellData(this.chunkPos, biome, pos.add(2, 1, 2)));
if(SeedCracker.get().getDataStorage().addBaseData(new DesertWellData(this.chunkPos, biome, pos.add(2, 1, 2)))) {
this.renderers.add(new Cuboid(pos, SIZE, new Vector4f(0.5f, 0.5f, 1.0f, 1.0f)));
this.renderers.add(new Cube(pos.add(2, 1, 2), new Vector4f(0.5f, 0.5f, 1.0f, 1.0f)));
}
});
return result;
@@ -72,7 +72,7 @@ public class DungeonFinder extends BlockFinder {
.map(pos -> this.getFloorCalls(this.getDungeonSize(pos), pos)).collect(Collectors.toList());
result.forEach(pos -> {
if(SeedCracker.get().onDecoratorData(new DungeonData(this.chunkPos, biome, starts, floorCallsList))) {
if(SeedCracker.get().getDataStorage().addBaseData(new DungeonData(this.chunkPos, biome, starts, floorCallsList))) {
this.renderers.add(new Cube(pos, new Vector4f(1.0f, 0.0f, 0.0f, 1.0f)));
Vec3i size = this.getDungeonSize(pos);
this.renderers.add(new Cuboid(pos.subtract(size), pos.add(size).add(1, -1, 1), new Vector4f(1.0f, 0.0f, 0.0f, 1.0f)));
@@ -1,7 +1,6 @@
package kaptainwutax.seedcracker.finder.population;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.DecoratorCache;
import kaptainwutax.seedcracker.cracker.population.EndGatewayData;
import kaptainwutax.seedcracker.finder.BlockFinder;
import kaptainwutax.seedcracker.finder.Finder;
@@ -14,7 +13,6 @@ import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.decorator.Decorator;
import java.util.ArrayList;
import java.util.List;
@@ -35,10 +33,6 @@ public class EndGatewayFinder extends BlockFinder {
//If no end gateway is supposed to populate in this chunk, return.
Biome biome = this.world.getBiome(this.chunkPos.getCenterBlockPos().add(8, 0, 8));
if(DecoratorCache.get().getSalt(biome, Decorator.END_GATEWAY, false) == DecoratorCache.INVALID) {
return new ArrayList<>();
}
List<BlockPos> result = super.findInChunk();
List<BlockPos> newResult = new ArrayList<>();
@@ -48,7 +42,7 @@ public class EndGatewayFinder extends BlockFinder {
if(height >= 3 && height <= 9) {
newResult.add(pos);
if(SeedCracker.get().onDecoratorData(new EndGatewayData(this.chunkPos, biome, pos, height))) {
if(SeedCracker.get().getDataStorage().addBaseData(new EndGatewayData(this.chunkPos, biome, pos, height))) {
this.renderers.add(new Cuboid(pos.add(-1, -2, -1), pos.add(2, 3, 2), new Vector4f(0.4f, 0.4f, 0.82f, 1.0f)));
}
}
@@ -26,7 +26,7 @@ public class EndPillarsFinder extends Finder {
public EndPillarsFinder(World world, ChunkPos chunkPos) {
super(world, chunkPos);
this.alreadyFound = SeedCracker.get().onPillarData(null);
this.alreadyFound = !SeedCracker.get().getDataStorage().addPillarData(null);
if(this.alreadyFound)return;
for(int i = 0; i < this.bedrockMarkers.length; i++) {
@@ -48,7 +48,7 @@ public class EndPillarsFinder extends Finder {
if(result.size() == this.bedrockMarkers.length) {
PillarData pillarData = new PillarData(result.stream().map(Vec3i::getY).collect(Collectors.toList()));
if(SeedCracker.get().onPillarData(pillarData)){
if(SeedCracker.get().getDataStorage().addPillarData(pillarData)) {
result.forEach(pos -> this.renderers.add(new Cube(pos, new Vector4f(0.5f, 0.0f, 0.5f, 1.0f))));
}
@@ -1,7 +1,6 @@
package kaptainwutax.seedcracker.finder.population.ore;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.DecoratorCache;
import kaptainwutax.seedcracker.cracker.population.EmeraldOreData;
import kaptainwutax.seedcracker.finder.BlockFinder;
import kaptainwutax.seedcracker.finder.Finder;
@@ -13,7 +12,6 @@ import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.decorator.Decorator;
import java.util.ArrayList;
import java.util.List;
@@ -35,13 +33,9 @@ public class EmeraldOreFinder extends BlockFinder {
public List<BlockPos> findInChunk() {
Biome biome = this.world.getBiome(this.chunkPos.getCenterBlockPos().add(8, 0, 8));
if(DecoratorCache.get().getSalt(biome, Decorator.EMERALD_ORE, false) == DecoratorCache.INVALID) {
return new ArrayList<>();
}
List<BlockPos> result = super.findInChunk();
if(!result.isEmpty() && SeedCracker.get().onDecoratorData(new EmeraldOreData(this.chunkPos, biome, result))) {
if(!result.isEmpty() && SeedCracker.get().getDataStorage().addBaseData(new EmeraldOreData(this.chunkPos, biome, result))) {
//TODO: support more ores.
this.renderers.add(new Cube(result.get(0), new Vector4f(0.0f, 1.0f, 0.0f, 1.0f)));
}
@@ -72,7 +72,7 @@ public class BuriedTreasureFinder extends BlockFinder {
});
result.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureFeatures.BURIED_TREASURE))) {
if(SeedCracker.get().getDataStorage().addBaseData(new StructureData(this.chunkPos, StructureFeatures.BURIED_TREASURE))) {
this.renderers.add(new Cube(pos, new Vector4f(1.0f, 1.0f, 0.0f, 1.0f)));
}
});
@@ -36,7 +36,7 @@ public class DesertTempleFinder extends AbstractTempleFinder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if( SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureFeatures.DESERT_PYRAMID))) {
if( SeedCracker.get().getDataStorage().addBaseData(new StructureData(this.chunkPos, StructureFeatures.DESERT_PYRAMID))) {
this.renderers.add(new Cuboid(pos, pieceFinder.getLayout(), new Vector4f(1.0f, 0.0f, 1.0f, 1.0f)));
}
});
@@ -86,7 +86,7 @@ public class EndCityFinder extends Finder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureFeatures.END_CITY))) {
if(SeedCracker.get().getDataStorage().addBaseData(new StructureData(this.chunkPos, StructureFeatures.END_CITY))) {
this.renderers.add(new Cuboid(pos, pieceFinder.getLayout(), new Vector4f(0.6f, 0.0f, 0.6f, 1.0f)));
this.renderers.add(new Cube(pos, new Vector4f(0.6f, 0.0f, 0.6f, 1.0f)));
}
@@ -45,7 +45,7 @@ public class IglooFinder extends AbstractTempleFinder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureFeatures.IGLOO))) {
if(SeedCracker.get().getDataStorage().addBaseData(new StructureData(this.chunkPos, StructureFeatures.IGLOO))) {
this.renderers.add(new Cuboid(pos, pieceFinder.getLayout(), new Vector4f(0.0f, 1.0f, 1.0f, 1.0f)));
this.renderers.add(new Cube(pos, new Vector4f(0.0f, 1.0f, 1.0f, 1.0f)));
}
@@ -36,7 +36,7 @@ public class JungleTempleFinder extends AbstractTempleFinder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureFeatures.JUNGLE_TEMPLE))) {
if(SeedCracker.get().getDataStorage().addBaseData(new StructureData(this.chunkPos, StructureFeatures.JUNGLE_TEMPLE))) {
this.renderers.add(new Cuboid(pos, pieceFinder.getLayout(), new Vector4f(1.0f, 0.0f, 1.0f, 1.0f)));
}
});
@@ -62,7 +62,7 @@ public class MansionFinder extends Finder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureFeatures.WOODLAND_MANSION))) {
if(SeedCracker.get().getDataStorage().addBaseData(new StructureData(this.chunkPos, StructureFeatures.WOODLAND_MANSION))) {
this.renderers.add(new Cuboid(pos, pieceFinder.getLayout(), new Vector4f(0.4f, 0.26f, 0.13f, 1.0f)));
this.renderers.add(new Cube(this.chunkPos.getCenterBlockPos().add(0, pos.getY(), 0), new Vector4f(0.4f, 0.26f, 0.13f, 1.0f)));
}
@@ -58,7 +58,7 @@ public class OceanMonumentFinder extends Finder {
positions.forEach(pos -> {
ChunkPos monumentStart = new ChunkPos(this.chunkPos.x + 1, this.chunkPos.z + 1);
if(SeedCracker.get().onStructureData(new StructureData(monumentStart, StructureFeatures.OCEAN_MONUMENT))) {
if(SeedCracker.get().getDataStorage().addBaseData(new StructureData(monumentStart, StructureFeatures.OCEAN_MONUMENT))) {
this.renderers.add(new Cuboid(pos, pieceFinder.getLayout(), new Vector4f(0.0f, 0.0f, 1.0f, 1.0f)));
this.renderers.add(new Cube(monumentStart.getCenterBlockPos().add(0, pos.getY(), 0), new Vector4f(0.0f, 0.0f, 1.0f, 1.0f)));
}
@@ -175,7 +175,7 @@ public class ShipwreckFinder extends BlockFinder {
mutablePos.setOffset(-4, -chestY, -15);
if((mutablePos.getX() & 0xf) == 0 && (mutablePos.getZ() & 0xf) == 0) {
if(SeedCracker.get().onStructureData(new StructureData(new ChunkPos(mutablePos), StructureFeatures.SHIPWRECK))) {
if(SeedCracker.get().getDataStorage().addBaseData(new StructureData(new ChunkPos(mutablePos), StructureFeatures.SHIPWRECK))) {
this.renderers.add(new Cuboid(box, new Vector4f(1.0f, 0.0f, 1.0f, 1.0f)));
this.renderers.add(new Cube(new ChunkPos(mutablePos).getCenterBlockPos().offset(Direction.UP, mutablePos.getY()), new Vector4f(1.0f, 0.0f, 1.0f, 1.0f)));
return true;
@@ -37,7 +37,7 @@ public class SwampHutFinder extends AbstractTempleFinder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureFeatures.SWAMP_HUT))) {
if(SeedCracker.get().getDataStorage().addBaseData(new StructureData(this.chunkPos, StructureFeatures.SWAMP_HUT))) {
this.renderers.add(new Cuboid(pos, pieceFinder.getLayout(), new Vector4f(1.0f, 0.0f, 1.0f, 1.0f)));
}
});
@@ -14,7 +14,7 @@ public class PopulationReversal {
private static final LCG SKIP_4 = Rand.JAVA_LCG.combine(4);
public static ArrayList<Long> getWorldSeeds(long populationSeed, int x, int z) {
populationSeed = populationSeed ^ Rand.JAVA_LCG.multiplier;
populationSeed ^= Rand.JAVA_LCG.multiplier;
ArrayList<Long> worldSeeds = new ArrayList<>();
if (x == 0 && z == 0) {
@@ -105,7 +105,7 @@ public class PopulationReversal {
seed = r.nextLong() & ((1L << 48)-1);
x = r.nextInt(16) - 8;
z = r.nextInt(16) - 8;
seeds = getWorldSeeds(Seeds.setPopulationSeed(null, seed,x,z), x, z);
seeds = getWorldSeeds(Seeds.setPopulationSeed(null, seed, x, z) ^ Rand.JAVA_LCG.multiplier, x, z);
if (!seeds.contains(seed)) {
System.out.println(seed);
@@ -20,5 +20,4 @@ public class RandomSeed {
return new Rand(seed, false).nextLong() == worldSeed;
}
}
@@ -4,8 +4,8 @@ import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.CommandDispatcher;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.command.ClientCommands;
import kaptainwutax.seedcracker.cracker.storage.HashedSeedData;
import kaptainwutax.seedcracker.finder.FinderQueue;
import kaptainwutax.seedcracker.util.Log;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.ClientPlayNetworkHandler;
@@ -50,8 +50,7 @@ public abstract class ClientPlayNetworkHandlerMixin {
@Inject(method = "onPlayerRespawn", at = @At("HEAD"))
public void onPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo ci) {
SeedCracker.get().hashedWorldSeed = packet.method_22425();
Log.warn("Fetched hashed world seed [" + SeedCracker.get().hashedWorldSeed + "].");
SeedCracker.get().getDataStorage().addHashedSeedData(new HashedSeedData(packet.method_22425()));
}
}
@@ -1,6 +1,7 @@
package kaptainwutax.seedcracker.mixin;
import com.mojang.brigadier.StringReader;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.command.ClientCommands;
import net.minecraft.client.network.ClientPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
@@ -13,6 +14,11 @@ import java.util.regex.Pattern;
@Mixin(ClientPlayerEntity.class)
public abstract class ClientPlayerEntityMixin {
@Inject(method = "tick", at = @At("HEAD"))
private void tick(CallbackInfo ci) {
SeedCracker.get().getDataStorage().tick();
}
@Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true)
private void onSendChatMessage(String message, CallbackInfo ci) {
if(message.startsWith("/")) {
@@ -16,7 +16,7 @@ public abstract class ClientWorldMixin {
@Inject(method = "disconnect", at = @At("HEAD"))
private void disconnect(CallbackInfo ci) {
SeedCracker.get().clear();
SeedCracker.get().getDataStorage().clear();
FinderQueue.get().clear();
}